一、概述

在Unity中需要配合使用材质和Unity Shader才能达到需要的效果。常见的流程:(1)创建一个材质;(2)创建一个Unity Shader,并把它赋给创建的材质;(3)把材质赋给要渲染的对象;(4)在材质面板中调整Unity Shader的属性,以得到满意的效果。

二、Unity中的材质

Unity中的材质需要结合一个GameObject的Mesh或者Particle Systems组件来工作。它决定了我们的游戏对象看起来是什么样子的。

三、Unity中的Shader

Unity提供了四种Unity Shader模板:

1、Standard Surface Shader:产生一个包含了标准光照模型的表面着色器。

2、Unlit Shader:产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器。

3、Image Effect Shader:为实现各种屏幕后处理效果提供基本模板。

4、Compute Shader:产生一种特殊的Shader文件,旨在利用GPU的并行性进行一些与常规渲染流水线无关的计算。

Shader文件说明:

Unity Shader概述-LMLPHP

Default Maps指定该Shader使用的默认纹理,当任何材质第一次使用该Shader时,这些纹理就自动被赋予到相应的属性上。

Surface shader和Fixed function用于表明该shader是一个表面着色器还是固定函数着色器,如果是某个着色器,在对应的位置就有Show generated code按钮,单击该按钮打开一个新的文件,该文件里将显示Unity在背后为该表面着色器生成的顶点/片元着色器。这可以方便我们对这些生成的代码进行修改(需要复制到一个新的Unity Shader中才可保存)和研究。

Cast shadows(是否会投射阴影)、Render queue(渲染队列)、LOD(LOD值)等,和我们在Shader中的标签设置有关。

Compile and show code下拉列表用于检查该Shader针对不同图像编程接口(例如OpenGL、D3D9等)最终编译成的Shader代码。

除此之外,面板还可以查看其是否关闭批处理(Disable batching)、属性列表(Properties)等信息。

四、Unity Shader的结构

基础结构:

Shader "ShaderName"{
Properties{
//属性
}
SubShader{
//显卡A使用的子着色器
}
SubShader{
//显卡B使用的子着色器
}
Fallback "VertexLit"
}

(1)Shader文件的第一行通过Shader语义指定该Shader的名字,由一个字符串来定义。通过在字符串中添加“/”,可以控制Shader在材质面板中出现的位置。例如:

Shader "Custom/MyShader"{   }

该Shader在材质面板中的位置就是:Shader->Custom->MyShader。

(2)Properties语义块包含了一系列属性,这些属性将会显示在材质面板中。

Properties {
Name ("display name", PropertyType) = DefaultValue
Name ("display name", PropertyType) = DefaultValue
//更多属性
}

指定名字(Name)可以让我们在Shader中访问它们。

显示的名字(display name)是出现在材质面板上的名字。

类型(Property Type)是每个值的类型。

默认值(DefaultValue)是为每个属性指定的默认值,当第一次把该Shader赋给某个材质时,面板上显示的就是这些值。

下面给出展示所有属性类型的例子:

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"
}

对于Int、Float、Range这些数字类型的属性,其默认值就是一个单独的数字;

对于Color和Vector这类属性,默认值就是用圆括号包围的一个四维向量;

对于2D、Cube、3D这类属性,默认值通过一个字符串后跟一个花括号来指定。其中,字符串要么是空的,要么是内置的纹理名称,花括号的作用原本是用于指定一些纹理属性,但在Unity5.0之后便取消了,如果需要类似功能,就需要自己在顶点着色器中编写计算相应纹理坐标的代码。

如果想要在材质面板上显示更多类型的变量,Unity允许重载默认的材质编辑面板,以提供更多自定义的数据类型。

参考: https://docs.unity3d.com/Manual/SL-CustomShaderGUI.html

(3)每一个Unity Shader文件可以包含多个SubShader语义块,但最少有一个。当Unity需要加载这个Unity Shader时,Unity会扫描所有的SubShader语义块,然后选择第一个能够在目标平台上运行的SubShader。如果都不支持的话,Unity就会使用Fallback语义指定的Unity Shader。Unity提供这种语义的目的在于适应不同能力的显卡。

SubShader {
//可选的
[Tags] //可选的
[RenderSetup] Pass {
}
//Other Passes
}

SubShader中定义了一系列Pass以及可选的状态([RenderSetup])和标签([Tags])设置。每个Pass定义了一次完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。因此,应尽量使用最小数目的Pass。

状态和标签同样可以在Pass声明。对于状态设置来说,SubShader和Pass是相同的,在SubShader进行的状态设置将会用于所有的Pass。不过对于标签设置来说,SubShader和Pass是不一样的。

状态设置

状  态  名  称设  置  指  令解    释
CullCull Back | Front | Off设置剔除模式:剔除背面/正面/关闭剔除
ZTestZTest Less Greater | LEqual | GEqual |Equal | NotEqual | Always设置深度测试时使用的函数
ZWriteZWrite On | Off开启/关闭深度写入
BlendBlend SrcFactor DstFactor开启并设置混合模式

SubShader的标签

SubShader的标签(Tags)是一个键值对,它的键和值都是字符串类型。

标  签  类  型说    明例    子
Queue控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有的透明物体可以在所有不透明物体后面被渲染,我们也可以自定义使用的渲染队列来控制物体的渲染顺序Tags{"Queue"="Transparent"}
RenderType对着色器进行分类,例如这是一个不透明的着色器,或是一个透明的着色器等。这可以被用于着色器替换(Shader Replacement)功能Tags{"RenderType"="Opaque"}
DisableBatching一些SubShader在使用Unity的批处理功能时会出现问题,例如使用了模型空间下的坐标进行顶点动画。这时可以通过该标签来直接指明是否对该SubShader使用批处理Tags{"DisableBatching"="True"}
ForceNoShadowCasting控制使用该SubShader的物体是否会投射阴影Tags{"ForceNoShadowCasting"="True"}
IgnoreProjector如果该标签值为“True”,那么使用该SubShader的物体将不会受Projector的影响。通常用于半透明物体Tags{"IgnoreProjector"="True"}
CanUseSpriteAtlas当该SubShader是用于精灵(sprites)时,将该标签设为“False”Tags{"CanUseSpriteAtlas"="False"}
PreviewType指明材质面板将如何预览该材质。默认情况下,材质将显示为一个球形,我们可以通过把该标签的值设为“Plane”“SkyBox”来改变预览类型Tags{"PreviewType"="Plane"}

Pass语义块

Pass {
[Name]
[Tags]
[RenderSetup]
//Other code
} 

我们可以在Pass中定义该Pass的名称,例如:

Name "MyPassName"

  通过这个名称,我们可以使用UsePass命令来直接使用其他Shader中的Pass。例如:

UsePass "MyShader/MYPASSNAME"

  这样可以提高代码的复用性。需要注意的是,Unity内部会把所有Pass的名称转换为大写字母表示。因此,使用UsePass命令时必须使用大写形式的名字。

Pass中设置的标签不同于SubShader的标签。这些标签也是用于告诉渲染引擎我们希望怎样来渲染物体。

标  签  类  型说    明例    子
LightMode定义该Pass在Unity的渲染流水线中的角色Tags{"LightMode"="ForwardBase"}
RequireOptions用于指定当满足某些条件时才渲染该Pass,它的值是一个由空格分隔的字符串。Tags{"RequireOptions"="SoftVegetation"}

除了上面普通的Pass定义外,Shader还支持一些特殊的Pass。

1.UsePass:可以使用该命令来复用其他Shader中的Pass。

2.GrabPass:该Pass负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass处理。

(4)当所有的SubShader都不能在某张显卡上运行时,就使用Fallback指定的最低级的Shader,语义如下:

Fallback "name"
//或者
Fallback Off

  事实上,Fallback还会影响阴影的投射。在渲染阴影纹理时,Unity会在每个Untiy Shader中寻找一个阴影透视的Pass。通常,我们不需要自己专门实现一个Pass,这是因为Fallback使用的内置Shader中包含了这样一个通用的Pass。因此,为每个Untiy Shader正确设置Fallback是非常重要的。

05-12 15:59