使用OpenGL来画个甜甜圈

  • 首先定义变量
 1 //设置角色帧,作为相机
 2 GLFrame             viewFrame;
 3 //使用GLFrustum类来设置透视投影
 4 GLFrustum           viewFrustum;
 5 //三角形批次类
 6 GLTriangleBatch     torusBatch;
 7 //模型视图矩阵
 8 GLMatrixStack       modelViewMatix;
 9 //投影矩阵
10 GLMatrixStack       projectionMatrix;
11 //着色器管道
12 GLGeometryTransform transformPipeline;
13 //着色器管理器
14 GLShaderManager     shaderManager;
  • 然后是main函数,我们之前已经讲过,都是一些固定的操作
  • 接下来SetupRC进行一些初始化,包括设置背景色,初始化着色器管理器,设置观察者视角
 1 void SetupRC()
 2 {
 3     //1.设置背景颜色
 4     glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
 5     
 6     //2.初始化着色器管理器
 7     shaderManager.InitializeStockShaders();
 8     
 9     //3.将相机向后移动7个单元:肉眼到物体之间的距离
10     viewFrame.MoveForward(10);
11 
12     //4.创建一个甜甜圈
13     gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
14     
15     //5.点的大小(方便点填充时,肉眼观察)
16     glPointSize(4.0f);
17 }

这个地方,我们用到了gltMakeTorus函数,它的每个参数分表代表GLTriangleBatch 容器帮助类,外边缘半径,内边缘半径以及主半径和从半径的细分单元数量

void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
  • 然后通过ChangeSize函数设置视口,投影模式,初始化投影矩阵和渲染管线
 1 void ChangeSize(int w, int h)
 2 {
 3     //1.防止h变为0
 4     if(h == 0)
 5         h = 1;
 6     
 7     //2.设置视口窗口尺寸
 8     glViewport(0, 0, w, h);
 9     
10 
11     // 3.设置透视模式,初始化其透视矩阵
12     viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
13     
14     //4.把透视矩阵加载到透视矩阵对阵中
15     projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
16     
17     //5.初始化渲染管线
18     transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
19 }
  • 最后通过RenderScene渲染场景(清缓存,设置MVP矩阵,渲染颜色,图元组合模式)
 1 void RenderScene()
 2 {
 3     //1.清除窗口和深度缓冲区
 4     //不清空颜色/深度缓冲区时.渲染会造成什么问题-->残留数据,颜色混合
 5     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 6     
 7     //2.把摄像机矩阵压入模型矩阵中
 8     modelViewMatix.PushMatrix(viewFrame);
 9 
10     //3.设置绘图颜色
11     GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
12     
13     //4.
14     //使用平面着色器
15     //参数1:平面着色器
16     //参数2:模型视图投影矩阵
17     //参数3:颜色
18     shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
19     
20     //5.绘制
21     torusBatch.Draw();
22 
23     //6.出栈 绘制完成恢复
24     modelViewMatix.PopMatrix();
25     
26     //7.交换缓存区
27     glutSwapBuffers();
28 }

这样,一个甜甜圈就绘制完成了,如果想要移动这个甜甜圈,那么还可以:

  • 通过上下左右键移动控制观察者角度
 1 void SpecialKeys(int key, int x, int y)
 2 {
 3     //1.判断方向
 4     if(key == GLUT_KEY_UP)
 5         //2.根据方向调整观察者位置 绕Y轴选择-5度
 6         viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
 7     
 8     if(key == GLUT_KEY_DOWN) //绕Y轴选择5度
 9         viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
10     
11     if(key == GLUT_KEY_LEFT) //绕X轴选择-5度
12         viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
13     
14     if(key == GLUT_KEY_RIGHT)//绕X轴选择5度
15         viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
16     
17     //3.重绘
18     glutPostRedisplay();
19 }

 甜甜圈画出来,乍一看是没问题,只要旋转变换过后,就会出现黑色的bug。

问题原因:

a) 从观察者视角,不可见部分也进行了渲染--->不可见区域应该丢弃,即隐藏面消除(Hidden surface elimination)

b) 从观察者视角,位于同一个平面的像素点,渲染出现问题--->开启深度测试

弊端:

远近离观察者一致时,无法处理--->深度测试

多余的去绘制 --->正背面剔除

3. 正面&背⾯剔除

何为正面?

默认从观察者角度,逆时针的面为正面,开发者也可以修改和指定正面,但此状态是全局的,一处修改其他渲染位置同时遵循指定的正面规则

正面&背⾯剔除,就是检查所有正面朝向观察者的面,渲染它们.而丢弃背面朝向的面. 同时用户也可以选择剔除哪一个面

 1 //开启表⾯剔除(默认背面剔除)
 2 void glEnable(GL_CULL_FACE);
 3 
 4 //关闭表面剔除(默认背面剔除)
 5 void glDisable(GL_CULL_FACE);
 6 
 7 
 8 //⽤户选择剔除那个面(正面/背面) 
 9 //mode参数为: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默认GL_BACK ⽤户指定绕序那个为正面
10 void glCullFace(GLenum mode);
11 
12 //指定正面
13 //GL_CW:指定顺时针环绕的多边形为正面;
14 //GL_CCW:指定逆时针环绕的多边形为正面
15 //mode参数为: GL_CW,GL_CCW,默认值:GL_CCW
16 void glFrontFace(GLenum mode);
17 
18 //剔除正面,方法一:
19 glCullFace(GL_BACK);
20 glFrontFace(GL_CW); 
21 
22 //剔除正面,方法二:
23 glCullFace(GL_FRONT);

正背面剔除之后,背面黑色部分绘制的问题解决了,可是一旋转,又产生了新的问题。

如果前后两个点都是正面或者背面,这时候OpenGL无法区分哪个面在前,哪个面在后。

下一篇 深度测试 中,我们将讨论如何解决这样的问题。

 

posted @ 2019-08-10 17:18  黑暗的咏叹  阅读(468)  评论(0编辑  收藏  举报