美文网首页
OpenGL 综合案例

OpenGL 综合案例

作者: Joker_King | 来源:发表于2020-07-19 14:38 被阅读0次

在绘制之前,我们先来介绍下一下几个头文件的作用。

// GLTool.h头⽂文件包含了大部分GLTool中类似C语言的独立函数
#include "GLTools.h"
// 着色器管理器
#include "GLShaderManager.h"
// 矩阵的⼯具类:可以利于GLMatrixStack 加载单元矩阵/矩阵/矩阵相乘/压栈/出栈/缩放/平移/旋转
#include "GLMatrixStack.h"
// 矩阵工具类,表示观察者的位置.通过设置vOrigin,vForward,vUps等
#include "GLFrame.h"
// 矩阵⼯具类,⽤来快速设置正/透视投影矩阵.完成坐标从3D->2D的映射过程
#include "GLFrustum.h"
// 三⻆形批次类,帮助类,利用它可以传输顶点/光照/纹理/颜⾊数据到存储着⾊器中
#include "GLBatch.h"
// 变换管道类,⽤来快速在代码中传输视图矩阵/投影矩阵/视图投影变换矩阵等
#include "GLGeometryTransform.h"

接下来我们需要在外部声明几个全局变量。

// 着色器管理器
GLShaderManager shaderManager;
// 模型视图矩阵   
GLMatrixStack modelViewMatrix;
// 投影矩阵 
GLMatrixStack projectionMatrix;
// 视景体, 用来完成物体从3D到2D的映射
GLFrustum           viewFrustum;
// 几何图形变换管道
GLGeometryTransform transformPipeline;
// 大球
GLTriangleBatch torusBatch;
// 小球             
GLTriangleBatch sphereBatch; 
//地板                     
GLBatch floorBatch;          
// 角色帧 照相机角色帧
GLFrame cameraFrame;
// 添加附加随机球
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];

绘制地板

main函数工作流程

int main(int argc, char* argv[])
{
    // 设置工作目录
    gltSetWorkingDirectory(argv[0]);
    // 初始化GLUT环境
    glutInit(&argc, argv);
    // 设置显示模式
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    // 设置初始化窗口的大小
    glutInitWindowSize(800,600);
    // 创建一个名为OpenGL SphereWorld的窗口
    glutCreateWindow("OpenGL SphereWorld");
    // 注册窗口改变时的回调函数
    glutReshapeFunc(ChangeSize);
    // 注册一个绘图的函数
    glutDisplayFunc(RenderScene);
    // 初始化一个GLEW库,确保OpenGL API对程序完全可用。在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    // 自定义函数,做一些变量的初始化
    SetupRC();
    // 开启mainloop,相当于OC中runloop,是一个运行循环
    glutMainLoop();    
    return 0;
}

glutInitDisplayMode

函数功能为设置初始显示模式。mode可取以下值或其组合:


-w638

SetupRC

void SetupRC()
{   //清理背景颜色
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    //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();
}

ChangeSize

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

RenderScene

void RenderScene(void)
{
    //1.颜色值
    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();
}

绘制大球

SetupRC

SetupRC中初始化球体的数据。

// 初始化球体的三角形批次,后面参数依次是,球半径,片段数,堆叠数
gltMakeSphere(torusBatch, 0.5f, 300, 80);

RenderScene

RenderScene函数中对大球进行绘制。

// 定义球体的颜色
static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
// 定义一个计时器,让球体旋转起来。
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();
// 绘制完毕后出栈
modelViewMatrix.PopMatrix();

添加以上代码,此时球体已经被渲染出来了,但是此时它并没有动起来。我们需要提交重新渲染,让它动起来。

glutPostRedisplay();

RenderScene的最终代码

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 };
    
    //2.基于时间动画
    static CStopWatch    rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
    
    //2.清除颜色缓存区和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //**// 压栈
    modelViewMatrix.PushMatrix();
    
    //3.绘制地面
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                 transformPipeline.GetModelViewProjectionMatrix(),
                                 vFloorColor);
    floorBatch.Draw();
    
    
    //4.获取光源位置
    M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
   
    //5.使得大球位置平移(3.0)向屏幕里面
    modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
    //6.压栈(复制栈顶)
    modelViewMatrix.PushMatrix();
    //7.大球自转
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    //8.指定合适的着色器(点光源着色器)
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
    torusBatch.Draw();
    //9.绘制完毕则Pop
    modelViewMatrix.PopMatrix();
    modelViewMatrix.PopMatrix();
    
    //10.执行缓存区交换
    glutSwapBuffers();
    glutPostRedisplay();
}

绘制小球

SetupRC

SetupRC函数中设置小球的模型

// 设置小球球模型
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);
}

RenderScene

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

接下来我们让一个小球围绕着大球转起来
我们需要在RenderScene函数中添加以下代码。

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

移动观察者

首先在模型视图矩阵堆栈中加入一个观察者矩阵,在RenderScene函数中添加如下代码。

// 创建一个观察者矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
// 将观察者矩阵压入模型视图堆栈中。
modelViewMatrix.PushMatrix(mCamera);

。。。。 
// 在最后需要pop
modelViewMatrix.PopMatrix();

接下来我们需要实现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);
    }
}

相关文章

  • 案例06:大球自转+小球公转+移动

    OpenGL + OpenGL ES +Metal 系列文章汇总 这个案例是OpenGL中的一个比较经典的综合案例...

  • OpenGL(九)-- 综合案例(公、自转)

    OpenGL(九)-- 综合案例(公、自转) 相信学习过OpenGL的同学应该过玩过这个经典案例:总和案例.gif...

  • OpenGL综合案例

    今天我们来做一个关于OpenGL的一个综合案例,里面包含了点,线,三角形,金字塔等图形的绘制。 需要准备的东西之前...

  • OpenGL 综合案例

    在绘制之前,我们先来介绍下一下几个头文件的作用。 接下来我们需要在外部声明几个全局变量。 绘制地板 main函数工...

  • OpenGL 综合案例

    先看结果 核心代码 栈的机制 在changeSize()函数中,我们加载了投影矩阵,并把投影矩阵压入管道trans...

  • OpenGL综合案例

    OpenGL已经学习了一段时间,现在进行一下综合整理工作。 绘制地板 以下是详细代码及注释,这里不再做一一解释,这...

  • OpenGL 综合案例

    结合前面学到的知识,做一个球公转自转的综合程序,代码如下: 运行后效果图

  • OpenGL综合案例

    实现效果 学习OpenGL也有一段时间了。今天使用三个批次类来完成一个球体世界的demo; 球体世界网格层面 网格...

  • OpenGL案例05:大球自转+小球公转+移动

    这个案例是OpenGL中的一个比较经典的综合案例,结合了OpenGL中大部分知识点,下面就来了解下这个案例 先来看...

  • OpenGL案例综合解析

      之前讲了很多关于OpenGL相关的案例,接下来用一个综合的案例,来熟悉一下OpenGL相关的API,以便于更深...

网友评论

      本文标题:OpenGL 综合案例

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