目录:Unity Shader - 知识点目录(先占位,后续持续更新) 原文:Shader semantics 版本:2019.1
Shader semantics着色器语义
当编写HLSL的shader programs着色器程序时,输入和输出变量需要了解他们的用意,使用 semantics (语义)来代表。这是HLSL着色器语言中的一个标准概念;查看 Semantics documentation on MSDN 了解更多详情。
你可以下载下面已展示的例子 zipped Unity project相关项目。 (如果下载不了,或是下载很慢,可以点击(提取码: vddu)这里,从百度网盘下载)
Vertex shader input semantics顶点着色器输入语义
顶点着色器函数(使用 #pragma vertex 编译指令指定的函数)的所有输入参数都需要指定语义。这对应着Mesh的单个顶点的数据,如:顶点坐标,法线,纹理坐标。查看 vertex program inputs 了解更多详情。
这里有个简单的顶点着色器,带有顶点坐标和纹理坐标作为输入参数。像素着色器将纹理坐标直接作为颜色显示。
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, // 输入的顶点坐标
float2 uv : TEXCOORD0 // 输入的纹理坐标,通过语义指定在 TEXCOORD0 寄存器
)
{
v2f o;
o.pos = UnityObjectToClipPos(vertex);
o.uv = uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.uv, 0, 0);
}
ENDCG
}
}
}
在顶点着色器中我们是在函数参数中一个个的定义声明的,也可以在顶点着色器函数中传入结构体,然后再结构体中一个个的声明变量,和语义指定。查看 shader program examples 学习如何实现。
片段着色器输出的语义
大多数片段(像素)着色器都会输出一个颜色值,并使用 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_TargetN: Multiple render targetsSV_Target: 多渲染目标
SV_Target1,SV_Target2,等等:这些是着色器编写的额外颜色。这用于一次性渲染到多目标(就是我们说的:Multiple Render Targets(多渲染目标)的渲染技术,简称:MRT)。SV_Target0和 SV_Target 一样的意思。
SV_Depth: Pixel shader depth outputSV_Depth: 像素着色器深度输出
通常片段着色器不会去覆盖 Z-Buffer的值,Z-Buffer的默认值是从常规的三角形光栅化时就得到的。然而,有些特殊的效果使用自定义输出逐像素的Z-Buffer深度值是很有用的。
在不了解用法意图的时候不要去覆盖原始的Z-Buffer数值。SV_Depth 的消耗因GPU架构的不同而不同,但总体上它与alpha测试的消耗相当类似(使用HLSL中的内置clip()函数)。渲染着色器在渲染常规不透明对象后将会修改深度(例如,使用 AlphaTest 的 rendering queue(渲染队列))。
输出的深度值占用一个单精度 float 数值类型。
Vertex shader outputs and fragment shader inputs顶点着色器的输出与片段着色器的输入
一个顶点着色器需要输出顶点最终裁剪空间的坐标,这样GPU才知道对应屏幕中光栅化的位置与深度。输出这些数值需要 SV_POSITION 语义来指定给一个 float4 类型的数值。
其它的输出根据你自己的着色器来定。顶点着色器输出的数值,将会被渲染出来三角面上对应的插值像素处理,然后这些所有插值出来的像素都将会传入到片段着色器中。
许多现代 GPU 都不太关心变量语义的指定;然而在一些老旧的系统(最值得注意的是,DX9的SM 2.0(着色器模型 2.0))中语义会有一些特定的规则:
- TEXCOORD0,TEXCOORD1 等等用于表示高精度的数据,如:纹理坐标或是其他顶点坐标。
- COLOR0,COLOR1 语义在顶点输出和片段输入中用于低精度,0-1范围的数据(如:简单的颜色数值)。
为了能够跨平台支持,顶点输出和片段输入都是用 TEXCOORDn 的语义来指定。
查看 shader program examples 例子。
Interpolator count limits插值器的数量限制
将用于顶点传到片段着色器的变量插值器的数量是有限的。具体限制数量依赖于平台和GPU,通常参考如下:
- Up to 8 interpolators (8个):OpenGL ES 2.0(iOS/Android),Direct3D 11 9.X 级别的(Windows Phone) 和 Direct3D 9 shader model 2.0(旧版PC)。所以插值器的数量是有限的,但没有插值器向量都有4个分量,shader中通常会充分利用这些分量,将打包数据放满到分量中使用。例如,两个纹理坐标可以传入到一个 float4 的变量中(.xy是第一个坐标的,.zw是第二个坐标的)。
- Up to 10 interpolators (10个):Direct3D 9 shader model 3.0 (#pragma target 3.0)。
- Up to 16 interpolators (16个): OpenGL ES 3.0(iOS/Android),Metal(iOS)。
- Up to 32 interpolators (32个):Direct3D 10 shader model 4.0(#pragma target 4.0)。
不论你的具体的目标硬件是怎么样的,通常为了性能原因尽可能使用少量的插值器来处理。
Other special semantics其他特殊的语义
Screen space pixel position: VPOS屏幕空间像素的坐标:VPOS
片段着色器能接收一个渲染为特定的语义 VPOS 的像素坐标。这个功能仅从 shader model 3.0 开始才有的,所有需要 shader 指定 #pragma target 3.0 编译指令。
在不同平台下屏幕空间坐标输入变量的类型也有会有所不同,所以为了最大化的可移植性,使用 UNITY_VPOS_TYPE 类型(在多数平台使用的是 float4,且在 Direct3D 9使用的是 float2)。
此外,使用像素坐标语义使剪辑空间位置(SV_POSITION)和VPOS很难同时位于同一个vertext -to-fragment结构体中。所以顶点着色器应该输出裁剪空间坐标为 “out” 变量。查看下面的shader例子:
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 将包含像素的整数坐标值。
// 用它们实现棋盘团来,替代渲染4x4的像素块(4x4纹理)
// 棋盘图案每个格将使用4x4的像素块
screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
float checker = -frac(screenPos.r + screenPos.g);
// 如果checker为负数,则使用HLSL 内置函数 clip 来停止绘制该像素
clip(checker);
// 保持绘制的像素将采样纹理值并输出
fixed4 c = tex2D (_MainTex, i.uv);
return c;
}
ENDCG
}
}
}
面的朝向: VFACE
片段着色器能接收一个代表当前渲染出来的表面是否朝向相机的变量。当渲染的几何体需要显示双面时是很有用的,通常用于树叶或是其他类似很薄的对象。 VFACE 语义指定给输入变量后,正值代表该光栅出来的三角面是正面,负值是背面。
这个功能是再shader model 3.0或之后才有的,所以shader需要 #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 // 关闭背面剔除
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 输入值 正值代表正面,负值代表背面。
// 输出正面颜色_ColorFont 还是背面颜色_ColorBack 取决于 facing值得正负
return facing > 0 ? _ColorFront : _ColorBack;
}
ENDCG
}
}
}
上面的shader使用了 Cull 状态设置来关闭背面剔除(默认三角面的背面是不渲染的)。这里该shader 应用于一堆旋转朝向不一的四边形网格。
顶点ID: SV_VertexID
顶点着色器能接收一个代表顶点编号的无符号整数。当你想从纹理或ComputeBuffers (计算缓存数据)中获取其他的顶点数据时是很有用的。
这个功能仅在 DX10(shader model 4.0)和 GLCore / OpenGL ES 3中存在,所以shader需要 #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, // 输入的顶点坐标
uint vid : SV_VertexID // 顶点ID,必须为uint类型
)
{
v2f o;
o.pos = UnityObjectToClipPos(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
}
}
}
你可以下载上面已展示的例子 zipped Unity project相关项目。 (如果下载不了,或是下载很慢,可以点击(提取码: vddu)这里,从百度网盘下载)