本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。
在第八节中,由于甜甜圈出现了BUG,讨论了解决这种情况的方法——油画算法,但是它并不是一个适用性广泛的方法,于是本节主要学习一下OpenGL的正背面剔除的方式来解决上一节的BUG。
一、关于正背面剔除
背景:
在绘制3D立体图形的时候,系统无法分辨哪个面应该绘制并展示,哪个面可以隐藏,造成了正背面全都显示,图形出现BUG的情况。
解决的问题:
如何知道哪个或者哪些面在观察者的视角是看不到的?我们需要分辨正背面。
任何一个图形无论有多少个平面,我们习惯性的会区分一个正面,一个背面,正面就是我们视野看得到的面,背面就是看不到,但客观存在的面。
OpenGL可以做到检测到所有正面朝向观察者的面,并且渲染正面,从而丢弃背面朝向的面,这样会节约片元着色器的性能。
分辨正背面的方法:
通过分析顶点数据的定义顺序。
如图1.1所示:
1.1.png以顶点A、B、C构成一个面。
背面:当其按照顺时针顺序定义的时候,即
A-->C-->B
顺序定义,则ABC三点构成的面是背面。
正面:当其按照逆时针顺序定义的时候,即
C-->A-->B
顺序定义,则ABC三点构成的面是正面。
观察者视角和正背面的关系
图形的正面与背面不是一成不变的。而OpenGL正背面的判断是与观察者视角的位置相关的。
如图1.2所示:
1.2.png当观察者的位置如图中所示的时候,以3-->1-->2
顶点定义顺序的面就是正面。
但是如果观察者的位置转换到另一侧,那么顶点顺序1-->2-->3
就由顺时针变为了逆时针,此时它才是正面。
总结
由以上的内容,我们可以总结出:
图形正背面的判定是由顶点的定义顺序和观察者视角的方向共同决定的。无论是顶点的定义顺序发生改变还是观察者视角的方向发生改变,OpenGL认定的正背面都可能发生改变。
二、OpenGL正背面提出函数的介绍
1. void glEnable(GLenum cap)
:
GLenum cap
:这是一个枚举值。GLenum
说明参数是一个unsigned int
类型,
cap具体的取值是OpenGL预置的常量,每个常量都代表了不同的预置功能。
具体的百度都能找到,太多了就不写了。但是我们本节只用正背面剔除,也就是
GL_CULL_FACE
,其默认的是背面剔除。
开启正背面剔除
glEnable(GL_CULL_FACE)
2. void glDisable(GLenum cap)
:
其功能是关闭,和上面的函数相反的作用。参数和上面一样的含义。
关闭正背面剔除
glDisable(GL_CULL_FACE)
3. void glCullFace(GLenum mode)
参数包括: GL_FRONT
,GL_BACK
,GL_FRONT_AND_BACK
功能是开发者可以选择剔除哪个面,默认是GL_BACK
。
剔除背面
glCullFace(GL_BACK)
4. void glFrontFace(GLEnum mode)
参数包括: GL_CW
,GL_CCW
。GL_CW
是设置顶点顺时针环绕为正面,GL_CCW
则是设置顶点逆时针环绕为正面。
功能是控制图形的正面是以哪种顶点环绕顺序决定的,默认是GL_CCW
。
三、修改上一节的BUG代码
1.在常用变量中增加一个变量,判断是否开启正背面剔除,以方便对比开启与关闭深度测试的差异
//是否开启正背面剔除,默认0,不开启
int isCull = 0;
//添加深度测试,下节再写,本节添加并没有使用到它
int isDepth = 0;
2.我们使用右键菜单栏,将是否开启正背面剔除添加成一个功能,首先在main函数里面注册一个右键菜单栏函数。
//在main函数里面注册函数的备注最下面添加
//注册右键菜单栏,并设置功能和名称
glutCreateMenu(ProcessMenu);
//深度测试,下一节再写
glutAddMenuEntry("深度测试",1);
//这里设置第二参数是2,是因为右键菜单栏的第一个功能需要保留给下一节的深度测试
glutAddMenuEntry("正背面剔除",2);
//以下的都是随便加的,不加也无所谓。就是把图形的填充方式更换一下。
glutAddMenuEntry("颜色填充", 3);
glutAddMenuEntry("线段填充", 4);
glutAddMenuEntry("点填充", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
3.添加ProcessMenu函数
void ProcessMenu(int value)
{
switch (value) {
case 1:
isDepth = !isDepth;
break;
case 2:
isCull = !isCull;
break;
case 3:
//更改填充模式
//第一个参数写正背面
//第二个参数写填充模式,这里是颜色填充,这也是默认的填充模式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case 4:
//线段填充
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
break;
case 5:
//点填充
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
break;
}
glutPostRedisplay();
}
4.在RenderScene里面添加开启正背面剔除
//代码要在清空缓冲区的代码后面添加。清空缓冲区一定要在渲染的第一步
if (isCull) {
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
}else {
glDisable(GL_CULL_FACE);
}
我们来看执行结果。如下图3.1所示:
3.1.png貌似BUG已经解决但是向上下进行旋转发现,如图3.2所示:
3.2.png还是有BUG,甜甜圈变成了破轮胎。
这个问题就涉及到了深度了,下一节继续改BUG。
网友评论