一、顶点/片元着色器基本结构
直接上代码:
这个着色器可以得到蓝色的纯色输出,如果顶点着色器得出了错误的裁剪空间坐标,那么会出现很明显表现错误
Shader "Jaihk662/NewSurfaceShader"
{
Properties
{
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 200
PASS
{
CGPROGRAM
#pragma vertex vert //声明顶点着色器的函数
#pragma fragment frag //声明片段着色器的函数
float4 vert(float4 v: POSITION): SV_POSITION {
return UnityObjectToClipPos(v); //将顶点乘上MVP矩阵,转入裁剪空间,同等于旧版的 mul(UNITY_MATRIX_MVP, v)
}
fixed4 frag(): SV_Target {
return fixed4(0.0, 0.0, 1.0, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
很多语句已经在 UnityShader4:UnityShader的形式 这一章提到过了,因此不再记录
#pragma vertex [Name]:告诉Unity哪个函数包含了顶点着色器的代码,函数名随意,片段着色器同理
POSITION 和 SV_POSITION 则是 CG/HLSL 中的语义 (semantics),它们是不可省略的,这些语义将告诉系统用户需要哪些输入值,以及用户的输出是什么
目前常见的语义如下:
- POSITION:让 Unity 将模型的顶点坐标填入对应的参数(代码中是参数 v)
- NORMAL/TEXCOORD0~9/TANGENT:同上,对应法向量、第x套纹理坐标、切向量
- SV_POSITION:Unity 顶点着色器输出的裁剪空间中的顶点坐标,顶点着色器的数据结构中必须包含此变量
- COLOR0~9:Unity 顶点着色器输出的自定义数据,一般都用于指定颜色
- SV_Target:通知渲染器把用户的输出颜色存储到某个渲染目标(render target)中,这里将输出到默认帧缓冲
填充到 POSITION/TANGENT/NORMAL 这些语义中的数据来源于材质中的 MeshRender 组件,在每帧调用 DrawCall 的时候 MeshRender 组件就会把它负责渲染的模型数据发送给 UnityShader
二、数据传递
上面那个例子将模型的顶点位置传递给了顶点着色器,但是除了位置,顶点还有纹理坐标和法线方向等属性,如果这些数据也需要拿来参与计算,那么最好把它们放在一个结构体里统一定义,并将整个结构体作为参数传入顶点着色器
struct vertData {
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 texcoord: TEXCOORD0;
};
float4 vert(vertData v): SV_POSITION {
return UnityObjectToClipPos(v.vertex); //将顶点乘上MVP矩阵,转入裁剪空间,同等于旧版的 mul(UNITY_MATRIX_MVP, v)
}
顶点着色器到片段着色器的数据传递也是类似的实现:
struct vert2frag {
float4 pos: SV_POSITION;
fixed4 color: COLOR0;
};
vert2frag vert(vertData v) {
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = fixed4(v.normal * 0.5, 1);
return o;
}
fixed4 frag(vert2frag i): SV_Target {
return i.color;
}
三、Properties使用
前面并没有在 Properties 语义块中定义任何属性,所有的属性都是内置的,那么在 Properties 语义块中定义的属性该如何使用呢?
很简单,只需要在下面的 CG 代码块中声明一个同名同类型的属性就可以了:
本章完整的代码和测试效果:
Shader "Jaihk662/NewSurfaceShader"
{
Properties
{
_Color ("Color", Color) = (0.0, 0.0, 1.0, 1.0)
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 200
PASS
{
CGPROGRAM
#pragma vertex vert //声明顶点着色器的函数
#pragma fragment frag //声明片段着色器的函数
fixed4 _Color;
struct vertData {
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 texcoord: TEXCOORD0;
};
struct vert2frag {
float4 pos: SV_POSITION;
fixed4 color: COLOR0;
};
vert2frag vert(vertData v) {
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = _Color;
return o;
}
fixed4 frag(vert2frag i): SV_Target {
return i.color;
}
ENDCG
}
}
FallBack "Diffuse"
}
同名好理解,但是同类型呢?Properties 语义块好像没有声明对应的类型啊,该怎么对应呢?
这是因为同一属性可以用多个不同的类型表示,所以 Properties 语义块中无需提前声明,在 UnityShader3:ShaderLab 这一章中提到了 Properties 语义块支持的属性类型,下面是对其表的一个补充,列出了 ShaderLab 属性类型和 CG 变量类型的匹配关系
其中左侧为 ShaderLab 属性类型,右侧为对应的 CG 变量类型(对应任意其一即可)
- Color, Vector:float4, half4, fixed4
- Range, Float:float, half, fixed
- 2D:sampler2D
- Cube:samplerCube
- 3D:sampler3D