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通道
};
Surface Shader最终会被编译为一个复杂的 vertex+fragment Shader,不过通过Surface Shader,开发者需要做的就是简单地调色,当然这个说法比较笼统,可以自己实际体会一下。关于Surface Shader的更详细说明在以后的Surface Shader一章中。