一个简单的顶点/片元着色器

代码
Shader "Unity Shaders Book/Chapter 5/Simple Shader" {
    Properties {
        // 声明一个Color类型的属性
        _Color ("Color Tint", Color) = (1.0,1.0,1.0,1.0)
    }
    SubShader {
        Pass {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            // 在Cg代码中,我们需要定义一个与属性名称和类型都匹配的变量
            fixed4 _Color;

            // 使用一个结构体来定义顶点着色器的输入
            struct a2v {
                // POSITION语义告诉Unity,用模型空间的顶点坐标填充vertex变量
                float4 vertex : POSITION;
                // NORMAL语义告诉Unity,用模型空间的法线方向填充normal变量
                float3 normal : NORMAL;
                // TEXCOORD0语义告诉Unity,用模型的第一套纹理坐标填充texcoord变量
                float4 texcoord : TEXCOORD0;
            };

            // 使用一个结构体来定义顶点着色器的输出
            struct v2f {
                // SV_POSITION语义告诉Unity,pos里包含了顶点在裁剪空间中的位置信息
                float4 pos : SV_POSITION;
                // COLOR0语义可以用于存储颜色信息
                fixed3 color : COLOR0;
            };

            v2f vert(a2v v) : SV_POSITION {
                v2f o;
                // 使用v.vertex来访问模型空间的顶点坐标
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 c = i.color;
                // 使用_Color属性来控制输出颜色
                c *= _Color.rgb;
                return fixed4(c, 1.0);
            }

            ENDCG
        }
    }
}

  • 模型数据从哪里来: a2v结构体定义了顶点着色器的输入
  • 顶点着色器和片元着色器之间如何通信: v2f结构体定义了顶点着色器的输入和片元着色器的输入
  • 如何使用属性: Properties 语义块中,并在其中声明了一个属性_Color。并在Cg代码中定一个变量。
  • 顶点着色器是逐顶点调用的,而片元着色器是逐片元调用的。片元着色器中的输入实际上是把顶点着色器的输出进行插值后得到的结果。

ShaderLab中属性的类型和Cg中变量的类型之间的匹配关系 如下:

ShaderLab属性类型Cg变量类型
Color,Vectorfloat4, half4,fixed4
Range,Floatfloat,half,fixed
2Dsampler2D
CubesamplerCube
3Dsampler3D

uniform关键词是Cg中修饰变量和参数的一种修饰词,它仅仅用于提供一些关于该变量的初始值是如何指定和存储的相关信息(这和其他一些图像编程接口中的uniform关键词的作用不太一样)。在Unity Shader中,uniform关键词是可以省略的。

Unity提供的内置文件和变量

Unity中一些常用的包含文件

文 件 名描述
UnityCG.cginc包含了最常使用的帮助函数、宏和结构体等
UnityShaderVariables.cginc在编译Unity Shader时,会被自动包含进来。包含了许多内置的全局变量,如UNITY_MATRIX_MVP等
Lighting.cginc包含了各种内置的光照模型,如果编写的是Surface Shader的话,会自动包含进来
HLSLSupport.cginc在编译Unity Shader时,会被自动包含进来。声明了很多用于跨平台编译的宏和定义

UnityCG.cginc中一些常用的结构体

名 称描 述包含的变量
appdata_base可用于顶点着色器的输入顶点位置、顶点法线、第一组纹理坐标
appdata_tan可用于顶点着色器的输入顶点位置、顶点切线、顶点法线、第一组纹理坐标
appdata_full可用于顶点着色器的输入顶点位置、顶点切线、顶点法线、四组(或更多)纹理坐标
appdata_img可用于顶点着色器的输入顶点位置、第一组纹理坐标
v2f_img可用于顶点着色器的输出裁剪空间中的位置、纹理坐标

UnityCG.cginc中一些常用的帮助函数

函 数 名描 述
float3 WorldSpaceViewDir (float4 v)输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
float3 ObjSpaceViewDir (float4 v)输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向
float3 WorldSpaceLightDir (float4 v)仅可用于前向渲染中 。输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。没有被归一化
float3 ObjSpaceLightDir (float4 v)仅可用于前向渲染中 。输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向。没有被归一化
float3 UnityObjectToWorldNormal (float3 norm)把法线方向从模型空间转换到世界空间中
float3 UnityObjectToWorldDir (float3 dir)把方向矢量从模型空间变换到世界空间中
float3 UnityWorldToObjectDir(float3 dir)把方向矢量从世界空间变换到模型空间中

Unity提供的Cg/HLSL语义

从应用阶段传递模型数据给顶点着色器时Unity支持的常用语义

语 义描 述
POSITION模型空间中的顶点位置,通常是float4类型
NORMAL顶点法线,通常是float3类型
TANGENT顶点切线,通常是float4类型
TEXCOORDn ,如TEXCOORD0、TEXCOORD1该顶点的纹理坐标,TEXCOORD0表示第一组纹理坐标,依此类推。通常是float2或float4类型
COLOR 顶点颜色,通常是fixed4或float4类型

从顶点着色器传递数据给片元着色器时Unity使用的常用语义

语 义描 述
SV_POSITION裁剪空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量。等同于DirectX 9中的POSITION,但最好使用SV_POSITION
COLOR0通常用于输出第一组顶点颜色,但不是必需的
COLOR1通常用于输出第二组顶点颜色,但不是必需的
TEXCOORD0~TEXCOORD7通常用于输出纹理坐标,但不是必需的

片元着色器输出时Unity支持的常用语义

语 义描 述
SV_Target输出值将会存储到渲染目标(render target)中。等同于DirectX 9中的COLOR语义,但最好使用SV_Target

UnityShader的Debug

  • 使用假彩色图像: 我们可以把需要调试的变量映射到[0, 1]之间,把它们作为颜色输出到屏幕上,然后通过屏幕上显示的像素颜色来判断这个值是否正确
  • 利用神器:Visual Studio
  • 帧调试器 (Frame Debugger)

渲染平台的差异

渲染纹理的坐标差异

OpenGL和DirectX的屏幕空间坐标的差异。在水平方向上,两者的数值变化方向是相同的,但在竖直方向上,两者是相反的。

在一种特殊情况下Unity不会为我们进行这个翻转操作,这种情况就是我们开启了抗锯齿

Shader的语法差异

opengldirectx
float4 v = float4(0.0);float4 v = float4(0.0, 0.0, 0.0, 0.0); 不能使用 float4(0.0)初始化
tex2Dtex2Dlod(tex, float4(uv, 0, 0)).
  • 表面着色器在顶点函数中需要把参数所有成员变量进行初始化
void vert (inout appdata_full v, out Input o) {
    // 使用Unity内置的UNITY_INITIALIZE_OUTPUT宏对输出结构体o进行初始化
    UNITY_INITIALIZE_OUTPUT(Input,o);
    // … 
}

Shader的语义差异

  • 使用SV_POSITION 来描述顶点着色器输出的顶点位置。一些Shader使用了POSITION 语义,但这些Shader无法在索尼PS4平台上或使用了细分着色器的情况下正常工作。
  • 使用SV_Target 来描述片元着色器的输出颜色。一些Shader使用了COLOR或者COLOR0 语义,同样的,这些Shader无法在索尼PS4上正常工作。

Shader整洁之道

Cg/HLSL中3种精度的数值类型

类 型精 度
float最高精度的浮点值。通常使用32位来存储
half中等精度的浮点值。通常使用16位来存储,精度范围是−60 000~+60 000
fixed最低精度的浮点值。通常使用11位来存储,精度范围是−2.0~+2.0
  • 大多数pc的GPU,以上精度是等价的。
  • 移动平台GPU,是不一致的
  • fixed只在一些较旧的移动设备上有效,在大多数现代移动设备GPU,fixed=half

规范语法

思维导图

--完--