着色器:顶点和片段程序

此教程将介绍如何在 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 片段位于 CGPROGRAMENDCG 关键字之间。开头的编译指令由 #pragma 语句给出:

编译指令后面是普通的 Cg 代码。首先,我们需要导入一个 内置 Cg 文件

  1. 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 带给您的无限乐趣。

Page last updated: 2013-06-27