OpenGL 正背面剔除及深度测试的理解

491 阅读5分钟

注:本文旨在记录笔者的学习过程,仅代表笔者个人的理解,如果有表述不准确的地方,欢迎各位指正!因为涉及到的概念来源自网络,所以如有侵权,也望告知!

前言

本文通过案例的形式来逐步拆解3D图形绘制流程,从而了解3D图形中正背面剔除及深度测试的概念。

正文

知识点回顾

首先我们来回顾一下,OpenGL绘制一个图形需要我们给顶点着色器传递顶点数据,同时也需要指定图元装配的规则。比如通过下面这段代码,我们就可以得到一个金字塔的图形,图形是通过4个三角形共12个顶点拼接成的,其中vPyramid是图形的顶点数据,GL_TRIANGLES则是图元的装配规则。


当然,绘制图形需要的顶点数据的数量也受图元装配方式的影响,不同的装配方式会不同程度的复用顶点数据,选择合适的图元装配方式可以达到高效的绘制效率。OpenGL为我们提供了以下这些图元装配方式:




绘制甜甜圈

闲话不多说,来看一下我们本次案例绘制的3D图形是什么?对!你没看错,是一个甜甜圈!那我们需要哪些过程才能得到我们的甜甜圈呢?


同样我们需要初始化OpenGL环境并设置图形的顶点数据图元装配方式,但是这里可以看到我们并没有自己去创建顶点数据,因为OpenGL提供了API为一些常用的图形获取顶点数据,gltMakeTorus函数就帮我们生成了甜甜圈的顶点数据


其中viewFrame对象大家可以理解为观察者,我们可以通过调整观察者的视角来查看整个3D图形。


同时3D图形的绘制,需要通过模型视图矩阵、投影矩阵等矩阵变换后才能将图形绘制到2D视图上。其中modelViewMatix就我们需要的模型视图矩阵;projectionMatrix是我们需要的投影矩阵;而transformPipeline则是我们的渲染管线,它可以为我们生成最终我们想要的变换矩阵。最后通过默认光源着色器将我们美味的甜甜圈渲染到了视图上。



正背面剔除

这时当我们调整观察者视角去查看我们的甜甜圈,你发现了什么?wtf!这是什么鬼?我们发现甜甜圈出现bug了,竟然出现了一些黑色块。


这又是为什么呢?大家可以结合我们的实际场景联想一下,从⼀个⽴⽅体的任意位置和⽅向上看,你能看到几个面?答案毫无疑问,你永远不可能看到多于3个⾯。在OpenGL中也是一样的道理,甜甜圈的背光面其实是看不到的部分,OpenGL将它绘制成了黑色,所以当我们调整观察者的时候就出现了上述的问题。

既然知道了问题的所在,那我们要怎么解决呢?其实很简单,只需要通过OpenGL的API将我们的背面剔除就可以啦,是不是So Easy!


这时我们再看一下,发现原先的黑块已经消失的无影无踪了,perfect!


这个时候你可能又会有疑问,虽然OpenGL提供了API可以直接调用,但是它是怎么知道哪一部分是正面哪一部分又是背面呢?


其实也很简单,OpenGL正⾯/背⾯区分:
• 正⾯: 按照逆时针顶点连接顺序的三⻆形⾯ 
• 背⾯: 按照顺时针顶点连接顺序的三⻆形⾯

深度测试

如果你以为这一切已经大功告成了,那你会发现意外总是会不合时宜的出现,wait!我又发现了什么!我的甜甜圈是被谁咬了一口吗?


想要解决这个问题,我们得先了解3D图形深度相关的概念:

• 什么是深度?
深度其实就是该像素点在3D世界中距离摄像机的距离,Z值。
• 什么是深度缓冲区?
深度缓存区,就是⼀块内存区域,专⻔存储着每个像素点(绘制在屏幕上的)深度值.深度值(Z值)越⼤,则离摄像机就越远。
• 为什么需要深度缓冲区?
在不使⽤深度测试的时候,如果我们先绘制⼀个距离⽐较近的物理,再绘制距离较远的物理,则距离远的位图因为后绘制,会把距离近的物体覆盖掉. 有了深度缓冲区后,绘制物体的顺序就不那么要的. 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中. 除⾮调⽤
glDepthMask(GL_FALSE).来禁⽌写⼊.
• 什么是深度测试?
深度缓冲区(DepthBuffffer)和颜⾊缓存区(ColorBuffffer)是对应的.颜⾊缓存区存储像素的颜⾊信息,⽽深度缓冲区存储像素的深度信息. 在决定是否绘制⼀个物体表⾯时, ⾸先要将表⾯对应的像素的深度值与当前深度缓冲区中的值进⾏⽐较. 如果⼤于深度缓冲区中的值,则丢弃这部分.否则利⽤这个像素对应的深度值和颜⾊值.分别更新深度缓冲区和颜⾊缓存区. 这个过程称为”深度测试”。

通过概念不难发现,这个问题其实是由于像素点的深度导致的,在不开启深度测试的情况下,就会出现像素点被覆盖的问题,也就出现了上面甜甜圈出现缺口的现象,那么我们只需要将OpenGL深度测试功能打开就可以解决问题啦。


打开深度测试后,我们再重新看一下我们的甜甜圈,果然不出所料,甜甜圈完整了。