OpenGL中的深度、深度缓存、深度测试

本文介绍OpenGL中的深度缓冲原理及深度测试方法,包括深度定义、深度缓冲区工作流程、深度测试函数使用等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     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' 值:

z'=\frac{\mathit{far}+\mathit{near}}{\mathit{far}-\mathit{near}} +\frac{1}{z} (\frac{-2 \cdot \mathit{far} \cdot \mathit{near}}{\mathit{far}-\mathit{near}} )

     其中 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。这些测试可以在各种应用中减少深度缓存处理的的计算。

### 关于 OpenGL ES 深度测试 #### 什么是深度测试深度测试是一种用于决定哪些片段应该被绘制到屏幕上,而哪些不应该的机制。它通过比较新片段深度当前帧缓冲中的深度值来实现这一功能。如果新的片段更靠近观察者,则更新帧缓冲;否则丢弃该片段。 在 OpenGL ES 中,深度测试可以通过 `glEnable(GL_DEPTH_TEST)` 启用,并使用 `glDepthFunc` 设置具体的比较函数[^2]。常见的比较函数有: - **GL_NEVER**: 新片段永远不会通过测试。 - **GL_LESS**: 如果新片段深度小于现有深度值则通过。 - **GL_EQUAL**: 如果两者相等则通过。 - **GL_LEQUAL**: 如果新片段深度小于等于现有深度值则通过。 - **GL_GREATER**, **GL_NOTEQUAL**, **GL_GEQUAL**, 和 **GL_ALWAYS** 提供其他不同的比较逻辑。 默认情况下,深度测试是禁用的,因此需要显式调用上述命令以激活并配置其行为。 #### 如何初始化深度缓冲区? 为了使深度测试生效,必须确保存在有效的深度缓冲区。这通常涉及设置合适的上下文属性以及分配足够的资源给深度缓存区域。例如,在创建 EGL 上下文时可以指定所需的深度位数: ```c EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 16, // 请求至少具有 16 位精度的深度缓冲区 EGL_NONE }; ``` 接着验证所选配置确实支持请求的功能集: ```c if (config->depthSize >= 16) { /* 配置满足需求 */ } else { /* 错误处理 */} ``` 此外还需要清除每一帧开始前的深度缓冲状态以便获得正确的渲染效果: ```cpp glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ``` #### 常见问题及其解决方案 1. **为什么我的模型看起来像是穿模?** 这可能是由于未能正确开启或设定深度测试所致。确认已执行如下代码片段: ```cpp glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // 或其他适合的选择 ``` 2. **即使启用了深度测试仍然看不到预期的结果怎么办?** 应当检查是否遗漏了清屏步骤或者错误设置了清除值。标准做法如下所示: ```cpp GLfloat clearDepthValue = 1.0f; glClearDepth(clearDepthValue); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ``` 3. **性能下降明显是什么原因引起的呢?** 当场景复杂度增加时可能会遇到这种情况。尝试优化几何结构减少不必要的绘图指令提交次数,同时考虑利用层级遮挡剔除技术(Hierarchical Z-culling)提高效率[^1]。 4. **如何调试深度相关的问题?** 利用工具如 RenderDoc 来捕获帧数据进而分析具体哪一部分出现了偏差可以帮助定位问题所在位置。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值