OpenGL

6、Open基础综合实战(-)-实现球的自转加公转

2019-05-21  本文已影响7人  溪浣双鲤

实现效果:

能随着观察者的不断移动,都能观察到小球的公转加自转

最终图片效果:

图1效果图 图2效果图

大体实现思路及步骤拆分:

1、先实现绿色方格地板部分

2、实现固定自转的红色大球和一个中型的蓝球,以及位置随机的50个小蓝球

3、实现中型蓝球绕红色大球的公转

4、实现移动观察者(camera)仍能观察红球自转及蓝球围绕红球公转

下面开始代码部分


一、先定义一些变量

GLShaderManager shaderManager; // 定义一个着色器管理器

GLMatrixStack modelViewMatrix; // 定义一个模型视图矩阵

GLMatrixStack projectionMatrix; // 定义一个投影矩阵

GLFrustum viewFrustum; // 视景体

GLGeometryTransform transformPipeline; // 几何图形变换管道

GLTriangleBatch    torusBatch;            //批次类  -大球

GLTriangleBatch    sphereBatch;            //批次类  -小球

GLBatch                floorBatch;          //简单的批次类  -地板

//角色帧 照相机角色帧

GLFrame  cameraFrame;

GLFrame  objectFrame;

//**4、添加附加随机球

#define NUM_SPHERES50

GLFrame spheres[NUM_SPHERES];

二、在主函数中初始化一些特殊键位操作以及设置窗口默认参数,注册变化函数

int  main (int  argc,  char* argv[])

{

//1、设置当前工作目录,针对MAC OS X    /*     `GLTools`函数`glSetWorkingDrectory`用来设置当前工作目录。实际上在Windows中是不必要的,因为工作目录默认就是与程序可执行执行程序相同的目录。但是在Mac OS X中,这个程序将当前工作文件夹改为应用程序捆绑包中的`/Resource`文件夹。`GLUT`的优先设定自动进行了这个中设置,但是这样中方法更加安全。     */

    gltSetWorkingDirectory(argv[0]);

//2.初始化一个GLUT库

    glutInit(&argc, argv);

/* 3、     初始化双缓冲窗口,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指     双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区         

 --GLUT_DOUBLE`:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后迅速转换成窗口视图,这种方式,经常用来生成动画效果;     

--GLUT_DEPTH`:标志将一个深度缓存区分配为显示的一部分,因此我们能够执行深度测试;     

--GLUT_STENCIL`:确保我们也会有一个可用的模板缓存区。     深度、模板测试后面会细致讲到    

 */

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

//3、GLUT窗口大小、窗口标题

    glutInitWindowSize(800,600);

    glutCreateWindow("OpenGL SphereWorld");

/*4、      GLUT 内部运行一个本地消息循环,拦截适当的消息。然后调用我们不同时间注册的回调函数。我们一共注册2个回调函数: 

    1)为窗口改变大小而设置的一个回调函数   

    2)包含OpenGL 渲染的回调函数     */

    glutReshapeFunc(ChangeSize);//注册函数,在窗口改变大小的时候调用

    glutDisplayFunc(RenderScene);//注册渲染函数

    glutSpecialFunc(SpeacialKeys);//注册特殊函数

/* 5、     初始化一个GLEW库,确保OpenGL API对程序完全可用。     在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题     */

    GLenumerr =glewInit();

    if(GLEW_OK!= err) {

        fprintf(stderr,"GLEW Error: %s\n",glewGetErrorString(err));

        return1;

    }

//6、设置我们的渲染环境

   SetupRC();

 //7、这是一个无限执行的循环,它会负责一直处理窗口和操作系统的用户输入等操作。(注意:不会执行在glutMainLoop()之后的所有命令。)

    glutMainLoop(); 

    return 0;

}

三、开始设置我们的渲染环境

void    SetupRC()

{

    //1.设置清屏颜色(背景颜色)

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

  //2.初始化着色器

 //没有着色器,在OpenGL 核心框架中是无法进行任何渲染的。初始化一  个渲染管理器。目前我们使用固定管线渲染,后面会用OpenGL着色语言来写着色器

   shaderManager.InitializeStockShaders();

 //3.修改物体的初始坐标,往后移动5.0f的距离也就是z为-5,z轴垂直屏幕指向我们观察者为正,z为-5,就是往屏幕里面移动5

    objectFrame.MoveForward(5.0f);

    //4.开启深度测试,3D图像渲染需要开启深度测试

    glEnable(GL_DEPTH_TEST);

  //5.开始设置地板的顶点数据

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

  //6.设置大球模型

    gltMakeSphere(torusBatch, 0.4f, 40, 80);

 //7.设置小球模型

    gltMakeSphere(torusBatch, 0.1f, 26, 13);

//8、随机位置放置小球

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

 }

四、进行调用以绘制场景

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

    //4、加入objectFrame,用于特殊键位操作观察者位置移动

    modelViewMatrix.PushMatrix(objectFrame);

    //5.获取光源位置

    M3DVector4fvLightPos = {0.0f,10.0f,5.0f,1.0f};

    //6.绘制地面

    shaderManager.UseStockShader(GLT_SHADER_FLAT,

                                 transformPipeline.GetModelViewProjectionMatrix(),

                                 vFloorColor);

    floorBatch.Draw();

    //7.使得大球位置平移(3.0)向屏幕里面

    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(inti =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. 让一个小篮球围绕大球公众自转

    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();//出栈

    //14.执行缓存区交换

    glutSwapBuffers();

    //15 发送重新渲染

    glutPostRedisplay();

}

五、屏幕大小更改或者已经初始化

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

}

六、特殊键位操作

void SpeacialKeys(int key,int x,int y){

    //设定移动步长

    float      linear =0.1f;

    //设定旋转度数

    float    angular =  float(m3dDegToRad(5.0f));//角度转弧度

    if(key ==GLUT_KEY_UP) {//键盘上的方向键  up键

        //MoveForward 平移

        objectFrame.MoveForward(linear);

    }

    if(key ==GLUT_KEY_DOWN) {//键盘上的方向键  down键

        objectFrame.MoveForward(linear);

    }

    if(key ==GLUT_KEY_LEFT) {//键盘上的方向键  left键

        //RotateWorld 旋转

        objectFrame.RotateWorld(angular,0,1,0);

    }

    if(key ==GLUT_KEY_RIGHT) {//键盘上的方向键  right键

        objectFrame.RotateWorld(angular,0,1,0);    }

}

七、最终效果视频地址优酷传送门:小球自转公转效果图

上一篇 下一篇

猜你喜欢

热点阅读