Unity内置的包含文件
appdata_full?
通过在 Cg/HLSL代码块中,使用
#include指令来完成包含。
CGPROGRAM
// ...
#include"UnityCG.cginc"
// ...
ENDCGUnity 中着色器include 文件 采用 .cginc
拓展名, 内置的着色器 include文件包括:
- HLSLSupport.cginc -_(自动包含)_用于跨平台着色器编译的 helper 宏和定义。
- UnityShaderVariables.cginc -_(自动包含)_常用的全局变量。
- UnityCG.cginc - 常用的 helper 函数。
- AutoLight.cginc - 光照和阴影功能,例如表面着色器在内部使用此文件。
- Lighting.cginc - 标准表面着色器光照模型;当您编写表面着色器时会自动包含。
- TerrainEngine.cginc - 地形和植被着色器的 helper 函数。
HLSLSupport.cginc
编译 CGPROGRAM 着色器时会自动包含此文件(但不会对 HLSLPROGRAM 着色器包含此文件)。此文件声明各种预处理器宏以帮助进行多平台着色器开发。
UnityShaderVariables.cginc
编译 CGPROGRAM 着色器时会自动包含此文件(但不会对 HLSLPROGRAM 着色器包含此文件)。此文件声明着色器中常用的各种内置全局变量。
UnityCG.cginc
Unity 着色器中通常会包含此文件。此文件声明大量内置 helper 函数和数据结构。
UnityCG.cginc 中的数据结构
- struct appdata_base:顶点着色器输入,包含位置、法线和一个纹理坐标。
- struct appdata_tan:顶点着色器输入,包含位置、法线、切线和一个纹理坐标。
- struct appdata_full:顶点着色器输入,包含位置、法线、切线、顶点颜色和两个纹理坐标。
- struct appdata_img: 顶点着色器输入,包含位置和一个纹理坐标。
预定义的着色器预处理器宏
目标平台
| 宏: | 目标平台: |
|---|---|
| SHADER_API_D3D11 | Direct3D 11 |
| SHADER_API_GLCORE | 桌面端 OpenGL“核心”(GL 3/4) |
| SHADER_API_GLES | OpenGL ES 2.0 |
| SHADER_API_GLES3 | OpenGL ES 3.0/3.1 |
| SHADER_API_METAL | iOS/Mac Metal |
| SHADER_API_VULKAN | Vulkan |
| SHADER_API_D3D11_9X | 适用于通用 Windows 平台的 Direct3D 11“功能级别 9.x”目标 |
| SHADER_API_PS4 | PlayStation 4。也定义了 SHADER_API_PSSL。 |
| SHADER_API_XBOXONE | Xbox One |
SHADER_API_MOBILE 是针对所有常规移动平台(GLES、GLES3、METAL)定义的。
此外,当目标着色语言为 GLSL 时,还会定义 SHADER_TARGET_GLSL(对于 OpenGL/GLES 平台来说始终会定义)。
着色器目标模型
SHADER_TARGET 被定义为与着色器目标编译模型匹配的数值(即匹配 #pragma target 指令)。例如,当编译到着色器模型 3.0 时,SHADER_TARGET 为 30。您可以在着色器代码中使用此宏来进行条件检查。例如:
# if SHADER_TARGET < 30
// 低于着色器模型 3.0:
// 着色器功能非常有限,执行近似操作
# else
// 不错的功能,执行更高级的任务
# endifUnity 版本
UNITY_VERSION 包含 Unity 版本的数值。例如,对于 Unity 5.0.1,UNITY_VERSION 为 501。如果您需要编写使用不同着色器内置功能的着色器,则可以将其用于版本比较。例如,#if UNITY_VERSION >= 500 预处理器检查仅在版本为 5.0.0 或更高时可以通过
编译的着色器阶段
编译每个着色器阶段时会定义预处理器宏 SHADER_STAGE_VERTEX、SHADER_STAGE_FRAGMENT、SHADER_STAGE_DOMAIN、SHADER_STAGE_HULL、SHADER_STAGE_GEOMETRY 或 SHADER_STAGE_COMPUTE。通常,在像素着色器和计算着色器之间共享着色器代码时,这些宏非常有用,可以解决某些工作必须以略有不同的方式来完成的情况。
平台差异 helper
不鼓励直接使用这些平台宏,因为它们并非始终有助于代码的未来验证。例如,如果您正在编写一个检查 D3D11 的着色器,您可能希望确保在将来将这项检查扩展为包含 Vulkan。应改用 Unity 定义的几个 helper 宏(在 HLSLSupport.cginc 中):
| 宏: | 用途: |
|---|---|
| UNITY_BRANCH | 在条件语句之前添加此宏,告知编译器应将其编译为实际分支。在 HLSL 平台上扩展为 [branch]。 |
| UNITY_FLATTEN | 在条件语句之前添加此宏,告知编译器应该将其展平以避免实际的分支指令。在 HLSL 平台上扩展为 [flatten]。 |
| UNITY_NO_SCREENSPACE_SHADOWS | 在不使用级联屏幕空间阴影贴图的平台(移动平台)上定义。 |
| UNITY_NO_LINEAR_COLORSPACE | 在不支持线性颜色空间的平台(移动平台)上定义。 |
| UNITY_NO_RGBM | 在不使用光照贴图 RGBM 压缩的平台(移动平台)上定义。 |
| UNITY_NO_DXT5nm | 在不使用 DXT5nm 法线贴图压缩的平台(移动平台)上定义。 |
| UNITY_FRAMEBUFFER_FETCH_AVAILABLE | 在可使用“帧缓冲颜色提取”功能的平台(通常为 iOS 平台 - OpenGL ES 2.0、3.0 和 Metal)上定义。 |
| UNITY_USE_RGBA_FOR_POINT_SHADOWS | 在点光源阴影贴图使用具有编码深度的 RGBA 纹理的平台(其他平台使用单通道浮点纹理)上定义。 |
| UNITY_ATTEN_CHANNEL | 定义光源衰减纹理的哪个通道包含数据;用于每像素光照代码。定义为“r”或“a”。 |
| UNITY_HALF_TEXEL_OFFSET | 在将纹理像素映射到像素时需要进行半纹素偏移调整的平台(例如 Direct3D 9)上定义。 |
| UNITY_UV_STARTS_AT_TOP | 始终定义值为 1 或 0。值为 1 表示在平台上的纹理之上的纹理 V 坐标为 0。Direct3D 类平台使用值 1;OpenGL 类平台使用值 0。 |
| UNITY_MIGHT_NOT_HAVE_DEPTH_Texture | 如果平台可以通过手动将深度渲染到纹理中来模拟阴影贴图或深度纹理,则定义此宏。 |
| UNITY_PROJ_COORD(a) | 给定一个 4 分量矢量,此宏返回一个适合投影纹理读取的纹理坐标。在大多数平台上,它直接返回给定值。 |
| UNITY_NEAR_CLIP_VALUE | 定义为近裁剪面的值。Direct3D 类平台使用 0.0,而 OpenGL 类平台使用 –1.0。 |
| UNITY_VPOS_TYPE | 定义像素位置输入 (VPOS) 所需的数据类型:D3D9 上为 float2,其他为 float4。 |
| UNITY_CAN_COMPILE_TESSELLATION | 在着色器编译器“理解”曲面细分着色器 HLSL 语法时定义(当前仅限 D3D11)。 |
| UNITY_INITIALIZE_OUTPUT(type,name) | 将给定_类型_的变量_名称_初始化为零。 |
| UNITY_COMPILER_HLSL, UNITY_COMPILER_HLSL2GLSL, UNITY_COMPILER_CG | 指示正在使用哪个着色器编译器来编译着色器 - 分别为:Microsoft 的 HLSL、HLSL 到 GLSL 转换器和 NVIDIA 的 |
UNITY_REVERSED_Z - 在使用反转 Z 缓冲区的平台上定义。存储的 Z 值的范围是 1 到 0,而不是 0 到 1。
阴影贴图宏
根据平台的不同,声明和采样阴影贴图可能会有很大差异。Unity 有几个宏可帮助解决这个问题: |宏: |用途: | |:-- |:-- | |UNITY_DECLARE_SHADOWMAP(tex)| 声明一个名为“tex”的阴影贴图纹理变量。 | |UNITY_SAMPLE_SHADOW(tex,uv)| 在给定的“uv”坐标处采样阴影贴图纹理“tex”(XY 分量是纹理位置,Z 分量是要比较的深度)。返回单个浮点值,阴影项的范围在 0 到 1 之间。 | |UNITY_SAMPLE_SHADOW_PROJ(tex,uv)| 与上面类似,但是会读取投影阴影贴图。“uv”是一个 float4,所有其他分量除以 .w 来执行查找。 |
注意:并非所有显卡都支持阴影贴图。请使用 SystemInfo.SupportsRenderTextureFormat 检查是否支持。
常量缓冲区宏
Direct3D 11 将所有着色器变量分组为“常量缓冲区”。Unity 的大多数内置变量已经分组,但对于您自己的着色器中的变量,更加理想的做法是,根据预期的更新频率将它们放入单独的常量缓冲区。 对此,请使用 CBUFFER_START(name) 和 CBUFFER_END 宏:
CBUFFER_START(MyRarelyUpdatedVariables)
float4 _SomeGlobalValue;
CBUFFER_END纹理/采样器声明宏
通常,在着色器代码中使用 texture2D 来声明纹理和采样器对。 但是在某些平台(例如 DX11)上,纹理和采样器是单独的游戏对象, 并且可能的采样器最大数量非常有限。Unity 有一些宏来声明 没有采样器的纹理,并使用另一个纹理中的采样器对纹理进行采样。 如果您遇到采样器限制,并且知道几个纹理实际上可以共享同一个采样器 (采样器定义纹理过滤和包裹模式),请使用这些宏。
| 宏: | 用途: |
|---|---|
| UNITY_DECLARE_TEX2D(name) | 声明纹理和采样器对。 |
| UNITY_DECLARE_TEX2D_NOSAMPLER(name) | 声明不含采样器的纹理。 |
| UNITY_DECLARE_TEX2DARRAY(name) | 声明纹理数组采样器变量。 |
| UNITY_SAMPLE_TEX2D(name,uv) | 使用给定的纹理坐标从纹理和采样器对中采样。 |
| UNITY_SAMPLE_TEX2D_SAMPLER( name,samplername,uv) | 使用另一个纹理中的采样器 (samplername),从纹理 (name) 中采样。 |
| UNITY_SAMPLE_TEX2DARRAY(name,uv) | 从具有 float3 UV 的纹理数组中采样;坐标的 z 分量是数组元素索引。 |
| UNITY_SAMPLE_TEX2DARRAY_LOD(name,uv,lod) | 从具有显式 Mipmap 级别的纹理数组中采样。 |
表面着色器通道指示符
编译表面着色器时,表面着色器会为各种通道生成大量代码以产生光照。编译每个通道时,将定义以下宏之一:
| 宏: | 用途: |
|---|---|
| UNITY_PASS_FORWARDBASE | 前向渲染基础通道(主方向光、光照贴图和 SH)。 |
| UNITY_PASS_FORWARDADD | 前向渲染附加通道(每个通道一个光源)。 |
| UNITY_PASS_DEFERRED | 延迟着色通道(渲染 G 缓冲区)。 |
| UNITY_PASS_SHADOWCASTER | 阴影投射物和深度纹理渲染通道。 |
| UNITY_PASS_PREPASSBASE | 旧版延迟光照基础通道(渲染法线和镜面反射指数)。 |
| UNITY_PASS_PREPASSFINAL | 旧版延迟光照最终通道(应用光照和纹理)。 |
禁用自动升级
UNITY_SHADER_NO_UPGRADE 允许您禁止 Unity 自动升级或修改着色器文件。
内置着色器 helper 函数
顶点变换函数
| 功能: | 描述: |
|---|---|
| float4 UnityObjectToClipPos(float3 pos) | 将对象空间中的点变换到齐次坐标中的摄像机裁剪空间。这等效于 mul(UNITY_MATRIX_MVP, float4(pos, 1.0)),应该在适当的位置使用。 |
| float3 UnityObjectToViewPos(float3 pos) | 将对象空间中的点变换到视图空间。这等效于 mul(UNITY_MATRIX_MV, float4(pos, 1.0)).xyz,应该在适当的位置使用。 |
UnityCG.cginc 中的通用 helper 函数
| 功能: | 描述: |
|---|---|
| float3 WorldSpaceViewDir (float4 v) | 返回从给定对象空间顶点位置朝向摄像机的世界空间方向(未标准化)。 |
| float3 ObjSpaceViewDir (float4 v) | 返回从给定对象空间顶点位置朝向摄像机的对象空间方向(未标准化)。 |
| float2 ParallaxOffset (half h, half height, half3 viewDir) | 计算视差法线贴图的 UV 偏移。 |
| fixed Luminance (fixed3 c) | 将颜色转换为亮度(灰阶)。 |
| fixed3 DecodeLightmap (fixed4 color) | 从 Unity 光照贴图(RGBM 或 dLDR,具体取决于平台)解码颜色。 |
| float4 EncodeFloatRGBA (float v) | 将 [0..1) 范围浮点数编码为 RGBA 颜色,用于存储在低精度渲染目标中。 |
| float DecodeFloatRGBA (float4 enc) | 将 RGBA 颜色解码为浮点数。 |
| float2 EncodeFloatRG (float v) | 将 [0..1) 范围浮点数编码为 float2。 |
| float DecodeFloatRG (float2 enc) | 解码先前编码的 RG 浮点数。 |
| float2 EncodeViewNormalStereo (float3 n) | 将视图空间法线编码为 0 到 1 范围内的两个数字。 |
| float3 DecodeViewNormalStereo (float4 enc4) | 从 enc4.xy 解码视图空间法线。 |
UnityCG.cginc 中的前向渲染 helper 函数
仅当使用前向渲染(ForwardBase 或 ForwardAdd 通道类型)时,这些函数才有用。 |功能: |描述: | |:-- |:-- | |float3 WorldSpaceLightDir (float4 v)| 根据给定的对象空间顶点位置计算朝向光源的世界空间方向(未标准化)。 | |float3 ObjSpaceLightDir (float4 v)| 根据给定对象空间顶点位置计算朝向光源的对象空间方向(未标准化)。 | |float3 Shade4PointLights (...)| 计算四个点光源的光照,将光源数据紧密打包到矢量中。前向渲染使用它来计算每顶点光照。 |
UnityCG.cginc 中的屏幕空间 helper 函数
以下 helper 函数可计算用于采样屏幕空间纹理的坐标。它们返回 float4,其中用于纹理采样的最终坐标可以通过透视除法(例如 xy/w)计算得出。
这些函数还处理渲染纹理坐标中的平台差异。
| 功能: | 描述: |
|---|---|
| float4 ComputeScreenPos (float4 clipPos) | 计算用于执行屏幕空间贴图纹理采样的纹理坐标。输入是裁剪空间位置。 |
| float4 ComputeGrabScreenPos (float4 clipPos) | 计算用于 GrabPass
纹理采样的纹理坐标。输入是裁剪空间位置。 |
UnityCG.cginc 中的顶点光照 helper 函数
仅当使用每顶点光照着色器(“Vertex”通道类型)时,这些函数才有用。
float3 ShadeVertexLights (float4 vertex, float3 normal) : 根据给定的对象空间位置和法线计算四个每顶点光源和环境光的光照。
内置着色器变量
Unity 为着色器提供了一些内置的全局变量:当前对象的变换矩阵、光源参数、当前时间等等。就像任何其他变量一样,可在着色器程序中使用这些变量,唯一的区别是不必声明它们,因为它们都是在自动包含的 UnityShaderVariables.cginc include 文件中声明的。
变换
所有这些矩阵都是 float4x4 类型。
| 名称 | 值 |
|---|---|
| UNITY_MATRIX_MVP | 当前模型 * 视图 * 投影矩阵。 |
| UNITY_MATRIX_MV | 当前模型 * 视图矩阵。 |
| UNITY_MATRIX_V | 当前视图矩阵。 |
| UNITY_MATRIX_P | 当前投影矩阵。 |
| UNITY_MATRIX_VP | 当前视图 * 投影矩阵。 |
| UNITY_MATRIX_T_MV | 模型转置 * 视图矩阵。 |
| UNITY_MATRIX_IT_MV | 模型逆转置 * 视图矩阵。 |
| unity_ObjectToWorld | 当前模型矩阵。 |
| unity_WorldToObject | 当前世界矩阵的逆矩阵。 |
摄像机和屏幕
这些变量将对应于正在渲染的摄像机。例如,在阴影贴图渲染中, 它们仍将引用摄像机组件值,而不是用于阴影贴图投影的“虚拟摄像机”。
| 名称 | 类型 | 值 |
|---|---|---|
| _WorldSpaceCameraPos | float3 | 摄像机的世界空间位置。 |
| _ProjectionParams | float4 | x 是
1.0(如果当前使用翻转投影矩阵进行渲染,则为 –1.0),y
是摄像机的近平面,z 是摄像机的远平面,w 是远平面的倒数。 |
| _ScreenParams | float4 | x 是摄像机目标纹理的宽度(以像素为单位),y 是摄像机目标纹理的高度(以像素为单位),z 是 1.0 + 1.0/宽度,w 为 1.0 + 1.0/高度。 |
| _ZBufferParams | float4 | 用于线性化 Z 缓冲区值。x 是 (1-远/近),y 是 (远/近),z 是 (x/远),w 是 (y/远)。 |
| unity_OrthoParams | float4 | x 是正交摄像机的宽度,y 是正交摄像机的高度,z 未使用,w 在摄像机为正交模式时是 1.0,而在摄像机为透视模式时是 0.0。 |
| unity_CameraProjection | float4x4 | 摄像机的投影矩阵。 |
| unity_CameraInvProjection | float4x4 | 摄像机投影矩阵的逆矩阵。 |
| unity_CameraWorldClipPlanes[6] | float4 | 摄像机视锥体平面世界空间方程,按以下顺序:左、右、底、顶、近、远。 |
时间
| 名称 | 类型 | 值 |
|---|---|---|
| _Time | float4 | 自关卡加载以来的时间 (t/20, t, t2, t3),用于将着色器中的内容动画化。 |
| _SinTime | float4 | 时间正弦:(t/8, t/4, t/2, t)。 |
| _CosTime | float4 | 时间余弦:(t/8, t/4, t/2, t)。 |
| unity_DeltaTime | float4 | 增量时间:(dt, 1/dt, smoothDt, 1/smoothDt)。 |
光照
光源参数以不同的方式传递给着色器,具体取决于使用哪个渲染路径, 以及着色器中使用哪种光源模式通道标签。
前向渲染(ForwardBase 和 ForwardAdd 通道类型):
| 名称 | 类型 | 值 |
|---|---|---|
| _LightColor0(在 Lighting.cginc 中声明) | fixed4 | 光源颜色。 |
| _WorldSpaceLightPos0 | float4 | 方向光:(世界空间方向,0)。其他光源:(世界空间位置,1)。 |
| _LightMatrix0(在 AutoLight.cginc 中声明) | float4x4 | 世界/光源矩阵。用于对剪影和衰减纹理进行采样。 |
| unity_4LightPosX0、unity_4LightPosY0、unity_4LightPosZ0 | float4 | (仅限 ForwardBase 通道)前四个非重要点光源的世界空间位置。 |
| unity_4LightAtten0 | float4 | (仅限 ForwardBase 通道)前四个非重要点光源的衰减因子。 |
| unity_LightColor | half4[4] | (仅限 ForwardBase 通道)前四个非重要点光源的颜色。 |
| unity_WorldToShadow | float4x4[4] | 世界/阴影矩阵。聚光灯的一个矩阵,方向光级联最多有四个矩阵。 |
延迟着色和延迟光照,在光照通道着色器中使用(全部在 UnityDeferredLibrary.cginc 中声明):
| 名称 | 类型 | 值 |
|---|---|---|
| _LightColor | float4 | 光源颜色。 |
| _LightMatrix0 | float4x4 | 世界/光源矩阵。用于对剪影和衰减纹理进行采样。 |
| unity_WorldToShadow | float4x4[4] | 世界/阴影矩阵。聚光灯的一个矩阵,方向光级联最多有四个矩阵。 |
为 ForwardBase、PrePassFinal 和 Deferred 通道类型设置了球谐函数系数 (由环境光和光照探针使用)。这些系数包含由世界空间法线求值的三阶 SH 函数(请参阅 UnityCG.cginc 中的 ShadeSH9)。 这些变量都是 half4 类型、unity_SHAr 和类似名称。
顶点光照渲染(Vertex 通道类型):
最多可为 Vertex 通道类型设置 8 个光源;始终从最亮的光源开始排序。因此,如果您希望 一次渲染受两个光源影响的对象,可直接采用数组中前两个条目。如果影响对象 的光源数量少于 8,则其余光源的颜色将设置为黑色。
| 名称 | 类型 | 值 |
|---|---|---|
| unity_LightColor | half4[8] | 光源颜色。 |
| unity_LightPosition | float4[8] | 视图空间光源位置。方向光为 (-direction,0);点光源/聚光灯为 (position,1)。 |
| unity_LightAtten | half4[8] | 光源衰减因子。x 是 cos(spotAngle/2) 或 –1(非聚光灯);y 是1/cos(spotAngle/4) 或 1(非聚光灯);z 是二次衰减;w 是平方光源范围。 |
| unity_SpotDirection | float4[8] | 视图空间聚光灯位置;非聚光灯为 (0,0,1,0)。 |
雾效和环境光
| 名称 | 类型 | 值 |
|---|---|---|
| unity_AmbientSky | fixed4 | 梯度环境光照情况下的天空环境光照颜色。 |
| unity_AmbientEquator | fixed4 | 梯度环境光照情况下的赤道环境光照颜色。 |
| unity_AmbientGround | fixed4 | 梯度环境光照情况下的地面环境光照颜色。 |
| UNITY_LIGHTMODEL_AMBIENT | fixed4 | 环境光照颜色(梯度环境情况下的天空颜色)。旧版变量。 |
| unity_FogColor | fixed4 | 雾效颜色。 |
| unity_FogParams | float4 | 用于雾效计算的参数:(density / sqrt(ln(2))、density / ln(2)、–1/(end-start) 和 end/(end-start))。x 对于 Exp2 雾模式很有用;y 对于 Exp 模式很有用,z 和 w 对于 Linear 模式很有用。 |
其他
| 名称 | 类型 | 值 |
|---|---|---|
| unity_LODFade | float4 | 使用 LODGroup 时的细节级别淡入淡出。x 为淡入淡出(0 到 1),y 为量化为 16 级的淡入淡出,z 和 w 未使用。 |
| _TextureSampleAdd | float4 | 根据所使用的纹理是 Alpha8 格式(值设置为 (1,1,1,0))还是不是该格式(值设置为 (0,0,0,0))由 Unity 仅针对 UI 自动设置。 |
多个着色器程序变体
通常方便的做法是保持大部分着色器代码固定,但也允许生成稍微不同的着色器“变体”。这种变体通常称为“大型着色器”或“超级着色器”,实现的方式是针对每种情况使用不同的预处理器指令多次编译着色器代码。
在 Unity 中,可通过向着色器代码片段中添加
#pragma multi_compile 或 #pragma shader_feature
指令来实现目的。这在表面着色器中也有效。 在运行时将从 Material
关键字(Material.EnableKeyword 和 DisableKeyword)或全局着色器关键字(Shader.EnableKeyword 和 DisableKeyword)中选取适当的着色器变体。
multi_compile 的工作方式
如下指令:
# pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON
将生成两个着色器变体,一个定义了 FANCY_STUFF_OFF,另一个定义了 FANCY_STUFF_ON。在运行时,将根据“Material”或全局着色器关键字激活其中一个变体。如果这两个关键字均未启用,则将使用第一个(“off”)。
一行 multi_compile 中可以有两个以上的关键字,例如,以下指令将生成四个着色器变体:
# pragma multi_compile SIMPLE_SHADING BETTER_SHADING GOOD_SHADING BEST_SHADING
当任何名称为全下划线时,将生成未定义预处理器宏的着色器变体。这通常用于着色器功能,以避免使用多达两个关键字(请参阅下面的关键字限制说明)。例如,下面的指令将生成两个着色器变体;第一个变体未定义任何内容,第二个变体定义了 FOO_ON:
pragma multi_compile __ FOO_ON
shader_feature 与 multi_compile 之间的区别
#pragma shader_feature 与
#pragma multi_compile 非常类似,唯一的区别是
shader_feature
着色器的未使用变体不会包含在游戏构建中。因此,shader_feature
对于将在材质上设置的关键字最合适,而 multi_compile
对于将通过代码进行全局设置的关键字最合适。
此外,它有一个只包含一个关键字的速记符号:
# pragma shader_feature FANCY_STUFF
这只是 #pragma shader_feature _ FANCY_STUFF 的快捷方式,即它会扩展为两个着色器变体(第一个没有定义;第二个有定义)。
合并多个 multi_compile 行
可提供多个 multi_compile 行,此情况下将针对所有可能的行组合来编译生成的着色器:
# pragma multi_compile A B C
# pragma multi_compile D E
此指令将为第一行产生三种变体,为第二行产生两种变体,或者总共产生六种着色器变体(A+D、B+D、C+D、A+E、B+E 和 C+E)。
最简单的方法是将每个 multi_compile 行视为控制单个着色器“功能”。请记住,着色器变体的总数会以这种方式急速增长。例如,十个带有两个选项的 multi_compile“功能”总共将生成 1024 个着色器变体!
关键字限制
使用着色器变体时,请记住 Unity 中关键字数量上限是 256,其中大约 60 个保留供内部使用(因此降低了可用上限)。此外,关键字会在整个特定 Unity 项目中全局启用,因此在多个不同着色器中定义多个关键字时,请注意不要超过限制。
内置 multi_compile 快捷方式
有几个“快捷方式”符号用于编译多个着色器变体;它们主要用于处理 Unity 中不同的光照、阴影和光照贴图类型。请参阅渲染管线以了解详细信息。
multi_compile_fwdbase 将编译 ForwardBase(前向渲染基础)通道类型需要的所有变体。这些变体处理不同的光照贴图类型以及开启或关闭阴影的主方向光。
multi_compile_fwdadd 将编译 ForwardAdd(前向渲染附加)通道类型的变体。这将编译变体来处理方向光、聚光灯或点光源类型,以及它们带有剪影纹理的变体。
multi_compile_fwdadd_fullshadows - 同上,但还能够让光源具有实时阴影。
multi_compile_fog 扩展为多个变体以处理不同的雾效类型 (off/linear/exp/exp2)。
大多数内置快捷方式都会产生许多着色器变体。如果您知道不需要某些变体,可通过使用 #pragma skip_variants 来跳过这些变体的编译。例如:
# pragma multi_compile_fwdadd
// 将跳过包含
// "POINT" 或 "POINT_COOKIE" 的所有变体
# pragma skip_variants POINT POINT_COOKIE
着色器硬件变体
使用着色器变体的一个常见原因是创建可同时在单个目标平台(例如 OpenGL ES)内的高端和低端硬件上高效运行的后备着色器或简化着色器。要为不同的硬件功能级别提供特别优化的变体集,可以使用着色器硬件变体。
要启用着色器硬件变体的生成,请添加 #pragma hardware_tier_variants renderer,其中 renderer 是着色器程序杂注的可用渲染平台之一。使用此 #pragma,将为每个着色器生成 3 个着色器变体,并忽略其他所有关键字。每个变体将定义以下指令之一:
UNITY_HARDWARE_TIER1
UNITY_HARDWARE_TIER2
UNITY_HARDWARE_TIER3
您可以使用它们为更低端或更高端硬件编写条件性回退或额外功能。在 Editor 中,您可以使用 Graphics Emulation 菜单测试任何层(允许您在每个层之间进行更改)。 为了帮助尽可能降低这些变体的影响,播放器中只加载了一组着色器。此外,任何最终相同的着色器(例如,如果您只为 TIER1 编写专用版本而其他所有版本都相同)将不占用磁盘上的任何额外空间。 在加载时,Unity 将检查其正在使用的 GPU 并自动检测层值;如果未自动检测到 GPU,它将默认为最高层。您可以通过设置 Shader.globalShaderHardwareTier 来覆盖此层值,但必须在加载要更改的任何着色器之前完成此操作。加载着色器后,着色器将选择一组变体,此值将无效。一个良好的设置位置是在加载主场景之前的预加载场景中。
请注意,这些着色器硬件层与播放器的 Quality 设置无关,它们纯粹是从运行播放器的 GPU 的相对功能中检测到的。
平台着色器设置
除了调整不同硬件层的着色器代码之外,您可能还需要调整 Unity
内部定义(例如,您可能希望在移动设备上强制使用级联阴影贴图)。您可以在
UnityEditor.Rendering.PlatformShaderSettings文档中找到与此相关的详细信息,其中提供了按层覆盖的最新支持功能的列表。
使用
UnityEditor.Rendering.EditorGraphicsSettings.SetShaderSettingsForPlatform
可根据平台按层调整硬件着色器设置。 请注意,如果设置为不同层的
PlatformShaderSetting 不相同,则即使缺少
#pragma hardware_tier_variants,也会为着色器生成层变体。
GLSL 着色器程序
着色器编译器
在 Unity 中,着色器程序是用 HLSL 语言的一个变种(也称为 Cg,但对于大部分实际使用情况,这两种语言没有区别)编写的。 目前,为了在不同平台之间实现最大程度的可移植性,请使用 DX9 风格的 HLSL 编写程序(例如,使用 DX9 风格的 sampler2D 和 tex2D 进行纹理采样,而不是使用 DX10 风格的 Texture2D、SamplerState 和 tex.Sample)。
在内部将使用不同的着色器编译器来编译着色器程序:
- Windows 和 Microsoft 平台(DX11、DX12 和 Xbox One)全部使用 Microsoft 的 HLSL 编译器(最新版本为 d3dcompiler_47)。
- OpenGL Core、OpenGL ES 3、OpenGL ES 2.0 和 Metal 使用 Microsoft 的 HLSL,然后使用 HLSLcc 按字节代码转换为 GLSL 或 Metal。
- OpenGL ES 2.0 可以通过 hlsl2glslfork 和 glsl 优化器进行源代码级别转换。这是通过添加 #pragma prefer_hlsl2glsl gles 实现的
- 其他游戏主机平台使用其各自的编译器(例如,PS4 使用 PSSL)。
- 表面着色器使用 Cg 2.2 和 MojoShader 来完成代码生成分析步骤。
如果确实需要确定正在使用哪个编译器(为了使用仅一种编译器支持的 HLSL 语法,或解决编译器错误),可以使用预定义的着色器宏。例如,使用 HLSL 编译器进行编译时设置 UNITY_COMPILER_HLSL(针对 D3D 或 GLCore/GLES3/GLES 平台);通过 hlsl2glsl 进行编译时设置 UNITY_COMPILER_HLSL2GLSL。
着色器目标
编写表面着色器或常规 着色器程序时,HLSL 源代码可以 编译到不同“着色器模型”中。 为了支持使用更现代的 GPI 功能,必须使用更高的着色器编译目标。 注意:使用更高的着色器编译目标可能会阻止着色器在较旧的 GPU 或平台上运行。 应使用 #pragma target 名称 指令或更具体的 #pragma require 功能 … 指令来指示编译目标。例如:
# pragma target 3.5
# pragma require integers 2darray instancing