这个案例是OpenGL中的一个比较经典的综合案例,结合了OpenGL中大部分知识点,下面就来了解下这个案例
先来看看最终的效果
整体的流程的如下
其中,比较重要的逻辑都在以下三个函数中
- SetupRC函数
- RenderScene函数
- SpecialKeys函数
下面来详细说说整体的效果的实现,可以大致分为4部分
- 地板
- 大球(自转)
- 小球(包含50个静态小球+1个围绕大球公转的动态小球)
- 移动(特殊键位上下左右触发)
准备工作
流程图中的ChangeSize、main函数,这里就不作过多说明,相信大家对这两个函数已经印象非常深刻,如果还有不明白的,可以结合OpenGL案例 03:金字塔、六边形、圆环的绘制先了解下这两个函数中到底做了哪些工作
地板
- SetupRC函数:准备地板的顶点数据
- RenderScene函数:利用平面着色器绘制地板
两个函数此时的流程图大致如下
-
地板流程
大球
在地板绘制完成的基础上,绘制大球,并实现其自转功能,主要步骤如下
- SetupRC函数:利用系统模型类创建大球
- RenderScene函数:分为三部分
- 设置定时器:基于时间的变化,记录当前时间的角度
- 设置大球变换(仅平移一次,并随定时器旋转)及绘制大球
- 开启定时器:通过提交重新渲染请求,实现定时器触发的效果
两个函数的流程图如下,图中红框部分流程均与大球相关

大球变换
大球变换的代码如下
//5.使得大球位置平移(3.0)向屏幕里面
modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
//6.压栈(复制栈顶)
modelViewMatrix.PushMatrix();
//7.大球自转
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
- 开启定时器:基于时间的变化,获得旋转的角度,此时,就相当ios中NSTimer的创建
//1、基于时间变化 CStopWatch--NSTimer定时器
static CStopWatch rotTimer;
//获得角度
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
- 根据模型变化,获得mvp矩阵
- 创建4个矩阵:平移、旋转、模型视图矩阵、模型视图投影矩阵
- 将球围绕y轴旋转,得到旋转矩阵
- 将平移矩阵 * 旋转矩阵,得到模型视图矩阵
- 将模型视图矩阵与投影矩阵相乘,得到mvp矩阵。这里需要注意的是,mvp矩阵相乘是有顺序的,传入API的矩阵顺序必须先传p,再传mv,否则会发现没得我们想要的就跟,其根本原因是矩阵相乘不满足交换律,所以不能打乱它的顺序
//3、创建3矩阵: 平移、旋转、模型视图矩阵、模型视图投影
M3DMatrix44f mTranslate, mRotate, mModelView, mModelViewProjection;
//往z轴移动-2.5f
m3dTranslationMatrix44(mTranslate, 0, 0, -2.5f);
//围绕y轴旋转
m3dRotationMatrix44(mRotate, m3dDegToRad(yRot), 0, 1, 0);
//矩阵相乘
m3dMatrixMultiply44(mModelView, mTranslate, mRotate);
//mvp是有顺序的:结果-p-mv,不满足交换律
// m3dMatrixMultiply44(mModelViewProjection, viewFrustum.GetProjectionMatrix(), mModelView);
m3dMatrixMultiply44(mModelViewProjection, mModelView, viewFrustum.GetProjectionMatrix());
- Translate:目的是为了更好的观察的大球,因为大球创建时默认是在(0,0,0)原点位置,当前的观察者也处于原点位置,不便于观察
- Rotate:大球围绕y轴旋转,实现自转
- 绘制球体
- 交换缓冲区
- 提交重新渲染:此时就相当于开启NSTimer,不断根据旋转的角度不断渲染球,实现自转的效果
小球
在绘制完大球的基础上,继续绘制小球
- SetupRC函数:初始化小球数据
- RenderScene函数:绘制静态+动态小球
- 绘制50个静态小球:每绘制一个小球都需要push和pop
- 矩阵堆栈记录平移+旋转的变换
- 绘制动态小球
此时两个函数的流程图如下:
-
小球流程
小球公转
其实小球的公转可以理解为: 自转+平移
,如果只有rotate变换,那么小球就是在原地围绕y轴自转,但是此时大球跟小球是处于同一个位置的,为了更好的观察,需要将小球平移,与大球之间有一定的间隔,且每次旋转,小球旋转的角度是大球的两倍,意味着小球比大球转的快,公转原理如图所示
-
-
注:这里的
rotate+tranalate
的顺序是不能互换的,因为矩阵相乘是叉乘,不满足交换律,详细的可参考OpenGL中的向量、矩阵其中的模型变换部分有提到,如果互换了得到的是另一种结果,经过测试,如果顺序是tranalate+rotate
,此时的小球是先移动后旋转,意味着小球位置变换后,是在原地实现的自转。具体的可以自己修改代码试试。
移动
到这步,主要的功能都基本实现了,只需要在增加特殊键位的移动即可,此时的移动是作用于所有图形的,所以需要在绘制图形前增加一个观察者,用于记录所有图形的变换
此时,项目中setupRc、RenderScene、SpecialKeys总的流程图如下
到此,这个综合项目就完成啦。
完整的代码见github - 06_OpenGL_综合项目
网友评论