目录

  • 一、问题描述
  • 二、排查问题
    • 2.1 怀疑createProgram链接着色器出了问题
    • 2.2 怀疑是不是没有draw()?
    • 2.3 对比以前写的可以正常显示的其他Demo代码
  • 三、解决问题
    • 3.1 去掉`glEnable(GL_DEPTH_TEST);`
    • 3.3 加上 `glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);`
    • 3.4 加上 `glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);`和 `glEnable(GL_DEPTH_TEST);`
  • 四、知其然知其所以然
    • 4.1 什么是深度缓冲区?
      • 4.1.1 了解深度
      • 4.1.2 思考下,为什么要使用深度缓冲区?
      • 4.1.3 究竟什么是深度缓冲区?
    • 4.2 什么是深度测试?
      • 4.2.1 启用深度测试
      • 4.2.2 深度缓冲区的深度值
    • 4.3 深度测试中的深度冲突现象
    • 4.3 开启深度测试的注意事项
  • 五、总结

一、问题描述

今日,写了个OpenGL的小程序,发现场景直接黑屏。

二、排查问题

2.1 怀疑createProgram链接着色器出了问题

看日志

  1. 成功加载了顶点着色器和片段着色器
  2. createProgram也是正常的,没有异常打印

正常创建了Program,也没有异常打印,所以应该不是这里的问题

2.2 怀疑是不是没有draw()?

加上日志打印,排查draw() 方法有没有正常执行,如何


日志在正常打印,说明在不停的绘制,但是为啥不停的绘制还是黑屏?还得继续排查

2.3 对比以前写的可以正常显示的其他Demo代码

我对比了之前可以正常显示的其他Demo的代码,发现我这个Demo使用了

glEnable(GL_DEPTH_TEST);

开启了深度测试,而其他的代码是没有开启深度测试的

有异常的代码如下所示:

void BasicLightingSample::draw() {LOGD("BasicLightingSample::Draw()")if (mProgram == GL_NONE || m_TextureId == GL_NONE) {LOGD("BasicLightingSample::Draw()  mProgram == GL_NONE || m_TextureId == GL_NONE")return;}// Use the program objectglUseProgram(mProgram);glEnable(GL_DEPTH_TEST);// ... 其他绘制逻辑

三、解决问题

3.1 去掉glEnable(GL_DEPTH_TEST);

我们直接将代码glEnable(GL_DEPTH_TEST);去掉,如下所示:

void BasicLightingSample::draw() {LOGD("BasicLightingSample::Draw()")if (mProgram == GL_NONE || m_TextureId == GL_NONE) {LOGD("BasicLightingSample::Draw()  mProgram == GL_NONE || m_TextureId == GL_NONE")return;}// Use the program objectglUseProgram(mProgram);//    glEnable(GL_DEPTH_TEST);// ... 其他绘制逻辑

发现此时有画面,但是画面会有异常,如下所示:

我们可以发现,随着手指触摸它,让它不停旋转,之前每一次旋转绘制的帧都没有清除掉, 很难看!

但是至少画面出来了,看来是因为开启深度测试导致的黑屏,我们继续排查为啥开启深度测试会导致黑屏呢?

3.3 加上 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

我们在上一步去掉 glEnable(GL_DEPTH_TEST);的情况下,加上代码

glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

完整代码如下:

void BasicLightingSample::draw() {LOGD("BasicLightingSample::Draw()")if (mProgram == GL_NONE || m_TextureId == GL_NONE) {LOGD("BasicLightingSample::Draw()  mProgram == GL_NONE || m_TextureId == GL_NONE")return;}// Use the program objectglUseProgram(mProgram);glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//    glEnable(GL_DEPTH_TEST);// ... 其他绘制逻辑

运行的效果也有异常,如下所示:


这一次比上一次好了,没有那么多历史的帧残留在绘制。但是光照效果还是有异常,我们加上深度测试看一看。

这是因为:如果关闭深度测试,OpenGL只根据绘制的先后顺序决定显示结果。那么后绘制的平面遮挡了一部分先绘制的本应该显示出来的立方体,这种效果是不符合实际的。

3.4 加上 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);

这一次我们不光加上 glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);,我们还加上 glEnable(GL_DEPTH_TEST); 如下所示:

void BasicLightingSample::draw() {LOGD("BasicLightingSample::Draw()")if (mProgram == GL_NONE || m_TextureId == GL_NONE) {LOGD("BasicLightingSample::Draw()  mProgram == GL_NONE || m_TextureId == GL_NONE")return;}// Use the program objectglUseProgram(mProgram);glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);// ... 其他绘制逻辑

这一次,我们可爱的超越妹妹终于正常显示了

不停旋转,画面也正常,如下所示:

看来,下面的两行代码必须搭配使用才可以正常。

glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);

但是为什么呢? 我们接下来分析。

四、知其然知其所以然

本节内容参考自下面两篇博客:

  • OpenGL-深度测试 https://www.it610.com/article/1409069347401465856.htm
  • LEARN OPENGL 深度测试 https://zhuanlan.zhihu.com/p/369781131
  • NDK OpenGL ES 3.0 开发(十):深度测试 https://blog.csdn.net/Kennethdroid/article/details/101709694

4.1 什么是深度缓冲区?

深度缓冲区(Detph buffer)同颜色缓冲区(color buffer)是对应的,
颜色缓冲区存储的像素的颜色信息,
而深度缓冲区存储像素的深度信息。

4.1.1 了解深度

深度其实就是该像素点在3D世界中距离摄像机的距离,z值

4.1.2 思考下,为什么要使用深度缓冲区?

  • 在不使用深度测试的时候
    如果我们先绘制一个距离比较近的物理再绘制距离较远的物理,则距离远的位图因为后绘制,会把距离近的物体覆盖掉

  • 有了深度缓冲区后
    绘制物体的顺序就不那么重要的。实际上,只要存在深度缓冲区OpenGL都会把像素的深度值写入到缓冲区中除非调用glDepthMask(GL FALSE).来禁止写入。

4.1.3 究竟什么是深度缓冲区?

其实深度缓冲区,就是一块内存区域,专门存储着每个像素点(绘制在屏幕上的)深度值,深度值(Z值)越大,则离摄像机就越远。

深度缓冲区一般由窗口管理系统来创建,深度值一般由16位,24位或者32位值表示,通常是24位。当然位数越高的话,深度的精确度越好

4.2 什么是深度测试?

在决定是否绘制一个物体的表面时,首先将表面对应像素的深度值当前深度缓冲区中的值进行比较
如果大于等于深度缓冲区中值,则丢弃这部分;
否则利用这个像素对应的深度值和颜色值,分别更新深度缓冲区和颜色缓冲区。
这一过程称之为深度测试(Depth Testing)

深度缓冲区通常用于隐藏表面的消除。

传统上,它保存渲染表面上每个像素与视点最近物体的距离值,对于每个新的输入片段,将其与视点的具体和存储值比较。

默认情况下,如果输入片段的深度值小于深度缓冲区中保存的值(意味着它离观看者更近),则 输入片段的深度值代替保存在深度缓冲区中的值,然后其颜色值代替颜色缓冲区中的颜色值。

4.2.1 启用深度测试

这是深度缓冲区的标准方法,如果这就是你想做的,那么调用带GL_DEPTH_TESTglEnable来启用深度测试。因为深度测试默认是关闭的,所以必须得调用下面的方法才能启动深度测试

glEnable(GL_DEPTH_TEST);

当它启用的时候,如果一个片段通过了深度测试的话,OpenGL会在深度缓冲中储存该片段的z值;如果没有通过深度缓冲,则会丢弃该片段。

如果你启用了深度缓冲,你还应该在每个渲染迭代之前使用GL_DEPTH_BUFFER_BIT清除深度缓冲否则你会仍在使用上一次渲染迭代中的写入的深度值: 这也就是我们为什么一开始黑屏的原因。

所以我们 需要在绘制场景前,清除颜色缓冲区时,清除深度缓冲区:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

4.2.2 深度缓冲区的深度值

清除深度缓冲区的默认值是1.0,表示最大的深度值,深度值的范围在[0,1]之间,值越小表示越靠近观察者,值越大表示远离观察者。

在进行深度测试时,当前深度值和深度缓冲区中的深度值,进行比较的函数,可以由用户通过glDepthFunc指定,这个函数包括一个参数

void glDepthFunc (GLenum func);


深度测试启用后,默认情况下深度测试函数使用 GL_LESS,这将丢弃深度值高于或等于当前深度缓冲区的值的片段。

例如我们可以使用GL_AWALYS参数,这与默认不开启深度测试效果是一样的

glDepthFunc(GL_ALWAYS);

4.3 深度测试中的深度冲突现象

深度测试中,深度冲突现象需要值得注意。深度冲突(Z-fighting)是指两个平面(或三角形)相互平行且靠近的过于紧密,模板缓冲区不具有足够的精度确定哪一个平面靠前,导致这两个平面的内容不断交替显示,看上去像平面内容争夺顶靠前的位置。

防止深度冲突的方法:

  1. 不要让物体之间靠得过近,以免它们的三角形面片发生重叠;
  2. 把近平面设置得远一些(越靠近近平面的位置精度越高);
  3. 牺牲一些性能,使用更高精度的深度值。

4.3 开启深度测试的注意事项

  1. 使用深度测试,最常见的错误是没有使用glEnable(GL_DEPTH_TEST);
  2. 开启深度测试,或者没有使用glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);清除深度缓冲区。
  3. 与深度缓冲区相关的另一个函数是glDepthMask,它的参数是布尔类型,GL_FALSE将关闭缓冲区写入,默认是GL_TRUE,开启了深度缓冲区写入。
  4. 注意防止深度冲突

五、总结

我们一开始启用深度测试,直接黑屏的原因就是因为没有使用glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);清除深度缓冲区。

因为如果不使用glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);清除深度缓冲区,那么就会仍在使用上一次渲染迭代中的写入的深度值。

深度测试(Depth Testing)过程,在决定是否绘制一个物体的表面时,首先将表面对应像素的深度值当前深度缓冲区中的值进行比较
如果大于等于深度缓冲区中值,则丢弃这部分;
否则利用这个像素对应的深度值和颜色值,分别更新深度缓冲区和颜色缓冲区。

所以,这样值不变,就会直接被丢弃,所以就黑屏了!

因此,我们使用深度测试的时候,最好如下面所示一样搭配使用:

glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glEnable(GL_DEPTH_TEST);

【我的OpenGL学习进阶之旅】关于OpenGL ES 开启深度测试,直接黑屏的问题的解决方法相关推荐

  1. 【我的OpenGL学习进阶之旅】OpenGL ES 3.0新功能

    目录 1.1 纹理 1.2 着色器 1.3 几何形状 1.4 缓冲区对象 1.5 帧缓冲区 OpenGL ES 2.0 开创了手持设备可编程着色器的时代,在驱动大量设备的游戏.应用程序和用户接口中获得 ...

  2. 【我的OpenGL学习进阶之旅】 OpenGL ES 实现 绿幕抠图 以及 替换绿幕背景的功能

    一.绿幕抠图 "近来,我们总是不经意间看到一些自媒体公众号,影评人在谈到某部上映的影视剧制作如何稀烂,演员如何不敬业时总会用到"抠图"这个词.似乎"抠图&quo ...

  3. 【我的OpenGL学习进阶之旅】介绍一下 绘制图元

    目录 一.绘制图元 1.1 `glDrawArrays` 1.1.1 `glDrawArrays`API说明 1.1.2 `glDrawArrays`API示例 1.2 `glDrawElements ...

  4. 【我的OpenGL学习进阶之旅】C++如何加载TGA文件?

    一.TGA文件相关介绍 通过前面的博客 [我的OpenGL学习进阶之旅]什么是TGA文件以及如何打开TGA文件? 地址:https://ouyangpeng.blog.csdn.net/article ...

  5. 【我的OpenGL学习进阶之旅】【持续更新】关于学习OpenGL的一些资料

    目录 一.相关书籍 OpenGL 方面 C方面 NDK 线性代数 二.相关博客 2.0 一些比较官方的链接 2.1 OpenGL着色器语言相关 2.2 [[yfan]](https://segment ...

  6. 【我的OpenGL学习进阶之旅】着色器和程序(上)------着色器

    着色器和程序 一.前言 二.着色器和程序 2.1 创建和编译一个着色器 2.1.1 创建着色器 2.1.2 删除着色器 2.1.3 提供着色器源代码 2.1.4 编译色器 2.1.4 查询有关着色器对 ...

  7. 【我的OpenGL学习进阶之旅】解决OpenGL绘制带透明通道的png纹理时出现黑边问题,并彻底了解其原理

    文章目录 一.问题描述 二.解决问题 2.1 解决效果 2.2 如何解决 三.知其然知其所以然 3.1 像素.抗锯齿的基本概念 3.2 alpha的两种类型,直通和预乘 3.3 直通和预乘的计算方式 ...

  8. 【我的OpenGL学习进阶之旅】收集到的关于如何在OpenGL ES上使用MSAA(Multisample Anti-aliasing)实现抗锯齿效果的资料和源码

    文章目录 一.需求 1.2 OpenGL的MSAA功能不能在OpenGL ES上使用,你崩溃了吗? 1.3 OpenGL ES的抗锯齿在离屏渲染和在屏渲染上的不同需求 二.OpenGL ES上实现的M ...

  9. 【我的OpenGL学习进阶之旅】关于3D模型知识之:什么是obj文件和mtl文件

    文章目录 一.学习3D模型的背景 二.3D模型效果展示 三.好奇3D模型文件是啥内容? 3.1 打开.obj文件 3.2 打开.obj文件 3.3 在外部使用查看3D模型的软件打开.obj文件 3.3 ...

最新文章

  1. form表单序列化去除空值
  2. python中一些实用而容易被忽视不常用的库
  3. Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
  4. MDK:assert_param函数未定义的错误:Error: L6218E
  5. cloudera manager 及CDH卸载
  6. mysql查询不确定的信息_mysql 07.18
  7. 排序代码(python,c++) 及 基本算法复杂度
  8. js客户端存储之Web存储
  9. android audio混音
  10. 实验一 顺序表基本操作的实现
  11. 快速使用CSS技术手册
  12. matlab对角和,matlab – 如何在对角线上赋值?
  13. Intel Thunderbolt 3 接口介绍
  14. Android客户端登录注册模块
  15. HDU 6143 Killer Names(容斥+组合)
  16. qt.modbus: (RTU client) Discarding response with wrong CRC, received: 16448 , calculated CRC: 49303
  17. zipJS 前端压缩使用
  18. 1.基于51单片机的蓝牙手机遥控小车
  19. swiper轮播图切换指示点改变背景颜色
  20. 实验报告:小学数学练习、石头剪刀布游戏

热门文章

  1. 交换机上如何测试出口带宽速度_万兆NAS模拟测试:是什么限制了性能?
  2. 智联招聘导出的文档有横线去除方法Office2007
  3. Linux数据库1——基本介绍
  4. 持续集成Jenkins大法好
  5. 《自然语言处理(哈工大 关毅 64集视频)》学习笔记:第七章 句法分析技术
  6. pyecharts-动态可视化(5)关系图/社会网络/涟漪特效
  7. Python字典dict
  8. linux嵌入式系统算法,嵌入式Linux操作系统调度算法研究
  9. 全球为何只有13台DNS根服务器
  10. 数据库的几种关闭方式