OpenGL 阐述隐藏面消除解决方案

OpenGL 阐述隐藏面消除解决方案

在OpenGL中如果没有设置隐藏背面,就会出现下面这种情况,正面红色和背面黑色同时被绘制了出来,OpenGL并不知道先绘制背面还是正面。这明显不符合我们的预期。

模型

我们需要对背面进行消除,这叫做“隐藏面消除(Hidden surface elimination)
目前有两种解决方案:
1. 先绘制背面后绘制正面(油画算法)
2. 只画正面(正背⾯面剔除(Face Culling))


第一种(油画算法)

油画算法有一条规则: 先绘制场景中距离观察者远的物体,再绘制近的物体。

远近不一的图

图中绘制顺序:红色->换色->灰色
有顺序的绘制就能解决同时绘制背面和正面的问题。

油画法的弊端

如果出现图形叠加的情况,油画法就显得力不从心。
例如下图:到底先画哪个图形呢?

图形叠加

接下来考虑第二种方法。


第二种(背面剔除)

正背面剔除有一条规则:看不到的不画!
想要做到看不到的不画,需要解决以下问题:

  • OpenGL如何知道哪个面看得到,哪个面看不到?
    答案是:通过分析顶点数据的顺序

顶点绘制的顺序不同就决定了是顺时针绘制还是逆时针绘制(下图)

GLfloat vertices[] = { 
   //顺时针
  vertices[0], // vertex 1
  vertices[1], // vertex 2 
  vertices[2], // vertex 3 
  // 逆时针
  vertices[0], // vertex 1 
  vertices[2], // vertex 3 
  vertices[1]  // vertex 2 
};

有了顺时针和逆时针,我们就可以通过观察者的所在位置,确定正面与背面:

  • 当观察者在右侧时,右边的三角形方向为逆时针方向则为正⾯面,而左侧三⻆形为顺时针则为背⾯。
  • 当观察者在左侧时,左边的三⻆角形为逆时针⽅方向,判定为正⾯,⽽右侧的三角形为顺时针判定为背⾯面

这样就能轻而易举的知道正面和背面了。


正面与背面
代码中如何实现
// 开启表面剔除(默认背面剔除)
void glEnable(GL_CULL_FACE);
// 关闭表面剔除(默认背面剔除)
void glDisable(GL_CULL_FACE);
/*
* 用户选择剔除那个面(正⾯/背面)
* mode : GL_FRONT(正⾯),GL_BACK(背面),GL_FRONT_AND_BACK (一起),默认GL_BACK
*/
void glCullFace(GLenum mode);
/* 
* 用户指定绕序那个为正面
* mode: GL_CW(顺时针),GL_CCW(逆时针),默认值:GL_CCW
*/
void glFrontFace(GLenum mode);

例如:开启背面剔除
void SetupRC(){
  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);
}
背面剔除成功效果

成功了!💐💐💐
我们在这里解决了背面绘制的问题,但于此同时又出现了另外一个问题: 出现了一个窟窿

窟窿

为了解决这个问题,我们需要了解“深度”
这是个什么问题


深度

  • 什么是深度(z值)?
    深度是物体的像素点在3d世界中,距离摄像机的距离,z值越大距离越大。
  • 什么是深度缓冲区(DEPTH_BUFFER)?
    深度缓冲区是一块内存区域,用于缓存每个像素点的深度值。
  • 为什么需要深度(DEPTH_BUFFER)?
    在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。
  • 深度缓冲区原理
    • 深度缓冲区原理就是把一个距离观察平面(近裁剪面)的深度值(或距离)与窗口中的每个像素关联起来。
    • 首先,使用glClear(GL_DEPTH_BUFFER_BIT),把所有像素的深度值设置为最大值(一般是远裁剪面)。
    • 然后,在场景中以任意次序绘制所有物体。硬件或者软件所执行的图形计算把每一个绘制表面转换为窗口上一些像素的集合,此时并不考虑是否被其他物体遮挡。
    • 其次,OpenGL会计算这些表面和观察平面的距离。如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把它的深度值和已经存储在这个像素的深度值进行比较。新像素深度值<原先像素深度值,则新像素值会取代原先的;反之,新像素值被遮挡,他颜色值和深度将被丢弃。
    • 为了启动深度缓冲区,必须先启动它,即glEnable(GL_DEPTH_TEST)。每次绘制场景之前,需要先清除深度缓冲区,即glClear(GL_DEPTH_BUFFER_BIT),然后以任意次序绘制场景中的物体。
  • 深度测试
    深度缓冲区的作用就是区分颜色所在的层次,防止把被遮挡住的颜色显示出来。
    当一个像素第二次被绘制时, 例如当一个物体在另一个物体之后被绘制,深度缓冲要么保留前面的深度值,要么使用第二个像素的深度值替换当前深度值。保留或抛弃哪个深度取决于你选择的深度函数。例如,如果当前深度函数是CompareFunction.LessEqual时,只有小于等于当前深度值的值才会被保留,而大于当前深度值的值会被抛弃。这叫做深度测试,每次绘制像素时都会进行深度测试。当对一个像素进行深度测试时,它的颜色会被写入渲染目标,而深度被写入深度缓冲。

上面我们出现了一个窟窿,原因就是物体的深度未知的,后面的像素由于是后面才绘制出来的,前面绘制的像素背后面的像素所覆盖,所以导致我们看到了后面的窟窿。我们开启深度测试修复这个问题:

  • 启用深度测试
// 在默认情况是将需要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,
// 如果比深度缓存中的值小,那么用新像素的颜色值更新帧缓存中对应像素的颜色值。
glEnable(GL_DEPTH_TEST);
/*
 * mode :GL_NEVER(用不处理)、GL_ALWAYS(处理所有)、GL_LESS(小于)、GL_LEQUAL(小于等于)、GL_EQUAL(等于)、GL_GEQUAL(大于等于)、GL_GREATER(大于)或GL_NOTEQUAL(不等于),其中默认值是GL_LESS。
 * 普遍使用GL_LEQUAL 来表达一般物体之间的遮挡关系。 
*/
glDepthFunc(GLEnum mode);
// 在绘制场景前,清除颜色缓存区,深度缓冲
// 清除深度缓冲区默认值为1.0,表示最⼤大的深度值,深度值的范围为(0,1)之间,
// 值越⼩小表示越靠近观察者,值越⼤大表示 越远离观察者
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

例如:
void RenderScene(void){
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);
}

开启深度测试后,甜甜圈的窟窿没有了。💐💐💐


开启深度测试

结束

到此甜甜圈的效果已经和我们想看到的样子非常像了。谢谢阅文。

文献:
OpenGL 深度缓冲区 Z缓冲区 介绍

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352