【转载】Shader山下(十七)语义Semantic

128 阅读3分钟

原文链接

版权声明:本文为CSDN博主「凯奥斯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:blog.csdn.net/ecidevilin/…

正文

当在编写 Cg 或者 HLSL 着色器程序的时候,我们需要使用语义(Semantic)来 指明输入输出变量的 “意图”

例如,下面的这段 Shader 代码:

Shader "Custom/VF" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		Pass {
			Tags { "LightMode" = "ForwardBase" }
			
			CGPROGRAM
			
			#pragma multi_compile_fwdbase
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			
			sampler2D _MainTex;
 
			float4 _MainTex_ST;
			
			struct a2v {
				float4 vertex : POSITION;
				fixed3 normal : NORMAL;
				fixed4 texcoord : TEXCOORD0;
				fixed4 color : COLOR;
			};
			
			struct v2f {
				float4 pos : POSITION;
				float2 uv : TEXCOORD0;
			};
 
			v2f vert(a2v v) {
				v2f o;
				
				//Transform the vertex to projection space
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				//Get the UV coordinates
				o.uv = TRANSFORM_TEX (v.texcoord, _MainTex); 
 
				return o;
			}
 
			float4 frag(v2f i) : COLOR {
				fixed4 texColor = tex2D(_MainTex, i.uv);
 
				return texColor;
			}
			ENDCG
		}
	} 
	FallBack "Diffuse"
}

其中 a2v 结构里变量后面用冒号隔开的就是语义。

a2v 作为 vert 函数的输入,它里面所标识的语义用来告诉 Unity 我们需要什么样的数据

此外,我们看到 frag 方法名的后面也添加了 COLOR 语义,这是告诉 Unity,这个方法 输出一个色彩值,然后 Unity 就可以把这个色彩值写入 GPU。

你可能会注意到 vert 的输出 frag 的输入 v2f 结构里的变量也使用了语义。

大部分语义是你可以随便使用的,为了方便 vertfrag 之间传递数据,但是有一个 例外,就是:

POSITION

float4 pos : POSITION;

这个 必须 声明为 float4 类型,并添加 POSITION(或 SV_Position,二者区别稍后会讲到)。因为 GPU 需要知道顶点转换到 裁剪空间(参考 Shader 山下(十六)坐标空间与转换矩阵)的坐标,而这个坐标便是由 vertex shader 提供的。

SV_

Direct3D 10 定义了一种新的语义类型,叫做 系统数值语义System-Value semantics),它们都是以 SV_ 打头。

例如

  • POSITION 对应 SV_POSITION
  • COLOR 对应 SV_Target
  • DEPTH 对应 SV_Depth(深度)。

虽然对于开发者来讲,它们之间并没有什么明显的区别,但是 平台之间的兼容性,却给我们带来了烦恼。

例如

  • XBox one 就只支持 SV 语义,
  • Direct3D 9 就不支持 SV 语义

所以还是 windows 自己家打起来了)。所以开发的时候还需要注意。

此外还有几个 特殊的语义

VPOS

VPOS:屏幕像素坐标,用于片段着色器,需要添加 #pragma target 3.0 编译指令。

例如:

Shader "Unlit/Screen Position"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0
 
            // note: no SV_POSITION in this struct
            struct v2f {
                float2 uv : TEXCOORD0;
            };
 
            v2f vert (
                float4 vertex : POSITION, // vertex position input
                float2 uv : TEXCOORD0, // texture coordinate input
                out float4 outpos : SV_POSITION // clip space position output
                )
            {
                v2f o;
                o.uv = uv;
                outpos = UnityObjectToClipPos(vertex);
                return o;
            }
 
            sampler2D _MainTex;
 
            fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
            {
                // screenPos.xy will contain pixel integer coordinates.
                // use them to implement a checkerboard pattern that skips rendering
                // 4x4 blocks of pixels
 
                // checker value will be negative for 4x4 blocks of pixels
                // in a checkerboard pattern
                screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
                float checker = -frac(screenPos.r + screenPos.g);
 
                // clip HLSL instruction stops rendering a pixel if value is negative
                clip(checker);
 
                // for pixels that were kept, read the texture and output it
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

因为对于 大多数平台VPOS 语义修饰的 屏幕坐标变量 类型是 float4,但是对于 Direct3D9 却是 float2 ,所以要使用 UNITY_VPOS_TYPE 这个宏来作为 screenPos 的类型。

VFACE

VFACE:被渲染的面 是否朝向摄像机,用于片段着色器,需要添加 #pragma target 3.0 编译指令。

VFace.png

例如:

Shader "Unlit/Face Orientation"
{
    Properties
    {
        _ColorFront ("Front Color", Color) = (1,0.7,0.7,1)
        _ColorBack ("Back Color", Color) = (0.7,1,0.7,1)
    }
    SubShader
    {
        Pass
        {
            Cull Off // turn off backface culling
 
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0
 
            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return UnityObjectToClipPos(vertex);
            }
 
            fixed4 _ColorFront;
            fixed4 _ColorBack;
 
            fixed4 frag (fixed facing : VFACE) : SV_Target
            {
                // VFACE input positive for frontbaces,
                // negative for backfaces. Output one
                // of the two colors depending on that.
                return facing > 0 ? _ColorFront : _ColorBack;
            }
            ENDCG
        }
    }
}

SV_VertexID

这是一个 无符号的整形 变量,表示顶点的 ID,用于顶点着色器,需要添加 #pragma target 3.5 编译指令。

例如:

Shader "Unlit/VertexID"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.5
 
            struct v2f {
                fixed4 color : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
 
            v2f vert (
                float4 vertex : POSITION, // vertex position input
                uint vid : SV_VertexID // vertex ID, needs to be uint
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // output funky colors based on vertex ID
                float f = (float)vid;
                o.color = half4(sin(f/10),sin(f/100),sin(f/1000),0) * 0.5 + 0.5;
                return o;
            }
 
            fixed4 frag (v2f i) : SV_Target
            {
                return i.color;
            }
            ENDCG
        }
    }
}