3.1 概述
3.1.1 材质与Unity Shader
Unity Shader需要与材质配合使用。具体流程为:(1)创建一个材质;(2)创建一个Shader,并把该Shader赋予材质;(3)将材质赋予一个物体;(4)在材质页面修改数据以达到预期效果。
3.1.3 Unity中的Shader
Unity提供了四种基本的Shader——Standard Surface Shader,Unlit Shader ,Image Effect Shader 以及Compute Shader。其中,Standard Surface Shader 会产生一个包含了标准光照模型的表面着色器模板,Unlit Shader 则会产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器,Image Effect Shader 则为我们实现各种屏幕后处理效果提供了一个基本模板。最后,Compute Shader 会产生一种特殊的Shader文件,这类Shader旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算。
常用Unlit Shader来生成一个基本的顶点/片元着色器模板。
3.2 ShaderLab
ShaderLab是Unity提供的Shader语言,用户只需要和ShaderLab打交道即可
Shader "ShaderName" {
Properties {
// 属性
}
SubShader {
// 显卡A使用的子着色器
}
SubShader {
// 显卡B使用的子着色器
}
Fallback "VertexLit"
}
Properties语块内包含着色所需要的属性,会出现在材质面板上
3.3 Unity Shader的结构
3.3.1 Shader命名
在Shader开头需要为Shader命名,可以用/来表示在材质的Shader选择界面的层级"Folder/Name"
Shader "Custom/MyShader" {
}
3.3.2 Properties
Properties {
Name ("display name", PropertyType) = DefaultValue
Name ("display name", PropertyType) = DefaultValue
// 更多属性
}
Name:该属性的名称,通常由下划线开头
display name:展现在材质面板的名称
PropertyType:该属性的数据类型
DefaultValue:默认值,不同属性类型有不同的默认值创建方式
表 Properties语义块支持的属性类型
对于2D、Cube、3D这样的纹理类型,默认值为一个字符串跟花括号,字符串要么为空,要么为内置的纹理,花括号的用处原本是用于指定一些纹理属性的。
Shader "Custom/ShaderLabProperties" {
Properties {
// Numbers and Sliders
_Int ("Int", Int) = 2
_Float ("Float", Float) = 1.5
_Range("Range", Range(0.0, 5.0)) = 3.0
// Colors and Vectors
_Color ("Color", Color) = (1,1,1,1)
_Vector ("Vector", Vector) = (2, 3, 6, 1)
// Textures
_2D ("2D", 2D) = "" {}
_Cube ("Cube", Cube) = "white" {}
_3D ("3D", 3D) = "black" {}
}
FallBack "Diffuse"
}
可以通过重载材质默认面板来显示自定义类型,编写相应的C#代码并在对应Shader末尾使用CustomEditor "CustomShaderGUI"来自定义面板
Shader "MyShader" {
Properties {}
SubShader {}
CustomEditor "CustomShaderGUI"
}
3.3.3 SubShader
每一个Unity Shader文件可以包含多个SubShader语义块,但最少要有一个。当Unity需要加载这个Unity Shader时,Unity会扫描所有的SubShader语义块,然后选择第一个能够在目标平台上运行的SubShader。如果都不支持的话,Unity就会使用Fallback语义指定的Unity Shader。
SubShader {
// 可选的
[Tags]
// 可选的
[RenderSetup]
Pass {
}
// Other Passes
}
标签(Tags)
标签为一个字符串键值对,这些键值对是SubShader和渲染引擎之间的沟通桥梁。它们用来告诉Unity的渲染引擎:我希望怎样以及何时渲染这个对象。Tags { "TagName1" = "Value1" "TagName2" = "Value2" }
表 SubShader的标签类型
状态设置(RenderSetup)
ShaderLab提供了一系列渲染状态的设置指令,这些指令可以设置显卡的各种状态,例如是否开启混合/深度测试等。
表 常见的渲染状态设置选项
SubShader内的状态设置会应用到该SubShader内的所有Pass里。如果不想这样就在每个Pass内分别设置状态
Pass语义块
每个Pass定义了一次完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。因此,我们应尽量使用最小数目的Pass。Pass内也可以声明状态与标签。
Pass {
[Name]
[Tags]
[RenderSetup]
// Other code
}
在Pass内可以定义改Pass块的名称Name "MyPassName",且可以在其他Shader内直接通过名称使用该Pass,例如Use "MyShader/MYPASSNAME(ShaderName/PassName),这样可以提高代码复用性,但注意,Unity内部会自动把Pass的名称转为全大写的格式,所以使用时请用大写。
除了上面普通的Pass定义外,Unity Shader还支持一些特殊的Pass ,以便进行代码复用或实现更复杂的效果。
UsePass :如我们之前提到的一样,可以使用该命令来复用其他Unity Shader中的Pass ;
GrabPass :该Pass 负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass 处理
3.3.4 Fallback
紧跟在各个SubShader语义块后面的,可以是一个Fallback指令。它用于告诉Unity,“如果上面所有的SubShader在这块显卡上都不能运行,那么就使用这个最低级的Shader吧!”。
Fallback "name"
// 或者
Fallback Off
3.4 Unity Shader的形式
在SubShader层级编写的代码控制的是表面着色器,在Pass内编写的代码控制的是片元/表面着色器。
3.4.1 表面着色器
Unity自创的着色器代码类型,渲染代价大,本质为片元/顶点着色器的更上一层抽象。需要处理更多光照细节时使用
3.4.2 顶点/片元着色器
更加复杂但灵活性高
3.4.3 固定函数着色器
为一些老旧设备使用的着色器
3.4.4 Shader选择
如果你想和各种光源打交道,你可能更喜欢使用表面着色器,但需要小心它在移动平台的性能表现。
如果你需要使用的光照数目非常少,例如只有一个平行光,那么使用顶点/片元着色器是一个更好的选择。
最重要的是,如果你有很多自定义的渲染效果,那么请选择顶点/片元着色器。
其他
Unity官网上关于Unity Shader方面的文档正在不断补充中,由于Unity封装了很多功能和细节,因此,如果读者在使用Unity Shader的过程中遇到了问题可以去到官方文档(http://docs.unity3d.com/Manual/SL-Reference.html )中查看。除此之外,Unity也提供了一些简单的着色器编写教程(http://docs.unity3d.com/Manual/ShaderTut1.html ,http://docs.unity3d.com/Manual/ShaderTut2.html )。由于在Unity Shader中,绝大多数可编程管线的着色器代码是使用Cg语言编写的,读者可以在NVIDIA提供的Cg文档(http://http.developer.nvidia.com/Cg/ )中找到更多的内容。NVIDIA同样提供了一个系列教程(http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter01.html )来帮助初学者掌握Cg的基本语法。