[toc]本篇暂时记录一些零散的ShaderLab笔记 渲染流水线包括可配置部分和可编程部分,本篇只包括可配置部分。 如需了解ShaderLab数学部分,参见ShaderLab数学。 可编程部分,也就是编辑vertex(顶点着色器)和fragment(片元做色器),参见ShaderLab函数。 在本篇中,包括了渲染流水线的各个可配置部分,如果有不足的地方可指出,谢谢。

参考文献

《Shader入门精要》 UnityShaderLab学习总结 Unity手册

为什么要学ShaderLab呢?

图形化工具制作shader虽然能快速实现效果,但是难以优化。 需要开发人员/技术美术有能力对Shader进行功能分析、效率评估、选择、优化、兼容、甚至是Debug。

渲染流水线解释

这里面包括很多个小阶段,有些是可配置的,有些是可编程的。 对应可配置的部分,需要了解这些流程都干了啥、可用的选项/开关的具体效果; 对应可编程的部分,需要了解对应的语法,也就是声明变量、计算、读写过程; 应用阶段 应用阶段是指几何阶段之前的阶段,CPU通过向GPU提交Draw Call(DC)来发起一次渲染; 加载所有数据到内存→网格和纹理到显存→设置RenderState→提交DC; 这些操作在Unity中通常都是Renderer组件或者CommandBuffer中完成的; 渲染相关的数据 相机相关数据:相机位置,V矩阵、P矩阵; 光源相关数据:光源位置,光源空间矩阵、环境光; 逐顶点数据:POSITION、NORMAL、TANGENT、TEXCOORD0~5、COLOR; 渲染状态:材质属性、贴图、shader变体、相关光源/Probe; 粗粒度剔除 提交DC之间,通过Renderer组件的Bounding box粗略判断是否在相机视椎体内; 不可见的物体不渲染; 在Animator、Particle System等组件中还有关于物体不可见时的逻辑设置; 描述一次DC 一次DC,绘制的内容最少到一个“后处理Quad”,最多到大量mesh; 这次DC所依赖的数据和渲染状态都在提交DC时设置好了,计算过程中不会改变; GPU很擅长做这种重复枯燥的任务,高并行运算,任务之间互相独立。 几何阶段 几何阶段用于描述一次DC的计算开始阶段,这个过程的计算是逐顶点的。 顶点着色器-可编程 Vertex Shader:每个顶点执行一次,无法获得顶点之间的联系,每个顶点独立计算并保存数据; 从这里开始,就需要接触到多种空间,用于3D计算: 模型空间(逐顶点数据)→世界空间→观察空间→裁剪空间–>(齐次去除); 齐次去除由后续硬件负责,得到归一化的设备坐标(NDC,Normalized Device Coordinates); 曲面细分着色器-可编程/可选 Tessellation Shader,细分图元,SM5.0以上。 通过对表面的细分,增加表面细节,用的少。 几何着色器-可编程/可选 Geometry Shader,可执行逐图元操作,或产生更多图元。 比如,实时计算生成一大片的草; 几何做色器编程产生的形状不能缓存,灵活应用的难度高。 裁剪-可配置 Clipping,NDC空间中判断顶点释放在视椎体内的过程被简化; 被边界阶段的面产生新的顶点。 屏幕映射-无法控制 Screen Mapping,将图元坐标转换到屏幕空间。 屏幕空间:xy分量为像素坐标,左下角为(0,0);z为深度值。 屏幕映射后决定了每个顶点对应屏幕上的哪个像素、深度。 NDC→屏幕空间。 光栅化阶段 光栅化阶段用于描述屏幕上每个像素被着色的过程,逐像素计算。 三角形设置-无法控制 Triangle Setup,通过三角面片的三个顶点计算出三角形边界的表示方式,为下个阶段做准备。 三角形遍历-无法控制 Triangle Traversal,检查逐像素是否被一个三角面片覆盖,被覆盖的像素生成一个片元。 片元:一个待定的像素,如果测试通过,片元的颜色将写入到目标; 逐片元数据:从逐顶点数据差值继承过来的数据、深度值; 片元着色器-可编程 Fragment Shader,使用逐片元数据计算逐片元的颜色。 逐片元操作-可配置 Per-Fragment Operations,输出合并阶段。 可见测试→透明度测试→(模板测试+深度测试)→混合操作→Mask→写入; 任务1:决定每个片元的可见性,也就是抛弃一些片元; 任务2:对通过了测试的片元与颜色缓冲区的颜色值进行混合操作; Fragment+Associated Data:逐片元数据; Pixel Ownership Test:如果当前窗口被其他窗口遮挡住了,就不会通过测试; Scissor Test:裁剪测试,超出视口的范围不会通过测试; Alpha Test:透明度测试,也就是clip、discard方法,用户在代码中可选调用; 在已过期的shader语言里会有渲染状态语法:AlphaTest 对比方法 测试值 Stencil Test:模板测试,提供逐像素的遮罩,用于标记/限制区域; Depth Test:深度测试,与深度RT中的深度值进行对比; Blending:片元再写入颜色时与旧颜色值之间的互动选项; Mask:可控制对哪些通道进行写入; RenderTexture(RT):GPU中表示一张图片、二维数组,可进行颜色读写; FrameBuffer:表示显示器的RT,将渲染结果提交到这里可完成一次DC; 关于Early Z Early Z表示Stencil Test和Depth Test两个操作,提前进行深度测试+写入; 提前的好处在于,可避免大量多余的frag计算; 如果用户在frag方法中使用了clip,就不会Early Z,效率下降; 因为Early Z如果通过,需要立刻写入深度缓冲/模板缓冲; 不能先写入深度缓冲/模板缓冲,再舍弃片元,这样会留下错误的深度数据; clip的作用是按条件舍弃片元,无法与深度测试进行互动。 作为硬件功能,Early Z无需开启,按照Queue渲染可最大化利用这个特性。 2021年3月6日注: Alpha

Properties

材质面板属性声明 在.shader文件中声明属性的意义是将属性暴露给材质面板。 所以即使不在Properties中声明,也可以通过代码为变量赋值。 Properties{ _Color (“Color”,Color)=(1,1,1,1) //变量名 标签 类型 初始值 _Vector(“Vector”,Vector)=(1,2,3,4) _int(“Int”,int)=100 _float(“Float”,float)=3.14 _Range(“Range”,Range(1,10))=6 _2D(“Texture”,2D)=”white”{} //2D纹理 _NormalMap(“NormalMap”,2D)=”bump”{} //法线贴图 _2DArray(“2DArray”,2DArray)=””{} //2D图集 _3D(“Texture”,3D)=”black”{} _Cube(“Cube”,Cube)=”_Skybox”{} //环境映射贴图 } CGPROGRAM属性声明 SubShader{Pass{CGPROGRAM float4 _Color; float4 _Vector; float _int; float _float; float _Range; sampler2D _2D; float4 _2D_ST; //xy分量为缩放 zw分量为平移 sampler2D _NormalMap; float4 _NormalMap_ST; //xy分量为缩放 zw分量为平移 UNITY_DECLARE_TEX2DARRAY(_2DArray); sampler3D _3D; samplerCUBE _Cube; ENDCG}} 浮点数精度: 在测试阶段建议使用float,优化阶段可以尝试使用half。

SubShader的Tags

指定shader对应的物体被渲染顺序和这个subshader的其他参数,先渲染的物体会被后来渲染的物体遮挡 语法:Tags { “Queue” = “Geometry+1” “RenderType” = “Opaque” } 指定标签名和对应的值,SubShader Tags不能用于pass tags。 Queue,指明渲染顺序。 RenderType,着色器替换功能中的标签。 DisableBatching,指明本subshader不参与批处理。 ForceNoShadowCasting,不投射阴影。 IgnoreProjector,不被投影,通常用于半透明物体。 CanUseSpriteAtlas PreviewType,指明材质预览使用的mesh。 Queue 指定该物体属于哪一个渲染顺序,默认有4个预定义的render queue,序列越小的先渲染。 1.Background/1000:最先渲染 2.Geometry(默认)/2000:适用于不透明物体。 3.AlphaTest/2450:低于指定alpha值的片元会被舍弃,造成了透明。 4.Transparent/3000:使用back-to-front顺序,可开启alpha-blended。 5.Overlay/4000:最后渲染,适用于覆盖效果。 注:Geometry+1相当于2001,可指定序列号用于控制渲染顺序 注:Geometry+500之前的序列是不透明队列,会通过优化渲染顺序提高性能。更高的序列是透明序列,先渲染最远的物体。 透明效果的实现 ①实现步骤 SubShader部分:Queue标签值为2500以上,建议使用”Transparent”。 使用此队列后,同序列值的不透明SubShader,先渲染最远的物体。 这个优先级是判断在物体层面,而不是片元级别的判断,在物体交错时依然会有片元遮挡。 Pass部分:ZTest的默认值是LEqual;ZWrite的默认值为On;Cull默认值为Back; Blend模式要主动设置,在设置的同时也会开启混合模式。 这里面会有多种排列组合,具体的设置可以参考Pass的Tags。 ②关闭深度写入的效果分析 ZWrite Off Cull Off Blend SrcAlpha OneMinusSrcAlpha 不再更新深度值,此时的深度值是不透明队列中的片元留下的记录。 所有同坐标的透明片元都会和这个不透明片元进行混合得到最终的颜色值。 玻璃有厚度,所以有2个面,在屏幕空间同一个坐标下会有2个片元待渲染,暂时不考虑其他透明物体的存在。 设靠近摄像机的片元颜色值为A(a,b,c,d),远离摄像机的片元颜色值为B(e,f,d,g),颜色缓冲为(f,i,j,1)。 先处理A时,最终颜色为:g(e,f,d,g)+(1-g)(d(a,b,c,d)+(1-d)(f,i,j,1)) 结果的r通道值为:eg+ad-adg+f-df-fg+dfg 先处理B时,最终颜色为:d(a,b,c,d)+(1-d)(g(e,f,d,g)+(1-g)(f,i,j,1)) 结果的r通道值为:ad+eg-deg+f-df-fg+dfg 两种处理顺序结果不一致,先处理B时相当于+adg后-edg,即增加B的影响同时减少A的影响。 结论:关闭深度写入难以得到完美的透明,深度测试也是必须的。 透明度混合时,未通过深度测试的片元不应被直接放弃,而是采用另一种公式弥补自己的贡献。 ③双Pass处理双面的效果分析 在上例中,我们分析的玻璃有2个面,我们是可以控制正面和反面渲染顺序的。例如: Pass1:Cull Front Zwrite Off Blend SrcAlpha OneMinusSrcAlpha Pass2:Cull Back Zwrite Off Blend SrcAlpha OneMinusSrcAlpha 这样我们就可以使点B先于A点进行混合。 ④更复杂的透明 假设有一个玻璃水杯,或者一个玻璃试管,此时在同一像素坐标下的透明片元会有4个。 假设有一个玻璃水缸里面盛满了蓝色水,斜着看水面的时候同一像素坐标下的透明片元会有5个。 RenderType 用于在运行时,通过代码控制场景中所有的已有材质中的shader的subshader(A),在符合:B中有相同Tag和对应值,且A中包含B所需的材质参数时,使用指定的shader中的subshader(B)。 public void RenderWithShader(Shader shader, string replacementTag); Unity中有内置的RenderType值,也可以用自定义的标签和值。 LOD Level of Detail,用于控制使用哪一个subshader,以控制细节级别。 约定中,在一个shader里可以写多个subshader,但是最终只会执行其中的一个。 这些subshader的LOD值越大,代表对应的subshader的性能越高。 在工程设置中定义LOD的最大值: Unity3D->Project Setting->QualitySettings中的Maximum LODLevel,0表示不进行判断,1表示LOD的最大值为100,2表示LOD的最大值为200,以此类推。此时大于设置值的subshader不会被编译。 脚本中设置全局的LOD的最大值: Shader.globalMaximumLOD = 100 脚本中设置单个shader的LOD的最大值 _shader.maximumLOD RenderState Render state set-up,用于描述Pass的意图。 SubShader下的渲染状态设置适用于后续的所有Pass。 也可以在Pass之间插入渲染状态设置。 Cull Cull Back Front Off 设置剔除模式:剔除背面(默认)/剔除正面/关闭剔除。 如果使用了Cull设置会覆盖掉其他设置;在向前渲染中可以使用多个ForwardBass Pass分别剔除前面和后面。 ZTest ZTest (Less Greater LEqual(默认) GEqual Equal NotEqual Always) 深度值是物体在世界空间中距离摄像机的远近。距离越近,深度值越小;距离越远,深度值越大。 默认值是LEqual,当片元的深度值小于等于深度缓冲值时,通过深度测试。 主动设置为Always,意味着片元总能通过ZTest。 ZWrite ZWrite On(默认) Off 设置对depth buffer的读写模式。 默认值是On,通过了ZTest的片元将更新depth buffer中的深度值。 主动设置为Off时,通过了ZTest的片元将不会更新depth buffer中的深度值。 Blend 参考Unity关于Blending的说明。 Blend Off 关闭混合(默认),直接使用片元的颜色值。 Blend SrcFactor DstFactor 最终颜色=SrcFactor*片元色值+DstFactor*颜色缓冲色值 Blend SrcFactor DstFactor, SrcFactorA DstFactorA 为RGB和Alpha通道使用不同的混合因子。 BlendOp XXX 混合操作,不指定时使用默认值Add。 BlendOp OpColor, OpAlpha,为RGB和Alpha通道使用不同BlendOp。 AlphaToMask On,Turns on alpha-to-coverage. When MSAA is used, alpha-to-coverage modifies multisample coverage mask proportionally to the pixel Shader result alpha value. This is typically used for less aliased outlines than regular alpha test; useful for vegetation and other alpha-tested Shaders.这个功能在大部分是全透明或不透明并且有很薄的“部分透明”区域的纹理上效果很好(grass, leaves and similar). 设置混合操作使用的公式,透明的片元的颜色与缓冲中已有的颜色进行计算得出半透明效果。 ShaderLab中的混合因子 One Zero SrcColor OneMinusSrcColor DstColor OneMinusDstColor SrcAlpha OneMinusSrcAlpha DstAlpha OneMinusDstAlpha ShaderLab中的BlendOp Add:默认值,不指定BlendOp时Blend操作的运算为:混合后的颜色相加。 Sub:指定Blend操作的运算为:混合后的源颜色-混合后的目标颜色。 RevSub:指定Blend操作的运算为:混合后的目标颜色-混合后的源颜色。 Min:指定Blend操作的运算为:逐分量取源颜色和目标颜色中的较小的值。 Max:指定Blend操作的运算为:逐分量取源颜色和目标颜色中的较大的值。 ColorMask RGBA0 参数表示该Pass将写入的颜色通道,默认值为RGBA,可以输入任意通道组合。 ColorMask 0 表示该Pass不写入任何颜色通道。 ColorMask RGB 3 表示在支持MRT的平台对RenderTarget #3输出RGB通道。 Offset Offset OffsetFactor, OffsetUnits Fallback SubShader外使用的标签;Fallback “Standard” 可在指定的.shader文件中引用LightMode为ShadowCaster的Pass。 Stencil Stencil { Ref [_StencilNo] ReadMask 33 WriteMask 33 Comp NotEqual Pass Keep Fail Keep } Stencil { Ref [_StencilNo] Comp Always Pass Replace Fail Replace } 读遮罩和写遮罩的作用: if(referenceValue&readMask comparisonFunction stencilBufferValue&readMask) 通过像素 else 抛弃像素 Ref值:0-255,默认为0. 对比方法(CompareFunction):Disabled/Never/Less/Equal/LessEqual/Greater/NotEqual/GreaterEqual/Always 成功/失败的操作(StencilOp):Keep/Zero/Replace/Invert/IncrementSaturate(自+1)/DecrementSaturate(自-1) IncrementWrap 自+1,若值为255则变为0(溢出) DecrementWrap 自-1,若值为0则变为255(溢出)

Pass的Tags

Pass(通道)是SubShader(子着色器)的组成部分,每个Pass代表一个完整的渲染流程。 Pass { [Name and Tags] [RenderSetup] } UsePass 引用其他shader文件中的指定Name值的Pass。 UsePass “MMD4Mecanim/MMDLit/FORWARD” 值中包括shader路径和pass的Name值,Name值需全大写。 GrabPass GrabPass{} 全屏截图,生成纹理sampler2D _GrabTexture传递给后面的Pass进行处理。 Name Name “FORWARD” 定义Name值后可以被其他shader通过UsePass命令调用 LightMode Tags { “LightMode”=” ForwardBase” } 如果没有指定LightMode,就无法获得光源参数,无法进行光照计算。 LightMode标签值为ForwardBase和ForwardAdd的Pass可以获得前向渲染路径的光照变量。 Always:默认值,不指定LightMode时使用此方案。总会被渲染,但不处理光照。 ForwardBase:计算环境光、最重要的逐像素光(必须是平行光、不能被设置为NOT important)、逐顶点/SH光源、Lightmaps。 有可能会存在多个ForwardBase的Pass,每个这样的Pass都执行一次。 ForwardAdd:计算额外的逐像素光源,每有一个像素光源就执行一次本Pass,额外像素光源数量可设置。 ShadowCaster:用于渲染产生阴影的物体,把物体的深度信息渲染到阴影映射纹理(shadowmap)或一张深度纹理中。 注:ForwardAdd与ForwardBase的不同之处 ①去掉了关于环境光、自发光的计算 ②去掉了ForwardBase中计算点光源和SH光源的光照计算 ③支持点光源、聚光灯的像素级别光照计算,可计算光衰减。 注:如何接受来自其他物体的阴影、向其他物体投射阴影 ①如果要接受来自其他物体的阴影,就需要在Pass中对阴影映射纹理/屏幕空间的阴影图进行采样,得到的阴影值作为diffuse光照的一个系数。 也就是使用”阴影三剑客”三个宏,分别完成定义属性、顶点着色器内的阴影偏移,片元着色器内的采样。 ②如果要向其他物体投射阴影,就需要在LightMode为ShadowCater的Pass中将物体加入到阴影映射纹理的计算中,如果使用了屏幕空间的投影映射(Screenspace Shadow Map)技术,这个Pass还会生成一张摄像机的深度图。 注:如何计算光照衰减 片元着色器中,将世界空间中的顶点转换到对应类型(Point/Spot)的光源空间,根据keyword判断应执行哪些采样代码。 RequireOptions 满足某些条件时才渲染该Pass,目前支持的选项:SoftVegetation。

CGPROGRAM

编译指令/声明函数

参见Unity手册 #pragma vertex name //将指定函数编译为顶点着色器 #pragma fragment name //将指定函数编译为片元做色器 #pragma geometry name //将指定函数便以为DX10 geometry shader,出现此选项时自动开启 #pragma target 4.0 指定shader编译目标级别,默认为2.5。 一些其他编译指令将自动提高target级别。编译目标级别越低适应性越好,2.0级别适用于全平台。 #pragma hull name #pragma domain name #pragma target name #pragma require feature #pragma require integers 2darray instancing #pragma only_renderers space separated names #pragma exclude_renderers space separated names #pragma multi_compile 变体:定义shader code中的一片区域为不同的版本。通过不同的预定义宏(#ifdef xx +内容 + #endif 实现一个关键字)多次编译实现。 定义规则:#pragma multi_compile SPHEREMAP_OFF SPHEREMAP_MUL 效果说明:产生2个shader variants,一个定义了SPHEREMAP_OFF,一个定义了SPHEREMAP_MUL。 在运行时,多个关键词只有一个会被激活,对应代码中#ifdef~#endif区域内的代码生效。 ①可以一次写2个以上的变体关键字,如: #pragma multi_compile SPHEREMAP_OFF SPHEREMAP_MUL SPHEREMAP_ADD ②当变体关键字为全下划线时,产生一个没有关键词的空宏,效果不变,如: #pragma multi_compile _ SPHEREMAP_MUL SPHEREMAP_ADD 默认激活规则: ①第一个被defined的关键词优先激活; ②当没有关键词被defined或者全部关键词都被defined时,使用第一个关键词。 ③当第一个关键词是由全下划线定义的空宏,其默认undefined状态。 主动激活规则: ①在运行时,通过C#代码控制单个材质(Material.EnableKeyword/DisableKeyword)或全局设置关键字(Shader.EnableKeyword and DisableKeyword)。 ②在材质Debug面板中,填写要激活的Shader Keywords。 观测方法: 在Frame Debugger中找到 Draw Mesh XXX,事件中注明了使用的Keywords。 #pragma shader_feature#pragma multi_compile的区别是:没有被用到的shader_feature变体在build时不会被包括进去。shader_feature主要在具体材质上设置关键字,multi_compile主要在代码全局设置。 #pragma shader_feature FANCY_STUFF 等效于 #pragma shader_feature _ FANCY_STUFF shader_feature关联材质面板MaterialPropertyDrawer 单选: [Toggle] _Invert ("Invert color?", Float) = 0 材质面板出现一个Invert color?的单选项,_Invert对应的关键字为_INVERT_ON。 关键字使用示例,这里判断语句用if/ifdef/if defined皆可。

1
2
3
#if _INVERT_ON
col = 1 - col;
#endif

[Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0 这里_Fancy对应的关键字被指定为ENABLE_FANCY。 [Space(50)] 装饰性语句。 [Header(Title)] + 换行:粗体标题 [MainTexture] [Normal] 法线贴图 [MainColor] [NoScaleOffset] + 贴图声明 这样面板上不会显示UV偏移 [HideInInspector] 不显示在面板上 通常用于程序化赋值的材质 [HDR] HDR相当于对普通颜色乘以一个系数,颜色分量可以超过1。 [NonModifiableTextureData] 不允许在Inspector编辑这个贴图属性 多选浮点值

1
2
3
4
5
[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend Mode", Float) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend Mode", Float) = 1
[Enum(Off, 0, On, 1)] _ZWrite ("ZWrite", Float) = 0
[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 0
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 1

可指定enum类型名称,最好注明其namespace。可指定最多7个name/值对为一组enum,比如Off和0是一对。 关键字Enum [KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0 为multi_compile提供的多项选,可切换关键字。需要定义关键字: #pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY 非线性滑条 [PowerSlider(3.0)] _Shininess ("Shininess", Range (0.01, 1)) = 0.08 滑条居中时,对应的值低于一半。 关键词限制:Unity中只能定义256个关键字,内置的已经用掉了60多个,而关键字限制是整个项目所有shader共享的,所以不应过多的定义关键字。 内置的multi_compile关键字:通常用于处理不同渲染路径中的 light、shadow、lightmap类型。 multi_compile_fwdbase:编译所有ForwardBase pass需要的变体。这些变体处理不同的lightmap和主平行光(开启/关闭阴影)。 #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight 如果不需要任何lightmap,可以跳过这些变体 multi_compile_fwdadd:编译所有ForwardAdd pass需要的变体。这些变体处理直线、锥形、点像素光源,和cookie textures。 multi_compile_fwdadd_fullshadows:和上一个一样,但还能处理实时阴影。 multi_compile_fog:控制数个不同fog类型(off/linear/exp/exp2)。 手动跳过指定shader变体:如果你知道你不会用到一些变体,可以手动剔除他们。 #pragma skip_variants POINT POINT_COOKIE Shader Hardware Variants:用于创建fallback或在单平台上同时适应于高端和低端机型。 #pragma hardware_tier_variants renderer 其中renderer为可用的渲染平台。将产生3个shader变体,分别带有UNITY_HARDWARE_TIER1/UNITY_HARDWARE_TIER1/UNITY_HARDWARE_TIER1关键字。可用于条件性fallback或针对不同终端的特性。使用Graphics Emulation可以测试不同tier的性能。 最终完全相同的的shader变体不会占用额外空间(不确定是否依然占用编译时间)。 Unity在加载时自带检测GPU并设置tier值,默认最高tier,可通过Shader.globalShaderHardwareTier设置,必须在shader被加载前设置,可在预加载场景设置。 硬件tier级别设置与Quality设置无关,只与GPU能力有关。 Platform Shader Setting:针对不同平台的不同tier级别,可以自主修改内置定义,比如你想强制在移动平台上使用联机阴影(cascaded shadowmaps 使近处的阴影更圆润)。参考PlatformShaderSettings来override一些能收支持特性的不同tier级别。参考USetShaderSettingsForPlatform来修改不同平台不同tier级别的设置。 优化Shader加载时间官方博客剥离shader变体Shader变体的打包与加载: 打包时,输出到目标平台包中的shader变体会变成文件形式。运行时使用的shader会加载到内存。 会被打包的shader变体: multi_compile中使用关键字定义的shader变体。 Graphics中设置的Always Included Shaders。 被使用了的shader_feature关键字定义的shader变体。 被使用了的Fog(雾效)或Lightmap的shader变体。 Build Assetbundle时,multi_compile定义和被使用到的shader变体。 运行时也可以编译shader为文件形式(解析)和加载到内存,可能造成卡顿。 #pragma enable_d3d11_debug_symbols #pragma hardware_tier_variants renderer name #pragma hlslcc_bytecode_disassembly #pragma disable_fastmath #pragma glsl_es2 其他命令从Unity5.0开始无效,如: #pragma glsl, #pragma glsl_no_auto_normalization, #pragma profileoption, #pragma fragmentoption. 必须是在主shader文件中的编译指令(#pragma)才能生效 不包括include文件中的编译指令,所以.cginc文件中不应包括#pragma内容。

include包含指令

常见指令

1
2
3
4
5
#include "UnityCG.cginc"
#include "UnityShaderVariables.cginc"
#include "HLSLSupport.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"

include部分在CGPROGRAM/ENDCG范围内,位置随意。因为从上至下编译,越底层的文件应放在上面。 包含后可以使用文件内提供的预定义宏(空宏、变量宏、代码块,作用类似于无脑复制)、变量、结构体、函数。 shader编译时,遇到了没有声明的变量时会立即报错,需要使用的变量应提前声明。 当主shader文件与.cginc文件中出现了已定义的同名函数时会报错。 shader主动在从上到下的“已知范围内”寻找指定的代码,遇到依赖时更换寻找目标。 当主shader中使用了用不到的函数式,会自动无视。 # if defined (XXX) defined (XXX) define XXX endif 在.cginc文件中经常出现这样的语法。判断已读区域中是否已定义括号里的内容,如已定义则向已阅读区域增加内容。 shader编译顺序举例:

1
2
3
4
5
6
7
8
Pass {
CGPROGRAM
#include "UnityCG.cginc"
#include "test.cginc"
#pragma vertex vert
#pragma fragment frag
ENDCG
}

shader先阅读UnityCG.cginc后再阅读test.cginc。test.cginc中包含自定义的代码。 如果先阅读test.cginc,就会出现报错,因为声明的结构体中包含未知的定义。 读到#pragma vertex vert时:程序主动在“已知范围”内寻找vert函数,这里的“已知范围”包括2个.cginc文件内容。当在”已知范围”内没有找到需要的函数时,才在#pragma vertex vert下方继续寻找。 只有主动寻找的代码块会参与编译,最终代码只与#pragma声明有关。 使用预定义宏的意义在于代码的复用。

贴图设置

表面颜色由镜面反射+漫反射决定 albedo:反照率,可用于绘制灰度图,物体吸收光的比率越小,反照率越高,物体越亮。 diffuse:漫反射系数,提供红色、绿色、蓝色分量的比例。 实际在不同工作流中,两者很可能是一样的。

纹理类型

默认为Texture。 base color:reflected diffuse color for dielectrics + reflectance for raw metal roughness:粗糙度贴图 Metallic:金属贴图,灰度图,非0即1,白色区域表示金属。 diffuse:diffuse color glossiness:平滑度贴图 specular:高光贴图 ambient occlusion:环境光遮蔽,AO normal:法线贴图,使用了a和g通道, height:高度贴图 cubemap:环境映射 RampTex:渐变纹理 MaskTex:遮罩纹理→高光遮罩 lightmap:光照贴图,用于预计算场景中物体表面的brightness,存储到图表或“light map”备用。 Unity使用Progressive Lightmapper系统烘焙场景中的lightmap,综合mesh、material、texture、light。光照贴图是渲染的一部分,GameObject在有lightmap时,会自动使用它。相关设置可参看Global Illumination。 光照烘焙准备: mesh需在Import settings中开启Generate Lightmap UVs选项。 在Window-Rendering-Lighting Settings-Scene-Lightmapping Settings-Lightmap Resolution指定光照贴图分辨率。 在Scene界面的Baked Lightmap模式中,1个格子表示一个像素。 需要光照烘焙的GameObject应被设置为Lightmap Static,可用Scale In Lightmap属性单独设置光照贴图分辨率。 在Light Explorer界面可全局管理场景中的光照设置。 生成光照贴图:Lightting Settings-Scene-Generate Lighting按钮,或者勾选自动生成。 查看已烘焙好的光照贴图:Scene界面旁边的2个页面。

Alpha Source

指定透明通道的值如何生成 None:A通道值固定为1; Input Texture Alpha:判断图片格式中是否存在A通道,尝试读取数据。 From Gray Scale:指定A通道由RGB通道的intensity与计算生成。 Alphpa Is Transparency:将完全透明区域的RGB通道数据删除

Wrap Mode

指定纹理坐标超过[0,1]后的平铺模式。 Repeat:舍弃整数部分 Clamp:取边界值

Filter Mode

滤波模式,影响纹理在放大或缩小(倾斜)时得到的图片效果 Point模式:放大或缩小时,采样一个像素 Bilinear模式:放大或缩小时,对目标像素和其临近的4个像素进行差值,有模糊效果。 Trilnear模式:和Bilinear类似,额外在多级渐远纹理之间进行混合。 Generate Mip Maps:多级渐远纹理技术,消耗更多的内存空间,生成多级渐远纹理,提高实时运行时采样效率。

纹理的最大尺寸与纹理模式

纹理的长宽应为2的幂。 纹理分辨率超过Max Texture Size时,会被Unity缩放至这个最大分辨率。