【Unity Shader】Unity提供的CG/HLSL语义

发表于2018-11-01
评论0 4.6k浏览
什么是语义

语义实际就是一个赋给Shader输入和输出的字符串,这个字符串表达了这个参数的含义。这些语义可以让Shader知道从哪里读取数据,并把数据输到哪里。

在DirectX10以后,有了一种新的语义类型,即系统数值语义(system-valuesemantics)。这类语义是以SV开头的,SV代表的就是系统数值。这些语义在渲染流水线中有特殊的含义,下面就来看看Unity提供的CG、HLSL语义吧。

Unity支持的语义

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

从顶点着色器传递给片元着色器时Unity支持的常用语义,表如下:

片元着色器输出时Unity支持的常用语义,表如下:

Unity中对UnityShader的调试方法:

1、使用假彩色图像
假彩色图像(false_colorimage)指的是用假彩色技术生成的一种图像。一张假彩色图像可以用于可视化一些数据,那么如何用它来对Shader进行调试呢?

主要思想是,我们可以把需要调试的变量映射到[0,1]之间,把它们作为颜色输出到屏幕上,然后通过屏幕上显示的像素颜色来判断这个值是否正确。

作为实例,下面我们会使用假彩色图像的方式来可视化一些模型数据,如法线、切线、纹理坐标、顶点颜色,以及它们之间的运算结果等,代码如下:
Shader"UnityShaderBook/Chapter5/FalseColor"{  
	SubShader{  
		Pass{  
			CGPROGRAM  
			#pragmavertexvert  
			#pragmafragmentfrag  
			#include"UnityCG.cginc"  
			structv2f{  
				float4pos:SV_POSITION;  
				fixed4color:COLOR0;  
			};  
			v2fvert(appdata_fullv)  
			{  
				v2fo;  
				o.pos=UnityObjectToClipPos(v.vertex);  
				//可视化法线方向  
				o.color=fixed4(v.normal*0.5+fixed3(0.5,0.5,0.5),1.0);  
				//可视化切线方向  
				o.color=fixed4(v.tangent*0.5+fixed3(0.5,0.5,0.5),1.0);  
				//可视化副法线方向  
				fixed3binormal=cross(v.normal,v.tangent.xyz)*v.tangent.w;  
				o.color=fixed4(binormal*0.5+fixed3(0.5,0.5,0.5),1.0);  
				//可视化第一组纹理坐标  
				o.color=fixed4(v.texcoord.xy,0.0,1.0);  
				//可视化第二组纹理坐标  
				o.color=fixed4(v.texcoord1.xy,0.0,1.0);  
				//可视化第一组纹理坐标的小数部分  
				o.color=frac(v.texcoord);  
				if(any(saturate(v.texcoord)-v.texcoord))  
				{  
					o.color.b=0.5;  
				}  
				o.color.a=1.0;  
				//可视化第二组纹理坐标的小数部分  
				o.color=frac(v.texcoord1);  
				if(any(saturate(v.texcoord1)-v.texcoord1))  
				{  
					o.color.b=0.5;  
				}  
				o.color.a=1.0;  
				//可视化顶点颜色  
				o.color=v.color;  
				returno;  
			}  
			fixed4frag(v2fi):SV_Target{  
				returni.color;  
			}  
				ENDCG  
		}  
	}  
}  

上面的代码中,我们使用了Uniyt内置的一个结构体——appdata_full。可以在UnityCG.cginc里找到它的定义:
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;
    float4 texcoord2 : TEXCOORD2;
    float4 texcoord3 : TEXCOORD3;
    fixed4 color : COLOR;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};
可以看出,appdata_full 几乎包含了所有的模型数据。

我们把计算得到的假彩色存储到了顶点着色器的输出结构体——v2f中的color 变量里,并且在片元着色器中输出了这个颜色。我们可以对其中的代码进行不同的注释,来观察不同运算和数据得到的效果。

Shader 整治之道

1、float、half、fixed
在CG/HLSL中,有三种精度的数值类型:float、half、fixed。这些精度将决定计算结果的数值范围,精度范围如下表:
上面的精度范围并不是绝对正确的,在不同的平台和GPU上,可能会有所不同。

尽可能使用精度较低的类型,因为这可以优化Shader的性能,这一点在移动平台上尤其重要。从它们大体的值域范围来看,我们可以使用fixed类型来存储颜色和单位矢量,如果要存储更大范围的数据可以选择half 类型,最差情况下再选择使用float。

2、避免不必要的计算
如果我们在Shader 中,尤其是片元着色器中,进行了大量的计算,那么很可能就会收到Unity的错误提示。不同的Shader Target、不同的着色器阶段,我们可以使用的临时寄存器和指令数目都是不同的。如下表:

3、慎用分支和循环语句
如果我们在Shader中使用了大量的流程控制语句,那么这个Shader的性能可能会成倍下降。解决方法就是,我们尽量把计算向流水线上端移动,比如,把放在片元着色器中的计算放到顶点着色器中,或者直接在CPU中进行预计算,再把结果传递给Shader。

4、不要除以0
不然结果不可预测。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引