着色器:顶点和片段程序
此教程将介绍如何在 Unity 着色器中编写自定义顶点和片段程序。有关 ShaderLab 的简介,请参阅表面着色器 (Surface Shaders)。
让我们一起回顾一下着色器的一般结构:
Shader "MyShaderName" {Properties {// ... properties here ... }SubShader {// ... subshader for graphics hardware A ...Pass {// ... pass commands ... }// ... more passes if needed ... }SubShader {// ... subshader for graphics hardware B ... }// ... Optional fallback ...FallBack "VertexLit"}
这里的结尾处介绍了一个新命令:FallBack "VertexLit"
您可在着色器结尾处使用 Fallback 命令;它指出当用户图形硬件上没有运行当前着色器中的任何子着色器 (SubShaders) 时,应该使用哪种着色器。其效果与备用着色器在末尾处添加所有子着色器 (SubShaders) 的效果相同。例如,如果您要撰写法线贴图着色器,则只需回退至内置顶点光照 (VertexLit) 着色器,无需为旧图形卡编写非常简单的非法线贴图着色器。
属性 (Properties)、子着色器 (SubShaders) 和通道 (Passes) 文档。
使用其他着色器中定义的通道可快速构建子着色器。命令 UsePass 可执行上述操作,因此,您可以巧妙地重复使用着色器代码。例如,以下命令使用内置高光 (Specular) 着色器中名为 "BASE" 的通道:UsePass "Specular/BASE"
要使 UsePass 有效运行,则必须为需要使用的通道命名。通道中的 Name 命令为其命名:Name "MyPassName"
顶点和片段程序
我们在第一个教程中介绍了一个只使用单个纹理合成的通道。现在,我们将介绍如何使用通道中的顶点和片段程序。
使用顶点和片段程序(所谓的“可编程管道”)时,程序将关闭图形硬件中的大部分硬编码功能(“固定功能管道”)。例如,使用顶点程序完全关闭标准三维转换、照明和纹理坐标生成功能。同样地,使用片段程序替换本应在 SetTexture 命令中定义的任何纹理合成模式;因而无需使用 SetTexture 命令。
编写顶点/片段程序需要对三维转换、照明和坐标空间有全面了解 ? 因为您必须像处理 OpenGL 一样重写内置到 API 的固定功能。另外,还可实现更多内置以外的功能!
在 ShaderLab 中使用 Cg
通常,我们通过在着色器文本中插入“Cg 片段”,以使用 Cg 编程语言在 ShaderLab 中编写着色器。Unity 编辑器将 Cg 片段编译至低级别的着色器程序集中,添加至游戏数据文件的最终着色器仅包含此低级别程序集。在工程视图 (Project View) 中选择着色器时,检视器 (Inspector) 在编译 Cg 后显示着色器文本,这可能有助于调试。Unity 自动编译 Direct3D、OpenGL 和 Flash 等平台的 Cg 片段,因此您的着色器可在所有平台上运行。注意,由于 Cg 代码由编辑器编译,无法在运行时使用脚本创建 Cg 着色器。
通常,Cg 片段位于通道 (Pass) 块中。示例如下:
Pass {// ... the usual pass state setup ...
CGPROGRAM// compilation directives for this snippet, e.g.:#pragma vertex vert#pragma fragment frag
// the Cg code itself
ENDCG// ... the rest of pass setup ...
}
以下示例介绍含 Cg 程序(将对象法线渲染为彩色)的完整着色器:
Shader "Tutorial/Display Normals" {SubShader {Pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f {float4 pos :SV_POSITION;float3 color :COLOR0;};v2f vert (appdata_base v){v2f o;o.pos = mul (UNITY_MATRIX_MVP, v.vertex);o.color = v.normal * 0.5 + 0.5;return o;}half4 frag (v2f i) :COLOR{return half4 (i.color, 1);}ENDCG }}Fallback "VertexLit"}
应用到对象时,将看到与下图类似的效果(当然是在图形卡支持顶点和片段程序的前提条件下):

“显示法线”着色器不含任何属性,只包含一个子着色器 (SubShader),其单一通道 (Pass) 只有 Cg 代码。最终确定内置顶点光照 (VertexLit) 着色器为备用着色器。让我们逐个部分分析 Cg 代码:
CGPROGRAM#pragma vertex vert#pragma fragment frag// ... snip ...ENDCG
整个 Cg 片段位于 CGPROGRAM 和 ENDCG 关键字之间。开头的编译指令由 #pragma 语句给出:
#pragma vertex name 说明给定函数的顶点程序包含的代码(此处为 vert)。
#pragma fragment name 说明给定函数的片段程序包含的代码(此处为 frag)。
编译指令后面是普通的 Cg 代码。首先,我们需要导入一个 内置 Cg 文件:
include UnityCg.cginc
UnityCg.cginc 文件包含常用声明和函数,从而令着色器保持简短(有关详细信息,请参阅着色器导入文件页面)。在这里,我们将使用该文件中的 appdata_base 结构。当然,可以不导入此文件,直接在着色器中定义这些内容。
接下来,定义一个“顶点至片段”结构(此处为 v2f)- 从顶点传递至片段程序的信息。我们传递位置和颜色参数。顶点程序计算出颜色并只在片段程序中输出。
然后定义顶点程序 - vert 函数。在这里计算位置并用一种颜色输出输入法线:o.color = v.normal * 0.5 + 0.5;
法线组件位于 -1..1 范围之间,但颜色位于 0..1 范围之间,所以我们微调并微移上述代码中的法线。接下来,定义一个片段程序 ? 只输出计算的颜色和 1 作为 alpha 组件的 frag 函数:
half4 frag (v2f i) :COLOR
{
return half4 (i.color, 1);
}
就是这样,着色器分析到此为止!即使这个着色器很简单,但这对可视化网格法线非常有用。
当然,此着色器根本不能响应光照。这让事情变得越来越有趣了;有关更多信息,请参阅表面着色器 (Surface Shaders)。
在 Cg 代码中使用着色器属性
在着色器中定义属性时,将其命名为 _Color 或 _MainTex。要在 Cg 中使用这些属性,只需定义一个与名称和类型相符的变量。Unity 将自动设置其名称与着色器属性相符的 Cg 变量。
以下是一个其纹理由颜色调整过的完整着色器。当然,您可以在纹理合成调用中执行上述操作,但这里旨在说明如何在 Cg 运用属性:
Shader "Tutorial/Textured Colored" {Properties {_Color ("Main Color", Color) = (1,1,1,0.5)_MainTex ("Texture", 2D) = "white" { }}SubShader {Pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"float4 _Color;sampler2D _MainTex;struct v2f {float4 pos :SV_POSITION;float2 uv :TEXCOORD0;};float4 _MainTex_ST;v2f vert (appdata_base v){v2f o;o.pos = mul (UNITY_MATRIX_MVP, v.vertex);o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);return o;}half4 frag (v2f i) :COLOR{half4 texcol = tex2D (_MainTex, i.uv);return texcol * _Color;}ENDCG }}Fallback "VertexLit"}
此着色器的结构与上一示例的结构相同。在这里,我们定义了两个属性,即 _Color 和 _MainTex 我们在 Cg 代码中定义相应变量:
float4 _Color;sampler2D _MainTex;
有关更多信息,请参阅在 Cg 中访问着色器属性。
此处的顶点和片段程序未制作出任何特殊效果;顶点程序使用 UnityCG.cginc 中的 TRANSFORM_TEX 宏确保纹理缩放和偏移得到正确应用,而片段程序只是对纹理采样,然后乘以颜色属性。
注意,我们正在编写自己的片段程序,因而无需使用任何 SetTexture 命令。着色器应用纹理的方式完全取决于片段程序。
总结
我们已介绍了如何通过几个简单的步骤生成自定义着色器程序。虽然这里列出的示例都非常简单,但这完全不阻碍您轻松撰写出复杂的着色器程序!这还有助于您充分利用 Unity 的优势,实现最理想的渲染效果。
此处有完整的 ShaderLab 组件手册。我们还在 forum.unity3d.com 建立了一个着色器论坛,还等什么,立即为你的着色器寻求帮助吧!祝您编程愉快,畅享强大的 Unity 和 Shaderlab 带给您的无限乐趣。
,