ShaderLab着色器包含的不只是”硬件着色器”。它们能做很多的事情。它们描述的属性在材质检视面板内可以查看到。为了不同的图形硬件包含实现了多个着色器,配置了固定程序硬件状态等等。这就像点和片段程序,实际上可编程着色器只是整个ShaderLab着色器概念的一部分。参看一下着色器教程这些基础介绍。在这里我们将调用低层次的硬件着色器着色编程。
如果你想编写一个与光照相互影响的着色器,请参看 表面着色器 文档。其余部分本页将负责介绍不与Unity中的光照相互影响的着色器。例如:特效, 图片效果等。
Cg程序小片段是编写在CGPROGRAM和ENDCG之间的。
在小片段(snippet)开始可以通过提供的 #pragma命令声明编译指令。Unity认可的指令有以下这些:
- #pragma vertex name 表明这是个以name为名字的函数的顶点程序。
- #pragma fragment name 表明这是个以name为名字的函数的片段程序。
- #pragma fragmentoption option 添加选项到编译的OpenGL 片段程序。 通过 ARB 片段程序 可以查询到所允许的规范的选项列表。 这个指令对顶点程序或者不是以OpenGL为编译目标的程序无效。
- #pragma target name 着色器目标编译。详情请参考 着色器目标。
- #pragma only_renderers space separated names 仅用给定的渲染器编译着色器。默认情况下用所有的渲染器都编译着色器。详情请参考渲染。
- #pragma exclude_renderers space separated names 不用给定的渲染器编译着色器。 默认情况下用所有的渲染器都编译着色器。详情请参考渲染。
- #pragma glsl 用桌面OpenGL平台编译着色器时,转换成GLSL里面的Cg/HLSL(而不是默认设置的ARB顶点/片段程序)。
每个小片段必须包含一个顶点程序或者片段程序(或者两者兼有。因此需要一个#pragma vertex命令或者#pragma fragment命令或者两者兼有。
默认情况下,Unity编译顶点程序与着色器模式1.1相同,片段程序与着色器模式2.0相同。使用#pragma target使着色器编译成其他等级的能力。
目前支持下面这些目标:
#pragma target default 编译默认目标:
在Direct3D 9环境中:顶点着色器1.1和像索着色器2.0
ARB顶点程序 有128位指令限制,ARB片段程序 有96位指令限制(32位纹理 + 64位算术运算),16个临时寄存器和4个间接纹理。
#pragma target 3.0 编译着色器模式3.0:
在Direct3D 9环境中:顶点着色器3.0和像索着色器 3.0
ARB顶点程序 没有指令限制,ARB片段程序 有1024位指令限制(512位纹理 + 512位算术运算),32个临时寄存器和4个间接纹理。可以通过使用 #pragma profileoption 命令扩展限制值。例如: #pragma profileoption MaxTexIndirections=256增加间接纹理上限到256个。需要注意的是某些着色器模式3.0中的特性不支持ARB顶点程序和ARB片段程序,比如派生指令这样的。你可以使用 #pragma glsl命令转换到GLSL中,这样子限制较少。
编译3.0目标时,顶点和片断程序需要同时存在。
Unity支持多种渲染API(比如:Direct3D 9 和 OpenGL),默认情况下被支持的渲染器会编译所有的着色器程序。你可以使用#pragma only_renderers 或 #pragma exclude_renderers指令来指定渲染器编译着色器。如果你只想针对Mac OS X系统(Mac 系统不支持Direct3D),或者只针对Windows系统(在Windows系统下Unity默认使用D3D),或者一些特定的着色器只能在一个渲染器中被编译,那么这两个命令是非常有用的。目前支持的渲染器有以下这些:
- d3d9 – Direct3D 9.
- opengl – OpenGL.
- gles – OpenGL ES 2.0.
- xbox360 – Xbox 360.
- ps3 – PlayStation 3.
- flash – Flash.
Eg: #pragma only_renderers d3d9
在Cg中访问着色器属性
在着色器语言中的属性类型与Cg变量类型的对应:
- Color和Vector属性对应 float4 类型。
- Range和Float属性对应 float 类型。
- Texture属性对应 普通2D纹理的sampler2D 类型。
- CUBE和RECT纹理对应 samplerCUBE 和 samplerRECT类型。
传递顶点数据到顶点程序
Cg顶点程序必须在结构中传递顶点数据。几种常用的顶点结构定义在文件UnityCG.cginc中。在大部分情况下仅仅使用它们就够了。结构如下:
- appdata_base: 包含顶点位置,法线 和一个纹理坐标。
- appdata_tan: 包含顶点位置,切线,法线 和一个纹理坐标。
如果你想访问个别的顶点数据,你必须自己声明顶点结构。结构中的成员必须是属于以下列表中的:
- float4 vertex 是顶点位置
- float3 normal 是顶点法线
- float4 texcoord 是第一UV坐标
- float4 texcoord1 是第二UV坐标
- float4 tangent 是切线向量(用在法线贴图中)
- float4 color 是每个顶点(per-vertex)颜色
着色器程序中的内置状态变量
Built-in matrices 内置矩阵
UNITY_MATRIX_MVP Current modelviewprojection matrix 当前模型视投影矩阵。(注:模型矩阵为 本地->世界)
UNITY_MATRIX_MV Current modelview matrix 当前模型视图矩阵
UNITY_MATRIX_V Current view matrix. 当前视图矩阵
UNITY_MATRIX_P Current projection matrix 当前投影矩阵
UNITY_MATRIX_VP Current viewprojection matrix 当前视图投影矩阵
UNITY_MATRIX_T_MV Transpose of modelview matrix 转置模型视图矩阵
UNITY_MATRIX_IT_MV Inverse transpose of modelview matrix 逆转置模型视矩阵
UNITY_MATRIX_TEXTURE0 to UNITY_MATRIX_TEXTURE3 Texture transformation matrices 纹理变换矩阵
Built-in vectors 内置向量
UNITY_LIGHTMODEL_AMBIENT Current ambient color. 当前环境色。
内置着色器包含文件
在Unity中Shader include文件的扩展名是.cginc,内置的文件如下:
- HLSLSupport.cginc –(自动包含)跨平台着色器编译帮助宏和定义
- UnityCG.cginc -常用的全局变量和辅助函数
- AutoLight.cginc -灯光和阴影功能,例如surface shaders 内在地使用此文件。
- Lighting.cginc -标准surface shader 灯光模型,当你写表面着色器时自动包含。
- TerrainEngine.cginc -地形和植被着色器的辅助函数。
这些文件存放在Unity程序中,在电脑上的具体位置是: Windows系统在{unity install path}/Data/CGIncludes/UnityCG.cginc Mac系统在 /Applications/Unity/Unity.app/Contents/CGIncludes/UnityCG.cginc 如果你想看一看里面的辅助代码究竟是什么,可以在这些位置找到具体文件。
HLSLSupport.cginc
当编译着色器时这个文件会自动包括。它主要声明各种预处理宏来帮助多平台着色器开发。
UnityCG.cginc
此文件经常包含在Unity着色器中,带来很多辅助函数和定义。
Data structures in UnityCG.cginc 在UnityCG.cginc中的数据结构
- struct appdata_base:带有位置、法线和一个纹理坐标的顶点着色器输入
- struct appdata_tan:带有位置、法线、切线和一个纹理坐标的顶点着色器输入
- struct appdata_full:带有位置、法线、切线、顶点色和两个纹理坐标的顶点着色器输入
- struct appdata_img:带有位置和一个纹理坐标的顶点着色器输入
Generic helper functions in UnityCG.cginc 在UnityCG.cginc中的通用辅助函数
- 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颜色到float。
Similarly, float2 EncodeFloatRG (float v) and float DecodeFloatRG (float2 enc) that use two color channels.
同样的,float2 EncodeFloatRG (float v) 和float DecodeFloatRG (float2 enc)使用的是两个颜色通道。 - float2 EncodeViewNormalStereo (float3 n) -编码视图空间法线到在0到1范围的两个数。
- float3 DecodeViewNormalStereo (float4 enc4) -从enc4.xy解码视图空间法线
UnityCG.cginc正向渲染辅助函数
这些函数只有当使用forward rendering(ForwardBase 或 ForwardAdd通道类型)时才有效。
- float3 WorldSpaceLightDir (float4 v) -根据局部空间顶点位置计算世界空间灯光方向(非规范化的)。
- float3 ObjSpaceLightDir (float4 v) -根据局部空间顶点位置计算局部空间灯光方向(非规范化的)。
- float3 Shade4PointLights (…) -从4个点光源计算亮度,来将光照数据牢固的装入向量中。正向渲染使用此来计算逐顶点光照。
UnityCG.cginc中的顶点光照辅助函数
这些函数只在当使用逐顶点光照着色器(”Vertex” pass type)时才有效。
- float3 ShadeVertexLights (float4 vertex, float3 normal) -根据局部空间位置和法线,从4个逐顶点灯光和环境光计算亮度。
当编译着色器程序时,Unity定义了一些预处理宏。
Target platform 目标平台
- SHADER_API_OPENGL – desktop OpenGL 桌面OpenGL
- SHADER_API_D3D9 – Direct3D 9
- SHADER_API_XBOX360 – Xbox 360
- SHADER_API_PS3 – PlayStation 3
- SHADER_API_D3D11 – desktop Direct3D 11 桌面Direct3D 11
- SHADER_API_GLES – OpenGL ES 2.0 (桌面或移动平台),使用SHADER_API_MOBILE确定移动平台
- SHADER_API_FLASH – Flash Stage3D
- SHADER_API_D3D11_9X – Direct3D 11 target for Windows RT
另外,目标着色器语言是GLSL时自动定义SHADER_TARGET_GLSL(SHADER_API_GLES 被定义时总是真;使用#pragma glsl 的SHADER_API_OPENGL时可以是真)。
SHADER_API_MOBILE is defined for SHADER_API_GLES when compiling for “mobile” platform (iOS/Android); and not defined when compiling for “desktop” (NativeClient).
当在移动平台(iOS/Android)编译时,SHADER_API_MOBILE 会因为SHADER_API_GLES 而定义;而在桌面版(NativeClient)上编译时不会被定义。
Platform difference helpers 平台差异帮助
我们不鼓励直接使用这些平台相关的宏,因为他们没有前瞻性。例如,当你写一个检验D3D9的shader时,将来这个检验将来可能会扩展到D3D11。相反,你应该使用Unity定义的一些帮助宏(在HLSLSupport.cginc中)来做这些。
- UNITY_ATTEN_CHANNEL -其中光衰减纹理通道包含这个数据;在逐像素光照代码中被使用;定义为’r’ 或者 ‘a’。
- UNITY_HALF_TEXEL_OFFSET -定义在做文本到像素映射时需要一个半文本偏移的调整的平台上(如Direct3D 9)。
- UNITY_UV_STARTS_AT_TOP -总是用1或0定义;当纹理的V坐标系原点在纹理顶部的平台上值是1。Direct3D类似平台使用1;OpenGL类似平台使用0。
- UNITY_MIGHT_NOT_HAVE_DEPTH_TEXTURE -当一个平台可能手动使用渲染深度来渲染阴影纹理或者深度纹理到贴图的时候定义。
- UNITY_PROJ_COORD(a) -输入一个四维向量,返回一个适合投影纹理读取的纹理坐标。大多数平台上,这直接返回给定值。
- UNITY_NEAR_CLIP_VALUE -定义了近裁剪面的值。Direct3D类的平台使用0.0,OpenGL类的平台使用-1.0。
- UNITY_COMPILER_CG, UNITY_COMPILER_HLSL or UNITY_COMPILER_HLSL2GLSL -决定使用哪个相关的着色器编译器;防止只有细微的语法差异,而你必须写不同的着色器代码。
- UNITY_CAN_COMPILE_TESSELLATION -当着色器编译器能解析曲面细分着色器的HLSL语法(目前只有D3D11)时被定义。
- UNITY_INITIALIZE_OUTPUT(type,name) -初始化type 类型的name 变量为0.
- UNITY_COMPILER_HLSL, UNITY_COMPILER_HLSL2GLSL, UNITY_COMPILER_CG -表示正在使用的着色器编译器。分别为,微软的HLSL(DX11,Xbox360以及WinRT上使用),HLSL到GLSL转换器(用于iOS/Android和使用#pragma glsl的桌面OpenGL),和NVIDIA的Cg (用于D3D9和非GLSL桌面OpenGL目标)。如果你遇到了非常具体的边缘情况,编译器之间的语法处理不同,使用它们来为每个编译器编写不同的代码。
Shadow mapping macros 阴影映射宏
声明和采样阴影贴图在不同的平台上可能会有很大的区别,所以Unity有几个宏可以帮助:
- UNITY_DECLARE_SHADOWMAP(tex) -声明一个名为”tex”的阴影贴图变量。
- UNITY_SAMPLE_SHADOW(tex,uv) -用给定的”uv”坐标(XY坐标是纹理的位置,z是比较用的深度)采样阴影贴图”tex”,在0..1范围内返回一个阴影边界内的单精度的浮点数。
- UNITY_SAMPLE_SHADOW_PROJ(tex,uv) -与上面想死,但是把投影阴影贴图的”uv”当float4读,所有其他的元素除以.w用来做查找。
Constant buffer macros 常量缓冲区宏
Direct3D 11把所有着色器变量分组放进“常量缓冲区”。大部分Unity的内置变量已经分组,但是你自己着色器里的变量可能放进单独的常量缓冲区会更有效率,取决于预期的更新频率。
使用 CBUFFER_START(name) 和 CBUFFER_END 宏如下:
CBUFFER_START(MyRarelyUpdatedVariables)
float4 _SomeGlobalValue;
CBUFFER_END
Surface shader pass indicators 表面着色器通道标记
Surface Shaders编译后,它们最终会为渲染光照的通道生成许多代码。当编译一个通道时,下列宏的某一个会被定义:
- UNITY_PASS_FORWARDBASE -正向渲染的基本通道(主要的平行光,光照贴图,SH)。
- UNITY_PASS_FORWARDADD -正向渲染的额外通道(一个灯光对应一个通道)。
- UNITY_PASS_PREPASSBASE -延迟着色的基本通道(渲染法线和高光指数)。
- UNITY_PASS_PREPASSFINAL -延迟着色的最终通道(应用光照和贴图)。
- UNITY_PASS_SHADOWCASTER -阴影投射渲染通道。
- UNITY_PASS_SHADOWCOLLECTOR -平行光阴影的阴影聚合通道。