美文网首页
OpenGL综合案例

OpenGL综合案例

作者: 大橘猪猪侠 | 来源:发表于2020-07-09 16:15 被阅读0次

今天我们来做一个关于OpenGL的一个综合案例,里面包含了点,线,三角形,金字塔等图形的绘制。

需要准备的东西之前已经说过了,不知道的,可以去翻之前的文章。

第一步:
引入头文件

#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

头文件解读:
GLTools.h:包含了大量类似c语言的独立函数
GLMatrixStack.h:矩阵的工具类,可以利用GLMatrixStack加载单元矩阵/矩阵/矩阵相乘/压栈/出栈等功能的实现
GLFrame.h:矩阵工具类,表示位置
GLFrustum.h:矩阵工具类,用来快速设置正/透视投影矩阵,完成坐标从3D->2D的转换
GLBatch.h:三角形批次类,帮助类,利用它可以传输顶点/光照/纹理/颜色等数据存储到着色器中。在本案例中,创建了7个批次类。
GLGeometryTransform.h:变换管道类,用来快速在代码中传输视图矩阵/投影矩阵/透视投影变换矩阵等。

第二步:
准备各种需要的类

//各种需要的类
GLShaderManager shaderManager;//存储着色器管理工具类
GLMatrixStack modelViewMatrix;//模型视图矩阵
GLMatrixStack projectionMatrix;//投影矩阵
GLFrame cameraFrame;//设置观察者视图坐标
GLFrame objectFrame;//设置图形环绕时,视图坐标
GLFrustum viewFrustum;//设置图元绘制时的投影方式

//容器类(7中不同的图元对应7中容器对象)

GLBatch pointBatch;
GLBatch lineBatch;
GLBatch lineStripBatch;
GLBatch lineLoopBatch;
GLBatch triangleBatch;
GLBatch triangleStripBatch;
GLBatch triangleFanBatch;

//几何变换的管道
GLGeometryTransform transformPipleline;

GLfloat vGreen[] = {0.0f,1.0f,0.0f,1.0f};
GLfloat vBlack[] = {0.0f,0.0f,0.0f,1.0f};


//跟踪效果步骤
int nStep = 0;

第三步
自定义函数:
1、SetupRC函数:设置你需要渲染的图形的顶点数据/颜色数据等
2、RenderScene函数:通过glutDisplayFun注册为显示渲染函数,当屏幕发生变化/或者开发者主动渲染会调用此函数,实现数据的渲染过程。
3、ChangeSize函数:通过glutReshaperFunc注册为重塑函数,当屏幕大小发生变化或第一次创建窗口时,会调用该函数调整窗口大小
键位函数:
1、SpecialKeys:特殊键位处理(上下左右)
2、KeyPressFunc:空格键位处理(按空格切换图像。)

下面来具体实现每个函数的功能:

setupRC函数:

void SetupRC(){
    //灰色的背景
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    
    //初始化着色器管理器
    shaderManager.InitializeStockShaders();
    glEnable(GL_DEPTH_TEST);
    //设置变换管线以使用两个矩阵堆栈
    transformPipleline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    //以观擦者的眼光去
    cameraFrame.MoveForward(-30.0f);
    
    //定义一些点,三角形形状。
    GLfloat vCoast[9] = {
        3,3,0,0,3,0,3,0,0
    };
    
    //用点的形式
    pointBatch.Begin(GL_POINTS, 3);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();
    
    //通过线的方式
    lineBatch.Begin(GL_LINES, 3);
    lineBatch.CopyVertexData3f(vCoast);
    lineBatch.End();
    
    //通过线段方式
    lineStripBatch.Begin(GL_LINE_STRIP, 3);
    lineStripBatch.CopyVertexData3f(vCoast);
    lineStripBatch.End();
    
    //通过线环的形式
    lineLoopBatch.Begin(GL_LINE_LOOP, 3);
    lineLoopBatch.CopyVertexData3f(vCoast);
    lineLoopBatch.End();
    
    //通过三角形创建金字塔
    GLfloat vPyramid[12][3] = {
        -2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f,

        2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,

        2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,

        -2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f
    };
    
    //GL_TRIANGLES 每3个顶点定义一个新的三角形
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
    
    //三角形扇形--六边形
    
    GLfloat vPoints[100][3];
    int nVerts = 0;
    //半径
    GLfloat r = 3.0f;
    //原点(x,y,z) = (0,0,0);
    vPoints[nVerts][0] = 0.0f;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;
    
    //M3D_2PI 就是2pi的意思,绘制圆形
    for (GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI/6.0f) {
        //数组下标自增(每自增1次表示一个顶点)
        nVerts++;
        /*
         弧长=半径*角度,这里的角度是弧度制,不是平时的角度制
         既然知道cos值,那么角度=arccos,求一个反三角函数
         */
        //x点坐标cos(angle) * 半径
        vPoints[nVerts][0] = float(cos(angle))*r;
        //y点坐标sin(angle) * 半径
        vPoints[nVerts][1] = float(sin(angle))*r;
        //z点坐标
        vPoints[nVerts][2] = -0.5;
    }
    
    //结束扇形 前面一共绘制7个顶点(包括圆心)
    //添加闭合的终点
    nVerts++;
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0;
    vPoints[nVerts][2] = 0.0f;
    
    //加载
    //GL_TRIANGLE_FAN 以一个圆心为中心呈扇形排列,共用相邻顶点的一组三角形
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    triangleFanBatch.CopyVertexData3f(vPoints);
    triangleFanBatch.End();
    
    //三角形带,一个小环或圆柱段
//    顶点下标
    int iCount = 0;
    //半径
    GLfloat radius = 3.0f;
    //从0度到360度,以0.3弧度为步长
    for (GLfloat angle = 0.0f; angle<=(2.0f*M3D_PI); angle+=0.3f) {
        //获取圆形的顶点x,y
        GLfloat x = radius * sin(angle);
        GLfloat y = radius * cos(angle);
        
        //绘制2个三角形
        vPoints[iCount][0] = x;
        vPoints[iCount][1] = y;
        vPoints[iCount][2] = 0.5;
        iCount++;
        
        vPoints[iCount][0] = x;
        vPoints[iCount][1] = y;
        vPoints[iCount][2] = 0.5;
        iCount++;
    }
    //关闭循环
    printf("三角形带的顶点数:%d\n",iCount);
    //结束循环,在循环位置生成2个三角形
    vPoints[iCount][0] = vPoints[0][0];
    vPoints[iCount][1] = vPoints[0][1];
    vPoints[iCount][2] = -0.5;
    iCount++;
    
    vPoints[iCount][0] = vPoints[1][0];
    vPoints[iCount][1] = vPoints[1][1];
    vPoints[iCount][2] = 0.5;
    iCount++;
    
    // GL_TRIANGLE_STRIP 共用一个条带(strip)上的顶点的一组三角形
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCount);
    triangleStripBatch.CopyVertexData3f(vPoints);
    triangleStripBatch.End();
}

此函数主要是渲染图形数据,做一系列数据加载初始化工作:
功能解读:
1、初始化工作
背景颜色
存储着色器管理器初始化
开启深度测试
设置变换管道中模型视图矩阵/投影矩阵
设置观察者视图坐标位置
2、三角形绘制
定义三角形顶点数据
使用三角形批次类,使用点/线/线环方式绘制图形
3、绘制金字塔准备工作
定义金字塔顶点数据
使用三角形批次类,使用GL_TRIANGLES绘制金字塔
4、绘制六角形准备
循环定义顶点数据
使用三角形批次类,使用GL_TRIANGLES_FAN传输数据
5、绘制三角形环准备
循环定义顶点数据
使用三角形批次类,使用GL_TRIANGLES_STRIP。传输数据

RenderScene函数


void DrwaWireFrameBatch(GLBatch *pBatch){

//设置着色器,变换矩阵,背景颜色
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipleline.GetModelViewProjectionMatrix(),vGreen);
    pBatch->Draw();
    
    //画黑色边框
    glPolygonOffset(-1.0f, -1.0f);//偏移深度,在同一位置要绘制填充和边线,会产生z冲突,所以要偏移
    glEnable(GL_POLYGON_OFFSET_LINE);
    
    //画反锯齿
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    //绘制线框几何黑色版 三种模式,实心,边框,点,可以作用在正面,背面,或者两面
    //通过调用glPolygonMode将多边形正面或者背面设为线框模式,实现线框渲染
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //设置线条宽度
    glLineWidth(2.5f);
    
    //绘制边线,黑边
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipleline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();
        
    // 复原原本的设置
    //通过调用glPolygonMode将多边形正面或者背面设为全部填充模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
}


void RenderScene(){
    //清空缓冲区
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
    
    //压栈
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后简存在堆栈的顶部。
    modelViewMatrix.MultMatrix(mCamera);
    
    M3DMatrix44f mObjectFrame;
    //只要使用GetMatrix函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载。用来使用GLShaderManager的使用。或者是获取顶部矩阵的顶点副本数据
    objectFrame.GetMatrix(mObjectFrame);
    
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后简存在堆栈的顶部。
    modelViewMatrix.MultMatrix(mObjectFrame);
    
    /* GLShaderManager 中的Uniform 值——平面着色器
    参数1:平面着色器
    参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
    --transformPipeline.GetModelViewProjectionMatrix() 获取的
    GetMatrix函数就可以获得矩阵堆栈顶部的值
    参数3:颜色值(黑色)
    */
    
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipleline.GetModelViewProjectionMatrix(),vBlack);
    
    switch (nStep) {
        case 0:
            //设置点的大小
            glPointSize(5.0f);
            pointBatch.Draw();
            glPointSize(1.0f);
            break;
        case 1:
            //设置线宽度
            glLineWidth(2.0f);
            lineBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 2:
            //设置线宽度
            glLineWidth(2.0f);
            lineStripBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 3:
            //设置线宽度
            glLineWidth(2.0f);
            lineLoopBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 4:
            DrwaWireFrameBatch(&triangleBatch);
            break;
        case 5:
            DrwaWireFrameBatch(&triangleStripBatch);
            break;
        case 6:
            DrwaWireFrameBatch(&triangleFanBatch);
            break;
    }
    //还原到以前的模型视图矩阵(单位矩阵)
    modelViewMatrix.PopMatrix();
    
    //进行缓冲区交换
    glutSwapBuffers();
}

1、此函数主要用于渲染,首先是清空缓冲区数据,将观察者坐标系压栈,将图形环绕坐标系压栈,固定管线渲染点线环等。
2、修改图形属性:判断金字塔/六边形/三角形环时,添加边框,让图形更逼真
3、绘制完毕则还原矩阵
4、交换缓冲区

DrwaWireFrameBatch函数:
1、填充图形内容
2、绘制边框部分
多边形偏移
颜色混合
绘制边框
3、将设置的属性还原

ChangeSize函数

void ChangeSize(int w,int h){
    glViewport(0, 0, w, h);
    //创建投影矩阵,并将它载入投影矩阵堆栈中
    viewFrustum.SetPerspective(35.0f, floor(w)/floor(h), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //调用顶部载入单元矩阵
    modelViewMatrix.LoadIdentity();
}

创建窗口视图函数,用于显示图像的窗口,在第一次创建或窗口大小发生改变时,会触发此函数。

SpecialKeys函数

void SpecialKeys(int key,int x,int y){
    if(key == GLUT_KEY_UP){
        //围绕一个指定的x,y,z轴旋转
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
    }
    if(key == GLUT_KEY_DOWN)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_LEFT)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
    
    if(key == GLUT_KEY_RIGHT)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
    
    
    //重新提交渲染
    glutPostRedisplay();
}

上下左右方向键移动图形切换图形坐标,然后重新提交渲染,绘制图形。

KeyPressFunc函数

void KeyPressFunc(unsigned char key,int x,int y){
    if(key == 32){
        nStep++;
        if(nStep>6)
            nStep = 0;
    }
    
    switch (nStep) {
        case 0:
            glutSetWindowTitle("GL_POINTS");
            break;
        case 1:
            glutSetWindowTitle("GL_LINES");
            break;
        case 2:
            glutSetWindowTitle("GL_LINE_STRIP");
            break;
        case 3:
            glutSetWindowTitle("GL_LINE_LOOP");
            break;
        case 4:
            glutSetWindowTitle("GL_TRIANGLES");
            break;
        case 5:
            glutSetWindowTitle("GL_TRIANGLES_STRIP");
            break;
        case 6:
            glutSetWindowTitle("GL_TRIANGLE_FAN");
            break;
    }
    
    //重新提交渲染
    glutPostRedisplay();
}

按下空格时,会不断的切换图形,循环切换7个图形,glutPostRedisplay是用于重新提交渲染的函数,每次切换都是重新绘制图形

main函数

int main(int argc,char* argv[]){
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    //申请一个颜色缓冲区,森度缓冲区,双缓冲区,模版缓冲区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //设置window尺寸
    glutInitWindowSize(800, 600);
    //创建window名称
    glutCreateWindow("GL_POINTS");
    //注册回调函数
    glutReshapeFunc(ChangeSize);
    //点击空格,调用函数
    glutKeyboardFunc(KeyPressFunc);
    //特殊键位函数(上下左右)
    glutSpecialFunc(SpecialKeys);
    //显示函数
    glutDisplayFunc(RenderScene);
    
    
    //判断一下是否能初始化glew库,确保项目能正常使用OpenGL框架
    GLenum err = glewInit();
    if(GLEW_OK != err){
        fprintf(stderr, "GLEW ERROR: %s\n",glewGetErrorString(err));
        return 1;
    }
    
    //绘制
    SetupRC();
    
    //runloop运行循环
    glutMainLoop();
    
    return 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/tsjscktx.html