其实在这一篇之前,应该还有一个关于坐标空间转换的内容,但是内容囤积的有些多,就先把Shader的基础结构先记录一下。
笔记主要记录在代码中,所以知识点主要是图和代码的展示。
Unity Shader分类
在Unity中,Shader的种类,主要是这几种
1.Standard Surface Shder(标准曲面着色器)
包含标准光照模型的表面着色器模板
2.Unlit Shder
不包含光照的基本顶点/片元着色器
3.Image Effect Shader
用于实现屏幕后处理的效果的基本模板
4.Comput Shader
利用GPU并行计算一些和常规渲染流水线无关的内容
5.Ray Tracing Shader
用于实现光线追踪效果的着色器
之后学习的也主要是Unlit Shader相关的知识。
下面来看看Shader的基本构成,我们创建一个Standard Surface Shder,可以发现它的主要结构是这样的
所以Shader 的主要结构就是这样的,接下来是每个部分的具体结构
着色器名字
着色器的名字主要用来决定外部材质使用时的路径,在有其他Shader引用时也会用到
Properties部分
这个部分主要用来定义一些外部属性,这样就可以通过外部赋值的方式来对Shader的数据进行更改。
可以看到属性的结构是这样的
//属性结构
// _name ("Display Name",type)=defaultValue[{options}]
// _name 属性名字,加下划线,方便获取
// Display Name 材质面板上显示的名字
// type 具体的类型 (数值,颜色和向量,纹理贴图)
//defaultValue 将Shader指定给材质的初始化的默认值
既然知道了具体的写法,那么来看看在Shader中的属性能够定义哪些类型。
Shader中的属性类型主要包括数值,颜色和向量,纹理贴图
数值类型
数值(整形,浮点型,范围浮点型)
基础类型是浮点型,整形会被自动转为浮点型
如
_intvalue ("IntValue",Int)=0
_floatvalue("FloatValue",Float)=0
_rangevalue("RangeValue",Range(1,2))=1.5
颜色和向量类型
颜色RGBA 向量XYZW 都是四个值,所以放一起
_ColorValue("ColorValue",Color)=(1,1,1,1)//取值0-1,映射0-255
_VectorValue("ColorValue",Vector)=(1,1,1,1)//取值不限
纹理贴图
纹理贴图主要包括:
2D纹理,包括漫反射贴图,法线贴图等
2DArray纹理(纹理数组,允许存储多层图像数据,每一层看作一个2D图像,一般使用脚本创建)
Cube(map texture)纹理,立方体纹理,由上下左右6张有联系的2d贴图拼成,如天空盒,反射探针
3D纹理,一般脚本创建,使用不多
//纹理贴图
_MainTex ("Texture", 2D) = "white" {}
注意
white:defaultValue
不写:默认为空
white:白色贴图
black:黑色贴图
gray:灰色贴图
bump:凸贴图
red:红色贴图
SubShader部分
subshader子着色器,在每一个Shader中,可能会存在多个SubShader,主要是为了设备多种设备,当程序运行时,会逐一去尝试每一个SubShader,直到找到能够使用的SubShader。
来看看他的构成:
/*组成部分
标签 Tags 确定什么时候渲染物体和如何渲染物体
渲染状态 确定渲染时候的剔除方式,深度测试,混合方式等内容
渲染通道 Pass 具体的代码实现,每个子着色器至少有一个,每定义一个Pass,就会对物体执行一次渲染,
对于一些高级的Shader,需要多个Pass叠加,我们需要尽量减少Pass,他会增加渲染消耗
*/
下面来逐一看
标签 Tags
//Tags 标签
//渲染队列
Tags {
"Queue"="BackGround"//队列号1000,最早渲染的物体,一般用于天空和或者背景
"Queue"="Geometry"//队列号 2000,一般用于不透明的集合体,当没有声明是,Unity会自动申明
"Queue"="AlphaTest"//队列号 2450,有透明通道的,需要透明测试的几何体
"Queue"="Transparent"//队列号 3000,这个队列中的几何体按照由远到近的顺序绘制,半透明的渲染队列,所有进行透明混合的几何体都应该在这个队列中,如玻璃,粒子特效等
"Queue"="Overlay"//队列号 4000,用于最后的叠加效果,如镜头光晕等
"Queue"="Transparent+1"//自定义队列,通过队列的加减确定队列顺序
}
//渲染类型,对着色器进行分类,用于着色器的替换功能
//摄像机上有对于的API,通过调用来替换着色器
Tags{
"RenderType"="Opaque"//不透明的,一些普通的Shader,如不透明,自发光,反射等
"RenderType"="Transparent"//透明的,一些半透明效果
"RenderType"="TransparentCutout"//透明切割,用于透明测试的Shader.如植物叶子等
"RenderType"="BackGround"//背景,一般用于天空和Shader
"RenderType"="Overlay"//覆盖,用于GUI纹理,光环,光晕等
//以下了解即可
"RenderType"="TreeOpaque"//地形系统中的树干
"RenderType"="TreeTransparentCutout"//地形系统中的树叶
"RenderType"="TreeBillboard"//地形系统中的树
"RenderType"="Grass"//地形系统中的草
"RenderType"="GrassBillboard"//地形系统中的Billboard草
}
//禁用批处理
//批处理时模型会被转换到世界空间,从而舍弃模型空间
//可能会导致一些使用模型顶点信息的Shader失效
//总是禁用批处理
Tags{"DisableBatching"="True"}
//Tags{"DisableBatching"="False"}//不禁用
//禁止投射阴影 控制SubShader的物体是否允许投射阴影
Tags{"ForceNoShadowCasting"="True"}//不投射
//Tags{"ForceNoShadowCasting"="False"} //投射阴影(默认)
//禁止投影机
//物体是否受到投影机的投射
//忽略投射(一般在一些半透明效果中使用)
Tags{"IngoreProjector"="True"}//不投射
Tags{"IngoreProjector"="False"}//投射(默认)
渲染状态 State
/*
States 渲染状态 确定渲染时候的剔除方式,深度测试,混合方式等内容
片元,光栅化阶段生成的像素或像素片段,是像素级操作和计算的基本单位,代表了像素的各种属性(颜色,深度值等)
基本结构
渲染状态关键词+空格+状态类型
剔除方式
所谓剔除,就是不渲染,主要有正面剔除,背面剔除(默认),和不剔除
分别是 Cull Front, Cull Back ,Cull Off
Cull Back
深度缓冲
是否写入深入缓冲区(Depth Buffer)
记录每一个像素的深度值,初始为最大深度,意味着所有像素都在屏幕外,通过深度测试之后,决定是否写入缓冲,最后渲染缓冲区中的像素
ZWrite On //写入 一般做透明等效果
ZWrite Off //不写入(默认)
深度测试
主要是保证像素按照正确的顺序进行渲染
我们可以自定义深度的对比方式,来进行像素的深度测试
ZTest Less //小于缓冲区的值就通过测试,决定是否写入缓冲 (透明)
ZTest Greater //大于缓冲区的值就通过测试,决定是否写入缓冲(描边)
ZTest LEqual //小于等于(默认)
ZTest GEqual //大于等于
ZTest Equal //等于
ZTest NotEqual//不等于
ZTest Always //总是
注意:深度测试和混合测试都是在光栅化阶段的逐片元操作中进行的,一般实现一些特殊效果我们回去设置
混合方式
设置图像颜色的混合方式,多种颜色的混合,比如透明,半透明,不透明之间的混合
默认为不混合
Blend One One //线性减淡
等等
其他
注意:这些渲染状态不仅可以在SubShader中申明,也可以在Pass中申明,只是两者的影响范围不同
*/
在这里我们需要了解一下什么是深度测试和颜色混合
深度测试
它主要是用在处理一些遮挡关系和透视关系,举个列子,方块A和方块B,相对于摄像机来说,A比B更近,并且两个方块存在遮挡关系,就像这样
那么摄像机怎么知道重叠部分怎么渲染呢?没错,深度测试。
在渲染管线中存在深度值缓冲区,里面有初始值,设置为最大,这意味着所有物体都在摄像机之外,都不会渲染。
在渲染片元(像素)的时候,他会去将这个位置的深度值和像素的深度值对比,在默认情况下,如果像素的深度值更小,他就会将这个值写入深度缓冲区(可以不写入),在下次渲染的时候,继续这个流程,知道渲染完成,那么留着缓冲区中的像素就是需要渲染的像素了,这样就能处理遮挡关系了。
需要注意的是,如果没有开启深度写入,当像素通过深度测试后,任然会被渲染。这表示,一个像素是否渲染,取决于是否通过深度测试。
颜色混合
和深度测试差不多的道理,这个属性一般用在一些透明效果上。
渲染通道 Pass
//Pass 渲染通道
//结构
/*
Pass
{
名字
渲染标签
渲染状态
着色器相关
}
名字:主要为了在其他Shader中服用Pass代码
如:
Pass{ Name MyPass }
其他Shader中 UsePass "Lesson1/Test01/MYPASS"
unity 会将Pass名字转为大写,所以引用的时候需要大写名字
Pass中的渲染标签
//和SubShader中的语法相同,只是它具有自己独立的渲染标签
1.Tags{"LightMode"="标签值"}
可以指定这个Pass应该在哪个阶段执行
可以将着色器代码分配给适当的渲染阶段,以实现具体的效果
Always 始终渲染,不应用光照
ForwardBase 在向前渲染中使用,应用环境光,主方向光,顶点/sh 光源和光照贴图
ForwardAdd 在向前渲染中使用,应用附加的每个像素光源(每个光源有一个通道)
Deferred 延迟渲染中使用,渲染G缓冲区
ShadowCaster 将对象深度渲染到阴影贴图或深度纹理中
等等
2.Tags{"RequoreOptions"="标签值"}
用于指定满足某些条件时才渲染这个pass
目前unity只支持SoftVegetation
仅当Quality窗口开启SoftVegetation时才渲染
3.Tags{"PassFlags"="标签值"}
指定一些特定的数据传入着色器中
目前unity只支持OnlyDirectional
在向前渲染中使用,这个标志只允许主方向光和环境光/光照探针数据传入
这意味着非重要的光源数据不会传递到 顶点光源或球谐函数着色器变量
Pass中的渲染状态
SubShader中的渲染状态同样在Pass中适用,只是两个的影响范围有所区别
此外,Pass中还可以使用固定管线着色器的命令
GrabPass命令
使用这个命令可以将绘制对象时的屏幕内容抓取到纹理中,在后续的通道中就可以使用这个纹理,从而执行一些高级效果
如:
将绘制对象之前的屏幕抓取到_BackGroundTexture纹理中
GrabPass{
"_BackGroundTexture"
}
注意:这个命令一般写在某个Pass之前,在之后的Pass中就可以利用_BackGroundTexture变量来进行处理
*/
备用Shader
/*
备用Shader
防止所有SubShader都没办法正常渲染的情况,让画面至少能被渲染
FallBack "Shader名"
或者不写(类似于FallBack Off)
*/
那么以上就是Shader的基础结构了,个人笔记,可能存在不全或错误
之后就是学习Shader的具体写法了。