一.Unity Shader概述

1.在Unity中我们需要配合使用材质(material)和Unity Shader才能达到需要的效果。材质是载体,Unity Shader是文本文件,我们在Unity Shader中写好渲染的代码(属性,顶点着色器,片元着色器等),加载到材质上,在材质上可以调节shader的属性,然后将材质赋给模型展示渲染效果。

2.Unity Shader不是传统意义上的的Shader,Unity Shader实际上指的是一个ShaderLab文件--以.shader为文件后缀的一种文件。但Unity Shader可以做的远多于一个传统意义的Shader。

a、传统Shader中,我们只可以编写特定类型的Shader,比如顶点着色器,或片元着色器等。Unity Shader中我们可以在一个文件中编写他们。

b、传统Shader中,我们不能做一些例如开启混合、深度测试等渲染测试,这是在另外的代码中设置的。Unity Shader中我们加几个指令就可以了。

c、传统Shader中,需要编写大量代码设置着色器的输入和输出。Unity Shader中,我们只需要声明一些属性,并可以在材质中改变这些属性。对于模型数据(顶点位置,纹理坐标,法线,切线等),Unity Shader中我们可以直接访问,传统Shader需要自行编码传给shader。

d、Unity Shader的高度封装,使其失去了更多的编写自由性,不过同时也使我们只需要和Unity Shader打交道,不用关心渲染引擎底层实现。

3.Unity 5.5版本中,提供了5种Unity Shader模板:Standard Surface Shader,Standard Surface Shader(Instanced),Unlit Shader,Image Effect Shader, Compute Shader。

a、Standard Surface Shader是基于物理渲染(PBS)的包含标准光照模型的表面着色器模板,在5.x版本中,我们新建一个材质,它会默认使用内置的Standard Shader,是使用基于物理渲染(PBS)的技术,区别于传统光照Shader,它对光照渲染有着更加强大的支持,当然如果想达到强大的效果,除了主纹理和法线纹理,你可能需要加入金属纹理,粗糙度纹理,遮挡纹理,细节纹理,并配置一堆属性,再加上光照探针,反射探针啥的,想清楚再用。

b、Standard Surface Shader(Instanced),据说是5.4版本以后加的,使用了Gpu Instancing技术,这个技术在大量使用相同材质和网格的情况下可以大幅度降低Draw Call,而这个模板在生成阴影pass中使用了该技术。

c、Unlit Shader:不包含光照但包含雾效的顶点片元着色器。

d、Image Effect Shader:只是一个简单的图片展示效果,为实现屏幕后处理效果提供了基本模板。

f、Compute Shader:产生特殊的shader文件,旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算。(原书照搬,不在研究范围)。

a和b是表面着色器,c和d是顶点片元着色器,请对应选择模板。(因为喜欢用顶点片元着色器,我喜欢用d)

 

二.ShaderLab

上面提到过Unity Shader不是传统意义的shader,编写传统意义的shader需要和很多文件和配置打交道,Unity为我们提供了一层抽象:Unity Shader,而专门为Unity Shader服务的语言就是ShaderLab。

 

三.Unity Shader结构

1.名字

每个Unity Shader第一行都需要定义一个名字,格式例子:Shader "Custom/MyShader","/"是路径,其对应位置为Shader->Custom->MyShader。

2.属性(Properties)

Properties {

Name ("display name", PropertyType) = DefaultValue

Name ("display name", PropertyType) = DefaultValue

....

}

Properties中包含了一系列属性,会在材质面板中展示,开发者可以方便调试它们,同时shader会访问它们,类似于untiy的public变量。如上所示,每条属性由Name(名字,代码中要用),"display name"(面板显示名字),PropertyType(属性类型),DefaultValue(默认值)组成。Unity Shader属性类型如下:

Shader理论《四》概述以及结构_加载

3.SubShader

一个Shader文件可以包含多个SubShader,但最少要一个。Unity加载该shader时,会扫描并选择一个能在目标平台运行的SubShader,如果都不支持,会使用Fallback指定的Unity Shader。

SubShader通常定义如下:

 


[html]  ​​view plain​​​  ​​​copy​

 



  1. SubShader {
  2. //可选
  3. [Tags]
  4. //可选
  5. [RenderSetup]

  6. Pass {
  7. }
  8. //其他Pass
  9. ...
  10. }


 

 

 

a、Tags:标签,由一组键值对组成,键和值都是字符串,它们告诉Unity渲染引擎,我希望怎样及何时渲染该SubShader。结构如下:

Tags { "TagName1" = "Value1"  "TagName2" = "Value2"}

新手注意,每个标签间隔不要加逗号或分号,只是空格,最后也没符号,新手这里容易写错。

SubShader支持的标签类型如下(书上截图,原谅我懒):

Shader理论《四》概述以及结构_渲染_02

注意,这里的标签是在SubShader里的,不是Pass里的,它们不同。

b、RenderSetup

状态设置,ShaderLab提供了一系列渲染状态的设置指令,可以设置在SubShader里,影响其下所有pass,也可以放某个pass里,只影响该pass。常见的渲染设置如下(接着截图):

Shader理论《四》概述以及结构_Unity_03

c、Pass

SubShader中可以有多个pass,每个Pass定义了一次完整的渲染流程,但Pass过多会造成渲染性能下降,所以我们尽量用最少数目的Pass。

Pass包含的语义如下:

 


[html]  ​​view plain​​​  ​​​copy​

 



  1. Pass {
  2. [Name]
  3. [Tags]
  4. [RenderSetup]
  5. //正式代码
  6. ....
  7. }


Name:该pass名字,可选,书写例子如下:

 

Name "MyPassName"

通过该名字,我们可以在其他的Unity Shader中用UsePass命令直接使用该Pass,非常方便,例如

UsePass "MyShader/MYPASSNAME"

需要注意的是Unity内部会把所有Pass名称转换成大写字母,所以用UsePass时必须使用大写形式的名字。

上面SubShader的RenderSetup(状态设置)同样适用于Pass。

Pass的标签不同于SubShader的标签,下面是Pass中使用的标签:

Shader理论《四》概述以及结构_加载_04

还有一些特殊的Pass

UsePass:前面提到了,可以用Pass的名字复用访问。

GrabPass:该Pass负责抓取屏幕并存到一张纹理中,后面的Pass可以对该纹理继续处理。

d、FallBack

放在最后,当所有SubShader都不合适,就用FallBack提供的Shader。语义如下:

FallBack "name" 或 FallBack Off

不要以为它没用,当你学到阴影那里就知道它的用处了,FallBack的内置shader往往包含一个通用的阴影投射Pass,所以你不用自己实现。