Draw graphs according to the 7 kinds of graph element connection methods introduced before: point, line, line segment, line ring, triangle, triangle band and triangle fan. And then there’s the geometry API calls that are available in OpenGL.

First, the effect drawing

Ii. Flow chart

Three, the source code


//======导入头文件======
#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;
}

Copy the code

Our above code is used to calculate the vertices themselves, passed into the triangle batch class, and then draw. So if we were to do some complicated graphing, how many vertices do we have to compute? How much trouble that would take!! GLTriangleBatch this class has some already packaged API, we can call directly!! Let’s take a look at what they have:

// GLTriangleBatch sphereBatch; / / ring GLTriangleBatch torusBatch; // GLTriangleBatch cylinderBatch; / / cone GLTriangleBatch coneBatch; // GLTriangleBatch diskBatch;Copy the code

Initialize in SetupRC()

// ball /* gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks); Parameter 1: sphereBatch, triangle batch class object parameter 2: fRadius, sphere radius parameter 3: iSlices, the number of triangle strips stacked from the bottom of the sphere to the top; 4: iStacks, the logarithm of triangles arranged around a circle of spheres suggests that the number of fragments of a sphere with good symmetry is twice the number of stacks, that is, iStacks = 2 * iSlices; The spheres are drawn around the z-axis so that + Z is the vertex of the sphere and -z is the bottom of the sphere. */ makesphere (sphereBatch, 3.0, 10, 20);Copy the code
// torus /* gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor); Parameter 1: torusBatch, triangle batch class object parameter 2: majorRadius, the radius from the center of the doughnut to the outer edge parameter 3: minorRadius, the radius from the center of the doughnut to the inner edge parameter 4: numMajor, the number of triangles along the main radius parameter 5: NumMinor, number of triangles along the inner smaller radius */ gltMakeTorus(torusBatch, 3.0f, 0.75f, 15, 15);Copy the code
* void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks); Parameter 1: cylinderBatch, triangle batch class object parameter 2: baseRadius, baseRadius parameter 3: topRadius, head radius parameter 4: fLength, circular length parameter 5: numSlices, number of triangle pairs around the z-axis parameter 6: NumStacks, number of triangles stacked from the bottom of the cylinder to the top ring */ gltMakeCylinder(cylinderBatch, 2.0F, 2.0F, 3.0F, 15, 2);Copy the code
// * void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks); Parameter 1: cylinderBatch, triangle batch class object parameter 2: baseRadius, baseRadius parameter 3: topRadius, head radius parameter 4: fLength, circular length parameter 5: numSlices, number of triangle pairs around the z-axis parameter 6: NumStacks, the number of triangles from the bottom of the cylinder to the top of the ring */ // the cylinder extends from 0 to the positive direction of the Z axis. // The radius of one end is 0, and the radius of the other end can be specified. GltMakeCylinder (coneBatch, 2.0F, 0.0F, 3.0F, 13, 2);Copy the code
// disk /* void gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks); Parameter 1: diskBatch, Triangle batch class object parameters 2:innerRadius, innerRadius parameter 3:outerRadius, outerRadius parameter 4:nSlices, number of triangle pairs around the z-axis of the disk 5:nStacks, number of triangles from the outer network of the disk to the inner circle */ GltMakeDisk (diskBatch, 1.5F, 3.0F, 13, 3);Copy the code

Draw in the RenderScene, calling the DrawWireFramedBatch method directly from the code above

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;
    }
Copy the code