2.2 Unity的ShaderLab所支持的Shader
在Unity的ShaderLab所提供的结构中,我们既能使用GLSL来写Shader的逻辑代码,也能使用Cg/HLSL来写。如果使用GLSL,则这些代码必须位于GLSLPROGRAM和ENDGLSL关键字之间;如果使用Cg/HLSL,则这些代码必须位于CGPROGRAM和ENDCG之间。我全部使用Cg编程语言编写。Cg语言并非像Mac平台开发的Object-C语言一样,让习惯了C、C++或者Java、 C#的程序员看起来怪怪的。这门语言和C:语言很类似,作为一种Shader编程语言,主要提供了一个全新的函数库。关于Cg的学习,读者可以查看附录,里面有NVIDIA官方的权威教程链接地址。
2.3 Unity中Shader的3种形态
2.3.1固定管线
固定管线是在老代GPU能力比较有限时,对Shader的约束性比较高的一种形态。为了市场占有率,新一代的显卡仍对其有所选择地进行支持,但是会在未来逐步被淘汰。在ShaderLab中,固定管线的形态和语法和NVIDIA的CgFX以及微软的FX文件比较类似。下面是固定管线的一个例子:
Shader "Tut/Shader/FixedFuncs/BaseForm_1’‘{ Properties{ _Color("Main Color",Color)=(1,1,1,0.5) _SpecColor("Spec Color",Color)=(1,1,1,1) _Emission("Emmisive Color",Color)=(0, 0, 0,0) _Shininess ("Shininess", Range(0.01,1)) = 0.7 _MainTex("Base (RGB)”,2D)="white"{ } } SubShader{ Pass{ Material{ Diffuse [ _Color] Ambient [ _Color] Shininess[ _Shininess] Specular [ _ SpecColor] Emission [ _Emission] } Lighting On SeparateSpecular On SetTexture [ _ MainTex] { constantColor [ _ Color] Combine texture*primary DOUBLE, texture*constant } } } }
固定管线的相关代码必须都处于一个Pass块之中。关于Unity中固定管线的详细讲解,可以参考Shader篇的关于固定管线的第16章,这里只说其大概形态。
2.3.2可编程Shader
如果你想自己处理照明,可以写vertex+fragment Shader,这是Unity中对可编程Shade:的一种支持。下面是可编程Shader的一个例子。
Shader "Tut/NewBie/FOUrthShader" Properties{ _MyTexture ("Texture (RGB)”,2D)="white"{ } _MyColor("Color of Object",Color)=(1, 1,1, 1) } SubShader{ Tags{"Queue"="Geometry""RenderType"="Opaque""IgnoreProjector"="True"} Pass{ CGPROGRAM #pragma vertex vert//声明vertex Shader的函数 #pragma fragment frag//声明fragment Shader的函数 #include "UnityCG.cg土nc"//包括外部文件 sampler2D _MyTexture; float4 _MyColor; struct v2f { //定义一个结构 float4 pos:SV_POSITION; }; v2f vert (appdata_full v)//vertex Shader { v2f o; o.pos= mul(UNITY_ MATRIX_MVP, v.vertex); return o; } float4 frag ( v2f i) : COLOR// fragment Shader { return float4(1); } ENDCG } } Fa1lBack "Diffuse" }
除了上面例子中有的,我们还可以使用下面一些指令告诉Unity如何具体地编译Shader。
//编译目标2.0,相当于Direct3D的Shader Model 2.0 //对应于。penGL下的256条ARB vertex shader的指令,32条ARB_fragment_shader的texture指令 //和64条普通指令,16个寄存器和4个贴图 #pragma target 2 .0 //如果编译目标3.0,则相当于Direct3D的Shader Model 3 //对于penGL的ARB vertex shader来说没有指令数目限制,对应ARB_fragment_shader的 //512条texture操作指令、512条普通指令、32个寄存器和4个贴图。
对于面向OpenGL接口的情况,还可以使用#pragma profileoption MaxTexIndirection = 256这样的指令。当编译平台为独立的可执行桌面OpenGL程序时,可以使用#pragma glsl把Cg代码转换为GLSL代码,从而绕过在Shader Model 3.0中的一些指令限制。如果大家熟悉OpenGL接口,也可以使用#pragma fragmentoption option来限制编译出的fragment函数。对于移动平台的GLSL,比如Android和IOS,可以使用#pragma glsl_no_auto_normalizationa来关闭在vertex函数中对法线和切向量的自动单位化处理。
为了增加平台适应性,Unity会把Shader针对不同的平台进行编译,对移动平台Unity做些优化。除此之外,开发者也可以使用下面这两条指令来限制Unity对不同平台的编译。
#pragma only_ renderersd3d9 gles #pragma exclude renderers xbox360
适应于这两条指令的值有d3d9、d3d11、opengl、gles(移动平台的OpenGL)、xbox360、p3(任天堂的PlayStation)、flash。
2.3.3 ShaderLab的骄傲:Surface Shader
如果你想写一个能处理不同的照明、点光源、平行光、光照贴图等,又能够处理不同的阴影选项,还同时能在Unity的两个渲染路径(Forward和Deferred )下正常工作,是一件很复杂、很烦人的事情。Unity通过Surface Shader把上面这一切复杂性包装了起来。下面是这样一个Surface Shader例子,其实也就是在编辑器中创建Shader的默认结果:
Shader "Tut/NewBie/SurfShader"{ Properties { _MainTex("Base (RGB)”,2D) = ”white"{ } } SubShader{ Tags {"RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; struct Input { float2 uv _MainTex; }; void surf (Input IN, inout Surfaceoutput o){ half4 c= tex2D ( _MainTex,IN.uv_MainTex); o.Albedo=c.rgb; o.Alpha = c.a; } ENDCG } Fa1lBack "Diffuse" }在Surface函数surf中,Surface0utput是一个包含大多数描述个物体表面渲染特征的结构,
struct Surfaceoutput{ half3 Albedo;//颜色纹理 half3 Normal;//法线 half3 Emission;//自发光,不受照明的影响 half Specular;//高光指数 half Gloss;//光泽度 half Alpha;//Alpha通道 };