首页 > 代码库 > UnityShader入门精要-3.5 UnityShader的形式
UnityShader入门精要-3.5 UnityShader的形式
UnityShader可以做的事情非常多(例如设置渲染状态等),但是其最重要的任务还是指定各种着色器所需的代码。这些着色器代码可以写在SubShader语义块中(表面着色器的做法),也可以写在Pass语义块中(定点/片元着色器和固定函数着色器的做法)。
在Unity中,我们可以使用下面3中形式来编写UnityShader。而不管使用哪种形式,真正意义上的Shader代码都需要包含在ShaderLab语义块中,如下所示:
Shader "MyShader"{ Properties{ //所需的各种属性 } SubShader{ //真正意义上的Shader代码会出现在这里 //表面着色器(Surface Shader)或者 //定点/片元着色器(Vertex/Fragment Shader)或者 // 固定函数着色器 (Fixed Function Shader) } SubShader{ //和上一个SubShader类似 } }
- 表面着色器
表面着色器(Surface Shader)是Unity自己创造的一种着色器代码类型。它需要的代码量很小,Unity在背后做了很多工作,但渲染的代价比较大。它在本质上和下面要讲到的定点/片元着色器是一样的。也就是说,当Unity提供一个表面着色器的时候,它在背后仍旧把它转换成对应的顶点/片元着色器。我们可以理解成,表面着色器是Unity对顶点/片元着色器的更高一层的抽象。它存在的价值在于,Unity为我们处理了很多光照细节,使得我们不需要操心这些“烦人的事情”。
一个非常简单的表面着色器的示例代码如下:
Shader "Custom/Simple Surface Shader"{ SubShader{ Tags{ "RenderType" = "Opaque"} CGPROGRAM #prama surface surf Lambert struct Input{ float4 color : COLOR; }; void surf (Input IN, inout SurfaceOutput o){ o.Albedo =1; } ENDCG } FallBack "Diffuse" }
从上述程序中可以看出,表面着色器被定义在SubShader语义块(而非Pass语义块)中的CGPROGAM和ENDCG之间。原因是,表面着色器不需要开发者关心使用多少个Pass、每个Pass如何渲染等问题,Unity会在背后为我们做好这些事情,我们只要告诉他:“嘿!使用这些纹理去填充颜色,使用这个法线纹理去填充法线,使用Lambert光照模型,其他的事情不要来烦我!”。
CGPRORAM和ENDCG之间的代码使用CG/HLSL编写的,也就是说,我们需要把CG/HLSL语言嵌套在ShaderLab语言中。值得注意的是,这里的CG/HLSL是Unity经封装后提供的,他的语法和标准的CG/HLSL语法几乎一样,但还是有细微的不同,例如有些原生的函数和用法Unity并没有提供支持。
2。最聪明的孩子:定点/片元着色器
在Unity中我们可以使用CG/HLSL语言来编写 顶点/片元着色器(Verter/Fragment Shader)。它们更加复杂,但灵活性也更高。
一个非常简单的顶点/片元着色器示例代码如下:
Shader "Custom/Simple VertexFragment Shader"{ SubShader{ Pass{ CGPROGRAM #prama vertex vert #prama fragment frag float4 vert (float4 v : POSITION) : SV_POSITION{ return mul (UNITY_MATRIX_MVP , v); } float4 frag ( ) : SV_Target{ return fixed4 (1.0 , 0.0 , 0.0 , 1.0); } ENDCG } } }
和表面着色器类似,顶点/片元着色器的代码也需要定义在CGPROGRAM和ENDCG之间,但不同的是,顶点/片元着色器是写在Pass语义块内,而非SubShader内的,原因是,我们需要自已定义每个Pass需要使用的Shader代码。虽然我们可能需要编写更多的代码,但带来的好处是灵活性很高,更重要的是,我们可以控制渲染的实现细节,同样这里的CGPROGRAM和ENDCG之间的代码也是使用CG/HLSL编写的。
3. 被抛弃的角落:固定函数着色器
上面两种Unity Shader 形式都使用了可编程管线。而对于一些较旧的设备(其CPU仅支持DirectX7.0、OpenGL 1.5 或 OpenGL ES 1.1),例如iPhone3,他们不支持可编程管线着色器,因此,这时候我们就需要使用固定函数着色器(Fixed Function Shader)来完成渲染。这些着色器往往可以完成一些非常简单的效果。
一个非常简单的固定函数着色器示例代码如下:
Shader "Tutorial/Basic"{ Properties{ _Color("Main Color",Color) = (1, 0.5, 0.5 , 1) } SubShader{ Pass{ Material { Diffuse [ _Color] } Lighting On } } }
可以看出,固定函数着色器的代码被定义在Pass语义块中,这些代码相当于Pass中的一些渲染设置,正如我们之前提到的一样。
对于固定函数着色器来说,我们需要完全使用ShaderLab的语法(即 使用ShaderLab的渲染设置命令) 来编写,而非使用 CG/HLSL。
由于现在大多数GPU都支持可编程的渲染管线,这种固定管线的编程方式已经逐渐被抛弃。实际上,在Unity5.2中,所有固定函数着色器都会在背后被Unity编译成对应的顶点/片元着色器,因此真正意义上的固定函数着色器已经不存在了。
对于选择Shader 的一些建议。
- 除非你有非常明确的需求必须要使用固定函数着色器,例如需要在非常旧的设备上运行你的游戏(这些设备非常少见),否则请使用可编程管线的着色器,即表明着色器或顶点/片元着色器。
- 如果你想和各种光源打交道,你可能更喜欢使用表明着色器,但需要小心他在移动平台的性能表现。
- 如果你需要使用的光照数目非常少,例如只有一个平行光,那么使用顶点/片元着色器是一个更好的选择。
- 最重要的是如果你有很多自定义的渲染效果,那么请选择顶点/片元着色器。
UnityShader入门精要-3.5 UnityShader的形式