当看到文章标题的时候,你是否有一个疑问,什么是正背面剔除?对于没有OpenGL实践经验的同学来说,并不清楚什么是正背面剔除,先不要着急,我们先通过一个例子来引入什么是正背面剔除
绘制一个甜甜圈(其实就是一个圆环)
绘制过程涉及到的函数:
Main
int main(int argc,char *argv[])
{
//初始化GLUT库,这个函数只是传说命令参数并且初始化glut库
glutInit(&argc, argv);
/*
初始化双缓冲窗口,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指
双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区
--GLUT_DOUBLE`:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后迅速转换成窗口视图,这种方式,经常用来生成动画效果;
--GLUT_DEPTH`:标志将一个深度缓存区分配为显示的一部分,因此我们能够执行深度测试;
--GLUT_STENCIL`:确保我们也会有一个可用的模板缓存区。
深度、模板测试后面会细致讲到
*/
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
//GLUT窗口大小、窗口标题
glutInitWindowSize(800, 600);
glutCreateWindow("Triangle");
/*
GLUT 内部运行一个本地消息循环,拦截适当的消息。然后调用我们不同时间注册的回调函数。我们一共注册2个回调函数:
1)为窗口改变大小而设置的一个回调函数
2)包含OpenGL 渲染的回调函数
*/
//注册重塑函数
glutReshapeFunc(changeSize);
//注册显示函数
glutDisplayFunc(RenderScene);
/*
初始化一个GLEW库,确保OpenGL API对程序完全可用。
在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
*/
GLenum status = glewInit();
if (GLEW_OK != status) {
printf("GLEW Error:%s\n",glewGetErrorString(status));
return 1;
}
//设置我们的渲染环境
setupRC();
glutMainLoop();
return 0;
}
main函数作为函数的入口,主要的功能有:
- 初始化GLUT库,这个函数只是传说命令参数并且初始化glut库
- 设置视口大小
- 注册重塑函数
- 注册显示函数
- 初始化一个GLEW库,确保OpenGL API对程序完全可用。
changeSize
//窗口改变
void ChangeSize(int w, int h)
{
//1.防止h变为0
if(h == 0)
h = 1;
//2.设置视口窗口尺寸
glViewport(0, 0, w, h);
//3.setPerspective函数的参数是一个从顶点方向看去的视场角度(用角度值表示)
// 设置透视模式,初始化其透视矩阵
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
//4.把透视矩阵加载到透视矩阵对阵中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//5.初始化渲染管线
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
ChangeSize函数在窗口大小改变时回调,这里的作用主要有:
- 窗口大小改变时,重新设置视口的大小
- 设置投影方式(透视投影)
- 得到投影矩阵并压入到投影栈中
- 初始化渲染管线并将投影矩阵,模型矩阵压入栈中
SetupRC
void SetupRC()
{
//1.设置背景颜色
glClearColor(0.1f, 0.1f, 0.1f, 1.0f );
//2.初始化着色器管理器
shaderManager.InitializeStockShaders();
//3.将相机向后移动7个单元:肉眼到物体之间的距离
viewFrame.MoveForward(7.0);
//4.创建一个甜甜圈
//void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
//参数1:GLTriangleBatch 容器帮助类
//参数2:外边缘半径
//参数3:内边缘半径
//参数4、5:主半径和从半径的细分单元数量
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
//5.点的大小(方便点填充时,肉眼观察)
glPointSize(4.0f);
}
SetupRC函数作用:
- 设置背景颜色
- 初始化着色器管理器
- 设置观察者位置
- 创建一个甜甜圈
- 设置点的大小(方便点填充时,肉眼观察)
RenderScene
//渲染场景
void RenderScene()
{
//1.清除窗口和深度缓冲区
//可以给学员演示一下不清空颜色/深度缓冲区时.渲染会造成什么问题. 残留数据
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//2.把摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
//3.设置绘图颜色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//4.
//使用平面着色器
//参数1:平面着色器
//参数2:模型视图投影矩阵
//参数3:颜色
// shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
//使用默认光源着色器
//通过光源、阴影效果跟提现立体效果
//参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
//参数2:模型视图矩阵
//参数3:投影矩阵
//参数4:基本颜色值
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
//5.绘制
torusBatch.Draw();
//6.出栈 绘制完成恢复
modelViewMatix.PopMatrix();
//7.交换缓存区
glutSwapBuffers();
}
RenderScene函数作用:
- 清除缓冲区(如果不清除,缓冲区可能保留上一次的数据,造成绘制结果不准确)
- 将模型视图矩阵压入栈中
- 设置画笔颜色
- 选用默认光源着色器进行绘制(也可以选用平面着色器)
- 绘制
- 绘制完成后将模型视图矩阵出栈
- 交换缓冲区
SpecialKeys
//键位设置,通过不同的键位对其进行设置
//控制Camera的移动,从而改变视口
void SpecialKeys(int key, int x, int y)
{
//1.判断方向
if(key == GLUT_KEY_UP)
//2.根据方向调整观察者位置
viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
//3.重新刷新
glutPostRedisplay();
}
SpecialKeys函数的作用:监听键盘的输入事件,当用户输入的时候此方法会回调,如果是方向键发生变化,则改变观察者的位置,从而达到视图旋转的目的
绘制结果:
甜甜圈
一切看起来都很正常,但是当我们旋转一下视图的时候你就会发现结果是这样的:
旋转后的甜甜圈
为什么是这样的呢 ?
这是因为我们设置的观察者的方向是在屏幕的正前方,而我们知道,当一个物体被光照射的时候,有正面和背面的区别,我们看到的正面就是红色部分,而旋转以后,我们就能看到背面,而背面没有光照,呈现黑色
我们知道了原因,怎么去解决这个问题呢 ?这就是我们今天的主题,正背面剔除
我们增加一些开关设置:
在main函数中增加这样一段代码:
// Create the Menu
glutCreateMenu(ProcessMenu);
glutAddMenuEntry("Toggle depth test",1);
glutAddMenuEntry("Toggle cull backface",2);
glutAddMenuEntry("Set Fill Mode", 3);
glutAddMenuEntry("Set Line Mode", 4);
glutAddMenuEntry("Set Point Mode", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
增加一个函数:
void ProcessMenu(int value)
{
switch(value)
{
case 1:
iDepth = !iDepth;
break;
case 2:
iCull = !iCull;
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();
}
当开关的值发生变化的时候,此函数会被回调
然后修改RenderScene函数
//渲染场景
void RenderScene()
{
//1.清除窗口和深度缓冲区
//可以给学员演示一下不清空颜色/深度缓冲区时.渲染会造成什么问题. 残留数据
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//开启/关闭正背面剔除功能
if (iCull) {
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
}else
{
glDisable(GL_CULL_FACE);
}
//2.把摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
//3.设置绘图颜色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//4.
//使用平面着色器
//参数1:平面着色器
//参数2:模型视图投影矩阵
//参数3:颜色
// shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
//使用默认光源着色器
//通过光源、阴影效果跟提现立体效果
//参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
//参数2:模型视图矩阵
//参数3:投影矩阵
//参数4:基本颜色值
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
//5.绘制
torusBatch.Draw();
//6.出栈 绘制完成恢复
modelViewMatix.PopMatrix();
//7.交换缓存区
glutSwapBuffers();
}
当我们开启了正背面剔除以后,旋转视图以后,显示终于正常了:
开启正背面剔除后
但是又引入了一个新的问题,如下图,当我们旋转到一定角度的时候,圆环上有了一个缺口:
圆环缺口
这有引入了我们下一章要讲的内容,深度测试
网友评论