OpenGL-甜甜圈demo(深度测试+正背面剔除)

开场白


这篇文章介绍一下正背面剔除和深度测试相关知识,demo中渲染一个甜甜圈,效果如图:


效果图

1.画个甜甜圈


1.1 甜甜圈的API

GLTools提供了API方法画甜甜圈
void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor)

  • 参数1:容器辅助类,使用一个三角形批次类。
  • 参数2:外边缘半径
  • 参数3:内边缘半径
  • 参数4、5:主半径和从半径的细分单元数量

SetupRC中的调用:
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
参数4、5:主从半径细分单元数量比例通常保持2:1

1.2 着色器设置

1.2.1 平面着色器

使用我们比较熟悉的平面着色器GLT_SHADER_FLAT:
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
来看看效果:

使用这个着色器看起来没有颜色的层次感,看来这个平面着色器不适合。

1.2.2 默认光源着色器

颜色没有层次感,也就看起来不立体。其实缺少的是光源和阴影。所以我们用默认光源着色器GLT_SHADER_DEFAULT_LIGHT看看效果:
RenderScene中代码调用:
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(),vRed);
注意:默认光源着色器需要四个参数
运行后看看效果:


完美!!!
但是,当你动的时候,你会看到:

甜甜圈好像变质了,这是什么原因?这个是我们接下来要了解的正背面剔除

2.解决甜甜圈变质-正背面剔除


2.1 变质原因:

在绘制3D场景中的物体时,我们能看到的正面是我们想看到的地方,反面是我们视角不可见的地方,但是当我们移动物体的时候,本该不可见的地方可见了,此时我们就得根据一定规则来确认和调整可视部分。

2.2 油画算法

比较简单的解决方法就是对于看不到的部分就丢弃掉,不进行渲染。这种情况叫做“隐藏面消除”。油画算法可以实现这种方式,油画算法是先绘制较远的物体,再绘制较近的物体。如图:


先绘制红色框框,再绘制黄色的圆,最后绘制带圆角的灰色框框。这样可以做到“隐藏面消除”的效果。

但是油画算法也有它的弊端,如图:



油画算法无法实现三个三角形互相叠加的效果,因为不知道哪个图形是较远的,哪个图形是较近的。简单的说,油画算法可以处理2D图形叠加情况,但无法处理3D图形。

2.3 正背面剔除

对于一个3D图形,我们该如何处理叠加情况呢?
我们从观察一个长方体来思考:



从任意方向看,最多能看到长方体的三个面,那么其他三个看不到面的数据,可以丢弃不用。这样还可以提高OpenGL的渲染效率。算下来最低可以提升50%的性能。

有些朋友会问,那当我移动物体的时候呢?
当物体每次移动时,OpenGL都会去进行每个像素的重新渲染,也就是调用RenderScene回调函数。
是中这种方式就可以解决3D图形叠加的情况。这种模式我们叫做正背面剔除法

如何确认哪些面观察者能看到?其实任何平面都有2个面,正面和北面,而我们只能在固定时间内看到一个面,要不正面,要不背面。
OpenGL是如何来确定哪个是正面的呢?



OpenGl默认规定,顶点顺序是逆时针连接的为正面,相反顺时针的情况为背面。但是这个取决于观察者的位置,所以观察者位置也是条件之一。
如上图:
当观察者在右侧的时候,看到的右边三角形为正面,而左侧的三角形为背面。当观察者在左侧的时候,则左侧为正面,右侧为背面。

简单总结:
正面和背面取决于顶点连接顺序的方向以及观察者的位置共同决定的。随着观察者位置的变化,正面和背面也是交替变化的。

2.4 代码实现

说了这么多理论,代码要如何写呢?
其实代码很简单,只要在OpenGl的状态机中开启相应的功能就可以:

//开启表面剔除
glEnable(GL_CULL_FACE);
//关闭表面剔除
glDisable(GL_CULL_FACE);

也可以用户自己选择剔除面

void glCullFace(GLenum mode); 
//mode参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默认GL_BACK

还可以修改顶点绕序。这个不建议修改,如果修改了,不被同事打4,也得被打残!

void glFrontFace(GLenum mode); 
//mode参数为: GL_CW,GL_CCW,默认值:GL_CCW

工程中代码实现:

void RenderScene(void) {
    ...
    if (iCull) {
        glEnable(GL_CULL_FACE);
        glFrontFace(GL_CCW);
        glCullFace(GL_BACK);
    } else {
        glDisable(GL_CULL_FACE);
    }
    ...
}

看看运行效果:



问题解决了,但是好像甜甜圈被要了一口!!!

3. 甜甜圈被咬一口解决方案-深度测试


先了解一些概念:

  • 深度:就是3D世界中z轴方向中值。
  • 深度缓冲区:一块内存区,专门存储像素点(绘制在屏幕上的)的深度值。深度值越大,说明离观察者越远。
  • 使用深度缓冲区存在的意义:当先绘制离观察者比较近的物体,再绘制比较远的物体。会出现远物体覆盖近物体的情况。如果有深度信息(存放在深度缓冲区中的信息),那么就可以不用在意绘制物体的先后顺序。其实只要有深度缓冲区,OpenGL就会把像素的深度记录在缓冲区中,除非调用glDepthMask(GL_FALSE)方法,明确说明禁止写入。

通过上面的概念,我们来了解一下什么是深度测试:
深度缓冲区(DepthBuffer)和颜⾊缓存区(ColorBuffer)是对应的.颜⾊缓存区存储像素的颜⾊信息,⽽深度缓冲区存储像素的深度信息. 在决定是否绘制⼀个物体表⾯时, ⾸先要将表⾯对应的像素的深度值与当前深度缓冲区中的值进⾏⽐较. 如果⼤于深度缓冲区中的值,则丢弃这部分.否则利⽤这个像素对应的深度值和颜⾊值.分别更新深度缓冲区和颜⾊缓存区. 这个过程称为”深度测试”

3.1开启深度测试:

glEnable(GL_DEPTH_TEST);
在绘制场景前,清除颜⾊缓存区,深度缓冲glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
清除深度缓冲区默认值为1.0,表示最⼤的深度值,深度值的范围为(0,1)之间.

3.2指定深度测试判断模式

void glDepthFunc(GLEnum mode);

参数是枚举类型,取值有:


3.3打开/阻断 深度缓存区写⼊

void glDepthMask(GLBool value); 
//value : GL_TURE 开启深度缓冲区写⼊; GL_FALSE 关闭深度缓冲区写⼊

3.4 代码实现

工程中代码实现:

void RenderScene(void) {
    ...
    if (iDepth) {
        glEnable(GL_DEPTH_TEST);
    } else {
        glDisable(GL_DEPTH_TEST);
    }
    ...
}

这样展示的效果就完美了

4. demo地址

demo地址
demo中是使用右击菜单选择的方式进行深度测试和正反面剔除效果展示的,想要看指定效果就选择指定的选线即可。

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