- 首先定义变量
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无法区分哪个面在前,哪个面在后。
下一篇 深度测试 中,我们将讨论如何解决这样的问题。