美文网首页
OpenGL综合案例(大球自转,小球公转)

OpenGL综合案例(大球自转,小球公转)

作者: 大橘猪猪侠 | 来源:发表于2020-07-18 14:49 被阅读0次

    在之前我写过几篇博客,关于OpenGL的一些专业名词的介绍,绘制一些简单的图形(三角形,正方形,球,金字塔等图案)。现在我通过一个案例,将之前所有写过的东西通过一个案例来展现出来。
    这个案例效果图如下:


    QQ20200718-134336-HD.gif

    这个案例用到的知识点:
    1、GLShaderManager着色器
    2、GLMatrixStack矩阵堆栈
    3、GLFrustum透视投影 视景体
    4、GLGeometryTransform几何变换管道
    5、GLTriangleBatch三角形批次类
    6、GLBatch批次类容器
    7、GLFrame角色帧
    8、M3DMatrix44f矩阵变换
    9、CStopWatch时间监视器

    下面来实现一下具体的过程
    一、引入头文件,定义对象、属性、容器类和自定义函数

    
    #include "GLTools.h"
    #include "GLShaderManager.h"
    #include "GLFrustum.h"
    #include "GLBatch.h"
    #include "GLMatrixStack.h"
    #include "GLGeometryTransform.h"
    #include "StopWatch.h"
    
    #include <math.h>
    #include <stdio.h>
    
    #ifdef __APPLE__
    #include <glut/glut.h>
    #else
    #define FREEGLUT_STATIC
    #include <GL/glut.h>
    #endif
    
    GLShaderManager shaderManager;  //着色器管理器
    GLMatrixStack modelViewMatrix;  //模型视图矩阵
    GLMatrixStack projectionMatrix; //投影矩阵
    GLFrustum viewFrustum;  //视景体
    GLGeometryTransform transformPipeline;  //几何图形变换管道
    
    GLTriangleBatch torusBatch; //大球
    GLTriangleBatch sphereBatch;    //小球
    GLBatch floorBatch; //地板
    
    GLFrame cameraFrame;    //角色帧 照相机角色帧
    
    #define NUM_SPHERES 50  //添加50个随机球
    GLFrame spheres[NUM_SPHERES];
    
    //创建窗口视图
    void ChangeSize(int w,int h){}
    
    //加载顶点数据
    void SetupRC(){}
    
    //场景绘制
    void RendeScene(void){}
    
    //键位移动
    void SpecialKeys(int key,int x,int y){}
    
    int main(int argc,char* argv[]){
        gltSetWorkingDirectory(argv[0]);
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH|GLUT_RGB);
        glutInitWindowSize(800, 600);
        glutCreateWindow("综合案例");
        
        glutReshapeFunc(ChangeSize);
        glutDisplayFunc(RendeScene);
        glutSpecialFunc(SpecialKeys);
        
        
        GLenum err = glewInit();
        if(GLEW_OK!=err){
            fprintf(stderr, "GLEW Error: %s\n",glewGetErrorString(err));
            return 1;
        }
        
        
        SetupRC();
        glutMainLoop();
        
        return 0;
    }
    

    二、窗口函数的实现

    //1.设置视口
        glViewport(0, 0, w, h);
        //2.创建投影矩阵。
        viewFrustum.SetPerspective(35.0, float(w)/float(h), 1.0f, 100.0f);
        //viewFrustum.GetProjectionMatrix()  获取viewFrustum投影矩阵
        //并将其加载到投影矩阵堆栈上
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        //3.设置变换管道以使用两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    

    三、设置底部的地板视图
    SetupRC设置位置

    //初始化颜色
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    //初始化着色器
    shaderManager.InitializeStockShaders();
        
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    //1、加载地板的坐标
    floorBatch.Begin(GL_LINES, 324);
    for (GLfloat x = -20; 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();
    

    在RendeScene绘制场景

    //设置颜色(小球,地板,大球)
    static GLfloat vFloorColor[] = {0.0f,1.0f,0.0f,1.0f};
    
    //清除颜色、深度缓冲区
    glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
    
    //1.绘制地面
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vFloorColor);
    floorBatch.Draw();
        
        
    //执行缓存区交换
    glutSwapBuffers();
    glutPostRedisplay();
    

    四、设置大球位置和自转

    SetupRC函数
    //2、设置大球模型
    gltMakeSphere(torusBatch, 0.4f, 40, 80);
    
    RendeScene函数
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    static CStopWatch    rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    modelViewMatrix.PushMatrix();
    
    //2、绘制大球
    //获取光源位置
    M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
    //使大球位置平移3.0向屏幕里面
    modelViewMatrix.Translate(0.0f,0.0f, -3.0f);
    //压栈
    modelViewMatrix.PushMatrix();
    //大球自转
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    //指定合适的着色器(点光源着色器)
    
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vtorusColor);
    torusBatch.Draw();
    //绘制完毕pop
    modelViewMatrix.PopMatrix();
    
    

    五、绘制小球和围绕大球公转

    SetupRC
    //3、设置小球模型
    gltMakeSphere(sphereBatch, 0.1f, 13, 26);
    //随机放置小球位置
    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);
    
    RendeScene
    static GLfloat vsphereColor[] = {0.0f,0.0f,1.0f,1.0f};
    
    //3、绘制小球
        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();
        }
        
    //让小球围绕大球自转
    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();
    

    六、设置视图变换

    RendeScene中加入观察者
    //4、加入观察者
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.PushMatrix(mCamera);
    
    void SpecialKeys(int key,int x,int y){
        //移动步长
        float linear = 0.1f;
        //旋转度数
        float angular = float(m3dDegToRad(5.0f));
        
        if(key == GLUT_KEY_UP)
            cameraFrame.MoveForward(linear);
        if(key == GLUT_KEY_DOWN)
            cameraFrame.MoveForward(-linear);
        if(key == GLUT_KEY_LEFT)
            cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
        if(key == GLUT_KEY_RIGHT)
            cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
    }
    

    在SpecialKeys的键位移动过程结束后,为什么不用glutPostRedisplay()函数重新提交渲染呢?
    因为在RendeScene函数中,使用了时间函数CStopWatch,它在每秒钟会自动调用glutPostRedisplay函数重新提交渲染,因此在SpecialKeys函数中不必重新提交渲染。

    可能也有人好奇为什么大球和小球同样设置旋转,为什么大球是自转,小球确实围绕大球转呢?
    那是因为大球的坐标在正中心,旋转围绕y轴旋转,所以大球旋转是自身旋转,而小球坐标不在坐标正中心,因此,小球需要围绕正中心旋转。因此,小球可以看作是围绕大球公转。

    至于小球和地板的坐标的设置,你们可以自行去算一下坐标。他们围绕的都是y轴旋转。
    完整代码地址:Demo

    相关文章

      网友评论

          本文标题:OpenGL综合案例(大球自转,小球公转)

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