Unity3D Shader 入门
什么是Shader
Shader(着色器)是一段能够针对3D对象进行操作、并被GPU所执行的程序。Shader并不是一个统一的标准,不同的图形接口的Shader并不相同。OpenGL的着色语言是GLSL, NVidia开发了Cg,而微软的Direct3D使用高级着色器语言(HLSL)。而Unity的Shader 是将传统的图形接口的Shader(由 Cg / HLSL编写)嵌入到独有的描述性结构中而形成的一种代码生成框架,最终会自动生成各硬件平台自己的Shader,从而实现跨平台。
Unity Shader 其实并不难,初学者往往很迷惑是因为它有太多固定的命令和结构,而这些命令又需要我们对3D渲染有一定的了解才能知道它们是做什么的。
Shader种类
OpenGL和Direct3D都提供了三类着色器:
?
顶点着色器:处理每个顶点,将顶点的空间位置投影在屏幕上,即计算顶点的二维
坐标。同时,它也负责顶点的深度缓冲(Z-Buffer)的计算。顶点着色器可以掌控顶点的位置、颜色和纹理坐标等属性,但无法生成新的顶点。顶点着色器的输出传递到流水线的下一步。如果有之后定义了几何着色器,则几何着色器会处理顶点着色器的输出数据,否则,光栅化器继续流水线任务。
? 像素着色器(Direct3D),常常又称为片断着色器(OpenGL):处理来自光栅化器的数
据。光栅化器已经将多边形填满并通过流水线传送至像素着色器,后者逐像素计算颜色。像素着色器常用来处理场景光照和与之相关的效果,如凸凹纹理映射和调色。名称片断着色器似乎更为准确,因为对于着色器的调用和屏幕上像素的显示并非一一对应。举个例子,对于一个像素,片断着色器可能会被调用若干次来决定它最终的颜色,那些被遮挡的物体也会被计算,直到最后的深度缓冲才将各物体前后排序。
? 几何着色器:可以从多边形网格中增删顶点。它能够执行对CPU来说过于繁重的生
成几何结构和增加模型细节的工作。Direct3D版本10增加了支持几何着色器的API, 成为Shader Model 4.0的组成部分。OpenGL只可通过它的一个插件来使用几何着色器。
Unity Shader 分为 表面着色器(Surface Shader)和 顶点片段着色器(Vertex And Fragment Shader)。
?
表面着色器(Surface Shader)是Unity提出的一个概念。编写着色器与光照的交
互是复杂的,光源有很多类型,不同的阴影选项,不同的渲染路径(正向和延时渲染),表面着色器将这一部分简化。Unity建议使用表面着色器来编写和光照有关的Shader。
? 顶点片段着色器(Vertex And Fragment Shader)和OpenGL,Direct3D中的顶
点着色器和片段着色器没有什么区别。顶点片段着色器比表面着色器使用更自由也更强大,当然光照需要自行处理。Unity也允许在里面编写几何着色器,一般用得不多。
Shader程序结构
//Shader语法: Shader \ //Properties 语法 Properties { Property [Property ...] } // Subshader 语法 Subshader { [Tags] [CommonState] Passdef [Passdef ...] } // Pass 语法 Pass { [Name and Tags] [RenderSetup] } // Fallback 语法 Fallback \? 属性定义(Property Definition):定义Shader的输入,这些输入可以在材质编
辑的时候指定
? 子着色器(SubShader):一个Shader可以有多个子着色器。这些子着色器互不
相干且只有一个会在最终的平台运行。编写多个的目的是解决兼容性问题。Unity会自己选择兼容终端平台的Shader运行。
? 回滚(Fallback):如果子着色器在终端平台上都无法运行,那么使用Fallback指
定的备用Shader,俗称备胎。
? Pass:一个Pass就是一次绘制。对于表面着色器,只能有一个Pass,所以不存在
Pass节。顶点片段着色器可以有多个Pass。多次Pass可以实现很多特殊效果,例如当人物被环境遮挡时还可以看到人物轮廓就可以用多Pass来实现。
? Cg代码:每个Pass中都可以包含自定义的Cg代码,从CGPROGRAM开始到
ENDCG结束。
基本的表面着色器示例:
Shader \ Properties { _MainTex (\ } SubShader { Tags { \ 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 } FallBack \ } 基本的顶点片段着色器示例:
Shader \ SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include \struct v2f { float4 pos : SV_POSITION; fixed4 color : COLOR; }; v2f vert (appdata_base v) { v2f o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.color.xyz = v.normal * 0.5 + 0.5; o.color.w = 1.0; return o; } fixed4 frag (v2f i) : SV_Target { return i.color; } ENDCG } } } Shader 输入 Shader的输入有两个来源,一是通过属性定义,一是通过Shader.SetGlobalXXX方法全局设置。 ?
属性定义变量:属性定义中的变量是Shader参数的主要设置方式。 它是随材质变化的,每个使用该Shader的材质都可以在Inspector或者脚本中设置这些参数。这些参数除了在