Unity中的材质着色器被称为ShaderLab,形式上接近于CgFX和Direct3D Effects (.FX)。
在Unity中配置属性的时候,其实就是针对Shader进行配置。下面我们看一个简单的脚本:
Shader "u3dcool.com/Tutorial/Basic" {
Properties {
_Color ("Main Color", Color) = (1,0.5,0.5,1)
}
SubShader {
Pass {
Material {
Diffuse [_Color]
}
Lighting On
}
}
}
从中我们可以看到一个Shader脚本的组成:
- Shader 包括所有代码,第一行的就是一个Shader的名称,斜杠表示目录结构;
- Properties 包括了可以在Inspector(编辑器)中进行配置的参数选项;
- SubShader 一个Shader中可以又多个SubShader来完成不同能力的渲染。
因为上面的例子比较简单,我们以一个比较复杂的VertexLit为例,再来说明一个Shader的组成,如下所示:
Shader "VertexLit" {
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
}
}
}
}
上面的这个例子稍微复杂一点,我们一点一点来讲。
Properties
首先就是这个Properties(属性)块,它定义了一些设计师可以在Inspector(Unity中的属性编辑界面)中进行配置的属性。
在Properties代码中,每一个属性值占一行。每一行的写法:首先是内部名称(Internal name),然后是Inspector中显示的名称(Inspector title),然后是该属性的类型(Property type),最后是该属性的默认值(Default value)。
所有属性的类型如下所示,而属性的默认值也因属性的不同而不同:
- name (“display name”, Range (min, max)) = number
- name (“display name”, Color) = (number, number, number, number)
- name (“display name”, 2D) = “name” { options }
- name (“display name”, Rect) = “name” { options }
- name (“display name”, Float) = number
- name (“display name”, Vector) = (number, number, number, number)
现在我们的属性定义就算是OK了,让我们再来看看如何编写真正的着色部分。
The Shader Body
不同的显卡有不同的处理能力,比如有的显卡就支持Fragment Program(片段编程),而有的就不支持。那为了更好的利用显卡的处理性能,所以在一个大的Shader中就包含了很多“小”Shader,被称之为SubShaders。当Unity渲染着色时,它会遍历所有SubShader,找到第一个被显卡支持的SubShader然后执行。如下所示:
Shader "Structure Example" { Properties { /* ...shader properties... */} SubShader { // ...subshader that uses vertex/fragment programs... } SubShader { // ...subshader that uses four textures per pass... } SubShader { // ...subshader that uses two textures per pass... } SubShader { // ...subshader that might look ugly but runs on anything
} }
Shader的这个特性,使得Unity既可以支持所有的硬件设备,又可以很好的区分并利用不同的硬件平台。但因此也使得Shader看起来有点长。
每一个SubShader定义了可供所有通道(All Passes)使用的渲染状态(Rendering State),同时又定义了渲染通道(Rendering Passes)。
Passes
每一个SubShader包含了很多通道,而通道又去渲染图形。因此,每个SubShader中必然包含至少一个Pass(通道)。比如刚才的VertexLit着色器代码所示:
// ...snip... 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 } } // ...snip...
在Pass中定义的命令告诉显卡应该去如何渲染特定的图形。
上面代码中的Material代码块绑定了一些属性。而Lighting On则激活了标准顶点光源(Standard Vertex Lighting),SperateSpecular On则激活了不同颜色的反射高光。
所有这些命令其实都映射到了OpenGL/Direct3D的硬件模型(Hardware Model)。
而SetTexture命令则定义了如何对贴图进行处理,在这里使用了Combine对贴图进行处理,其语法为:
Combine ColorPart, AlphaPart
Summary
到现在我们已经基本实现了一个Shader,里面包含一个SubShader和其中的一个Pass。其实我们可以定义多个SubShader和多个Pass。但现在的这个Shader已经满足了我们的需要,因为写成这样即可。
First教程,搞定~