Shader 学习之路-渲染队列、Zwrite、ZTest(十)
第一篇
【颜色缓冲区】
颜色缓冲区(COLOR_BUFFER)就是帧缓冲区(FRAME_BUFFER),你需要渲染的场景最终每一个像素都要写入该缓冲区,然后由它在渲染到屏幕上显示.
【深度缓冲区】
深度缓冲区(DEPTH_BUFFER)与帧缓冲区对应,用于记录上面每个像素的深度值,通过深度缓冲区,我们可以进行深度测试,从而确定像素的遮挡关系,保证渲染正确.
【模板缓冲区】
模版缓冲(STENCIL_BUFFER)与深度缓冲大小相同,通过设置模版缓冲每个像素的值,我们可以指定在渲染的时候只渲染某些像素,从而可以达到一些特殊的效果.
深度缓冲区的作用就是区分颜色所在的层次,防止把被遮挡住的颜色显示出来。
当一个像素第二次被绘制时– 例如当一个物体在另一个物体之后被绘制- 深度缓冲要么保留前面的深度值,要么使用第二个像素的深度值替换当前深度值。那个深度保留哪个深度抛弃取决于你选择的深度函数。例如,如果当前深度函数是CompareFunction.LessEqual时,只有小于等于当前深度值的值才会被保留,而大于当前深度值的值会被抛弃。这叫做深度测试,每次绘制像素时都会进行深度测试。当对一个像素进行深度测试时,它的颜色会被写入渲染目标,而深度被写入深度缓冲
深度缓冲区原理以及为什么要用它
----------------参考资料《OpenGL编程指南》
1.在开始介绍深度缓存之前,先了解一下隐藏表面消除。隐藏表面消除(hidden-surface removal)就是消除实心物体被其他物体所遮挡的那部分。实现隐藏表面消除最简单方法就是使用深度缓冲区(又叫Z缓冲区)。
2.深度缓冲区原理深度缓冲区原理就是把一个距离观察平面(近裁剪面)的深度值(或距离)与窗口中的每个像素相关联。首先,使用glClear(GL_DEPTH_BUFFER_BIT),把所有像素的深度值设置为最大值(一般是远裁剪面)。然后,在场景中以任意次序绘制所有物体。硬件或者软件所执行的图形计算把每一个绘制表面转换为窗口上一些像素的集合,此时并不考虑是否被其他物体遮挡。其次,OpenGL会计算这些表面和观察平面的距离。如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把它的深度值和已经存储在这个像素的深度值进行比较。新像素深度值<原先像素深度值,则新像素值会取代原先的;反之,新像素值被遮挡,他颜色值和深度将被丢弃。为了启动深度缓冲区,必须先启动它,即glEnable(GL_DEPTH_TEST)。每次绘制场景之前,需要先清除深度缓冲区,即glClear(GL_DEPTH_BUFFER_BIT),然后以任意次序绘制场景中的物体。
第二篇
openGL里常出现深度测试,一直不清楚。今天就来弄清楚。
1、深度
所谓深度,就是在openGL坐标系中,像素点Z坐标距离摄像机的距离。摄像机可能放在坐标系的任何位置,那么,就不能简单的说Z数值越大或越小,就是越靠近摄像机。
2、深度缓冲区
深度缓冲区原理就是把一个距离观察平面(近裁剪面)的深度值(或距离)与窗口中的每个像素相关联。
首先,使用glClear(GL_DEPTH_BUFFER_BIT),把所有像素的深度值设置为最大值(一般是远裁剪面)。
然后,在场景中以任意次序绘制所有物体。硬件或者软件所执行的图形计算把每一个绘制表面转换为窗口上一些像素的集合,此时并不考虑是否被其他物体遮挡。
其次,OpenGL会计算这些表面和观察平面的距离。如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把它的深度值和已经存储在这个像素的深度值进行比较。新像素深度值<原先像素深度值,则新像素值会取代原先的;反之,新像素值被遮挡,他颜色值和深度将被丢弃。
为了启动深度缓冲区,必须先启动它,即glEnable(GL_DEPTH_TEST)。每次绘制场景之前,需要先清除深度缓冲区,即glClear(GL_DEPTH_BUFFER_BIT),然后以任意次序绘制场景中的物体。
数学基础:
待渲染的照相机空间中的深度经常定义为近距 near 到远距 far 之间的 z 值,Z坐标和X、Y坐标一样。在变换、裁减和透视除法后,Z的范围为-1.0~1.0。DepthRange映射指定Z坐标的变换,这与用于将X和Y映射到窗口坐标的视口变换类似,在透视变换之后,得到新的 z' 值:
其中 是照相机空间的值,它有时候也表示为 w 或者 w'。
结果 z' 是在 -1 到 1 之间归一化之后的值,其中近距 near 平面位于 -1 处,远距 far 平面位于 1 处。在这个范围之外的相应点在视图体之外,不需要进行渲染。
为了实现深度缓冲,在整个屏幕空间上的对当前多边形顶点之间进行插值来计算 z' 的值,通常这些中间数值在深度缓冲区中用定点数格式保存。距离近距 near 平面越近,z' 值越密;距离越远,z' 值越稀。这样距离照相机越近精度越高。near 平面距离照相机越近,则远距离位置的精度越低。near 平面距离照相机太近是在远距离物体产生人为误差的一个常见因素。
3、深度测试
OpenGL中的深度测试是采用深度缓存器算法,消除场景中的不可见面。在默认情况下,深度缓存中深度值的范围在0.0到1.0之间,这个范围值可以通过函数:
glDepthRange (nearNormDepth, farNormalDepth);
将深度值的范围变为nearNormDepth到farNormalDepth之间。这里nearNormDepth和farNormalDepth可以取0.0到1.0范围内的任意值,甚至可以让nearNormDepth > farNormalDepth。这样,通过glDepthRange函数可以在透视投影有限观察空间中的任意区域进行深度测试。
另一个非常有用的函数是:
glClearDepth (maxDepth);
参数maxDepth可以是0.0到1.0范围内的任意值。glClearDepth用maxDepth对深度缓存进行初始化,而默认情况下,深度缓存用1.0进行初始化。由于在进行深度测试中,大于深度缓存初始值的多边形都不会被绘制,因此glClearDepth函数可以用来加速深度测试处理。这里需要注意的是指定了深度缓存的初始化值之后,应调用:
glClear(GL_DEPTH_BUFFER_BIT); 完成深度缓存的初始化。
在深度测试中,默认情况是将需要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新帧缓存中对应像素的颜色值。这种比较测试的方式可以通过函数:
glDepthFunc(func);
进行修改。其中参数func的值可以为GL_NEVER(没有处理)、GL_ALWAYS(处理所有)、GL_LESS(小于)、GL_LEQUAL(小于等于)、GL_EQUAL(等于)、GL_GEQUAL(大于等于)、GL_GREATER(大于)或GL_NOTEQUAL(不等于),其中默认值是GL_LESS。这些测试可以在各种应用中减少深度缓存处理的的计算。
那么,重点来了:
1、当ZWrite为On时,ZTest通过时,该像素的深度才能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值也会写入颜色缓存。
2、当ZWrite为On时,ZTest不通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。
3、当ZWrite为Off时,ZTest通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值会写入颜色缓存。
4、当ZWrite为Off时,ZTest不通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。
可以看到,像素的深度能否成功写入深度缓存,条件是ZWrite为On,ZTest通过;
写入深度缓存的作用就是为ZTest的比较做准备。
因为ZWrite默认值为On,ZTest默认值为LEqual,所以这很好地解释了为什么在unity中,距离相机近的东西会阻挡住距离相机远的东西。如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这时我们可以通过修改ZWrite和ZTest来改变物体的遮挡关系!
测试环境(蓝色方块距离相机较近,注意这个不是Game视图):
测试的Shader代码(两个方块的shader代码起始都是一样的,测试时修改的是测试区里的代码):
Shader "Custom/ZTest" {
Properties {
_MainTex("Base(RGB)",2D)= "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
/////////////////////////////////////////测试区
Tags{ "Queue" = "Geometry" }
//ZWrite Off
//ZTest Off
/////////////////////////////////////////测试区
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf(Input IN,inout SurfaceOutput o){
half4 c = tex2D(_MainTex,IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
渲染顺序:先蓝色方块再白色方块(以下简称蓝,白)
注意ZWrite默认值为On,ZTest默认值为LEqual,没有渲染物体时,深度缓存中的深度可以理解为无限大
1.
/////////////////////////////////////////蓝色方块测试区
Tags{ "Queue" = "Geometry+200" }
ZWrite Off
//ZTest Off
/////////////////////////////////////////蓝色方块测试区
/////////////////////////////////////////白色方块测试区
Tags{ "Queue" = "Geometry+300" }
//ZWrite Off
//ZTest Off
/////////////////////////////////////////白色方块测试区
结果:白在前面
分析:蓝没有将像素写进深度缓存中,ZTest通过了,颜色缓存中存放了蓝的颜色值
白将像素写进深度缓存中,ZTest通过了,颜色缓存的值变为白的,所以显示白
2.
/////////////////////////////////////////蓝色方块测试区
Tags{ "Queue" = "Geometry+200" }
//ZWrite Off
//ZTest Off
/////////////////////////////////////////蓝色方块测试区
/////////////////////////////////////////白色方块测试区
Tags{ "Queue" = "Geometry+300" }
//ZWrite Off
//ZTest Off
/////////////////////////////////////////白色方块测试区
结果:蓝在前面
分析:蓝将像素写进深度缓存中,ZTest通过了,颜色缓存中存放了蓝的颜色值
而白的像素深度大于蓝的,既没有成功将像素写进深度缓存,同时ZTest不通过,像素被舍弃,所以显示蓝
3.
/////////////////////////////////////////蓝色方块测试区
Tags{ "Queue" = "Geometry+200" }
ZWrite Off
ZTest Off
/////////////////////////////////////////蓝色方块测试区
/////////////////////////////////////////白色方块测试区
Tags{ "Queue" = "Geometry+300" }
//ZWrite Off
//ZTest Off
/////////////////////////////////////////白色方块测试区
结果:白在前面
分析:蓝没有将像素写进深度缓存中,ZTest通过了,颜色缓存中存放了蓝的颜色值
白将像素写进深度缓存中,ZTest通过了,颜色缓存中存放为白的颜色值
4.
/////////////////////////////////////////蓝色方块测试区
Tags{ "Queue" = "Geometry+200" }
//ZWrite Off
//ZTest Off
/////////////////////////////////////////蓝色方块测试区
/////////////////////////////////////////白色方块测试区
Tags{ "Queue" = "Geometry+300" }
//ZWrite Off
ZTest Off
/////////////////////////////////////////白色方块测试区
结果:白在前面
分析:蓝将像素写进了深度缓存中
白将像素写进了深度缓存中,ZTest通过了,白将颜色缓存中的蓝的像素颜色值替换了。
5.
/////////////////////////////////////////蓝色方块测试区
Tags{ "Queue" = "Geometry+200" }
//ZWrite Off
//ZTest Off
/////////////////////////////////////////蓝色方块测试区
/////////////////////////////////////////白色方块测试区
Tags{ "Queue" = "Geometry+300" }
ZWrite Off
ZTest Off
/////////////////////////////////////////白色方块测试区
结果:白在前面
分析:蓝将像素写进了深度缓存中
白没能将像素写进了深度缓存中(ZWrite为off),但ZTest通过了,此时颜色缓存的值变为白的,但是深度缓存的值是蓝的
6.
/////////////////////////////////////////蓝色方块测试区
Tags{ "Queue" = "Geometry+200" }
//ZWrite Off
ZTest Off
/////////////////////////////////////////蓝色方块测试区
/////////////////////////////////////////白色方块测试区
Tags{ "Queue" = "Geometry+300" }
//ZWrite Off
ZTest Off
/////////////////////////////////////////白色方块测试区
结果:白在前面
分析:蓝将像素写进了深度缓存中
白将像素写进了深度缓存中,ZTest通过了,此时颜色缓存的值变为白的
7.
/////////////////////////////////////////蓝色方块测试区
Tags{ "Queue" = "Geometry+200" }
ZWrite Off
//ZTest Off
/////////////////////////////////////////蓝色方块测试区
/////////////////////////////////////////白色方块测试区
Tags{ "Queue" = "Geometry+300" }
ZWrite Off
//ZTest Off
/////////////////////////////////////////白色方块测试区
结果:白在前面
分析:白、蓝都没有将像素写入深度缓存中,所以深度缓存中的深度值为无穷大,最后因为白中ZTest默认值的原因,所以显示白
8.
/////////////////////////////////////////蓝色方块测试区
Tags{ "Queue" = "Geometry+200" }
ZWrite Off
ZTest Off
/////////////////////////////////////////蓝色方块测试区
/////////////////////////////////////////白色方块测试区
Tags{ "Queue" = "Geometry+300" }
ZWrite Off
ZTest Off
/////////////////////////////////////////白色方块测试区
结果:白在前面