UNITY SHADER入门精要_学习笔记

UNITY SHADER入门精要 看了将近三个月,虽然去年因为有点事情暂停了看书计划,今年在业余时间一直看,终于看完了。大致了解了shader的渲染,但是写shader还是。。。。。。需要多看大佬的shader代码,学习更多的书籍。

ch2_0.渲染流水线:

应用阶段->几何阶段->光栅化阶段

应用阶段:输出渲染图元

几何阶段:处理所有和我们要绘制的几何相关的事情, 将顶点坐标变换到屏幕坐标

光栅化阶段:决定每个渲染图元中的那些像素要绘制在屏幕上

ch2_1.注意OpenGL和DirectX的坐标轴:

ch3_0.部分专业名词介绍

#pragma surface surfaceFunction lightModel[optionalparams]

声称着色器 表面着色器 着色器代码名称 光照模型

It uses #pragma surface ... directive to indicate it’s a surface shader.

eg:#pragma surface surf Standard fullforwardshadows

介绍lightModel

1)Standard:

This lighting model uses SurfaceOutputStandard output struct,

and matches the Standard (metallic workflow) shader in Unity.

2)Standard:

This Specular lighting model uses SurfaceOutputStandardSpecular output struct,

and matches the Standard (specular setup) shader in Unity.

3).Lambert and BlinnPhong:郎伯模型

This lighting models are not physically based (coming from Unity 4.x),

but the shaders using them can be faster to render on low-end hardware.

3.Unity shader类型

1)Standard Surface Shader:标准光照模型

2)Standard Surface Shader(instanced)

3)Unlit Shader:不包含光照,包含雾效

4)Image Effect Shader:实现屏幕后处理效果

5)Compute Shader:产生一个特殊的shader文件,目的是利用cpu的并行性进行一些与常规渲染流水线无关·

ch3_1.Unity Shader语法

查API:https://docs.unity3d.com/Manual/SL-CullAndDepth.html

SubShader{

Tags {}

Log 200

Pass{

.........

Cull off

.......

}

UsePass :Pass的名字时候一定要大写,

ch3_1.自定义材质编辑界面:https://docs.unity3d.com/Manual/SL-CustomShaderGUI.html

ch4_0.unity内置的变化矩阵:

举例:把顶点从观察空间变换到模型空间

ch4_1.摄像机和屏幕参数

ch4_2.CG是行优先,一行一行的填充矩阵。Unity Shader使用的是行类型,但是有一种矩阵类型Matrix4x4是列优先。

ch4_3.矩阵变换:约定俗成 先缩放,在旋转,最后平移

ch4_4.渲染流水线顶点的空间变化过程:

ch5_0.ShaderLab属性和CG变量匹配的关系:

ch5_1:常用语义:常用,经常看

ch5_2:关于shader调试

1)用unity自带的FrameDebug:windows->Frame Debug

2)用vs的Graphics Debugger: http://blog.sina.com.cn/s/blog_12d58e52d0102x4qk.html

ch5_3:注意平台差异

当开启抗锯齿后,需要处理DirectX下的图片翻转。

ch6_0:常用的函数

ch6_1:标准光照模型

1)环境光:C(ambient) = g(ambient)

2)自发光:C(emissive) = M(emissive)

3)漫反射:

a.兰伯特定律 :C(diffuse) = (C(light) * M(diffuse)) * max(0, n* I );

n:表面法线 I:指向光源的单位矢量 m(diffuse):材质的漫反射颜色 C(light):光源颜色

b.半兰伯特模型:C(diffuse) = (C(light) * M(diffuse)) * (α(n * I) + β)

大多数情况下:α β 都是0.5

3)高光:

a.Phong模型: C(specular) = (C(light)*M(specular)) * max(0, v * r) ^ m(gloss)

C(light):光源颜色和强度 m(gloss):光泽度 m(specular):材质的高光反射颜色

V:视角方向 r:反射方向

b.Blinn-Phong模型:

n:法线方向

h矢量:定义为

ch7_1:unity中使用纹理名_ST 获取某个纹理的属性,ST(Scale和translation的缩写)

纹理名_ST.xy:获取缩放值

纹理名_ST.zw:获取偏移量

法线纹理和切线方向映射:

ch8:透明效果

1)大部分游戏引擎用的渲染顺序:

缺点:无法解决多个物体重叠

优点:容易实现足够有效

避免缺点:模型尽量凸面体,复杂的模型拆分独立排序的子物体

2)Queue标签:

3)Blend命令:

混合要求:使用blend命令 并且设置混合因子DstFactor

混合后的颜色:

SrcFactor(设为SrcAlpha),DstFactor(设为OneMinusSrcAlpha)

3)混合透明:

Albedo:我们通常理解的对光源的反射率。它是通过在Fragment Shader中计算颜色叠加时,和一些变量(如vertex lights)相乘后,叠加到最后的颜色上的。

4)渲染命令:ColorMask RGB | A | 0 (0-不写入任何颜色通道,即不会输出任何颜色)

5)混合命令:

Src:SourceColor 源颜色

Dst:Destination Color 目标颜色

O:输出颜色

eg: Blend SrcAlpha OneMinusSrcAlpha, One Zero

输出颜色的透明度值就是源颜色的透明度

仔细看之前的公式:

O (a) = SrcAlpha * One + OneMinusSrcAlpha * Zero

ch9 更复杂的光照

1)LightMode标签:

(1_1)前向渲染:

ForwardBase:

Tags{"LightMode"="ForwardBase"}

#pragam multi_compile_fwdbase (Bass Pass)

ForwardAdd:

Tags{"LightMode"="ForwardAdd"}

#pragam multi_compile_fwdadd (Additional Pass)

前向渲染可以使用的内置光照变量:

前向渲染可以使用的内置光照函数:

(1_2)顶点照明渲染路径:只是用逐顶点方法计算光照,属于前向渲染的子集。运行快,效果差,基本不用。

(1_3)延迟渲染路径:解决前向渲染的瓶颈,例如同一区域多个光源影响区域覆盖的问题

2)光照衰减:unity中使用一张纹理作为查找表来在片元着色器中逐像素光照衰减。

(用于计算点光源和聚光灯,平行光无衰减)

_LightTexture0: 没使用cookie

_LightTextureB0:使用了cookie

eg:_LightTexture0(x,y)表明了光源空间中不同位置的点的衰减值

(0,0)与光源位置重合点的衰减值

(1,1) 光源空间中最远的点的衰减

_LightMatrix0:顶点从世界空间变换到光源空间

dot(lightCoord, lightCoord) 算出来的是平方值,.rr是取r的属性

UNITY_ATTEN_CHANNEL 得到衰减纹理中衰减值所在的分量

eg:光源的线性衰减

关于聚光灯和点光源计算公式推导:

https://github.com/candycat1992/Unity_Shaders_Book/issues/47

3)阴影:注意光源要开启shadow,物体的castShadows属性也要开启

SHADOW_COORDS:声明了_ShadowCoord的阴影纹理坐标变量

TRANSFER_SHADOW(v2f :在顶点着色器中计算声明的阴影纹理坐标

SHADOW_ATTENUATION:计算阴影值

使用时必须注意:

a2f结构体顶点坐标变量名必须为vertex

顶点着色器的输出结构体v2f必须命名为v

v2f顶点坐标变量必须命名为pos

ch10:高级纹理

1)立方体纹理:

unity官方推荐使用第一种:第一种可以压缩纹理数据,支持边缘修正,光滑反射,HDR等功能

2)反射,折射,菲涅尔反射:

折射公式:

3)渲染纹理(Render Texture):

4)程序纹理:

Texture2D.SetPixel() 写好要显示的像素

Texture2D.Apply()

ch11:让画面动起来

1)帧动画:

uv是0-1之间,所以 time感觉是距离,_Time.y是一个增量,time与之不断增加,time / _HorizontalAmount 获取当前是第几行, row*_VerticalAmount是这几行走过的距离,time-该距离,就等于水平方向的长度

2)动画阴影:配置这几处,顶点跟着计算就可以了

ch12 屏幕后处理效果

1)抓取屏幕函数:

2)关于亮度,饱和度,对比度计算?

摘抄:https://www.cnblogs.com/lancidie/p/8780514.html

参考网址https://wenku.baidu.com/view/2413b07131126edb6f1a105b.html

最简单的是亮度,我们可以直接在采样texture后乘以一个系数,达到放大或者缩小rgb值的目的,这样就可以调整亮度了。

其次是饱和度,饱和度是离灰度偏离越大,饱和度越大,我们首先可以计算一下同等亮度条件下饱和度最低的值,根据公式:gray = 0.2125 * r + 0.7154 * g + 0.0721 * b即可求出该值(公式应该是一个经验公式),然后我们使用该值和原始图像之间用一个系数进行差值,即可达到调整饱和度的目的。

最后是对比度,对比度表示颜色差异越大对比度越强,当颜色为纯灰色,也就是(0.5,0.5,0.5)时,对比度最小,我们通过在对比度最小的图像和原始图像通过系数差值,达到调整对比度的目的。

3)边缘检测:

xxx_TexelSize 是untiy提供的访问xxx纹理对应的每个纹素的大小

Sobel:书上有错

Gx Gy

-1 0 1 -1 -2 -1

-2 0 2 0 0 0

-1 0 1 1 2 1

4)高斯方程:

ch13 使用深度和法线文理

1)获取深度和法线纹理:

camera.depthTextureMode = DepthTextureMode.Depth

camera.depthTextureMode |= DepthTextureMode.DepthNormal

对深度纹理采样:(不用tex2D是因为平台差异)

float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);

NDC下的z分量:

根据公式: d = 0.5*Z(ndc) + 0.5

在unity视角空间,z值均为负值,所以要取反, Z'(visw) = 1 / ( (Near-Far)*d/(Near*Far) + 1 / Near )

d的范围要在[0,1]之间,所以除以Far, Z(01) = 1/ ( (Near-Far)* d / Near + Far/Near)

Unity提供的内置函数:

LinearEyeDepth:负责把深度纹理的采样结果转换到世界空间下的深度值,即Z'zisw

Linear01Depth:返回一个范围[0,1]的线性深度值,即 Z(01)

_ZBufferParams:得到远近裁剪平面的距离

2)雾化:

快速从深度纹理重建世界坐标:

世界坐标:

_WorldSpaceCameraPos:是摄像机在世界空间下的位置

linearDepth * interpolatedRay:得到该像素相对摄像机的偏移量

interpolatedRay:由顶点着色器输出并插值后得到的射线

此图理解:dist =( |TL| / |Near| ) * depth

雾的计算: 雾效系数f

关于计算方式:

ch14 非真实渲染

1)处理高光平滑:

ch15 使用噪音

1)消融原理:噪音纹理+透明度测试

2)水波效果:噪音纹理作为一个高度图,不断修改水面的法线方向。为了模拟水不断流动的效果,我们会使用和时间相关的变量来对噪音纹理进行采样,当得到法线信息后,再进行正常的反射+折射计算,得到水面波动效果。

ch16 Unity中的渲染技术优化

1)优化技术:

CPU优化:批处理技术减少drawcall

GPU优化:

减少需要处理的顶点数量-优化几何体,使用模型的LOD技术,使用遮挡剔除技术

减少需要处理的片元数量-控制绘制顺序,警惕透明物体,减少实时光照

减少计算复杂度-使用shader的LOD技术,代码优化

节省内存带宽-减少纹理大小,使用分辨率缩放

影响游戏性能的瓶颈:

CPU:过多的drawCall,复杂的脚本或者物理模拟

GPU:顶点处理-过多的顶点,过多的逐顶点计算;片元处理-过多的片元,过多的逐片元计算

带宽:使用了尺寸大未压缩的纹理;分辨率过高的帧缓存

2)渲染分析工具:

unity的:渲染统计窗口,性能分析器

Android:Adreno GPU Profiler分析工具,NVPerfHUD

IOS:PowerVRAM的PVRUniSCo shader分析器,XCode的OpenGL ES Driver Instruments

https://docs.unity3d.com/Manual/MobileProfiling.html

3)批处理(batching):

静态批处理:自由度很高,限制少,但是会占用更多的内存,静态批处理后的物体不能移动

动态批处理:unity自动完成,物体可以移动,但是限制多,容易导致unity无法动态批处理一些使用了相同材质单物体。

4)批处理的注意事项:

a。尽可能用静态批处理,但注意内存消耗以及物体不可移动

b。用动态批处理,注意条件限制,物体尽量少顶点属性和数目

c。小道具,例如拾取的金币可以用动态批处理

d。包含动画的物体,无法全部静态批处理,但是可以把不动的部分标识城static。

e。批处理需要多个模型变换到世界空间下合并他们,因此,shader存在模型空间下的坐标运算,可能会出错。一个解决方法:DisbleBatching。使用半透明的物体需要严格的从后往前渲染,对于这些物体,unity会保证绘制顺序,然后在批处理。即:当绘制顺序无法满足是,批处理无法被执行。

5)减少顶点数目:移除不必要的硬边和纹理衔接(避免边界平滑和纹理分离),LOD技术,遮挡剔除技术。

减少片元数量:减少overdraw(同一个像素被绘制了多次):控制绘制顺序,警惕透明物体,减少实时光照和阴影

6)节省贷款:减少纹理大小-长宽值最好2的整数幂,分辨率缩放

7)减少计算复杂度:LOD技术-控制使用的shader等级,代码方面-用低精度的浮点数、数据类型少转换、不要分支和循环语句、避免使用sin\tan\pow\log等复杂的数学运算(用查找表代替),不要使用discard。

float、highp :存储例如顶点坐标等变量

half、mediump:标量、纹理坐标等变量

fixed、lowp:绝大多数颜色变量和归一化方向矢量,它的计算速度大约是float的4倍

ch17:Unity的表面着色器探索

1)表面着色器:定义了模型表面的反射率、法线、高光。

光照模型:选择使用兰伯特还是Blinn-Phong模型。

光照着色器:计算光照衰减、阴影等。

2)表面函数:

https://docs.unity3d.com/Manual/SL-SurfaceShaders.html

void surfI(Input IN, inout SurfaceOutput o)

void surfI(Input IN, inout SurfaceOutputStandard o)

void surfI(Input IN, inout SurfaceOutputStandardSpecular o)

struct SurfaceOutput{ fixed3 Albedo; //对光源的反射率,通常由纹理采样和颜色属性的乘积计算而得 fixed3 Normal; // 表面法线方向

fixed3 Emission;//自发光 eg:c.rgb += o.Emission;(unity通常会在片元着色器最后输出前计算) half Specular; // specular power in 0..1 range 高光反射中的指数部分的系数 fixed Gloss; // specular intensity 高光反射中的强度系数 fixed Alpha; // alpha for transparencies 透明通道,开启了透明度的话,会使用该值进行颜色混合};

struct SurfaceOutputStandard{ fixed3 Albedo; // base (diffuse or specular) color fixed3 Normal; // tangent space normal, if written half3 Emission; half Metallic; // 0=non-metal, 1=metal half Smoothness; // 0=rough, 1=smooth half Occlusion; // occlusion (default 1) fixed Alpha; // alpha for transparencies};struct SurfaceOutputStandardSpecular{ fixed3 Albedo; // diffuse color fixed3 Specular; // specular color fixed3 Normal; // tangent space normal, if written half3 Emission; half Smoothness; // 0=rough, 1=smooth half Occlusion; // occlusion (default 1) fixed Alpha; // alpha for transparencies};

Input结构体属性:

注意:主纹理和法线纹理的采样坐标uv_MainTex和uv_BumpMap,必须以“uv”为前缀。只要在input里面声明就可以使用,不需要计算。例外:自定义了顶点修改函数,需要传递数据。

光照函数:

#pragma surface surf Lambert

#pragma surface surf BlinnPhong

3)编译指令:自己看官网吧

https://docs.unity3d.com/Manual/SL-Properties.html

ch18_基于物理的渲染(PBS)

1)辐射率:单位面积、单位方向上光源的辐射通量,通常用L来表示,被认为是对单一光线的亮度和颜色评估。

BRDF(双向反射分布函数):f(l,v),l为入射方向,v为观察方向

反射等式:给定观察视角V,该方向上的出辐射率LO(V)等于所有入射方向的辐射率积分乘以BRDF值f(I,V),乘以余弦值(N * I)

精准光源:大小为无限小,方向确定的光源,如点光源,聚光灯

精准光源的出射辐射率: Ic:方向 C(light):颜色

2)高光反射:

微面元理论:物体表面实际上有很多人眼看不到的微面元组成的。

菲涅尔效应:https://baike.baidu.com/item/%E8%8F%B2%E6%B6%85%E5%B0%94%E6%8A%98%E5%B0%84%E7%8E%87/2712906?fr=aladdin

例如:我们站在湖边的时候,低头看脚下的水,水是透明的,反射不是特别强烈;远处的湖面,你会发现水并不是透明的,并且反射非常强烈。这就是“菲涅尔效应”。 

简单的讲,就是视线垂直于表面时,反射较弱,而当视线非垂直表面时,夹角越小,反射越明显。如果你看向一个圆球,那圆球中心的反射较弱,靠近边缘较强。不过这种过度关系被折射率影响。

D(h):法线分布函数,用于计算有多少比例的微面元的法线满足m==h,只有这部分微面元才会把光线从I方向反射到v上。

G(I,V,h):阴影-遮掩函数:用于计算满足满足m==h的微面元中有多少由于被遮挡而不会被人眼看到,给出了活跃的微面元所占的浓度。

F(I,h):菲涅尔反射函数,每个活跃的微面元会把多少入射光线反射到观察方向上,即反射光线占入射光线的比率。

4(n*I)(n*v):用于校正从微面元的局部空间到整体宏观表面数量差异的校正因子。

3)unity5实现的2中PBS模型:基于GGX模型,基于归一化的Blinn-Phong模型

GGX模型:http://www.taikr.com/article/2796 GGX具有更亮的高光部分并在其周围蔓延着光晕,给人以更加真实的视觉效果。

unity使用的BRDF的漫反射项:

D(h)采用了GGX模型的一种实现: a =roughness 的平方

G(I,V,h)使用了GGX衍生出的Smith-Schlick模型: k = roughness的平方 / 2

F(I,h):使用了Schlick菲涅尔近似等式, F0表示高光反射系数,在unity往往指的是高光反射颜色。

这些公式看的我很晕,只要记得这个是由前人根据效果,总结出来的就行了。

4)Unity支持两种基于物理的工作流程:金属工作流和高光反射工作流 。

金属工作流是默认的工作流程,这个名字来源与它定义了材质表面的金属值,并非它只能模拟金属类型的材质。

下图介绍了:builtin_shaders-5.x\CGIncludes里面定义的一些头文件

金属材质:几乎没有漫反射,强烈的高光反射,高光反射带颜色

非金属材质:大多数角度高光反射的强度比较弱,高光反射的颜色比较单一,漫反射的颜色多种多样

5)使用基于物理的渲染方法:线性空间 和伽马校正

https://blog.csdn.net/bill2ccssddnn/article/details/53423410

伽马:来源于伽马权限,最早用于处理拍摄的图像。原因:在正常的光照条件下,人眼对于较暗区域的变化更加敏感,人眼对于亮度上的变化感知是非线性的。

通常在显示设备使用的颜色缓冲中每个通道的精度为8位,为了用更多的空间存储暗部区域,这样存储空间可以充分利用起来了。

一个巧合:CRT显示的伽马值大约就是编码伽马的倒数。

绝大数的摄像机、PC和打印机都是用sRGB标准。

(编码伽马和显示伽马)

Unity的线性空间并不支持所有平台。例如:移动平台无法使用,必须自己在shader中进行伽马校正。

float3 difuseCol = pow(tex2D(diffTex, texCoord), 2.2); //非线性输入纹理的矫正代码

fragColor.rgb = pow(fragColor.rgb, 1.0/2.2); //最后输出前,对输出像素值的矫正代码处理

6)预计算实时全局光照:动态为场景实时更新复杂的光照结果。一旦物体和光源的位置被固定了,这些物体对光线的反弹路径以及漫反射光照也是固定的,与摄像机位置无关。在实时运行时,只要光源的位置不变,即使改变了光源颜色和强度、物体材质属性(漫反射和自发光相关的属性),这些信息一直是有效的,不需要实时更新。在预计算阶段,Enlighten会吧所有静态物体组成的场景上,进行简单的“光线追踪”过程。它会把场景分割成很多个子系统,目的是为了得到物体之间的关系。实时阶段,unity会根据预计算得到的信息来计算光照信息,并把它们存储在额外的光照纹、光照探针或Cubemap中,再和物体材质进行必要的光照计算,得到最后的渲染效果。

7)HDR:高动态范围。使用这个,可以获取到更多的高亮度区域。HDR使用远远高于8位的精度记录亮度信息。

动态范围:最高的和最低的亮度值之间的比值。

8)PBS的优点:只需要一个万能的shader就可以渲染相当一部分类型的材质,而不是使用传统的做法为每种材质编写一个特定的shader。

结束语:

不知不觉这本书已经看完了,大概明白了shader的语法。但是很多关键字不明白,很多陌生的API,看来渲染学习之路很长,还需要多看几遍。。。。。。。

猜你喜欢

转载自blog.csdn.net/xj1009420846/article/details/69524045