上一篇博客 介绍了Shader的基本结构,这里我们继续来说Shader的编写,也就是要在 CGPROGRAM
中写代码。首先我们把之前的Shader结构代码复制过来。
1 | Shader "iMoeGirl/MyShader" { // Shader 名字 |
怎样使用 Properties 中定义的属性
Unity3D定义Shader属性所使用的语法,和CG所使用的说法是不一样的,所以我们要在一个Pass中使用Properties中定义的属性,需要在Pass中再以CG的语法再写一遍,其实就是变量名相同,而数据类型不同,在Shader在编译的时候,就会自动将两个变量关联起来。看下面的代码
1 | Shader "iMoeGirl/MyShader" { // Shader 名字 |
从上面代码可以看出,对于 Unity 的数据类型,有4个值的,都可以使用 float4 来对应,而有1个值的,可以使用 float 来对应,2D 贴图类型在 CG 中对应的是 sampler2D,3D 纹理对应的是 sampler3D,等等
对于CG来说,有float, float2, float3, float4, half, half2, half3, half4, fixed, fixed2, fixed3, fixed4 等等,最多不能超过4元向量,float 系列和 half 系列以及 fixed系列可以互相替换,但是有什么区别呢? 他们都是用来表示浮点数的,只是精度不同,float 系列是32位浮点数,而 half 系列是16位,fixed 系列是12位。例如,我们如果要表示颜色,完全可以用 fixed4 就足够了,因为Color的每一元素值最大就是1,fixed4 完全满足。在 Pass 中定义了 Properties 中的同名变量,也就可以在 CGPROGRAM 中使用了。
顶点处理函数和片元处理函数
这里我们先说另一个问题,两个重要的函数,一个是顶点处理函数,一个是片面处理函数。通俗来讲,顶点处理函数就是处理顶点的,一个模型的每一个顶点都会传入顶点处理函数被处理后返回。而片元处理函数是处理像素的,一个模型经过各种计算后被转换成屏幕的上像素,而每一个像素都是会经过片元处理函数处理的。而我们定义的顶点处理函数和片元处理函数,都是会被系统自动调用的,不需要我们来调用,就像写 MomoBehaviour 脚本时的 Awake、Start、Update 等函数。
1 | Shader "iMoeGirl/02 Second Shader" { |
注意看上面代码中的注释,这里稍微做一下解释,#pragma vertex vert
就是告诉系统我们的顶点处理函数是什么,vert 就是函数名,当然不一定叫这个,可以随便命名,例如 #pragma vertex myvert
都可以。下面的 #pragma fragment frag
当然就是声明片元函数的。
1 | float4 vert(float4 v : POSITION) : SV_POSITION { |
CG 中函数的定义和普通编程差不多,这里有一点不同就是 : 后面的,是语义,可以理解为告诉系统需要什么数据。例如参数 v 是用 POSITION 语义来描述的,就是告诉系统,这里的 v,表示需要坐标值,就是让系统把顶点的坐标传进来,而后面的 SV_POSITION 是描述返回值的,也就是需要返回裁剪空间中的定点坐标,这也是顶点函数做的最重要的事情,也就是把顶点从模型空间转换到裁剪空间。函数中的 UnityObjectToClipPos(v);
是Unity的内置函数,也就是这个转换过程不需要我们自己写,直接调用 Unity 的函数就可以了。
再来看一下片元函数
1 | float4 frag() : SV_TARGET { |
这个函数返回的是一个 float4 类型的,而 SV_Target 语意则说明了返回的是一个颜色值。注意函数里面我们使用了 fixed4,而函数名前使用了 float4,这是完全可以的。
在Unity中新建一个材质,然后使用我们新编写的Shader,Shader名是 02 Second Shader,把材质赋予一个胶囊体或者立方体都行,就可以看到一个纯白的东西显示在屏幕中。可以尝试自己修改一下片元函数中的返回值,例如改成 return fixed4(1, 0, 0, 1)
这时就会变成红色。前三位分别代表RGB三通道的值,最后一位是Alpha,也就是透明通道的值。