深度缓冲区
深度缓冲(Depth Buffer)来防止
被阻挡的面渲染到其它面的前面。讨论这些储存在深度缓冲(或z缓冲(z-buffer))
中的深度值(Depth Value),以及它们是如何确定一个片段是处于其它片段后方的。
每个片段都存储了信息,和颜色缓冲区一样有宽度和高度,深度缓存区是由窗口自己创建的。绝大多数是24深度值来存储的。一般的存储为16、24、32位float存储。
开启深度之后,每一个片段的深度值进行对比,会执行一个深度测试,通过了就更新,失败了就丢弃.
如果测试通过就更新,没有通过就丢弃了。
执行位置
深度缓存区在片段着色器之后执行,在屏幕空间执行
屏幕空间坐标和viewport密切相关 可以通过gl_FragCoord进访问屏幕空间
这个xy代表了屏幕的坐标,gl_FragCoord中也包含了一个z分量,它包含了片段真正的深度值。z值就是需要与深度缓冲内容所对比的那个值
· 大多数的Gpu由一个提前深度测试,运行在运行之前,只要一个片段是永远不会可见的,就会丢弃它。
默认关闭 开启
glEnable(GL_DEPTH_TEST);
当它启用的时候
- 片段通过深度测试 OpenGL会在深度缓冲中储存该片段的z值
- 如果没有通过深度缓冲 会丢弃该片段
还应该在每个渲染迭代之前使用GL_DEPTH_BUFFER_BIT来清除深度缓冲,否则你会仍在使用上一次渲染迭代中的写入的深度值:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
进行测试,但是不更新深度值
一般的情况是使用只读深度测试,opengl可以禁用深度缓冲的写入,只需要设置掩码
将掩码变为false
glDepthMask(GL_FALSE);
上面是在开启了深度测试,但是设置上面之后,会变为只读,只会读不会更新。
深度测试的比较
允许修改深度值使用比较运算符,我们可以控制丢弃和通过一个片段,什么时候更新缓冲区.
GL_ALWAYS 永远通过深度测试
GL_NEVER 永远不通过深度测试
GL_LESS 在片段深度值小于缓冲的深度值时通过测试
GL_EQUAL 在片段深度值等于缓冲区的深度值时通过测试
GL_LEQUAL 在片段深度值小于等于缓冲区的深度值时通过测试
GL_GREATER 在片段深度值大于缓冲区的深度值时通过测试
GL_NOTEQUAL 在片段深度值不等于缓冲区的深度值时通过测试
GL_GEQUAL 在片段深度值大于等于缓冲区的深度值时通过测试
默认情况下使用的深度函数是GL_LESS,它将会丢弃深度值大于等于当前深度缓冲值的所有片段。
比如3d绘制一个地板,地板上放一个椅子,如果设置为一直通过,那么这个椅子就会被地板盖住
z值的大小
观察空间的z值可能是投影平截头体的近平面(Near)和远平面(Far)之间的任何值。一般的我们会将他们放到0到1之间。
F = z - near / far - near;
near和far提供给投影的平截头体,需要一个Z值,变换到0,1之间。
一般不适应线性的
在实践中是几乎永远不会使用这样的线性深度缓冲(Linear Depth Buffer)的。要想有正确的投影性质,需要使用一个非线性的深度方程,它是与 1/z 成正比的。它做的就是在z值很小的时候提供非常高的精度,而在z值很远的时候提供更少的精度。
花时间想想这个:我们真的需要对1000单位远的深度值和只有1单位远的充满细节的物体使用相同的精度吗?线性方程并不会考虑这一点。
非线性方程与1/z成正比,1.0和2.0之间的z值变为1.0-0.5之间。深度缓冲中0.5的值并不代表着物体的z值是位于平截头体的中间了,这个顶点的z值实际上非常接近近平面!
z越小,精度越大。
深度缓冲可视化
内建gl_FragCoord向量包含z值特定的深度值,加入深度用颜色表示。
FragColor = vec4(vec3(gl_fragCoord.z),1.0)
屏幕的空间深度值是非线性的,z值越小精度越高,如果z很大的时候,精度很低
深度冲突
两个屏幕或者三角形非常紧密深度没有足够的精度来区分,就会使得两个一直前后切换,这就是深度冲突。
防止冲突
- 永远不要将两个物体进行重叠,设置一个偏移值
- 尽可能进平面设置远一些。(靠近进平面的精度高,所以远点不容易发现)
- 使用更高精度的缓冲(牺牲性能)
使用
绘制之前开启深度测试,绘制不同深度的颜色值。