原文链接
版权声明:本文为CSDN博主「凯奥斯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
正文
当在编写 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
结构里的变量也使用了语义。
大部分语义是你可以随便使用的,为了方便 vert
和 frag
之间传递数据,但是有一个 例外,就是:
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
编译指令。
例如:
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
}
}
}