前言
前面写到了逐片元操作的大概流程,下面是对其步骤的细化与unity shader的整体概述
一、逐片元操作
逐片元操作是一个OpenGL当中的词汇,在DirectX中把它叫做输出合并阶段
这个阶段的任务如图所示
模板测试
可以将模板测试理解为蒙版测试,也即指定一片区域,这个区域将用模板缓冲来表示,如下图,值为1的地方被保留,为0的地方被丢弃
深度测试
当场景中有一个片元被另个片元盖住而不可见的时候,就要使用深度测试来决定显示前面的片元还是显示后面的片元,我们使用的是深度缓冲区的方法来实现这一环节。在决定是否绘制一个物体的表面时,首先将表面对应像素的深度值与当前深度缓冲区中的值进行比较,如果大于等于深度缓冲区中值,则丢弃这部分;否则利用这个像素对应的深度值和颜色值,分别更新深度缓冲区和颜色缓冲区。
混合
如果场景中的物体都是不透明的,则混合功能对场景渲染并不会有太大影响,若果想要实现玻璃,镜子等效果,混合步骤则是必不可少的。GPU通过模板测试,深度测试等决定每一个片元的可见性,将通过各种测试的片元的颜色值与颜色缓冲区中的颜色混合再存入颜色缓冲区中
其中混合的大概意思就是当有多个片元存在于屏幕坐标系的同一位置上时,GPU就要根据透明通道等因素来对这些片元进行混合,例如相加,相减,相乘等,所以这一步骤在透明材质的实现上有很重要的作用
二. Unity Shader
在Unity中,用户可以通过一个叫做Unity Shader的地方来让开发者更加轻松的管理shader代码
总体来说,为了实现游戏中较好的模拟效果,需要配合使用材质和Unity Shader。一个最常见的流程为
(1)创建一个材质
(2)创建一个Unity Shader,并把它赋给上一步创建的材质
(3)把材质赋给要渲染的对象
(4)在材质面板中调整Unity Shader的属性
Material
与建模软件中的材质类似,可以简单调整金属度,糙度等参数
shader
在unity中提供了几种shader模板
每一个shader被创建后需要与材质结合才能发挥其效果
ShaderLab
ShaderLab是unity中一种专门用来定义Unity Shader的语言
一个基本的Unity Shader结构代码如下
Shader "ShaderName"{
Properties{
//属性
}
SubShader{
//显卡A使用的子着色器
}
SubShader{
//显卡B使用的子着色器
}
FallBack "VertexLit"
}
Unity会在背后将这些伪代码编译成真正的代码和Shader文件
起名
Shader "Custom/MyShader"{
}
每个Unity Shader文件的第一行都要通过这句话来为这个文件起个名字以及控制其存放的位置
Properties
其定义通常如下
Properties{
Name ("display name", PropertyType) = DefaultValue
Name ("display name", PropertyType) = DefaultValue
//更多属性
}
声明这些属性是为了更好的调整各种材质属性
Name 是每个属性的名字,其通常由一个下划线开始
dispalyname 是出现在材质面板上的名字
PropertyType 则为属性的类型,常见的类型如图所示
在代码中这些属性是这样的
Shader "Custom/Example"{
Properties{
_Int("Int", Int) = 2
_Float("Float", Float) = 1.5
_Range("Range", Range(0.0, 5.0)) = 3.0
_Color("Color", Color) = (1, 1, 1, 1)
_Vector("Vector", Vector) = (2, 3, 6, 1)
_2D("2D", 2D) = ""{}
_Cube("Cube", Cube) = "white"{}
_3D("3D", 3D) = "black"{}
}
}
DefaultValue 是一个默认值,当第一次将这个unity shader赋给某个材质时,材质面板上显示的就是这些值
properties代码块的作用是为了让以上这些属性能够出现在材质面板中
SubShader
SubShader是UnityShader中的重量级成员,其定义通常如下
SubShader{
//可选的
[Tags]
//可选的
[RenderSetup]
Pass{
}
}
其中,[Tags]为标签,[RenderSetup]为状态,Pass定义了一次完整的渲染流程
[RenderSetup] 提供了一系列渲染状态指令,常见的状态如下
[Tags] 是一个键值对,是SubShader和渲染引擎之间沟通的桥梁,表明在何时以及怎样渲染此对象
其基本格式如下Tags { "TagName1" = "Value1" "TagName2" = "Value2" }
Pass语义块
Pass{
[Name]
[Tags]
[RenderSetup]
//Other Code
}
首先可以在Pass中定义该Pass的名称(与Unity Shader代码很像),例如
Nmae "MyPassName"
通过这个名称就可以在其他的Unity Shader代码中调用这个Pass块
UsePass "Myshader/MYPASSNAME"
pass中的[RenderSetup]和SubShader中的是通用的,也就是说,可以用SubShader中的状态来设置pass
而pass中的[Tags]则不一样
FallBack
紧跟在SubShader后的代码块,功能与c语言case中的default类似
Fallback "name"
即如果显卡等设备无法运行上面的SubShader的话,就运行这个最低级的unity shader