OpenGL综合案例
OpenGL已经学习了一段时间,现在进行一下综合整理工作。
绘制地板
以下是详细代码及注释,这里不再做一一解释,这里只摘录了changeSize、SetupRC、RenderScene这几个方法中的代码,其他代码及他们的执行流程,可以参照之前文章:
GLShaderManager shaderManager; // 着色器管理器
GLMatrixStack modelViewMatrix; // 模型视图矩阵
GLMatrixStack projectionMatrix; // 投影矩阵
GLFrustum viewFrustum; // 视景体
GLGeometryTransform transformPipeline; // 几何图形变换管道
GLTriangleBatch torusBatch; //大球
GLTriangleBatch sphereBatch; //小球
GLBatch floorBatch; //地板
//角色帧 照相机角色帧
GLFrame cameraFrame;
//**4、添加附加随机球
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
//屏幕更改大小或已初始化
void changeSize(int w,int h)
{
//设置视口大小
glViewport(0, 0, w, h);
//创建投影矩阵 并将它载入到投影矩阵堆栈中
viewFrustum.SetPerspective(35.0f, (float)w/(float)h, 1.0f, 100.0f);
//viewFrustum.GetProjectionMatrix()用来获取投影矩阵
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//3.设置变换管道以使用两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
//初始化操作
void setupRC()
{
//进行初始化操作
//1.清理颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//初始化着色器
shaderManager.InitializeStockShaders();
//开启深度测试
glEnable(GL_DEPTH_TEST);
//3. 使用批次类设置地板顶点数据
floorBatch.Begin(GL_LINES, 324);
for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {
floorBatch.Vertex3f(x, -0.55f, 20.0f);
floorBatch.Vertex3f(x, -0.55f, -20.0f);
floorBatch.Vertex3f(20.0f, -0.55f, x);
floorBatch.Vertex3f(-20.0f, -0.55f, x);
}
floorBatch.End();
}
//进行调用以绘制场景
void RenderScene(void)
{
//设置地板颜色值
static GLfloat vFloorColor[] = {0.0f,1.0f,0.0f,1.0f};
//2.清除颜色缓存区和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//3.绘制地面 GLT_SHADER_FLAT平面着色器
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vFloorColor);
//进行绘制
floorBatch.Draw();
//交换缓冲区
glutSwapBuffers();
}
执行后结果:
绘制大球
代码及注释:
//以上面的代码为基础,changeSize中不需要更改,在setupRC设置大球模型
//设置大球模型 因为OpenGL中自带球类模型,不需要自己画顶点,直接调用gltMakeSphere方法,使用批次类,传对应的参数及可。
gltMakeSphere(torusBatch, 0.4f, 40, 80);
//RenderScene中改动较大,详细代码如下:
void RenderScene(void)
{
//设置地板颜色值
static GLfloat vFloorColor[] = {0.0f,1.0f,0.0f,1.0f};
//设置大球颜色
static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//创建定时器,用于控制大球转动
static CStopWatch rotTimer;
//得到大球每秒转动的角度
float yRot = rotTimer.GetElapsedSeconds() *60.0;
//2.清除颜色缓存区和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//进行压栈,为了确保每次操作独立,不影响其他操作 每一次压栈PushMatrix都要对应一次出栈PopMatrix
modelViewMatrix.PushMatrix();
//3.绘制地面 GLT_SHADER_FLAT平面着色器
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vFloorColor);
//进行绘制
floorBatch.Draw();
//用矩阵设置一处光源
M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
//为了获得更好的观察效果,让大球想屏幕里移动3.0
modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
//再次压栈,为了下面的旋转操作
modelViewMatrix.PushMatrix();
//大球绕y轴旋转
modelViewMatrix.Rotate(0.0f, 1.0f, 0.0f, 1.0f);
//设置着色器(点光源着色器) 模型视图变换矩阵 投影矩阵 点光源位置 颜色值
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vTorusColor);
//绘制
torusBatch.Draw();
//进行出栈 2次
modelViewMatrix.PopMatrix();
modelViewMatrix.PopMatrix();
//交换缓冲区
glutSwapBuffers();
//重新提交渲染
glutPostRedisplay();
}
执行结果:
绘制多个小球
代码如下:
//如添加大球时的操作,同样SetupRC中添加小球模型,并随机放置50个
//设置小球模型
gltMakeSphere(sphereBatch, 0.2f, 13, 26);
//随机放置 NUM_SPHERES上面定义过的宏
for (int i = 0; i < NUM_SPHERES; i++) {
//y轴不变,X,Z产生随机值 让小球处于同一平面
GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
//在y方向,将球体设置为0.0的位置,这使得它们看起来是飘浮在眼睛的高度
//对spheres数组中的每一个顶点,设置顶点数据
spheres[i].SetOrigin(x, 0.0f, z);
}
//同样在RenderScene中去绘制这50个小球
//画小球,for循环画50个,单个小球画法和大球差不多,不同的是通过压栈来保持每一次小球的独立
for (int i = 0; i < NUM_SPHERES; i++) {
modelViewMatrix.PushMatrix();
modelViewMatrix.MultMatrix(spheres[i]);
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(), vLightPos, vSphereColor);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
}
生成小球
小球围绕大球转动
代码:
//在RenderScene方法中加入
//让一个小篮球围绕大球公众自转
modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vSphereColor);
sphereBatch.Draw();
键位控制
代码:
//mian函数中注册键位控制函数
glutSpecialFunc(SpeacialKeys);
//通过相应的操作控制移动旋转
void SpeacialKeys(int key,int x,int y){
//移动步长
float linear = 0.1f;
//旋转度数
float angular = float(m3dDegToRad(5.0f));
//上
if (key == GLUT_KEY_UP) {
//MoveForward 平移
cameraFrame.MoveForward(linear);
}
//下
if (key == GLUT_KEY_DOWN) {
cameraFrame.MoveForward(-linear);
}
//左
if (key == GLUT_KEY_LEFT) {
//RotateWorld 旋转
cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
}
//右
if (key == GLUT_KEY_RIGHT) {
cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
}
}
//然后在RenderScene中加入一个观察者 平移10步(地板,大球,小球,小小球),通过移动观察者来达到目的
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.PushMatrix(mCamera);
这里大球自转,小球围绕大球公转的案例就完成了。