OpenGL初探

第十五节—综合代码

2020-09-01  本文已影响0人  L_Ares

本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。

本节作为前几节知识的综合训练。只放代码,里面有非常详细的注释了,效果图如文章末尾"效果图"所示。

建议电脑配置不高的同学们不要把球的数据绘制的太多,数据可以随意减少的,2020款最新MacPro32+512G的电脑,跑时间长一点还是会发热的。


//
//  main.cpp
//  08综合
//
//  Created by EasonLi on 2020/8/30.
//  Copyright © 2020 EasonLi. All rights reserved.
//

#include <stdio.h>

#pragma mark - 引用类

//GLTools工具类。包含了绝大多数GLTool中类似C语言的函数
#include "GLTools.h"
//着色器管理器
#include "GLShaderManager.h"
//参考帧。表示世界坐标系中任意物体的位置与方向
#include "GLFrame.h"
//三角形批次类。容器类
#include "GLBatch.h"
//矩阵堆栈类
#include "GLMatrixStack.h"
//管道类。管理模型视图投影矩阵堆栈的
#include "GLGeometryTransform.h"
//矩阵投影工具类
#include "GLFrustum.h"
//一个计时器
#include "StopWatch.h"
//3d数学库
#include "math3d.h"

//宏定义根据不同的系统引入不同的GLUT
//Windows用静态的,MacOS用glut/glut.h
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

#pragma mark - 公共变量
//着色器管理器
GLShaderManager shaderManager;

//容器类
//地板线条的三角形批次类容器
GLBatch floorBatch;
//大球容器类
GLTriangleBatch bigSphereBatch;
//小球容器类
GLTriangleBatch smallSphereBatch;

//参考帧
//观察者参考帧
GLFrame cameraFrame;

//矩阵堆栈类
//模型视图矩阵堆栈
GLMatrixStack modelViewMatrixStack;
//投影视图矩阵堆栈
GLMatrixStack projectionMatrixStack;

//矩阵投影工具类
GLFrustum viewFrustum;

//矩阵堆栈管道
GLGeometryTransform transformPipeline;

//常规属性
//地板颜色
GLfloat vGreen[] = {0.f,1.f,0.f,1.f};
//大球颜色
GLfloat vRed[] = {1.f,0.f,0.f,1.f};
//小球颜色
GLfloat vBlue[] = {0.f,0.f,1.f,1.f};

//小球的数量
#define SMALL_SPHERE_NUM 50
//定义一个小球数组
GLFrame spheresNum[SMALL_SPHERE_NUM];

#pragma mark - 函数
//设置窗口尺寸等关联信息
void ChangeSize(int w,int h)
{
    
    //设置视口
    glViewport(0, 0, w, h);
    
    //创建投影矩阵
    viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 500.f);
    
    //将投影矩阵加载到投影矩阵堆栈中
    projectionMatrixStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //设置矩阵堆栈变换管道来管理矩阵堆栈
    //GLGeometryTransform的初始化:
    //通过将GLGeometryTransform对象的内部指针分别设置成modelViewMatrixStack和projectionMatrixStack来完成初始化。
    transformPipeline.SetMatrixStacks(modelViewMatrixStack, projectionMatrixStack);
    
}

//设置渲染环境
void SetUpRC()
{
    
    /**********************公共**********************/
    //设置清屏颜色,避免残留的屏幕颜色对渲染造成影响
    glClearColor(0.f, 0.f, 0.f, 1.f);
    
    //初始化着色器管理器
    shaderManager.InitializeStockShaders();
    
    //因为要用到立体图形了,需要立体的效果,所以开启深度测试
    glEnable(GL_DEPTH_TEST);
    /***********************************************/
    
    
    
    /********************设置地板********************/
    //设置地板的图元和顶点数量
    floorBatch.Begin(GL_LINES, 324);
    //利用for循环设置坐标
    //x坐标的范围随意去定,每次+0.5控制的是没两条平行线之间的距离
    //值越小,平行线密度越大
    for (GLfloat x = -20.f; x <= 20.f; x += 0.5f) {
        //设置纵向的线
        //因为我们要的是地板的效果,所以发生变化的是x和z,y是不变的,这才是一个地板
        //平面的效果
        floorBatch.Vertex3f(x, -0.5f, 20.f);
        floorBatch.Vertex3f(x, -0.5f, -20.f);
        
        //设置横向的线
        floorBatch.Vertex3f(20.f, -0.5f, x);
        floorBatch.Vertex3f(-20.f, -0.5f, x);
    }
    //完成地板批次类的设置
    floorBatch.End();
    /***********************************************/
    
    
    
    /********************设置大球*********************/
    //直接用系统给的就行
    gltMakeSphere(bigSphereBatch, 0.5f, 50, 100);
    /***********************************************/
    
    
    
    /********************设置小球*********************/
    //直接用系统给的就行
    gltMakeSphere(smallSphereBatch, 0.1f, 13, 26);
    //利用for循环随机放置小球位置
    for (int i = 0; i < SMALL_SPHERE_NUM; i++) {
        
        //因为还是同一平面的问题,从我们的角度来看,就是Y轴不变,那么X,Z轴随机坐标
        GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
        GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
        
        //对数组中的每一个顶点都设置顶点数据
        //Y就给0,看起来效果像是飘起来的
        spheresNum[i].SetOrigin(x, 0.f, z);
        
    }
    /***********************************************/
    
}

//渲染
void RenderScene()
{
    
    /**********************公共**********************/
    //清空缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    //定义一个矩阵用来存储观察者矩阵
    M3DMatrix44f mCamera;
    //获取观察者矩阵并且放入上面定义的观察者矩阵
    cameraFrame.GetCameraMatrix(mCamera);
    //把观察者矩阵压入模型视图矩阵堆栈,做好乘法,矩阵变换,放在栈顶
    modelViewMatrixStack.PushMatrix(mCamera);
    /***********************************************/
    
    
    
    /********************绘制地板********************/
    //使用着色器
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vGreen);
    //利用地板的三角形批次类绘制
    floorBatch.Draw();
    /***********************************************/
    
    
    
    /********************绘制大球********************/
    //定义一个静态的定时器,全局都要用。
    static CStopWatch timer;
    //设置每秒的旋转角度
    float yRot = timer.GetElapsedSeconds() * 60.f;
    //给定一个光源位置,因为光源是向量,所以如下定义
    M3DVector4f lightPos = {0.f,10.f,5.f,1.f};
    //给大球一个平移,这样保证大球不在原点位置,不然和观察者重叠了
    modelViewMatrixStack.Translate(0.f, 0.f, -3.f);
    /**现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
    //观察者mv平移矩阵
    //压栈。因为后面要绘制其他的东西,大球就画自己的,画完就出栈,不要影响其他的绘制
    modelViewMatrixStack.PushMatrix();
    /**现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
    //观察者mv平移矩阵-->观察者mv平移矩阵
    //设置大球自转
    modelViewMatrixStack.Rotate(yRot, 0.f, 1.f, 0.f);
    //设置成点光源着色器
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),lightPos,vRed);
    //利用大球的三角形批次类绘制
    bigSphereBatch.Draw();
    /**现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
    //观察者mv平移矩阵
    modelViewMatrixStack.PopMatrix();
    /***********************************************/
    
    
    
    /********************绘制随机小球******************/
    //还是利用for循环把随机出来的小球绘制出来
    for (int i = 0; i < SMALL_SPHERE_NUM; i++) {
        //压栈完成后
        /**现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
        //观察者mv平移矩阵-->观察者mv平移矩阵
        modelViewMatrixStack.PushMatrix();
        //直接给数组中的每一个小球参考帧矩阵都做矩阵变换
        /**完成后,现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
        //观察者mv平移矩阵-->观察者mv平移和小球位置矩阵
        modelViewMatrixStack.MultMatrix(spheresNum[i]);
        //设置着色器
        shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),lightPos,vBlue);
        //利用小球的三角形批次类绘制
        smallSphereBatch.Draw();
        //出栈
        /**完成后,现在模型视图矩阵堆栈里面从栈底到栈顶有如下几个矩阵:*/
        //观察者mv平移矩阵
        modelViewMatrixStack.PopMatrix();
        
    }
    /***********************************************/
    
    
    
    /********************绘制公转小球******************/
    //设置绕大球公转(其实就是绕着Y轴公转,因为大球中心线和Y轴是同平面中的平行关系)
    //现在的mv矩阵堆栈中只有观察者mv矩阵,旋转后就变成了观察者mv旋转矩阵
    modelViewMatrixStack.Rotate(yRot * 2.f, 0.f, 1.f, 0.f);
    //再把小球拉出来点,拉出的距离必须比大球大,不然就不是绕着大球公转了
    //平移后变成观察者mv旋转平移矩阵
    modelViewMatrixStack.Translate(1.f, 0.f, 0.f);
    //着色器设置
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),lightPos,vBlue);
    //小球三角形批次类绘制
    smallSphereBatch.Draw();
    /***********************************************/
    
    
    
    /**********************公共**********************/
    //出栈。回复到最初的原始堆栈
    modelViewMatrixStack.PopMatrix();
    //交换缓冲区
    glutSwapBuffers();
    glutPostRedisplay();
    /***********************************************/
    
    
}

//特殊键位
void SpecialKeys(int key,int x,int y)
{
    
    //设置前后是移动,左右是旋转
    //前后每次移动的步长
    float nStep = 0.1f;
    //左右每次旋转的角度
    float nRot = float(m3dDegToRad(5.f));
    
    //判断键位,这里用观察者视角来做变化,也可以用物体做变化
    //如果用物体的话,定义一个物体的参考帧objFrame,RenderScene中直接把objFrame
    //压栈到模型视图矩阵堆栈就行了,就不要把观察者矩阵加进去了,一样能实现效果
    switch (key) {
            
        case GLUT_KEY_UP:
            cameraFrame.MoveForward(nStep);
            break;
            
        case GLUT_KEY_DOWN:
            cameraFrame.MoveForward(-nStep);
            break;
        case GLUT_KEY_RIGHT:
            cameraFrame.RotateWorld(-nRot, 0.f, 1.f, 0.f);
            break;
        case GLUT_KEY_LEFT:
            cameraFrame.RotateWorld(nRot, 0.f, 1.f, 0.f);
            break;
            
    }
    //这里就不用发送重新渲染的信号了,因为RenderScene中有个定时器,已经在
    //RenderScene中一直的重新渲染了,加上也无所谓
    glutPostRedisplay();
    
}

#pragma mark - main
int main(int argc,char *argv[])
{
    
    //设置工作目录和项目目录都到/Resouce下面
    //GLUT优先级已经设置过,手动保证安全
    gltSetWorkingDirectory(argv[0]);
    
    //GLUT初始化
    glutInit(&argc, argv);
    
    //初始化显示模式
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    
    //设置窗口尺寸
    glutInitWindowSize(600, 600);
    
    //创建窗口并命名
    glutCreateWindow("综合");
    
    //注册相关函数
    //重塑函数
    glutReshapeFunc(ChangeSize);
    //渲染函数
    glutDisplayFunc(RenderScene);
    //特殊键位函数
    glutSpecialFunc(SpecialKeys);
    
    //初始化Glew库
    GLenum status = glewInit();
    if (status != GLEW_OK) {
        printf("glew init error : %s \n",glewGetErrorString(status));
        return 1;
    }
    
    //设置渲染环境
    SetUpRC();
    
    //构建本地循环
    glutMainLoop();
    
    return 0;
    
}


效果图如下,大球是自转的,就是看的不清楚,小球是围绕大球公转的,大球的自转后面加入纹理后可以看的很清楚。

效果图.png
上一篇下一篇

猜你喜欢

热点阅读