阅读 47

OpenGL(8)——综合案例

本篇文章主要讲解一个综合案例,类似于太阳系一样,中间一个红色大球自转, 其余的蓝色小球围绕大球公转。效果图如下:

关于OpenGL的绘制流程,前面已经介绍过了,这里不再赘述。主要还是用到了以下结果方法:

SetupRC()
RenderScene()
ChangeSize()
SpeacialKeys()
复制代码

下面将主要按照绘制流程,逐步讲解绘制的代码流程。

1.画网格地板

现在SetupRC初始化数据。地板使用一条一条的线来绘制的,所以就需要的大量的线,总共需要324条线,用一个for循环来初始化数据。

void SetupRC(){
    //1 初始化
    glClearColor(0, 0, 0, 1);
    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();
    ...
    
}
复制代码

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();
}
复制代码

完成后运行结果如下:

2.画大球

同样在SetupRC中设置大球的数据模型。

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

绘制大球
CStopWatch类似于iOS里的定时器 GetElapsedSeconds函数返回从程序开始要现在的时间,单位为秒。乘以60意思就是一分钟转60度。6分钟转自转一圈。
为了时大球能够为观察者观察到,大球需要向后移动3个单位。此时观察者在原点位置。大球向后移动的越多,在观察者眼里,球体越小。
使用点光源着色器进行绘制大球。 记得最要两次出栈,因为我们前面进行了两次压栈操作。

//大球颜色为红色
static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };

//CStopWatch 类似于iOS里的定时器 GetElapsedSeconds函数返回程序到
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;

//获取光源位置
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();
modelViewMatrix.PopMatrix();
复制代码

运行效果如下:

这里大球在自转,但是因为它整体都是红色的原因,我们暂时还区分不出大球是静止不动还是在旋转。这个放在后面解决。

3.画小球

设置小球模型,并设置50个随机小球位置。小球的y值都为0,这样可以保证小球全部在同一水平面上。

//设置小球球模型
 gltMakeSphere(sphereBatch, 0.1f, 26, 13);

 //随机位置放置小球
 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);
 }
复制代码

画小球。 这里我们只使用了一个批次类来绘制小球, 通过循环将50个小球全部绘制完成,记得每次的压栈和出栈。

//设置小球颜色
static GLfloat vSphereColor[] = { 0.0f, 0.0f, 1.0f, 1.0f};
//循环画小球
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();
    }
复制代码

小球公转。 因为是小球绕着大球旋转,所以小球的旋转半径要比大球的半径要大。设置小球位移0.8f距离。并且小球的旋转速度要比大球的转速快一些。

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();
复制代码

效果如下:

4. 视角移动

首先在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) {
        cameraFrame.MoveForward(linear);
    }
    if (key == GLUT_KEY_DOWN) {
        cameraFrame.MoveForward(-linear);
    }
    
    if (key == GLUT_KEY_LEFT) {
        cameraFrame.RotateWorld(angular, 0, 1, 0);
    }
    if (key == GLUT_KEY_RIGHT) {
        cameraFrame.RotateWorld(-angular, 0, 1, 0);
    }

}
复制代码

最终效果就是我们文章开头所展示的那样。
完整demo链接github

文章分类
阅读
文章标签