OpenGL 图元连接方式

223 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

IMG_0068.JPG


  1. Swift Optional
  2. Swift Enum
  3. iOS开发 做一个三角形
  4. Swfit 指针类型
  5. Swift 属性(下)
  6. Swift 属性
  7. Swift 小结
  8. Swift 类与结构体(下)
  9. Swift 类的生命周期
  10. Swift 类的初始化器
  11. Swift 类与结构体
  12. OpenGL 压缩纹理
  13. OpenGL 隧道坐标计算
  14. 0penGL 像素格式及数据类型
  15. OpenGL 纹理对象
  16. OpenGL 纹理
  17. OpenGL 模型变化
  18. OpenGL 视图
  19. OpenGL 矩阵
  20. OpenGL 向量
  21. OpenGL 颜色混合
  22. OpenGL 深度测试的潜在风险
  23. OpenGL 浅析深度测试
  24. OpenGL 浅析隐藏面消除
  25. OpenGL 图元连接方式
  26. 记WKWebView与HTML完成交互两三事
  27. OpenGL 渲染流程图解析
  28. OpenGL 控制你的正方形
  29. OpenGL 专业名词解释
  30. OpenGL 环境搭建 - MacOS


今天我们一起总结下OpenGL中的图元连接方式都是有哪些。

OpenGL基本图元连接方式

图元描述
GL_POINTS每个顶点在屏幕上都是单独点
GL_LINES每一对顶点定义一个线段
GL_LINE_STRIP一个从第一个顶点依次经过每一个后续顶点而绘制的线条
GL LINE LOOP和GL LINE_ STRIP相同,但是最后一个顶点和第一个顶点连接起来了
GL_TRIANGLES每3个顶点定义一个新的三角形
GL_TRIANGLE _STRIP共用一个条带(strip)上的顶点的一组三角形
GL_TRIANGLE_FAN以一个圆点为中心呈扇形排列,共用相邻顶点的一组三角形

用一张图片来更加直观的解释下上面的几种连接方式:

OpenGL基本图元连接方式.001.png


使用流程

我们在测试使用图元连接方式的时候,应该重点了解 对 模型视图矩阵 的使用:

  • 清空缓冲区之后,通过 压栈(记录一些状态参数,可以进行撤回操作)modelViewMatrix.PushMatrix();

  • 观察者矩阵拿到之后 记录起来 cameraFrame.GetCameraMatrix();

  • 物体矩阵拿到之后,也做记录objectFrame.GetMatrix();

  • 使用模型视图矩阵 对 观察者举证和物体矩阵进行多矩阵混合计算 modelViewMatrix.MultMatrix(mCamera);+objectFrame.GetMatrix(mObjectFrame);

  • 对模型视图矩阵进行绘制shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);

  • 之后,我们可以自定义点或线的一些参数,画完之后要还原;

  • 最后,我们要 出栈 modelViewMatrix.PopMatrix();

这就是绘制以上图元连接的核心流程内容。


下面我们完成一个案例来实践一下上面点流程:

OpenGL基本图元连接方式.gif

按键控制旋转

还是之前我们处理过的四个按键的监听处理:

GLfloat p = 10.f;
// 键盘按键回调 (上下左右)
void KeyboardCallback( int key, int x, int y ) {
    
    // 1、旋转这个物体
    //
    if (key == GLUT_KEY_UP) {
        objectFrame.RotateWorld(m3dDegToRad(-p), 1.0f, 0.0f, 0.0f);
    }
    if (key == GLUT_KEY_DOWN) {
        objectFrame.RotateWorld(m3dDegToRad(p), 1.0f, 0.0f, 0.0f);
    }
    if (key == GLUT_KEY_LEFT) {
        objectFrame.RotateWorld(m3dDegToRad(-p), 0.0f, 1.0f, 0.0f);
    }
    if (key == GLUT_KEY_RIGHT) {
        objectFrame.RotateWorld(m3dDegToRad(p), 0.0f, 1.0f, 0.0f);
    }
    
    //提交渲染 之后会执行 RenderScene 方法
    glutPostRedisplay();
}

定义我们需要用到的 矩阵和容器以及几何变换管道

//定义一个,着色管理器
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    transformPipeline;

按键控制切换窗口名称

在这里,我们通过 nStep 来区分当前的图元链接方式的切换;

// 空格键回调,切换不同的窗口名称
void KeyPressFunc(unsigned char key, int x, int y) {
    
    if (key == 32) {
        nStep++;
        if (nStep > 6) {
            nStep = 0;
        }
    }
    
    switch (nStep) {
        case 0:
            glutSetWindowTitle("OpenGL 图元连接形式 - GL_POINTS");
            break;
        case 1:
            glutSetWindowTitle("OpenGL 图元连接形式 - GL_LINES");
            break;
        case 2:
            glutSetWindowTitle("OpenGL 图元连接形式 - GL_LINE_STRIP");
            break;
        case 3:
            glutSetWindowTitle("OpenGL 图元连接形式 - GL_LINE_LOOP");
            break;
        case 4:
            glutSetWindowTitle("OpenGL 图元连接形式 - GL_TRIANGLES");
            break;
        case 5:
            glutSetWindowTitle("OpenGL 图元连接形式 - GL_TRIANGLE_STRIP");
            break;
        case 6:
            glutSetWindowTitle("OpenGL 图元连接形式 - GL_TRIANGLE_FAN");
            break;
    }
    
    //提交渲染 之后会执行 RenderScene 方法
    glutPostRedisplay();
}

初始化我们7个例子中的顶点数据

将我们例子中的顶点数据定义并存储起来,在切换的时候,来直接使用,切换至对应的顶点数据;

//为程序作一次性的设置
void SetupRC() {
    
    //设置背影颜色
    glClearColor(0.3f,0.5f,0.8f,1.0f);
    
    //初始化着色管理器
    shaderManager.InitializeStockShaders();
    
    // 平面着色器
    
    //投影变换(矩阵),移动变换(矩阵)
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    //修改观察者的位置
    cameraFrame.MoveForward(-15.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;
    
    float din = 10.0f;
    
    //M3D_2PI 就是2Pi 的意思,就一个圆的意思。 绘制圆形
    for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / din) {
        
        //数组下标自增(每自增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.6f;
    }
    
    
    // 结束扇形 前面一共绘制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, din+2);
    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) {
    
    // 清空缓存区
    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;
    objectFrame.GetMatrix(mObjectFrame);
    //
    modelViewMatrix.MultMatrix(mObjectFrame);
    
    // 模型视图矩阵(观察者矩阵 - 物体变换矩阵) ,投影矩阵
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    
    switch (nStep) {
        case 0:
            //设置点大小
            glPointSize(5.0f);
            pointBatch.Draw();
            glPointSize(1.0f);
            break;
        case 1:
            //设置线宽度
            glLineWidth(5.0f);
            lineBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 2:
            glLineWidth(5.0f);
            lineStripBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 3:
            //设置点大小
            glLineWidth(5.0f);
            lineLoopBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 4:
            DrawWireFramedBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFramedBatch(&triangleFanBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
            
        default:
            break;
    }
    //还原到以前的模型视图矩阵(单位矩阵)
    modelViewMatrix.PopMatrix();
    // 进行缓冲区交换
    glutSwapBuffers();
}