案例:大球自传、小球自传围绕大球公转

247 阅读3分钟

首先我们看个gif动画


整体流程是main->ChangeSize->SetupRC->RenderScene

通用设置

  • main函数:程序入口,调用设置窗口、初始化视图以及函数监听等

    int main(int argc,char* argv[])
    
    {
        
        //设置当前工作目录,针对MAC OS X
        
        gltSetWorkingDirectory(argv[0]);
        
        //初始化GLUT库
        
        glutInit(&argc, argv);
        
        /*初始化双缓冲窗口,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指
         
         双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区*/
        
        glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
        
        //GLUT窗口大小,标题窗口
        
        glutInitWindowSize(800,600);
        
        glutCreateWindow("OpenGL SphereWorld");
        
        //注册回调函数
        
        glutReshapeFunc(ChangeSize);
        
        glutDisplayFunc(RenderScene);
        
        glutSpecialFunc(SpeacialKeys);
        
        //驱动程序的初始化中没有出现任何问题。
        
        GLenum err = glewInit();
        
        if(GLEW_OK != err) {
            
            fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
            
            return 1;
            
        }
        
        //调用SetupRC
        
        SetupRC();
        
        glutMainLoop();
        
        return 0;
        
    }
    
    
    

  • SetupRC函数:设置背景颜色(黑色)、初始化着色器、开启深度测试

    glClearColor(0.0f,0.0f,0.0f,1.0f);
            //初始化着色管理器
        shaderManager.InitializeStockShaders();
        //开启深度测试
        glEnable(GL_DEPTH_TEST);
    
    
    

  • ChangeSize函数:设置窗口大小、创建投影矩阵、加载矩阵到堆栈上、设置 变换管道

    void ChangeSize(int w,int h)
    {
        //设置视口
        glViewport(0,0, w, h);
        //创建投影矩阵
        viewFrustum.SetPerspective(35.0f, (float)w/(float)h, 1.0f, 100.0f);
        //加载到投影矩阵堆栈上
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        //设置变换管道
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    }
    
    
    

  • RenderScene函数:清空缓存背景色

    //清除一个或一组特定的缓冲区
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    
    

绘制地板

  • SetupRC函数:负责准备地板的顶点数据

    //设置地板数据
        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();

  • RenderScene函数:负责绘制地板

    //绘制地板
        shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vFloorColor);
        floorBatch.Draw();
    
    

需要注意的是压栈和出栈要配对使用

绘制大球

  • SetupRC函数:设置大球模型

    //设置大球模型gltMakeSphere(torusBatch, 0.4f, 40, 80);
    

  • RenderScene函数:主要是绘制大图

    //获取光源位置
        M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
        //大球向屏幕里面平移-5
        modelViewMatrix.Translate(0.0f, 0.0f, -5.0f);
        //大球入栈
        modelViewMatrix.PushMatrix();
        //大球自传(围绕y轴)
        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函数:设置环绕大球公转的小球以及随机生成其他小球
    //设置小球
        gltMakeSphere(sphereBatch, 0.2, 10, 20);
        //设置随机生成的小球
        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函数:绘制随机小球以及公转小球

    //绘制随机小球
        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.PushMatrix();
        //设置小球自传
        modelViewMatrix.Rotate(yRot * -2.0f, 0, 1.0f, 0.0);
        //设置小球公转(就是平移)
        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();
    
    

设置观察者

观察者设置主要是为了可以通过上下左右键控制窗口移动

  • RenderScene函数:设置观察者,要放在设置地板代码前面

    //加入观察者
        M3DMatrix44f mCamera;
        cameraFrame.GetCameraMatrix(mCamera);
        modelViewMatrix.PushMatrix(mCamera);
    
    
    

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