OpenGL 基础渲染(图元)

图元

  • GL_POINTS

    每个顶点在屏幕上都是一个单独的点。

  • GL_LINES

    每一对顶点定义了一个线段

  • GL_LINE_STRIP

    一个从第一个顶点依次经过每个后续顶点而绘制的线条

  • GL_LINE_LOOP

    一个从第一个顶点依次经过每个后续顶点而绘制的线条,最后一个顶点和第一个顶点也连接了起来

  • GL_TRIANGLES

    每三个顶点定义了一个新的三角形

  • GL_TRIANGLE_STRIP

    共用一个条带上的顶点的一组三角形

  • GL_TRIANGLE_FAN

    以一个圆点为中心呈扇形排列,共用相邻顶点的一组三角形

  • GL_POLYGON

    将所有点连接起来,并填充其中的颜色。

  • GL_QUADS

    每四个顶点定义了一个新的四边形

  • GL_QUAD_STRIP

    共用带上的顶点的一组四边形

示例代码

点是最简单的图元。默认情况,点的大小为一个像素。

  1. 我们可以通过调用glPointSize改变默认点的大小。

    glPointSize(4.0f);

  2. 设置点的⼤⼩范围和点与点之间的间隔

    GLflfloat sizes[2] = {2.0f,4.0f};

    GLflfloat step = 1.0f;

  3. 获取点⼤⼩范围和最⼩步⻓

    glGetFloatv(GL_POINT_SIZE_RANGE ,sizes);

    glGetFloatv(GL_POINT_GRAULARITY ,&step);

  4. 通过使⽤程序点⼤⼩模式来设置点⼤⼩

    glEnable(GL_PROGRAM_POINT_SIZE);

  5. 这种模式下允许我们通过编程在顶点着⾊器或⼏何着⾊器中设置点⼤⼩。着⾊器内建变量:

    gl_PointSize(内建变量),并且可以在着⾊器源码直接写

    gl_PointSize = 5.0

线

线是在两个顶点之间绘制的。

  1. 设置线段宽度

    glLineWidth(2.5f);

三角形
  • 环绕方式

    //GL_CW 顺时针环绕的多边形为正面
    glFrontFace(GL_CW)
    //GL_CCW 逆时针环绕的多边形为正面
    glFrontFace(GL_CCW)
    
  • 三角形带和三角形环的优点

    • 复用节省代码和数据存储空间

    • 提升运算性能和减少数据传输

批次容器

GBatch,在GLTools库中是一个简单的容器类。

  1. 初始化。参数1:图元,参数2:顶点数,参数3:⼀组或者2组纹理坐标(可选)

    void GLBatch::Begin(GLeunm primitive,GLuint nVerts,GLuint nTexttureUnints = 0);
    
  2. 复制顶点数据(⼀个由3分量x,y,z顶点组成的数组)

    void GLBatch::CopyVerterxData3f(GLflfloat *vVerts);
    
  3. 复制表⾯法线数据

    void GLBatch::CopyNormalDataf(GLflfloat *vNorms);
    
  4. 复制颜色数据

    void GLBatch::CopyColorData4f(GLflfloat *vColors);
    
  5. 复制纹理坐标数据

    void GLBatch::CopyTexCoordData2f(GLFloat *vTextCoords, GLuint uiTextureLayer);
    
  6. 结束数据复制

    void GLBatch::End(void);
    
  7. 绘制图形

    void GLBatch::Draw(void);
    

示例程序

绘制流程图
示例绘制流程图.png
核心流程
setupRC()
  • 基本设置

    背景色、初始化着色器管理器

    glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
    shaderManager.InitializeStockShaders();
    
  • 配置变化管道

    投影矩阵栈&模型视图矩阵栈

    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
  • 配置观察者观察距离

    //z轴移动-15像素点
    cameraFrame.MoveForward(-15.0f);
    //y轴移动
    cameraFrame.MoveUp(-15.0f);
    //x轴移动
    cameraFrame.MoveRight(-15.0f);
    
  • 配置顶点数据,批次容器复制顶点数据

ChangeSize()
  • 设置窗口

     glViewport(0, 0, width, height);
    
  • 设置透视投影

    //参数1:垂直方向上的视场角度
    //参数2:视口纵横比 = w/h
    //参数3:近裁剪面距离
    //参数4:远裁剪面距离
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
    //根据投影方式获得投影矩阵 压入投影矩阵栈中
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //模型视图矩阵堆栈加载单元矩阵,对角线为1,其它为0.
    modelViewMatrix.LoadIdentity();
    
RenderScene
  • 清空缓存区

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
  • 为了不对栈顶数据改变,也可以说绘制之前,压入一个新的单元矩阵,用于对矩阵的计算

    modelViewMatrix.PushMatrix();
    
  • 获取观察者矩阵以及物体矩阵进行相乘

    //camera > 观察者矩阵
    M3DMatrix44f mCamera;
    //构建观察者矩阵
    cameraFrame.GetCameraMatrix(mCamera);
    // 矩阵相乘 > 观察者矩阵 * 单元矩阵
    modelViewMatrix.MultMatrix(mCamera);
    // 物体矩阵
    M3DMatrix44f mObject;
    //构建物体矩阵
    objectFrame.GetMatrix(mObject);
    //矩阵相乘 > 物体矩阵 * 观察者矩阵 =  模型视图矩阵
    modelViewMatrix.MultMatrix(mObject);
    
  • 根据最终的模型视图投影矩阵(mvp)进行平面着色器配置

    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
    
  • 最终绘制

示例完整代码
//演示了OpenGL背面剔除,深度测试,和多边形模式
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"

#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

GLFrame objectFrame;
//观察者
GLFrame cameraFrame;
GLShaderManager shaderManager;
GLBatch pointBatch;
GLBatch lineBatch;
GLBatch linestripBatch;
GLBatch lineLoopBatch;

GLBatch triangleBatch;
GLBatch triangleStripBatch;
GLBatch triangleFanBatch;
//变换管道
GLGeometryTransform transformPipeline;
GLFrustum viewFrustum;
//矩阵
GLMatrixStack projectionMatrix;

GLMatrixStack modelViewMatrix;
int nStep = 0;

void DrawWireFramedBatch(GLBatch* pBatch)
{
    /*------------画绿色部分----------------*/
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
     --transformPipeline 变换管线(指定了2个矩阵堆栈)
     参数3:颜色值
     */
    GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();

    /*-----------边框部分-------------------*/
    /*
     glEnable(GLenum mode); 用于启用各种功能。功能由参数决定
     参数列表:http://blog.csdn.net/augusdi/article/details/23747081
     注意:glEnable() 不能写在glBegin() 和 glEnd()中间
     GL_POLYGON_OFFSET_LINE  根据函数glPolygonOffset的设置,启用线的深度偏移
     GL_LINE_SMOOTH          执行后,过虑线点的锯齿
     GL_BLEND                启用颜色混合。例如实现半透明效果
     GL_DEPTH_TEST           启用深度测试 根据坐标的远近自动隐藏被遮住的图形(材料

     glDisable(GLenum mode); 用于关闭指定的功能 功能由参数决定

     */

    //画黑色边框
    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);

    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
     --transformPipeline.GetModelViewProjectionMatrix() 获取的
     GetMatrix函数就可以获得矩阵堆栈顶部的值
     参数3:颜色值(黑色)
     */
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.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 ChangeSize(int width,int height){
    glViewport(0, 0, width, height);
    //设置透视投影
    viewFrustum.SetPerspective(35.5, float(width) / float(height), 1.0, 500);
    //获得投影矩阵 存在矩阵栈中
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //加载单元矩阵,对角线为1,其它为0.
    modelViewMatrix.LoadIdentity();
}

void RenderScene(void){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    //压栈一个新的单元矩阵
    modelViewMatrix.PushMatrix();

    //camera > 观察者矩阵
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    // 矩阵相乘 > 观察者矩阵 * 单元矩阵
    modelViewMatrix.MultMatrix(mCamera);

    // 物体矩阵
    M3DMatrix44f mObject;
    objectFrame.GetMatrix(mObject);
    //矩阵相乘 > 模型视图矩阵 * 观察者矩阵
    modelViewMatrix.MultMatrix(mObject);

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

    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);

    switch (nStep) {
        case 0:
            glPointSize(10.0f);
            pointBatch.Draw();
            glPointSize(1.0f);
            break;
        case 1:
            glLineWidth(10.f);
            lineBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 2:
            glLineWidth(10.f);
            linestripBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 3:
            glLineWidth(10.f);
            lineLoopBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 4:
            DrawWireFramedBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleFanBatch);
        default:

            break;
    }
    modelViewMatrix.PopMatrix();
    glutSwapBuffers();

}

void SetupRC(){
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
    shaderManager.InitializeStockShaders();

    //投影变换矩阵 & 移动变换矩阵  > 变换管道 > 快速去矩阵相乘 > 遍历
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

    //向远移动15;
    cameraFrame.MoveForward(-15.0f);
    //y轴移动
    //cameraFrame.MoveUp(-15.0f);
    //x轴移动
    //cameraFrame.MoveRight(-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

    };
    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 SpecialKeys(int key, int x, int y){
    if (key == GLUT_KEY_UP) {
        //世界坐标系旋转
        objectFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0, 0.0);
    }
    if(key == GLUT_KEY_DOWN){
        objectFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0, 0.0);
    }

    if (key == GLUT_KEY_LEFT) {
        //世界坐标系旋转
        objectFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0, 0.0);
    }

    if (key == GLUT_KEY_RIGHT) {
        //世界坐标系旋转
        objectFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0, 0.0);
    }
    //提交重新渲染
    glutPostRedisplay();
}

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_TRIANGLE_STRIP");
            break;
        case 6:
            glutSetWindowTitle("GL_TRIANGLE_FAN");
            break;

        default:
            break;
    }
    //触发重新渲染
    glutPostRedisplay();
}

int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(800, 600);
    glutCreateWindow("GL_POINTS");
    glutReshapeFunc(ChangeSize);
    //空格回调函数
    glutKeyboardFunc(KeyPressFunc);
    glutSpecialFunc(SpecialKeys);
    glutDisplayFunc(RenderScene);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }

    SetupRC();

    glutMainLoop();
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容