Shader-ZTest, ZWrite

来源:互联网 发布:短信平台和网络 编辑:程序博客网 时间:2024/06/05 17:33
参考博客:http://blog.csdn.net/lyh916/article/details/45317571参考博客:http://blog.csdn.net/cbbbc/article/details/70118013参考博客:http://blog.csdn.net/ecidevilin/article/details/52864349介绍深度测试(ZTest)和深度写入(ZWrite)意义之前先解释下二者的概念:颜色缓存color buffer/pixel buffer:储存该点即将显示的颜色,RGBA值。深度缓存depth buffer/z buffer:储存该点的深度   

(1)什么是深度?
深度其实就是该像素点在3d世界中距离摄像机的距离。离摄像机越远,则深度值(Z值)越大。

(2)什么是深度缓存?
深度缓存中存储着准备要绘制在屏幕上的像素点的深度值。如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把该像素的深度值和深度缓存的深度值进行比较。如果新像素深度值<深度缓存深度值,则新像素值会取代原先的;反之,新像素值被遮挡,其颜色值和深度将被丢弃。(深度主要起的是比较的作用)

(3)什么是深度测试?
在深度测试中,默认情况是将要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新深度缓存中对应像素的颜色值。

(4)为什么需要深度?
在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。

渲染队列

Unity中的几种内置的渲染队列,按照渲染顺序,从先到后进行排序,队列数越小的,越先渲染,队列数越大的,越后渲染。

Background(1000) 最早被渲染的物体的队列。
Geometry (2000) 不透明物体的渲染队列。大多数物体都应该使用该队列进行渲染,也是Unity Shader中默认的渲染队列。
AlphaTest (2450) 有透明通道,需要进行Alpha Test的物体的队列,比在Geomerty中更有效。
Transparent(3000) 半透物体的渲染队列。一般是不写深度的物体,Alpha Blend等的在该队列渲染。
Overlay (4000) 最后被渲染的物体的队列,一般是覆盖效果,比如镜头光晕,屏幕贴片之类的。

RenderType
在写shader的时候在Tag中有一项叫RenderType,不过这个没有Render Queue那么常用,这里顺便记录一下:
Opaque: 用于大多数着色器(法线着色器、自发光着色器、反射着色器以及地形的着色器)。
Transparent:用于半透明着色器(透明着色器、粒子着色器、字体着色器、地形额外通道的着色器)。
TransparentCutout: 蒙皮透明着色器(Transparent Cutout,两个通道的植被着色器)。
Background: 天空盒着色器。
Overlay: GUITexture,镜头光晕,屏幕闪光等效果使用的着色器。
TreeOpaque: 地形引擎中的树皮。
TreeTransparentCutout: 地形引擎中的树叶。
TreeBillboard: 地形引擎中的广告牌树。
Grass: 地形引擎中的草。
GrassBillboard: 地形引擎何中的广告牌草。

unity先将渲染队列中较前的队列进行渲染,然后再执行ZWrite,ZTest。
不透明的物体,是采用了从前到后的渲染顺序进行渲染,不透明物体在进行完vertex阶段,进行Z Test,然后就可以得到该物体最终是否在屏幕上可见了,如果前面渲染完的物体已经写好了深度,深度测试失败,那么后面渲染的物体就直接不会再去进行fragment阶段。
透明物体不会写深度,也就是说透明物体之间的穿插关系是没有办法判断的,所以半透明的物体在渲染的时候一般都是采用从后向前的方法进行渲染,由于透明物体多了,透明物体不写深度,那么透明物体之间就没有所谓的可以通过深度测试来剔除的优化,每个透明物体都会走像素阶段的渲染,会造成大量的Over Draw。这也就是粒子特效特别耗费性能的原因。
Unity支持我们自定义渲染队列,比如我们需要保证某种类型的对象需要在其他类型的对象渲染之后再渲染,就可以通过自定义渲染队列进行渲染。而且超级方便,我们只需要在写shader的时候修改一下渲染队列中的Tag即可。比如我们希望我们的物体要在所有默认的不透明物体渲染完之后渲染,那么我们就可以使用Tag{“Queue” = “Geometry+1”}就可以让使用了这个shader的物体在这个队列中进行渲染。

ZWrite可以取的值为:On/Off,默认值为On,代表是否要将像素的深度写入深度缓存中(同时还要看ZTest是否通过)。
ZTest可以取的值为:Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never/Off,默认值为LEqual,代表通过比较深度来更改颜色缓存的值。例如当取默认值的情况下,如果将要绘制的新像素的z值小于等于深度缓存中的值,则将用新像素的颜色值更新深度缓存中对应像素的颜色值。需要注意的是,当ZTest取值为Off时,表示的是关闭深度测试,等价于取值为Always,而不是Never!Always指的是直接将当前像素颜色(不是深度)写进颜色缓冲区中;而Never指的是不要将当前像素颜色写进颜色缓冲区中,相当于消失。

1.深度测试通过,深度写入开启:写入深度缓冲区,写入颜色缓冲区;
2.深度测试通过,深度写入关闭:不写深度缓冲区,写入颜色缓冲区;
3.深度测试失败,深度写入开启:不写深度缓冲区,不写颜色缓冲区;
4.深度测试失败,深度写入关闭:不写深度缓冲区,不写颜色缓冲区;

混合(Blend)被用于创建透明的对象。渲染图像时,当所有的着色器执行完并且所有的纹理都被应用之后,像素点会被写入屏幕。如何将这些像素与已有图像进行组合?我们就需要使用混合命令来告诉GPU混合的方式。

混合命令有以下这些:
Blend Off 关闭混合(默认)
Blend SrcFactor DstFactor 片元产生的颜色乘以SrcFactor,加上屏幕上已有的颜色乘以DstFactor,
得到最终的颜色(写入颜色缓存)
Blend SrcFactor DstFactor, SrcFactorA DstFactorA 同上,只不过使用单独的因子SrcFactorA和DstFactorA来混合透明度通道
BlendOp BlendOperation 用其他的操作来取代加法混合。
BlendOp OpColor, OpAlpha 同上,只不过对于透明度通道使用不同的操作。
注:此外还有AlphaToMask On命令,用来开启alpha-to-coverage,如果有机会我们会在后续文章中介绍。

混合操作(BlendOp):
Add 加法:
FinalColor=SrcFactor*SrcColor+DstFactor*DstColor
Sub 减法(源-目标):
FinalColor=SrcFactor*SrcColor-DstFactor*DstColor
RevSub 减法(目标-源):
FinalColor=DstFactor*DstColor-SrcFactor*SrcColor
Min 较小值(逐个通道比较)
Max 较大值(逐个通道比较)
注:还有其他一些混合操作,但是目前只有DX11.1支持

混合因子(Blend)
One 混合因子1,表示完全的源颜色或目标颜色
Zero 混合因子0,舍弃掉源颜色或目标颜色
SrcColor 源颜色值
SrcAlpha 源透明度
DstColor 目标颜色值
DstAlpha 目标透明度
OneMinusSrcColor 1-SrcColor
OneMinusSrcAlpha
1-SrcAlpha
OneMinusDstColor
1-DstColor
OneMinusDstAlpha
1-DstAlpha
常用的混合命令有:
[cpp] view plain copy
Blend SrcAlpha OneMinusSrcAlpha // 传统透明度
Blend One OneMinusSrcAlpha // 预乘透明度
Blend One One // 叠加
Blend OneMinusDstColor One // 柔和叠加
Blend DstColor Zero // 相乘——正片叠底
Blend DstColor SrcColor // 两倍相乘
当然,相乘/正片叠底的混合命令写成:
[cpp] view plain copy
Blend Zero SrcColor

也是合情合理合法的。

最后,我们还可以扩展一下:
参考百度百科滤色,我们知道,滤色的公式是:
结果色=255-[(255-基色)×(255-混合色)]/255
对应于[0,1]范围内也就是
FinalColor=1-(1-Dst)*(1-Src)
也就是
Dst+Src-Dst*Src
也就是
Src*1+Dst*(1-Src)
或者
Src*(1-Dst)+Dst*1
所以滤色的混合命令我们可以写成下面两种:
[cpp] view plain copy
Blend OneMinusDstColor One

面的剔除 Cull

在渲染的时候,默认情况下是只有朝向摄像机的面才会被渲染,可以告诉Unity,我想渲染哪一个朝向的面,使用Cull命令在计算体积阴影的时候会用到对Cull的操作来计算和物体相交的投影

Cull 有三种
Cull Off 不剔除
Cull Back 剔除背面(背向摄像机的面)
Cull Front 剔除前面 (朝向摄像机的面)

阅读全文
0 0
原创粉丝点击