在编写 HLSL 着色器程序 时,输入和输出变量需要通过 语义 来表示它们的“意图”。这是 HLSL 着色器语言中的标准概念;有关更多详细信息,请参阅 MSDN 上的语义文档。
主顶点 着色器在 GPU 上运行的程序。 更多信息
见术语表 函数(由 #pragma vertex
指令指示)在所有输入参数上都需要有语义。这些对应于单个 网格Unity 中的主要图形原语。网格构成了您的 3D 世界的大部分。Unity 支持三角剖分的或四边形网格。Nurbs、Nurms、Subdiv 表面必须转换为多边形。 更多信息
见术语表 数据元素,如顶点位置、法线网格和纹理坐标。有关更多详细信息,请参阅 顶点程序输入。
以下是一个简单示例,该顶点着色器输入顶点位置和纹理坐标。该 像素计算机图像中最小的单位。像素大小取决于您的屏幕分辨率。像素着色在每个屏幕像素上计算。 更多信息
见术语表 着色器将纹理坐标可视化为一个颜色。
Shader "Unlit/Show UVs"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct v2f {
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
v2f vert (
float4 vertex : POSITION, // vertex position input
float2 uv : TEXCOORD0 // first texture coordinate input
)
{
v2f o;
o.pos = UnityObjectToClipPos(vertex);
o.uv = uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.uv, 0, 0);
}
ENDCG
}
}
}
除了逐个列出所有输入之外,还可以声明一个结构体,并指示结构体中每个成员变量的语义。
通常,片段(像素)着色器输出一个颜色,并且有一个 SV_Target
语义。上面的示例片段着色器正是这样做的
fixed4 frag (v2f i) : SV_Target
frag
函数的返回类型为 fixed4
(低精度 RGBA 颜色)。因为它只返回一个值,所以它的语义在函数本身上表示,即 : SV_Target
。
还可以通过返回一个结构体来返回输出。上面的片段着色器也可以这样重写,并且会产生完全相同的效果
struct fragOutput {
fixed4 color : SV_Target;
};
fragOutput frag (v2f i)
{
fragOutput o;
o.color = fixed4(i.uv, 0, 0);
return o;
}
从片段着色器返回结构体主要用于那些不只是返回单个颜色的着色器。片段着色器输出支持的其他语义如下。
SV_Target1
、SV_Target2
等:这是着色器写入的附加颜色。当一次渲染多个渲染目标时(称为多渲染目标渲染技术,或 MRT)使用此功能。 SV_Target0
与 SV_Target
相同。
通常,片段着色器不会覆盖 Z 缓冲区值,而是使用常规三角形 光栅化通过为几何中的每个多边形或三角形计算像素来生成图像的过程。这是光线追踪的替代方案。
见术语表 的默认值。但是,对于某些效果,有必要为每个像素输出自定义 Z 缓冲区深度值。
请注意,在许多GPU上,这会关闭一些深度缓冲区优化,所以除非有充分的理由,否则不要随意覆盖Z缓冲区值。由于SV_Depth
的使用成本取决于GPU架构,但在总体上它与alpha测试的成本(使用HLSL内置的clip()
函数)相当。在所有常规不透明着色器之后修改深度的渲染着色器。例如,可以通过使用AlphaTest
渲染队列。
深度输出值需要是单个float
。
顶点着色器需要输出顶点的最终裁剪空间位置,以便GPU知道在哪里在屏幕上光栅化以及深度是多少。这个输出需要具有SV_POSITION
语义,并且类型为float4
。
顶点着色器产生的任何其他输出(“插值器”或“变量”)都是特定着色器所需的。从顶点输出的值将在渲染三角形的面之间插值,每个像素的值将作为输入传递给片元着色器。
许多现代GPU实际上不关心这些变量的语义;然而,一些旧系统(尤其是着色器模型2的GPU)确实对语义有特别的规定。
TEXCOORD0
、TEXCOORD1
等用于表示任意高精度数据,如纹理坐标和位置。COLOR0
和COLOR1
语义用于低精度、0-1范围内的数据(如简单的颜色值)。为了获得最佳的多平台支持,请将顶点输出和片元输入标记为TEXCOORDn
语义。
可用于从顶点传递信息到片元着色器的插值器变量数量有限。这个限制取决于平台和GPU,一般的指南是:
float4
变量中传输两个纹理坐标(.xy为一个坐标,.zw为第二个坐标)。#pragma target 3.0
)。#pragma target 4.0
)。无论您的特定目标硬件如何,出于性能原因,通常最好使用尽可能少的插值器。
片元着色器可以接收作为一个特殊VPOS
语义正在渲染的像素的位置。该功能只从着色器模型3.0开始存在,因此着色器需要具备?pragma target 3.0
编译指令。
在不同的平台上,屏幕空间位置输入的底层类型各异,因此为了最大可移植性,可以使用UNITY_VPOS_TYPE
类型,它通常是大多数平台上的float4
。
此外,使用像素位置语义会使同时具有裁剪空间位置(SV_POSITION)和VPOS在同一个顶点到片元结构中变得困难。因此,顶点着色器应该输出裁剪空间位置作为一个单独的“out”变量。请参阅下面的示例着色器。
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
}
}
}
片段着色器可以接收一个变量,表示渲染表面是面向相机还是背对相机。这在渲染应该从两侧都能看到的几何体时非常有用——通常用于树叶和类似薄的物体。VFACE
语义输入变量将含有正值表示前向三角形,负值表示后向三角形。
这个特性从着色器模型3.0开始存在,因此着色器需要具有#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
}
}
}
上面的着色器使用清除状态来禁用背面剔除(默认情况下不会渲染后向三角形)。这里是将其应用于不同方向的多个四边形一个类似于平面但边长只有1个单位的基本对象,它使用只有4个顶点,表面位于局部坐标空间的XY平面中。 更多信息
参见术语表网格的结果。
顶点着色器可以接收一个变量,该变量以无符号整数表示“顶点号”。这在您想要从纹理或计算缓冲区中获取额外的顶点数据时非常有用。
这个特性从DX10(着色器模型4.0)和GLCore / OpenGL ES 3开始存在,因此着色器需要具有#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
}
}
}