您的位置:首页 > 移动开发 > Unity3D

[Unity3D]Shader学习笔记之ShaderLab基础

2016-08-30 20:08 381 查看

简介

  Unity Shader为控制渲染过程提供了一层抽象。如果没有它,开发者需要和很多文件设置打交道,才能让画面呈现出想要的效果;而在Unity Shader的帮助下,开发者只需要使用ShaderLab来编写Unity Shader文件就可以完成所有工作。

  在Unity中,所有的Unity Shader都是使用ShaderLab来编写的。ShaderLab是Unity提供的编写Unity Shader的一种说明性语言

  一个Unity Shader的基础结构如下:

Shader "Shader Name"
{
Properties
{
//属性
}

SubShader
{
//显卡A使用的子着色器
}

SubShader
{
//显卡B使用的子着色器
}

FallBack "VertexLit"
}


   Unity在背后会根据使用的平台来把这些结构编译成真正的代码和Shader文件,而开发者只需要和Unity Shader打交道即可。

结构

Shader命名

Shader "Custom/MyShader" {}


  如上定义了一个名叫“MyShader”且在“Custom”路径下的UnityShader。路径名用“/”分隔。

材质面板属性定义

Properties
{
Name("显示名字", 属性类型) = 默认值
//....
}


  属性定义模块是包含在Shader命名模块下的。

支持的属性类型

属性类型默认值定义语法例子
Intnumber_Int(“Int”, Int) = 2
Floatnumber_Float(“Float”, Float) = 1.5
Range(min,max)number_Range(“Range”,Range(0.0, 5.0)) = 3.0
Color(number,number,number,number)_Color(“Color”, Color) = (1,1,1,1)
Vector(number,number,number,number)_Vector(“Vector”, Vector) = (2,3,6,1)
2D“defaulttexture”{}_2D(“2D”, 2D) = “”{}
Cube“defaulttexture”{}_Cube(“Cube”, Cube) = “white”{}
3D“defaulttexture”{}_3D(“3D”, 3D) = “black”{}
  范例:

Shader "Custom/Shader"
{
Properties
{
_Int("Int", Int) = 0
_Float("Float", Float) = 0.0
_Range("Range", Range(0,1)) = 0.0
_Color("Color", Color) = (1,1,1,1)
_Vector("Vector", Vector) = (0,0,0,0)
_2D("2D", 2D) = ""{}
_Cube("Cube", Cube) = "white"{}
_3D("3D", 3D) = "black"{}
}

Fallback "Diffuse"
}


  属性面板显示如下:

  



   Unity允许重载默认的材质编辑面板,以提供更多自定义数据类型。

   即使不在Properties语义块中声明这些属性,也可以在Cg代码片中定义变量。

SubShader

SubShader
{
//可选
[Tags]

//可选
[RenderSetup]

Pass
{
}

//其他Pass
}


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

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

状态设置

  常见的渲染状态设置选项

状态名称设置指令解释
CullCull Back/Font/Off设置剔除模式:剔除背面/正面/关闭剔除
ZTestZTest Less Greater/LEqual/GEqual/Equal/NotEqual/Always设置深度测试时使用的函数
ZWriteZWrite On/Off开启/关闭深度写入
BlendBlend SrcFactor DstFactor开启并设置混合模式
  当在SubShader块中设置了上述渲染状态时,将会应用到所有的Pass。如果不想这样,可以单独在Pass块中设置。  

SubShader的标签

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


  SubShader的标签([Tags])是一个键值对(Key/Value Pair),它的键和值都是字符串类型。

  这些键值对是SubShader和渲染引擎之间的沟通桥梁。用来告诉Unity的渲染引擎希望怎样以及如何渲染这个对象。

  SubShader的标签块支持的标签类型如下:

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

Pass语义块

Pass
{
[Name]
[Tags]
[RenderSetup]
//其他代码
}


  在Pass中定义该Pass的名称,如下:

Name "MyPassName"


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

UsePass "MyShader/MYPASSNAME"


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

  Pass也可以设置渲染状态。SubShader的状态设置同样适用与Pass。

  Pass也可以设置标签,但它的标签不同于SubShader的标签,这些标签也是告诉渲染引擎应该怎样来渲染物体。

  Pass中使用的标签类型如下:

标签类型说明例子
LightMode定义该Pass在Unity的渲染流水线中的角色Tags{“LightMode” = “ForwardBase”}
RequireOptions用于定义当满足某些条件时才能渲染该Pass,它的值是一个由空格分隔的字符串。目前,Unity支持的选项有SoftVegetation。Tags{“RequireOptions” = “SoftVegetation”}
  除了上面普通的Pass定义外,Unity Shader还支持一些特殊的Pass,以便进行代码复用或实现更复杂的效果。

UsePass:该命令用来复用其他Unity Shader中的Pass

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

Fallback

Fallback "ShaderName"
//或
Fallback Off


  紧跟在各个SubShader语义块后面,用于告诉Unity,当上面所有的SubShader在这块显卡上都不能运行时,就用Fallback指定的这个Shader。

  Fallback会影响到阴影的投射,因此正确设置是非常重要的

Unity Shader的形式

  必要的结构如下:

Shader "MyShader"
{
Properties
{
//所需的各种属性
}

SubShader
{
//真正意义上的Shader代码会出现在这里
//表面着色器(Surface Shader)或者
//顶点/片元着色器(Vertex/Fragment Shader)或者
//固定函数着色器(Fixed Function Shader)
}

SubShader
{
//和上一个SubShader类似
}
}


表面着色器

  表面着色器是Unity自己创造的一种着色器代码类型。它需要的代码少,Unity在背后做了很多工作,但渲染的代价比较大。

  在背后Unity任然把它转换成对应的顶点/片元着色器。可以理解成,表面着色器是Unity对顶点/片元着色器的更高一层的抽象。它存在的价值在于,Unity为我们处理了很多光照细节,使我们不需要在操行这些。

  简单范例:

Shader "Custom/Simple Surface Shader"
{
SubShader
{
Tags {"RenderType" = "Opaque"}
CGPROGRAM
#pragma surface surf Lambert
struct Input
{
float4 color : Color;
};
void Surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = 1;
}
ENDCG
}
Fallback "Diffuse"
}


  表面着色器被定义在SubShader语义块(而非Pass语义块)中CGPROGRAM和ENDCG之间。

  表面着色器不需要关心使用多少个Pass、每个Pass如何渲染等问题,Unity会在背后处理好。

  CGPROGRAM和ENDCG之间的代码使用Cg/HLSL编写的,语法几乎和标准的一样,但有些原生的函数并没有提供支持。

顶点/片元着色器

  在Unity中可以使用Cg/HLSL语言来编写顶点/片元着色器,它们更加复杂,但更加灵活。

  简单范例如下:

Shader "Custom/Simple VertexFragment Shader"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert (float4 v : POSITION) : SV_POSITION
{
return mul (UNITY_MATRIX_MVP, v);
}
fixed4 frag () : SV_Target
{
return fixed4 (1.0, 0.0, 0.0, 1.0);
}
ENDCG
}
}
}


  顶点/片元着色器是写在Pass语义内的,而非SubShader内

固定函数着色器

  上面两种都使用的可编程管线,但对于一些老旧设备,它们并不支持可编程管线着色器,因此就要使用到固定函数着色器,但往往只可以完成一些非常简单的效果。

  简单范例如下:

Shader "Custom/Basic"
{
Properties
{
_Color ("Main Color", Color) = (1, 0.5, 0.5, 1)
}
SubShader
{
Pass
{
Material
{
Diffuse [_Color]
}
Lighting On
}
}
}


  对于它来说,需要完全使用ShaderLab的语法来编写,而非使用Cg/HLSL。

其他

Unity Shader不是真正的Shader

  在Unity Shader中,可以做的事情远多于一个传统意义上的Shader。

传统ShaderUnity Shader
仅可编写特定类型的Shader,例如:顶点着色器、片元着色器等可以在同一个文件中同时包含顶点着色器和片元着色器
无法设置一些渲染设置,例如:是否开启混合、深度测试等。这些是开发者在另外的代码中设置的通过一行特定的指令就可以完成这些设置
需要编写冗长的代码来设置着色器的输入和输出,要小心的处理这些输入和输出的位置对应关系等只需要在特定语句块中声明一些属性,就可以依靠改变材质来方便的改变这些属性。而且对于模型自带的数据(如顶点位置、纹理坐标、法线等),Unity Shader也提供了直接访问的方法,不需要开发者自行编码来传给着色器
高度封装,可编写的Shader类型和语法被限制
对于一些类型的Shader,如曲面细分着色器、几何着色器等的支持相对较差
一些高级的Shader语法不支持

Unity Shader和Cg/HLSL之间的关系

  通常,Cg的代码片段是位于Pass语义内部的,如下:

Pass
{
//Pass的标签和状态设置
CGPROGRAM
//编译指令,例如
#pragma vertex vert
#pragma fragment frag
//Cg代码
ENDCG
//其他一些设置
}


  从本质上讲Unity中只存在顶点/片元着色器,表面着色器和固定函数着色器会在背后转化为顶点/片元着色器。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息