OpenGL之 深度测试

前言

  在上一篇<OpenGL之 甜甜圈与背面剔除>中,讲述了如何绘制甜甜圈,以及在绘制甜甜圈的时候,遇到的隐藏面消除的问题。同时,也讲述了如何使用正背面剔除来解决该问题。

  但是,当我们把之前的demo跑起来并且左右旋转之后,会发现又出现了一个问题(如下图),甜甜圈感觉缺了一块,这里的话,就要开启深度测试了。


image.png

一、问题产生的原因

  当甜甜圈旋转到前后两个位置重叠的时候,而前后都是正面或者背面,OpenGL无法确定哪个图层应该在前,哪个图层应该在后,所以也就出现了图示的情况。

二、如何解决问题

2.1 深度缓存区

2.1.2 深度

   深度就是OpenGL坐标系中,像素点的z坐标离观察者的距离。观察者是可以放在坐标系的任意位置的,因此并不能简单的说z值越大或者越小就越靠近观察者。

  • 如果观察者在z轴的正方向,则z的值越大,像素点就越靠近观察者
  • 如果观察者在z轴的负方向,则z的值越小,像素点就越靠近观察者
2.1.2 深度缓存区

   深度缓存区本质上是一种GPU缓存区,存储在显存中,专门存储每个像素点的深度值。它的原理就是把距离观察者平面(近裁剪面)的深度值与窗口中的像素点一一对应,进行关联和存储。
   如果想禁止写入的话,可以调用以下函数。

//可以调用来禁止写入。
glDepthMask(GL_FALSE)

2.2 深度测试

在之前的demo中的RenderScene()函数,始终有着一行代码。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   这行代码的目的就是清除颜色缓存区和深度缓存区。当我们清除深度缓存区的时候,底层操作其实是把所有像素点的深度值设为最大值,默认是1.0(深度值的范围是[0,1]之间)
   深度缓冲区和颜⾊缓存区是对应的,颜⾊缓存区存储像素的颜⾊信息,⽽深度 缓冲区存储像素的深度信息。当我们之后要绘制一个物体表面的时候,首先会将表面像素点的深度与当前深度缓存区对应的深度进行比较,如果是大于深度缓存区对应的深度,则丢弃这部分值;否则的话,则使用当前像素点的颜色和深度更新颜色缓存区和深度缓存区。而这个丢弃和比较的过程,就是 深度测试

深度测试同时也可以解决隐藏面消除的问题。

2.2.1 开启/关闭深度测试

首先,之前的文章中有讲述到main函数的代码编写,里面有一行代码如下

//初始化双缓冲窗口
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);

这里主要就是申请颜色缓存区以及深度缓存区。如果不申请深度缓存区,后续在开启深度测试的时候,会没有效果。

而后,我们需要在之前代码的基础上,在RenderScene()方法中先清除颜色、深度缓存区,然后添加开启深度测试以及关闭深度测试的代码。

//清除颜色、深度缓存区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//开启深度测试
glEnable(GL_DEPTH_TEST);
//关闭深度测试
glDisable(GL_DEPTH_TEST);

到这里,我们可以重新跑一遍我们的甜甜圈程序,会得到一个比较完美的甜甜圈了:


image.png
2.2.2 深度测试规则修改

可以通过下述方法修改深度测试规则

void glDepthFunc(GLenum func)
参数 说明
GL_ALWAYS 总是通过测试
GL_NEVER 总是不通过测试
GL_LESS 当前深度值 < 存储的深度值时通过
GL_EQUAL 当前深度值 = 存储的深度值时通过
GL_LEQUAL 当前深度值 <= 存储的深度值时通过
GL_GREATER 当前深度值 > 存储的深度值时通过
GL_NOTEQUAL 当前深度值 != 存储的深度值时通过
GL_GEQUAL 当前深度值 >= 存储的深度值时通过

三、解决问题之后

3.1 Z-Fighting闪烁问题

3.1.1 原因

   开启深度测试之后,之前遇到的问题都得到了解决。但是由于精度的限制,对于相差非常小的深度值来说(例如在同一个深度进行了两次渲染),OpenGL可能会出现无法正确判断深度值的情况,从而导致测试结果随机出现,造成了画面交替显示,产生闪烁的问题。这就是Z-Fighting闪烁问题。(如下图)

image.png

   举个例子,如果一个像素点的深度是0.1000000000000000009,另一个是0.10000000000000000000009,对于低精度的设备来说,在他们看来二者可能都是0.10000000000000000。
   从这里也可以知道,现在的设备相当于以往的低精度设备来说其实并不容易出现Z-Fighting。

3.1.2 解决方法

目前OpenGL对Z-Fighting闪烁问题提供了一个解决方案,那就是多边形偏移( Polygon Offset)

解决步骤

  • 启用多边形偏移
    基本原理就是,在深度测试之前,增大重叠或者深度值极为接近的两个图形的深度值间隔。
//启用多边形偏移
glEnable(GL_POLYGON_OFFSET_FILL)
Polygon Offset ⽅式 光栅化模式
GL_POLYGON_OFFSET_POINT GL_POINT
GL_POLYGON_OFFSET_LINE GL_LINE
GL_POLYGON_OFFSET_FILL GL_FILL
GL_FILL光栅化模式.png
GL_LINE光栅化模式.png
GL_POINT光栅化模式.png

上面三张图分别对应着 GL_FILL光栅化模式、GL_LINE光栅化模式、GL_POINT光栅化模式,默认情况下是GL_FILL光栅化模式。
而不同的光栅化模式,在开启多边形偏移的时候,就要设置对应的多边形偏移方式。

  • 指定偏移量

指定偏移量的时候,需要2个参数,分别是factor , units。使用下述方法来设置。

void glPolygonOffset(GLFloat factor,GLFloat units)

在计算偏移量的时候,遵循的是一个总偏移计算方程式,如下:

Offset = ( m * factor ) + ( r * units);

m:多边形的深度的斜率的最⼤值,理解⼀个多边形越是与近裁剪⾯平⾏,m 就越接近于0
r : 能产⽣于窗⼝坐标系的深度值中可分辨的差异最⼩值.r 是由具体是由具体OpenGL 平台指定的 ⼀个常量.

在使用glPolygonOffset方法的时候,开发者是无法直接得知m和r具体值的,因此这里不必过多纠结于这两个参数,而对于factor和units来说,一般情况下都设置为-1.0即可。

  • 当Offset的结果小于0的时候,将使z值距离观察者更近;
  • 当Offset的结果大于0的时候,将使z值距离观察者更远;

关于这部分内容,附上部分网络上资料,感兴趣的朋友可以自行深入了解。

image.png
  • 关闭多边形偏移
//关闭多边形偏移,参数必须要和开启的时候一样
glDisable(GL_POLYGON_OFFSET_FILL)
3.1.2 如何预防
  • 首先应该尽量避免将两个物体靠的太近,避免渲染的时候,三角形叠在一起。
  • 尽可能将近裁剪⾯设置得离观察者远⼀些,观察者离近裁剪面越近,则对深度测试的精度要求就越高,因此,可以尽量让近裁剪⾯远⼀些。但是同时,也有可能导致距离观察者较劲的物体被裁剪。因此需要调试好裁剪面参数。
  • 使用更高位数的深度缓冲区,通常使⽤的深度缓冲区是24位的,如果能够使⽤32位的缓冲区,则可以使深度测试的精确度得到提⾼。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345