前言:
上篇文章中我们大致解释了什么是Shader,以及使用哪些语言可以进行Shader程序的编写等,那我接下来要做的就是开始在Unity引擎的环境下,编写一些Shader并查看是否达到预想的效果。
在Unity中定义了ShaderLab来组织Shader的内容,针对不同平台进行编译。
一、第一个Shader:
Shader "Custom/FirstShader" {
Properties {
_Color("Main Color", Color) = (0, 0, 0, 1)
}
SubShader {
Pass{
Color[_Color]
}
}
FallBack "Diffuse"
}
1.在当前场景中创建一个Plane面片,用于展示Shader着色后的效果:
2.绑上我们新创建的Shader文件,但先不设置_Color属性的值,使用默认缺省值进行着色,结果如下:
3.通过点击颜色选择框选中某个颜色值,得到新的着色结果为:
二、代码解析:
以上是一个Shader程序,以及其应用于Plane上进行着色的效果,我们可以从中看出Shader编程的一些特征:
1、在Unity中Shader程序文件以.shader为后缀:
2、Shader程序都是以关键字“Shader”开始的,后面紧接着的是Shader的命名(引用名),可以像目录一样,但是Shader的文件名和引用名不必一样。我们可以尝试在需要绑定Shader的组件上找到刚才创建的Shader,不难发现Shader的索引目录与命名对应:
3、Properties:
Properties中用来声明一些可用于可视化配置的属性,一般声明的格式为:
name("display name",为属性赋值的数据类型) = 赋值内容(缺省值或初始值)
就像上例中的“_Color("Main Color", Color) = (0, 0, 0, 1)”,其中name是属性索引(通常带下划线,例如:_Color、__MainTex等),display name是属性在Unity中展示名称,由于各种属性的赋值类型不尽相同,所以对其赋值之前要先规定其属性对应的数据类型,并在最后填写一个默认的属性值(缺省值)。常见的属性有:
Properties{
_MyTexture("Texture (RGB)",2D) = "white" {}//图片形式的属性
_MyColor("Color of Object",Color) = (1,1,1,1)//颜色属性
_MyCube("Environment map",Cube) = "white"{}//3D贴图,需要两张图片
_MyVector("Vector",vector) = (1,1,1,1)//4个元素的向量
_MyFloat("Float Value",float) = 1.0//浮点小数
_MyRange("Another type of float",range(-13,14)) = 1.0//限定范围的浮点数
}
4、SubShader:
一个Shader中可以包含多个SubShader(理论上可以有无限个),在实际使用时只会呈现第一个符合当前显卡的SubShader的内容。
5、Pass:
Pass是具体对渲染进行操作的单元,可用于设置:颜色、材质或者贴图等。例如此处使用到的“Pass{Color[_Color]}”就是设置渲染颜色属性的设置操作。
渲染路径的,用来告诉渲染引擎这个Pass应该在什么渲染路径下被渲染。Unity支持3种渲染路径(RenderingPath),分别是:VertextLit、Forward和Deferred Lighting,这些对应了Pass中使用的一些LightMode标签:Vertex、ForwardBase、ForwardAdd、PrepassBase、PrepassFinal等。
6、FallBack:
这是Unity自己预制的Shader实现,一般能够在所有显卡上运行。加入此段代码是为了保证Shader的广泛性,因为用户自己定的SubShader可能在某个平台上出现所有SubShader都失败的情况,此时就需要使用默认的FallBack来进行渲染。
7.Shader编程语言的选择:
GLSL来编写Shader程序,也可以使用Cg/HLSL来进行编写,但需要遵循一些规则:
GLSLPROGRAM和ENDGLSL之间
代码必须位于CGPROGRAM和ENDCG之间
例如:
Shader "MyShader/FirstShader" {
Properties{
_MyTexture("Texture (RGB)",2D) = "white" {}
_MyColor("Color of Object",Color) = (1,1,1,1)
}
SubShader{
Pass{
CGPROGRAM
//Cg代码
ENDCG
}
}
}
三、Shader Language原理:
现在主要的Shader Language有三种:基于OpenGL的GLSL,基于Direct3D的HLSL,还有基于NVIDIA公司的Cg语言。
使用Shader language编写的程序称为shader program(着色程序),着色程序又分为两类:vertex shader program(顶点着色程序)和fragment shader program(片段着色程序)。那么我们就需要提及两个对应的GPU上的组件: Programmable Vertex Processor (可编程顶点处理器,又称为顶点着色器)和 Programmable Fragment Processor (可编程片断处理器,又称为片断着色器)。
顶点着色程序和片段着色程序的分工为:Vertex program负责顶点坐标变换,Fragment program负责像素颜色计算,前者的输出是后者的输入。
上图是可编程图形硬件的输入\输出流程图,其中:输入寄存器存放输入的图元信息;输出寄存器存放处理后的图元信息;纹理buffer存放纹理数据(目前大多数可编程图形硬件只支持片段处理器处理纹理);从外部宿主程序传入的常量放在常量寄存器中;临时寄存器存放着色程序在执行过程中产生的临时数据。
四、总结:
一个Shader中包含了一个或多个SubShader,每个SubShader中包装了一套渲染方案,每个SubShader由多个Pass,每个Pass包含着渲染一个几何物体的具体代码。
附件:
应用程序阶段、几何阶段和光栅阶段
1.应用程序阶段:使用高级语言(C、C++等)进行开发,主要和CPU、内存打交道,诸如:碰撞检测、场景图建立、空间八叉树更新、视锥裁剪等经典算法都在此阶段。在该阶段末端,几何体数据(顶点坐标、法向量、纹理等)通过数据总线传送给图形硬件。
2.几何阶段:主要负责顶点坐标变换、光照、裁剪、投影以及屏幕映射,该阶段基于GPU进行计算。在该阶段末端,得到经过变换和投影之后的顶点坐标、颜色、以及纹理坐标。
3.光栅阶段:基于几何阶段的输出数据,为像素(Pixel)正确配色,以便绘制完整图像,该阶段进行的都是单个像素的操作,每个像素的信息存储在颜色缓冲器(color buffer或者frame buffer)中。
例如:光照计算属于几何阶段、雾化以及涉及物体透明度的计算属于光栅阶段、深度信息(Z值)的计算属于几何阶段并传递给光栅阶段。