美文网首页
OpenGL综合练习

OpenGL综合练习

作者: 奉灬孝 | 来源:发表于2020-07-18 15:05 被阅读0次

大球自传小球公转案例是OpenGL比较综合的一个经典案例,案例效果如下所示,接下来我们将一步一步得来完成这个经典案例。


大球自转小球公转

这个案例主要分为4个部分:
一、绘制地板
二、绘制大球(自转)
三、绘制小球(包含50个静态小球+1个围绕大球公转的动态小球)
四、移动(特殊键位上下左右触发)

一. 绘制地板

既然刚开始绘制,首先按照OpenGL绘制图形的顺序一个一个的来

1. SetupRC函数
  • 设置背景颜色
  • 初始化着色器管理器
  • 开启深度测试
  • 绘制地板顶点数据
void SetupRC()
{
    //1.初始化
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    shaderManager.InitializeStockShaders();
    
    //2.开启深度测试
    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();
}
2. 窗口大小改变回调函数ChangeSize
  • 设置视口
  • 创建投影矩阵,并加入堆栈
  • 设置变换管道
//屏幕更改大小或已初始化
void ChangeSize(int nWidth, int nHeight)
{
    //1.设置视口
    glViewport(0, 0, nWidth, nHeight);
    
    //2.创建投影矩阵,。
    viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
    //viewFrustum.GetProjectionMatrix()  获取viewFrustum投影矩阵
    //并将其加载到投影矩阵堆栈上
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    
    //3.设置变换管道以使用两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
    //初始化GLGeometryTransform 的实例transformPipeline.通过将它的内部指针设置为模型视图矩阵堆栈 和 投影矩阵堆栈实例,来完成初始化
    //当然这个操作也可以在SetupRC 函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
3. RenderScene函数:利用平面着色器绘制地板
  • 设置地板颜色
  • 清理缓冲区
  • 交换缓冲区
//进行调用以绘制场景
void RenderScene(void)
{
    //1.颜色值(地板,大球,小球颜色)
    static GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};

    //2.清除颜色缓存区和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //3.绘制地面
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 vFloorColor);
    floorBatch.Draw();
 
    //4.执行缓存区交换
    glutSwapBuffers();

}

二、绘制大球(自转)

地板已经绘制好了,我们只需要在绘制地板的几个函数中加入我们的大球模型并对大球模型进行矩阵变换即可

1. SetupRC函数
  • 设置大球模型

void SetupRC()
{
    //1.初始化
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    shaderManager.InitializeStockShaders();
    
    //2.开启深度测试
    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();
    
    //4.设置大球模型
     gltMakeSphere(torusBatch, 0.4f, 40, 80);
}
2. RenderScene函数
  • 设置大球颜色
  • 设置定时器:基于时间的变化,记录当前时间的角度
  • 设置大球变换(仅平移一次,并随定时器旋转)及绘制大球
  • 开启定时器:通过提交重新渲染请求,实现定时器触发的效果
//进行调用以绘制场景
void RenderScene(void)
{
    //1.颜色值(地板,大球,小球颜色)
    static GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    
    //2.基于时间动画
    static CStopWatch    rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    
    //2.清除颜色缓存区和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //**// 压栈
    modelViewMatrix.PushMatrix();
    
    //3.绘制地面
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 vFloorColor);
    floorBatch.Draw();
       
    //4.获取光源位置
    M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
   
    //5.使得大球位置平移(3.0)向屏幕里面
    modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
    //6.压栈(复制栈顶)
    modelViewMatrix.PushMatrix();
    //7.大球自转
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    //8.指定合适的着色器(点光源着色器)
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
    torusBatch.Draw();
    //9.绘制完毕则Pop
    modelViewMatrix.PopMatrix();
   
    modelViewMatrix.PopMatrix();
    
    //10.执行缓存区交换
    glutSwapBuffers();
    
    glutPostRedisplay();
}

三、绘制小球(包含50个静态小球+1个围绕大球公转的动态小球)

地板和大球已经绘制好了,我们只需要在之前绘制的几个函数中加入我们的小球模型并对自传小球模型进行矩阵变换即可

1. SetupRC函数
  • 设置小球模型
void SetupRC()
{
    //1.初始化
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    shaderManager.InitializeStockShaders();
    
    //2.开启深度测试
    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();
    
    //4.设置大球模型
     gltMakeSphere(torusBatch, 0.4f, 40, 80);
    
    //5. 设置小球球模型
    gltMakeSphere(sphereBatch, 0.1f, 26, 13);
   
    //6. 随机位置放置小球球
    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);
    }
}
2. RenderScene函数
  • 设置小球颜色
  • 绘制50个静态小球:每绘制一个小球都需要push和pop
  • 绘制公转动态小球
//进行调用以绘制场景
void RenderScene(void)
{
    //1.颜色值(地板,大球,小球颜色)
    static GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f};
    static GLfloat vSphereColor[] = { 0.0f, 0.0f, 1.0f, 1.0f};
    
    //2.基于时间动画
    static CStopWatch    rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    
    //3.清除颜色缓存区和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    modelViewMatrix.PushMatrix();
    
    //4.绘制地面
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 vFloorColor);
    floorBatch.Draw();

    //5.获取光源位置
    M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
   
    //6.使得大球位置平移(3.0)向屏幕里面
    modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
    //7.压栈(复制栈顶)
    modelViewMatrix.PushMatrix();
    //8.大球自转
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    //9.指定合适的着色器(点光源着色器)
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
    torusBatch.Draw();
    //10.绘制完毕则Pop
    modelViewMatrix.PopMatrix();

    //11.画小球
    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();    
    }

    //12. 让一个小篮球围绕大球公众自转
    modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
    modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vSphereColor);
    sphereBatch.Draw();

    modelViewMatrix.PopMatrix();
    modelViewMatrix.PopMatrix();
    //13.执行缓存区交换
    glutSwapBuffers();

    
    glutPostRedisplay();
}
小球公转

其实小球公转可以理解为小球自传,画面就是小球和大球一起围绕y轴自传,这样我们看不到大球,因为被我们的大球挡住了。既然是围绕大球公转,所以我们需要围绕y轴平移一段距离进行自传。这里的rotate+tranalate的顺序是不能互换的,因为矩阵相乘是叉乘,不满足交换律。具体效果可自行修改查看效果。这便是如下这段代码的含义:

//12. 让一个小篮球围绕大球公众自转
modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);

四、移动(特殊键位上下左右触发)

到这步,主要的功能都基本实现了,只需要在增加特殊键位的移动即可,此时的移动是作用于所有图形的,所以需要在绘制图形前增加一个观察者,用于记录所有图形的变换
首先,我们需要先在RenderScene函数矩阵堆栈顶部先加入观察者矩阵

RenderScene函数
  • 加入观察者
//进行调用以绘制场景
void RenderScene(void)
{
    //1.颜色值(地板,大球,小球颜色)
    static GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f};
    static GLfloat vSphereColor[] = { 0.0f, 0.0f, 1.0f, 1.0f};
    
    //2.基于时间动画
    static CStopWatch rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    
    //3.清除颜色缓存区和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    modelViewMatrix.PushMatrix();
    
    //4.加入观察者 平移10步(地板,大球,小球,小小球)
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.PushMatrix(mCamera);

    //5.获取光源位置
    M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
    
    //6.绘制地面
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 vFloorColor);
    floorBatch.Draw();
    
    //大球默认位置(0,0,0)Z深度(3.0) 正负指的是方向, 数字指的移动距离
    //7.使得大球位置平移(3.0)向屏幕里面  只移动1次
     modelViewMatrix.Translate(0.0f, 0.0f,-3.0f);
    
    //8.压栈(复制栈顶)
    modelViewMatrix.PushMatrix();
   
    //9.大球自转
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    
    //10.指定合适的着色器(点光源着色器)
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
    torusBatch.Draw();
    //11.绘制完毕则Pop
    modelViewMatrix.PopMatrix();

    //12.画小球
    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();      
    }

    //13. 让一个小篮球围绕大球公众自转
    //学员提问: 为什么这里老师没有加PushMatrix/PopMatrix ,是可以加的.但是因为是最后的绘图因为没有不会影响就么有添加. 这边给大家添加一组.
    modelViewMatrix.PushMatrix();
    
    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();
    modelViewMatrix.PopMatrix();
    
    modelViewMatrix.PopMatrix();
    modelViewMatrix.PushMatrix();
    //14.执行缓存区交换
    glutSwapBuffers();
    
    //15
    glutPostRedisplay();
}

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);
    }
}

到此为止,我们的大球自传小球公转的综合练习就完成了。

Demo:
OpenGLComplexDemo

相关文章

  • OpenGL综合练习

    大球自传小球公转案例是OpenGL比较综合的一个经典案例,案例效果如下所示,接下来我们将一步一步得来完成这个经典案...

  • OpenGL综合练习

  • OpenGL综合练习

    1. 最终效果image.png 1. 代码实现 3. 部分讲解 地板相比于原来demo的324个小地板拼成一个大...

  • OpenGL纹理综合练习

    球体 金字塔 隧道 针对以上三种立体图形添加纹理后的效果图,其绘制流程均是一下流程: 主要针对纹理添加过程作出分析...

  • 六、OpenGL综合练习

    1. 最终效果 这次利用前面学过的知识来完成一个综合项目,在一个场景中绘制地板、大球、小球、公转自转和相机的移动。...

  • 007-OpenGL综合练习

    学习OpenGL已经有一段时间,今天使用OpenGL来完成一个小例子,如下图 首先我们将要完成的功能做一个拆解 地...

  • 八、OpenGL综合练习+纹理

    1. 最终效果 给 六、OpenGL综合练习 的demo配上纹理,再添加一个镜面效果。 2. 代码 3. 部分讲解...

  • 九、OpenGL ES - 综合练习

    音视频开发:OpenGL + OpenGL ES + Metal 系列文章汇总[https://www.jians...

  • 九、OpenGL综合练习-球体世界

    最终效果 代码 #include "GLTools.h" #include"GLShaderManager.h" ...

  • 案例06:大球自转+小球公转+移动

    OpenGL + OpenGL ES +Metal 系列文章汇总 这个案例是OpenGL中的一个比较经典的综合案例...

网友评论

      本文标题:OpenGL综合练习

      本文链接:https://www.haomeiwen.com/subject/udgxkktx.html