终于到第三章了,Unity shader概述
一、材质 与 unity shader
物体的渲染是由材质和unity shader一起工作来控制的。
我们需要创建一个材质,
再创建一个Unity shader ,并把shader赋值给材质。
再把材质赋值给需要渲染的对象。
再在材质面板中调整unity shader的属性,用来达到我们满意的效果。
unity shader中定义了渲染所需要的各种代码(顶点、片元着色器),一些属性(使用哪些纹理),指令(渲染和标签设置)。
而材质允许我们调节这些属性,并赋值给我们最终需要渲染的模型。
1.unity中材质(Material)
模型上组件(Mesh Render)中赋值Materials。
后可以在material资源中调节渲染参数。
2.Unity Shader(注意Unity shader与 渲染管线 shader区别)
Unity shader分为4种(Standard Surface Shader(包含标准光照模型,使用Unity5中基于物理的渲染方法)、Unlit Shader(产生一个不包含光照但包含雾效的基本顶点/片元着色器)、Image Effect Shader(为我们实现各种屏幕后处理效果提供基本模板)、Compute Shader(特殊的Shader文件))。
我们基础学习大多使用Unlit Shader。
shader 面板介绍:
Default Maps : 描述Unity Shader使用的默认纹理
Surface Shader : 是否是一个表面着色器
Fixed Function Shader : 是否是一个固定函数着色器
投射阴影
渲染队列
LOD值
忽视投影
是否关闭批处理
属性列表
按钮 Show generated code: 打开文件显示Unity背后为该表面着色器生成的 顶点/片元着色器。
如果 unity shader 是一个固定函数着色器,那么其相应位置也会多一个 Show generated code 按钮,用来查看固定函数着色器生成的 顶点/片元着色器。
Compile and show code 按钮后方的下拉列表 让开发者检查Unity Shader针对不同图像编程接口最终编译成的Shader代码,我们通过代码来分析和优化着色器。
3.ShaderLab
Unity 内部为了方便学习,提供的一层高级层的渲染抽象层叫 Unity Shader,我们与这层抽象打招呼的语言就叫做 ShaderLab。
Unity中所有的 Unity Shader都是使用ShaderLab来编写的。
ShaderLab定义了一个材质需要的所有东西,而不仅仅是着色器代码。
ShaderLab使用了一些嵌套在花括号里的语义来描述Unity Shader文件结构。
其中包含许多渲染所需要的数据(例如 Properties语句块中定义了着色器所需要的各种属性,这些属性也会显示在材质面板中)
Unity Shader 基础结构(Unity在背后会根据使用的平台来把这些结构编译成真正的代码和Shader文件,而开发者只需要和Unity Shader打交道即可):
Shader "ShaderName"{
Properties{
//属性
}
SubShader{
//显卡A使用的子着色器
}
SubShader{
//显卡B使用的子着色器
}
Fallback “VertexLit”
}
4.Unity Shader 结构
(1)名字
Shader “Custom/MyShader” { }
Unity shader 在面板中出现的 位置及名字
Shader/Custom/MyShader
(2)材质与Unity Shader之间的桥梁——属性(Properties)
properties的语义块定义:
Properties {
Name(“display name”,PropertyType)= DefaultBValue
Name(“display name”,PropertyType)= DefaultBValue
//更多属性
}
声明的属性可以在材质中方便调整
当我们在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
//Color and Vectors
_Color("Color",Color)=(1,1,1,1)
_Vector("Vector",Vector)=(2,3,6,1)
//Textures
_2D("2D",2D)=""{}
_3D("3D",3D)="black"{}
_Cube("Cube",Cube)="white"{}
}
FallBack "Diffuse"
}
对于2D、3D、Cube,默认值是字符串加上一个花括号,
字符串是用来定义内置的纹理名称,花括号指定纹理属性(原来),后被移除,如果想实现类似功能,需要在顶点着色器中编写计算相应的纹理坐标的代码。
上面说的这些属性的定义仅仅是为了使之显示在材质面板中。
在Cg代码块中使用使用属性值时,需要通过脚本向Shader中传递这些属性。
(3)重量级成员: SubShader
一个Unity Shader文件可以包含多个SubShader文件,但至少要有一个。
当Unity加载一个Unity Shader时,会按顺序依次扫描SubShader语义块,然后选择第一个能够在目标平台上运行的SubShader。如果都不支持,Unity会选择Fallback语义指定的Unity Shader。
以上语义是因为不同显卡有不同的能力。在不同的显卡上定义不同的操作数,使不同性能的显卡有不同的显示画面。
SubShader中包含的定义通常如下
SubShader
{
//可选的,定义标签设置
[Tags]
//可选的,状态设置
[RenderSetup]
Pass{} //定义一次完整的渲染流程
//Other Passes
}
每个pass定义的都是一次完整的渲染流程。
但是Pass过多会导致性能浪费。所以我们要尽量使用较小数目的Pass。
状态(RenderSetup)和标签(Tags)也可以定义在Pass中,
SubShader中
标签设置是特定的,与在Pass中的不同。
状态设置使用语法相同,
在SubShader中定义的,会适用于所有Pass。
状态设置:
31页表3.2 SubShader中标签设置是特定的,与在Pass中的不同,会适用于所有Pass。
例如要在 双面渲染中 我们想在第一个 Pass 中剔除正面对背面渲染,在第二个 Pass 中剔除背面对正面渲染,此时我们需要在Pass语块中单独进行上面的设置。
标签设置:
结构:
Tags{"TagName1"="Value1" "TagName2"="Value2"}
标签是一个键值对,它的键和值都是字符串类型。
这些键值对是SubShader与渲染引擎之间的沟通桥梁,它们用来告诉Unity引擎,我希望怎么样以及何时进行渲染这个对象。
类型:32页表3.3, 表中仅可在SubShader中声明,与Pass中的不同。
Pass块
语义结构:
Pass{
[Name]
[Tags]
[RenderSetup]
//Other code
}
定义名称举例
Name “MyPassName”
通过定义名称,我们可以使用ShaderLab中的UsePass命令来在其他Shader中调用此Pass,提高代码的复用性。
例如:
UsePass “MyShader/MYPASSNAME” //需要全部更改成大写字母形式
定义设置标签
此标签与SubShader中的不同,此处是告诉引擎我们是希望怎样渲染引擎。标签类型详细见 33页表3.4。
定义渲染状态
与SubShader中的相同,除此之外我们还可以使用固定管线着色器命令。
另外两种Pass:
UsePass:使用其他Shader中的Pass。
GrabPass:此Pass负责抓取屏幕并将结果存储在一张纹理中,用于后续的Pass处理。
(3)Fallback
定义语义
Fallback “name”
//或者
Fallback Off
//举例
Fallback “VertexLit”
为了确定每一个SubShader都不能使用时,有一个最低级的默认,如果有Off,那就是不需要使用SubShader了。
Fallback另外一个作用是会影响阴影的投射,Unity会在每个Unity Shader中寻找一个Pass来实现阴影的投射,正常情况,我们不需要单独设置,因为Fallback内置的shader包含了这个通用Pass,所以我们还是要尽可能地去正确设置Fallback。
(4)其他语义
CustomEditor语义来扩展编辑界面,自定义材质面板编辑界面。
Category对命令分组等等。
总结
所有的着色器代码都在SubShader语义块中,
表面着色器通常直接写在SubShader中,
顶点/片元/固定函数着色器通常写在 Pass 语块中。
1.表面着色器:
写在SubShader中,它是Unity自创的,有很多的背后封装,我们使用表面着色器时,Unity会浪费很大的资源代价转换成 顶点/片元着色器。所以它相对于 顶点/片元 更加方便,不需要我们具体的进行光照、法线等很多事情。
在很多光源时,多被使用,但你需要小心其在移动平台的表现。
2.顶点/片元着色器:
写在Pass中,相对于表面着色器灵活性更高,可以自己实现细节。
可以实现更多的渲染效果,适用于光源较少时。
以上三种着色器都是下面的格式(只是需要注意在不同位置):
CGPROGRAM
//中间写Cg/HLSL代码(Untiy封装后的,与正常的有些不同)。
ENDCG
3.固定函数着色器:
老旧设备不支持可编程管线着色器,所以只能使用固定函数着色器实现简单效果。也被定义在Pass中。
使用ShaderLab,而不是Cg/HLSL。