美文网首页
OpenGL-08-入门级案例3:绘制7种图元连接图形、几何图形

OpenGL-08-入门级案例3:绘制7种图元连接图形、几何图形

作者: 宇宙那么大丶 | 来源:发表于2020-07-22 00:06 被阅读0次

根据之前介绍的7种图元连接方式:点、线、线段、线环、三角形、三角形带、三角形扇 进行绘制图形。还有OpenGL中现成的几何图形API调用。

一、效果图

001.gif

二、流程图

image.png

三、源码


//======导入头文件======
#include "GLShaderManager.h"
#include "GLTools.h"
#include <GLUT/GLUT.h>

//矩阵工具类。加载单元矩阵、矩阵、矩阵相乘、压栈、出栈、缩放、平移、旋转
#include "GLMatrixStack.h"
//矩阵工具类。表示位置。通过设置origin、forward、up
#include "GLFrame.h"
//矩阵工具类。快速设置正投影、透视投影。完成3D->2D映射过程
#include "GLFrustum.h"
//三角形批次类。利用它将顶点、光照、纹理、颜色等数据传入存储着色器
#include "GLBatch.h"
//变换管道类。用来快速在代码中传输视图矩阵、投影矩阵、视图投影变换矩阵等。
#include "GLGeometryTransform.h"
//数学库
#include <math.h>
//======导入头文件======

//======声明全局变量======
//存储着色器
GLShaderManager shaderManager;
//视图模型矩阵
GLMatrixStack modelViewMatrix;
//透视矩阵
GLMatrixStack projectionMatrix;
//观察者视图坐标
GLFrame cameraFrame;
//图形环绕时用到的视图坐标
GLFrame objectFrame;
//图元绘制时的投影方式
GLFrustum viewFrustum;
//容器类
//点、线、线段、线环、三角形、三角形带、三角形扇
GLBatch                pointBatch;
GLBatch                lineBatch;
GLBatch                lineStripBatch;
GLBatch                lineLoopBatch;
GLBatch                triangleBatch;
GLBatch             triangleStripBatch;
GLBatch             triangleFanBatch;
//几何变换管道。存储 模型矩阵、投影矩阵、模型视图矩阵
GLGeometryTransform transformPipeline;
//定义个黑色
GLfloat vBlack[] = {0.0f,0.0f,0.0f,1.0f};
//绿色
GLfloat vGreen[] = {0.0f,1.0f,0.0f,1.0f};
//记录当前按空格的次数,用来切换图案
int nStep = 0;

 

//======声明全局变量======

//填充颜色+画黑边
void DrawWireFramedBatch(GLBatch* pBatch)
{
    /*------------1、填充颜色-画绿色部分----------------*/
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
          --transformPipeline 变换管线(指定了2个矩阵堆栈)
     参数3:颜色值
    */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();
    
    /*-----------2、画边框-黑色边框-------------------*/
 
    /*
     glEnable(GLenum mode); 用于启用各种指定功能。功能由参数决定
     glDisable(GLenum mode); 用于关闭指定功能 功能由参数决定
     参数列表:http://blog.csdn.net/augusdi/article/details/23747081
     注意:glEnable() 不能写在glBegin() 和 glEnd()中间
     */
    //----开启----
    //开启多边形偏移
    glEnable(GL_POLYGON_OFFSET_LINE);
    //设置偏移量
    glPolygonOffset(-1.0f, -1.0f);
    //开启反锯齿,让线变得更光滑好看
    glEnable(GL_LINE_SMOOTH);
    //开启颜色混合
    glEnable(GL_BLEND);
    //设置混合因子
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //设置正面背面的填充模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //设置线条宽度
    glLineWidth(2.5f);
    
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
         --transformPipeline.GetModelViewProjectionMatrix() 获取的
          GetMatrix函数就可以获得矩阵堆栈顶部的值
     参数3:颜色值(黑色)
     */ 
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();

    //----关闭+复原----
    //复原填充方式和线段宽度
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glLineWidth(1.0f);
    //关闭偏移
    glDisable(GL_POLYGON_OFFSET_LINE);
    //关闭颜色混合
    glDisable(GL_BLEND);
    //关闭反锯齿
    glDisable(GL_LINE_SMOOTH);
     
    
    
}

//窗口变化时候的回调函数
void changeSize(int w,int h)
{
    //防止宽高比中除数为0的bug
    if(h==0) h=1;
    
    //1、创建窗口
    glViewport(0, 0, w, h);
    //2、创建投影矩阵->透视投影(角度、宽高比、最小距离、最大距离)
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
    //3、拿到这个投影矩阵,把它加载到我们声明的透视矩阵
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //4、在我们的视图模型矩阵 顶部载入单元矩阵,相当于初始化
    modelViewMatrix.LoadIdentity();
    //5、设置变换管道
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
}


//所有必要的初始化操作在这里进行
void setupRC()
{
    //1、背景色
    glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
    //2、初始化着色器
    shaderManager.InitializeStockShaders();
    //3、开启深度测试
    glEnable(GL_DEPTH_TEST);
    //4、观察者的初始化位置
    cameraFrame.MoveForward(-15.0f);
    //5、设置顶点数据
    //6、提交批次类 ==注意没一个批次类对于一种图形
    //=====点====
    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
        
    };
    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.5f;
    }
    
    // 结束扇形 前面一共绘制7个顶点(包括圆心)
    //添加闭合的终点
    //课程添加演示:屏蔽177-180行代码,并把绘制节点改为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 iCounter = 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个三角形(他们的x,y顶点一样,只是z点不一样)
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = -0.5;
        iCounter++;
        
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = 0.5;
        iCounter++;
    }
    
    // 关闭循环
    printf("三角形带的顶点数:%d\n",iCounter);
    //结束循环,在循环位置生成2个三角形
    vPoints[iCounter][0] = vPoints[0][0];
    vPoints[iCounter][1] = vPoints[0][1];
    vPoints[iCounter][2] = -0.5;
    iCounter++;
    
    vPoints[iCounter][0] = vPoints[1][0];
    vPoints[iCounter][1] = vPoints[1][1];
    vPoints[iCounter][2] = 0.5;
    iCounter++;
    
    // GL_TRIANGLE_STRIP 共用一个条带(strip)上的顶点的一组三角形
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
    triangleStripBatch.CopyVertexData3f(vPoints);
    triangleStripBatch.End();
    
}

//进行渲染
void RenderScene(void)
{
    //1、清楚缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT);
    //2、压栈 -> 为了记录最初状态,用来撤回 顶部是单元矩阵
    modelViewMatrix.PushMatrix();
    //3、初始化一个观察者矩阵
    M3DMatrix44f mCamreaMatrix;
    //把观察者矩阵放进去
    cameraFrame.GetCameraMatrix(mCamreaMatrix);
    //矩阵相乘放入顶部 : 单元矩阵(1) * mCamreaMatrix = New_mCamreaMatrix
    modelViewMatrix.MultMatrix(mCamreaMatrix);
    
    //4、初始化一个物体本身矩阵
    M3DMatrix44f mObjectMatrix;
    objectFrame.GetMatrix(mObjectMatrix);
    //New_mCamreaMatrix * mObjectMatrix = New_mObjectMatrix
    modelViewMatrix.MultMatrix(mObjectMatrix);
    
    //5、使用平面着色器存储栈顶的矩阵 (平面着色器,几何图形的变换矩阵,颜色)
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
    
    //6、设置属性,draw,还原属性
    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:
            DrawWireFramedBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleFanBatch);
            break;
            
            
        default:
            break;
    }
    //7、出栈、进行撤回操作。
    modelViewMatrix.PopMatrix();
    
    //8、进行交换缓冲区
    glutSwapBuffers();
}


//空格键回调
void KeyPressFunc(unsigned char key, int x, int y)
{
    //这里32刚好对应空格键
    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_TRIANGLE_STRIP");
            break;
        case 6:
            glutSetWindowTitle("GL_TRIANGLE_FAN");
            break;
    }
    
    glutPostRedisplay();
}
//上下左右键回调
void SpecialKeys(int key, int x, int y)
{
    //上下=沿着X轴旋转   左右=沿着Y轴旋转
    if(key == GLUT_KEY_UP)
        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();
}






int main(int argc,char *argv[])
{
    //1、设置工作目录、初始化
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    //2、初始化缓冲区 双缓冲、颜色、深度、模板缓冲区
    glutInitDisplayMode(GLUT_DOUBLE |GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
    //3、设置窗口大小、标题
    glutInitWindowSize(800, 600);
    glutCreateWindow("jpy");
    //========================
    //4、注册所需回调函数
    //窗口重塑回调
    glutReshapeFunc(changeSize);
    //渲染回调
    glutDisplayFunc(RenderScene);
    //空格回调
    glutKeyboardFunc(KeyPressFunc);
    //特殊键位回调
    glutSpecialFunc(SpecialKeys);
    //========================
    //5、固定操作。初始化GLEW库,做检测
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n",glewGetErrorString(err));
        return 1;
    }
    
    
    //进行初始化操作
    setupRC();
    //开始loop监听各种回调
    glutMainLoop();
    return 0;
}

四、拓展

我们上面的代码使用的是自己计算各个顶点,传入三角形批次类中,然后进行绘制。那么如果我们进行一些复杂图形绘制,又要计算多少个顶点呢?那得多麻烦啊!!!
GLTriangleBatch 这个类里面有一些已经封装好的API,我们可以直接调用!!!
我们来看一下都有哪些:

0621.gif
//首先我们要声明一下

//球
GLTriangleBatch     sphereBatch;
//环
GLTriangleBatch     torusBatch;
//圆柱
GLTriangleBatch     cylinderBatch;
//锥
GLTriangleBatch     coneBatch;
//磁盘
GLTriangleBatch     diskBatch;

在SetupRC()中进行初始化

// 球
    /*
      gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
     参数1:sphereBatch,三角形批次类对象
     参数2:fRadius,球体半径
     参数3:iSlices,从球体底部堆叠到顶部的三角形带的数量;其实球体是一圈一圈三角形带组成
     参数4:iStacks,围绕球体一圈排列的三角形对数
     
     建议:一个对称性较好的球体的片段数量是堆叠数量的2倍,就是iStacks = 2 * iSlices;
     绘制球体都是围绕Z轴,这样+z就是球体的顶点,-z就是球体的底部。
     */
    gltMakeSphere(sphereBatch, 3.0, 10, 20);
    
    // 环面
    /*
     gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
     参数1:torusBatch,三角形批次类对象
     参数2:majorRadius,甜甜圈中心到外边缘的半径
     参数3:minorRadius,甜甜圈中心到内边缘的半径
     参数4:numMajor,沿着主半径的三角形数量
     参数5:numMinor,沿着内部较小半径的三角形数量
     */
    gltMakeTorus(torusBatch, 3.0f, 0.75f, 15, 15);
    // 圆柱
    /*
     void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
     参数1:cylinderBatch,三角形批次类对象
     参数2:baseRadius,底部半径
     参数3:topRadius,头部半径
     参数4:fLength,圆形长度
     参数5:numSlices,围绕Z轴的三角形对的数量
     参数6:numStacks,圆柱底部堆叠到顶部圆环的三角形数量
     */
    gltMakeCylinder(cylinderBatch, 2.0f, 2.0f, 3.0f, 15, 2);
    
    //锥
    /*
     void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
     参数1:cylinderBatch,三角形批次类对象
     参数2:baseRadius,底部半径
     参数3:topRadius,头部半径
     参数4:fLength,圆形长度
     参数5:numSlices,围绕Z轴的三角形对的数量
     参数6:numStacks,圆柱底部堆叠到顶部圆环的三角形数量
     */
    //圆柱体,从0开始向Z轴正方向延伸。
    //圆锥体,是一端的半径为0,另一端半径可指定。
    gltMakeCylinder(coneBatch, 2.0f, 0.0f, 3.0f, 13, 2);
    
    // 磁盘
    /*
    void gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks);
     参数1:diskBatch,三角形批次类对象
     参数2:innerRadius,内圆半径
     参数3:outerRadius,外圆半径
     参数4:nSlices,圆盘围绕Z轴的三角形对的数量
     参数5:nStacks,圆盘外网到内围的三角形数量
     */
    gltMakeDisk(diskBatch, 1.5f, 3.0f, 13, 3);

在 RenderScene 中进行绘制,直接调用上面代码中的 DrawWireFramedBatch 方法

switch(nStep) {
        case 0:
            DrawWireFramedBatch(&sphereBatch);
            break;
        case 1:
            DrawWireFramedBatch(&torusBatch);
            break;
        case 2:
            DrawWireFramedBatch(&cylinderBatch);
            break;
        case 3:
            DrawWireFramedBatch(&coneBatch);
            break;
        case 4:
            DrawWireFramedBatch(&diskBatch);
            break;
    }

相关文章

网友评论

      本文标题:OpenGL-08-入门级案例3:绘制7种图元连接图形、几何图形

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