上一篇传送门!!!
在Unity Shader 中,ShaderLab语言只是起到阻止代码结构作用,而真正实现渲染效果的部分是用CG语言编写的。
CG程序片段通过指令嵌入在Pass中,夹在指令CGPROGRAM和ENDCG之间,通常结构如下:
Pass
{
// ...设置渲染状态...
CGPROGAM
//编译指令
#pragma vertex vert
# pragma fragment frag
//CG代码
ENDCG
}
在CG程序片段之前,通常需要先使用 #pragma声明编译指令,下面是总结出来的表格:
编译指令 | 作用 |
---|---|
# pragma vertex name | 定义顶点着色器的名称,通常会使用 vert |
# pragma fragment name | 定义片段着色器的名称,通常会使用 frag |
# pragma target name | 定义 Shader 要编译的目标级别,默认2.5 |
下面是一个最简单的Shader代码片:
Shader "Custom/Simplest Shader"
{
SubShader
{
Pass
{
// ...设置渲染状态...
CGPROGAM
//编译指令
#pragma vertex vert //定义顶点着色器的名称
# pragma fragment frag //定义片段着色器的名称
//通常情况下,为了代码的可读性和易用性考虑,一般都会使用vert和frag来命名。
//CG代码
void vert (in float4 vertex : POSITION,
out float4 position : SV_POSITION)
{
position = UnityObjectToClipPos(vertex);
}
void frag (in float4 vertex : SV_POSITION,
out fixed4 color : SV_TARGET)
{
color = fixed4(1,0,0,1);
}
ENDCG
}
}
}
这是一个顶点-片段着色器(Simple Shader)。在这个Shader中只包含一Subshader中只包含一个Pass,最核心的CG程序嵌套在CGPROGAM和ENDCG之间。
在这个代码块主要是通过 顶点函数和片段函数来实现的。
函数一般有无返回值和返回值,其中无返回值就是函数不会返回任何变量,而是通过out关键词将变量输出,上述Simple Shader中的顶点函数和片段函数使用的就是无返回值的函数,
无返回值函数的语法结构如下:
void name (in 参数 , out 参数)
{
//函数体
}
关键词解释:
1).void: 表示返回空值。
2).name:定义函数的名称,后续可以通过这个名称调用函数。
3).in:输入参数,语法:in + 数据类型 + 名称,一个函数可以有多个输入,关键词in可以省略。
4).out : 输出参数,语法为:out + 数据类型 + 名称,一个函数可以有多个输出。
v顶点函数):来看下面这段:
void vert (in float4 vertex : POSITION, out float4 position : SV_POSITION)
{
position = UnityObjectToClipPos(vertex);
}
顶点着色器输入一个float4类型的数据,名称为vertex,经过Unity内置空间变换函数UnityObjectToClipPos把模型空间坐标转换到裁切空间坐标,然后输出为float4类型的position。
frag):来看下面这段:
void frag (in float4 vertex : SV_POSITION, out fixed4 color : SV_TARGET)
{
color = fixed4(1,0,0,1);
}
顶点函数输出的顶点坐标输入到片段函数之后,最终输出float4类型的数据,名称为color。在函数内color变量被赋予值为(1,0,0,1),红色。最终效果现实为红色。
有返回值的函数不在使用out关键词输出参数,而是会在最后通过return关键词返回一个变量,语法结构是这样的:
type name (in 参数)
{
//函数体
return 返回值;
}
数据类型 | 描述 |
---|---|
fixed,fixed2,fixed3,fixed4 | 低精度浮点值,使用11位精度进行存储,数值区间[-2,0,2,0],用于存储颜色、标准化后的向量。 |
half,half2,half3,half4 | 中精度浮点值,使用16 位精度进行存储,数值区间为[-60000,60000] |
float,float2,float3,float4 | 高精度浮点值,使用 32 位精度进行存储,用于存储顶点坐标、未标准化的向量、纹理坐标等 |
struct | 结构体,可以将多个变量整体进行打包 |
标准化向量:标准化向量指的是那些长度为1的向量,标准化向量也被称为归一化的向量或者单位向量。
下面是把Simplest Shader 中无返回值的函数改写为有返回值的函数,改写后的代码如下:
Shader "Custom/Simplest Shader"
{
SubShader
{
Pass
{
CGPROGAM
#pragma vertex vert //定义顶点着色器的名称
# pragma fragment frag //定义片段着色器的名称
//CG代码
float4 vert (in float4 vertex : POSITION) : SV_POSITION
{
//返回裁切空间顶点坐标
return UnituObjectToClipPos(vertex);
}
fixed4 frag (in float4 vertex : SV_POSITION) : SV_TARGET)
{
//返回颜色值
return fixed4(1,0,0,1);
}
ENDCG
}
}
}
改写完后的两个Shader最终现实的效果是一样的。
不论有没有返回值,在输入和输出的参数后面都会有一个冒号 : ,然后跟一个全是大写的关键词,这些关键词就是语义。这里解释一下,这些是CG/HLSL提供的语义,它们是用来传递数据信息的。SV的含义是系统数值(system-value),其中POSITION和SV_POSITION的区别在于应用的平台不一样,例如在索尼PS4上必须使用SV_POSITION来修饰顶点着色器的输出,否则无法正常运行。同时也欢迎读者来补充。
关于语义部分,我会放在下一篇。