【我的OpenGL学习进阶之旅】关于OpenGL ES 开启深度测试,直接黑屏的问题的解决方法
目录
- 一、问题描述
- 二、排查问题
- 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链接着色器出了问题
看日志
- 成功加载了顶点着色器和片段着色器
- 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_TEST
的glEnable
来启用深度测试。因为深度测试默认是关闭的,所以必须得调用下面的方法才能启动深度测试
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)是指两个平面(或三角形)相互平行且靠近的过于紧密,模板缓冲区不具有足够的精度确定哪一个平面靠前,导致这两个平面的内容不断交替显示,看上去像平面内容争夺顶靠前的位置。
防止深度冲突的方法:
- 不要让物体之间靠得过近,以免它们的三角形面片发生重叠;
- 把近平面设置得远一些(越靠近近平面的位置精度越高);
- 牺牲一些性能,使用更高精度的深度值。
4.3 开启深度测试的注意事项
- 使用深度测试,最常见的错误是没有使用
glEnable(GL_DEPTH_TEST);
- 开启深度测试,或者没有使用
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
清除深度缓冲区。 - 与深度缓冲区相关的另一个函数是
glDepthMask
,它的参数是布尔类型,GL_FALSE
将关闭缓冲区写入,默认是GL_TRUE
,开启了深度缓冲区写入。 - 注意防止深度冲突
五、总结
我们一开始启用深度测试,直接黑屏的原因就是因为没有使用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 开启深度测试,直接黑屏的问题的解决方法相关推荐
- 【我的OpenGL学习进阶之旅】OpenGL ES 3.0新功能
目录 1.1 纹理 1.2 着色器 1.3 几何形状 1.4 缓冲区对象 1.5 帧缓冲区 OpenGL ES 2.0 开创了手持设备可编程着色器的时代,在驱动大量设备的游戏.应用程序和用户接口中获得 ...
- 【我的OpenGL学习进阶之旅】 OpenGL ES 实现 绿幕抠图 以及 替换绿幕背景的功能
一.绿幕抠图 "近来,我们总是不经意间看到一些自媒体公众号,影评人在谈到某部上映的影视剧制作如何稀烂,演员如何不敬业时总会用到"抠图"这个词.似乎"抠图&quo ...
- 【我的OpenGL学习进阶之旅】介绍一下 绘制图元
目录 一.绘制图元 1.1 `glDrawArrays` 1.1.1 `glDrawArrays`API说明 1.1.2 `glDrawArrays`API示例 1.2 `glDrawElements ...
- 【我的OpenGL学习进阶之旅】C++如何加载TGA文件?
一.TGA文件相关介绍 通过前面的博客 [我的OpenGL学习进阶之旅]什么是TGA文件以及如何打开TGA文件? 地址:https://ouyangpeng.blog.csdn.net/article ...
- 【我的OpenGL学习进阶之旅】【持续更新】关于学习OpenGL的一些资料
目录 一.相关书籍 OpenGL 方面 C方面 NDK 线性代数 二.相关博客 2.0 一些比较官方的链接 2.1 OpenGL着色器语言相关 2.2 [[yfan]](https://segment ...
- 【我的OpenGL学习进阶之旅】着色器和程序(上)------着色器
着色器和程序 一.前言 二.着色器和程序 2.1 创建和编译一个着色器 2.1.1 创建着色器 2.1.2 删除着色器 2.1.3 提供着色器源代码 2.1.4 编译色器 2.1.4 查询有关着色器对 ...
- 【我的OpenGL学习进阶之旅】解决OpenGL绘制带透明通道的png纹理时出现黑边问题,并彻底了解其原理
文章目录 一.问题描述 二.解决问题 2.1 解决效果 2.2 如何解决 三.知其然知其所以然 3.1 像素.抗锯齿的基本概念 3.2 alpha的两种类型,直通和预乘 3.3 直通和预乘的计算方式 ...
- 【我的OpenGL学习进阶之旅】收集到的关于如何在OpenGL ES上使用MSAA(Multisample Anti-aliasing)实现抗锯齿效果的资料和源码
文章目录 一.需求 1.2 OpenGL的MSAA功能不能在OpenGL ES上使用,你崩溃了吗? 1.3 OpenGL ES的抗锯齿在离屏渲染和在屏渲染上的不同需求 二.OpenGL ES上实现的M ...
- 【我的OpenGL学习进阶之旅】关于3D模型知识之:什么是obj文件和mtl文件
文章目录 一.学习3D模型的背景 二.3D模型效果展示 三.好奇3D模型文件是啥内容? 3.1 打开.obj文件 3.2 打开.obj文件 3.3 在外部使用查看3D模型的软件打开.obj文件 3.3 ...
最新文章
- form表单序列化去除空值
- python中一些实用而容易被忽视不常用的库
- Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
- MDK:assert_param函数未定义的错误:Error: L6218E
- cloudera manager 及CDH卸载
- mysql查询不确定的信息_mysql 07.18
- 排序代码(python,c++) 及 基本算法复杂度
- js客户端存储之Web存储
- android audio混音
- 实验一 顺序表基本操作的实现
- 快速使用CSS技术手册
- matlab对角和,matlab – 如何在对角线上赋值?
- Intel Thunderbolt 3 接口介绍
- Android客户端登录注册模块
- HDU 6143	 Killer Names(容斥+组合)
- qt.modbus: (RTU client) Discarding response with wrong CRC, received: 16448 , calculated CRC: 49303
- zipJS 前端压缩使用
- 1.基于51单片机的蓝牙手机遥控小车
- swiper轮播图切换指示点改变背景颜色
- 实验报告:小学数学练习、石头剪刀布游戏