OpenGL-综合案例:旋转的大小球
不停的演示正方形和三角形已经有点乏味了,这篇文章我们来一个综合案例,绘制公转自转的大小球
一些main函数的准备工作,这里就不多阐述了,不了解的可以看之前的文章
1.地板绘制
还是熟悉的ChangeSize函数开始:
- 设置视口glviewport
- 设置图形投影的方式:因为是立体图形,所以选择透视投影设置透视投影(3d效果)
1 //参数1:垂直方向上的视场角度 2 //参数2:视口纵横比 = w/h 3 //参数3:近裁剪面距离 4 //参数4:远裁剪面距离 5 viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
- 通过设置的投影方式获得投影矩阵,并将其存入投影矩阵中
1 projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
- 将模型视图矩阵和投影矩阵放到变换管道中(方便快速进行矩阵相乘)
1 transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
然后来到第二个部分,SetupRC初始化:
设置背景色->初始化着色器->开启深度测试->设置地板的顶点数据
接下来通过RenderScene绘制场景:
设置地板颜色->清理颜色缓冲区和深度缓冲区->绘制地板->交换缓冲区
1 // 绘制地板 2 shaderManager.UseStockShader(GLT_SHADER_FLAT, 3 transformPipeline.GetModelViewProjectionMatrix(), 4 vFloorColor); 5 floorBatch.Draw();
绘制地板这里我们采用最简单的平面着色器,然后通过我们之前设置的变换管道,获取模型视图投影矩阵(MVP)放入其中,并设置上地板颜色进行绘制,我们的地板就绘制成功了。
2.绘制大球
在地板绘制完成的基础上,我们开始绘制大球,并实现其自转功能
首先是SetupRC函数,初始化大球的顶点数据,这里我们用到gltMakeSphere函数,引用一个三角形批次、设置球半径和组成球体的片段及其堆叠数量,
我们可以将球体想象成围绕成球形的一系列三角形带:
1 // 参数iStacks是这些从球体底部堆叠到顶部的三角形的数量。 2 // 参数iSlices是围绕球体排列的三角形对数 3 void gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
大球的旋转上,首先设置一个定时器,通过这个定时器得到弧度,再通过大球的绘制配合旋转方法实现大球的自转。
1 static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f }; 2 //设置点光源位置 3 M3DVector4f vLightPos = {0,10,10,1}; 4 modelViewMatrix.Translate(0.0f, 0.2f, -3.0f); 5 modelViewMatrix.PushMatrix(); 6 modelViewMatrix.Rotate(yRot, 0, 1, 0); 7 shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transform Pipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vTorusColor); 8 torusBatch.Draw(); 9 modelViewMatrix.PopMatrix();
- Translate:移动一段距离,便于观察
- PushMatrix:拷贝矩阵堆栈栈顶并压栈
- Rotate:大球围绕y轴旋转,实现自转
3.绘制小球
小球的绘制和大球步骤是一样的,首先SetupRC函数,初始化小球的顶点数据
1 // 设置小球球模型 2 gltMakeSphere(sphereBatch, 0.1f, 13, 26); 3 // 随机位置放置小球 4 for (int i = 0; i < NUM_SPHERES; i++) { 5 6 //y轴不变,X,Z产生随机值 7 GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f); 8 GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f); 9 10 //在y方向,将球体设置为0.0的位置,这使得它们看起来是飘浮在眼睛的高度 11 //对spheres数组中的每一个顶点,设置顶点数据 12 spheres[i].SetOrigin(x, 0.0f, z); 13 }
然后RenderScene绘制:
1 // 画小球 2 for (int i = 0; i < NUM_SPHERES; i++) { 3 modelViewMatrix.PushMatrix(); 4 modelViewMatrix.MultMatrix(spheres[i]); 5 shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(), 6 transformPipeline.GetProjectionMatrix(), vLightPos, vSphereColor); 7 sphereBatch.Draw(); 8 modelViewMatrix.PopMatrix(); 9 10 }
多个静态小球,每绘制一个小球都需要进行push和pop操作。
4.让小蓝球围着大红球转起来
1 // 让小蓝球围绕大球转起来 2 modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f); 3 modelViewMatrix.Translate(0.8f, 0.0f, 0.0f); 4 shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vSphereColor); 5 sphereBatch.Draw();
这个案例的重点和难点,就在RenderScene函数里面的矩阵堆栈的理解。
在OpenGL 的维度:
变换顶点向量 = M_pro * M_view * M_model * V_local
变换顶点向量 = 投影矩阵 ✖ 视图变换矩阵 ✖ 模型矩阵 ✖ 顶点
- 使用
PushMatrix
方法,会将栈顶信息复制一份,放入栈顶。 - 使用
MultMatrix
方法做矩阵相乘时,将该矩阵与栈顶矩阵相乘,覆盖栈顶矩阵。 - 使用
PopMatrix
做出栈操作时,移除栈顶矩阵对象。(根据栈的特点,只能pop
栈顶)
后续的移动操作就是在增加特殊键位的移动即可
1 void SpeacialKeys(int key,int x,int y){ 2 3 float linear = 0.1f; 4 float angular = float(m3dDegToRad(5.0f)); 5 6 if (key == GLUT_KEY_UP) { 7 cameraFrame.MoveForward(linear); 8 } 9 if (key == GLUT_KEY_DOWN) { 10 cameraFrame.MoveForward(-linear); 11 } 12 13 if (key == GLUT_KEY_LEFT) { 14 cameraFrame.RotateWorld(angular, 0, 1, 0); 15 } 16 if (key == GLUT_KEY_RIGHT) { 17 cameraFrame.RotateWorld(-angular, 0, 1, 0); 18 } 19 20 }