Unity-Shader读书笔记(4)
一个最简单的顶点/片元着色器
UnityShader代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| Shader "Learn/SimpleShaderr" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag
float4 vert(float4 v : POSITION) : SV_POSITION { return UnityObjectToClipPos(v); }
fixed4 frag() : SV_TARGET0 { return fixed4(1.0,1.0,1.0,1.0); }
ENDCG } } }
|
PS:书中是mul(UNITY_MATRIX_MVP,v),但是我的编译器2019.4.40f1自动将其变成了UnityObjectToClipPos(v)。Unity的解释:Upgrade
NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with
'UnityObjectToClipPos(*)
现在我们来了解一下上述的GC代码。所有的CG代码由CGPROGRAM和ENDCG包裹。然后我们会遇到两行非常重要的编译指令。
1 2
| #pragma vertex vert // 指定顶点 #pragma fragment frag // 指定片元
|
这两行告诉Unity哪个函数包含了顶点着色器的代码,哪个包含了片元着色器的代码。更加通用的写法如下:
1 2
| #pragma vertex name // 指定顶点 name表示指定的函数名 #pragma fragment name // 指定片元 name表示指定的函数名
|
name不一定要是我们上面定义的vert或是frag。只要符合函数规范的都可以。下面展示的就是例子中的顶点着色器。
1 2 3 4
| float4 vert(float4 v : POSITION) : SV_POSITION { return UnityObjectToClipPos(v); }
|
通过POSITION语义,我们指定vert函数输入v包含了顶点信息,然后使用UnityObjectToClipPos函数将模型空间中的顶点变成裁剪空间中的顶点给片元着色器。上面我们可以看到两个语义:POSITION和SV_POSITION。他们都是CG/HLSL的语义,它们也不可省略。这些语义会告诉系统用户需要哪些输入值,以及用户的输出是什么。比如POSITION就是告诉Unity把模型的顶点位置填充到输入参数v中,SV_POSITION告诉Unity顶点着色器输出的是裁剪空间下的坐标。着色器语义
- Unity 手册
最后的片元着色器:
1 2 3 4
| fixed4 frag() : SV_TARGET0 { return fixed4(1.0,1.0,1.0,1.0); }
|
在例子中frag没有任何的输入,它仅仅输出一个fixed4类型的变量,并且使用了SV_TARGET0语义进行限定(在我这个Unity版本中SV_TARGET等同于SV_TARGET0)。而片元着色器最终的输出就表示其物体显示的颜色。
更多数据的例子
上面的例子中我们仅使用了POSITION来获取到模型的顶点位置,如果我们想要更多的模型数据呢?比如模型上每个顶点的法线方向和纹理坐标。因此我们需要为顶点着色器定义一个新的输入参数,这个参数不再是简单的数据类型而是一个结构体。例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| Shader "Unlit/MoreDataShader" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; };
float4 vert(a2v v) : SV_POSITION { return UnityObjectToClipPos(v.vertex); }
fixed4 frag() : SV_TARGET0 { return fixed4(1.0,1.0,1.0,1.0); }
ENDCG } } }
|
这里我们定义了一个a2v的结构体,自定义的结构体我们必须使用如下的格式来定义它:
1 2 3 4 5
| struct StructName{ Type Name : Semantic; Type Name : Semantic; ...... }
|
这里语义是不能被省略的。
顶点着色器和片元着色器之间的通信
我们希望将顶点着色器中的一些数据传给片元着色器。比如模型的法线、纹理坐标等。所以我们要再定义一个结构体来表示片元着色器的数据。例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| Shader "Learn/FinalSimpleShader" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; };
struct v2f { float4 pos : SV_POSITION; float3 color : COLOR0; };
v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.color = v.normal * 0.5 + fixed3(0.5,0.5,0.5); return o; }
fixed4 frag(v2f i) : SV_TARGET0 { return fixed4(i.color,1.0); }
ENDCG } } }
|
Ps:这时候顶点着色器函数的定义变成了v2f而不是float4,如果大家没有改过来就会报cannot convert from 'struct v2f' to 'float4'
的错误
在上面的代码中,我们声明了一个新的结构体v2f来进行顶点着色器和片元着色器间的通信。我们需要注意的是顶点着色器是逐顶点而片元着色器是逐片元的。所以片元着色器中的输入是依靠顶点着色器的输出做插值得到的。
使用属性的Shader
在Unity
Shader中,我们可以通过设置属性与材质中的数据进行联系。从而我们只需要设置Unity
Shader的属性就可以更改材质的数据。现在我们有一个需求,我们希望材质所显示的颜色可以在Inspector面板上进行调试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| Shader "Learn/ShaderUseProperties" { Properties { _Color ("Color Select", Color) = (1,1,1,1) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag
fixed4 _Color;
struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcood : TEXCOORD0; };
struct v2f { float4 pos : SV_POSITION; fixed3 color : COLOR0; };
v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.color = v.normal * 0.5 + fixed3(0.5,0.5,0.5); return o; };
fixed4 frag(v2f i) : SV_TARGET0 { fixed3 rgb = i.color; rgb *= _Color.rgb; return fixed4(rgb,1); }
ENDCG } } }
|
上面的代码中,我们添加了Properties语义块,并在其中声明了一个属性_Color,它的类型就是Color且初始值为(1,1,1,1)即白色。为了在CG代码块中能够使用它,我们还要再CG代码中定义一个变量,这个变量的名称和类型必须和Properties语义块中的属性定义相匹配。从上面我们可以知道ShaderLab中属性的类型Color对应的CG中变量的类型是fixed4。更多的对应项可以查看官方的文档使用
Cg/HLSL 访问着色器属性 - Unity 手册。
Unity提供的内置文件和变量
我们可以查看官网中的内置着色器
include 文件 - Unity
手册信息来了解。或是直接查看Unity应用程序的CGIncludes文件夹中的信息。在Windows平台下的位置为Unity安装路径/Data/CGIncludes。