6、OpenGL初探之OpenGL渲染基础综合实战

OpenGL初识之OpenGL渲染基础概念传送门https://www.jianshu.com/p/d26879a736af

接着上一节我们学习了OpenGL渲染基础,现在我们来综合使用一下,练习一下绘制OpenGL图元并组合图元:

一、绘制图元点
二、绘制图元线段/连续线段/线环
三、绘制金字塔
四、图元渲染三角形带/三角形扇

一、绘制图元点


先上绘制的总结思路图


OpenGL渲染业务介绍.png

开始准备环境和对应的函数(详细介绍请移步我之前的文章,传送门:基本函数解释https://www.jianshu.com/p/b98be0925051)
这里主要说明有区别的地方

1、设置全局变量

GLShaderManager shaderManager;//着色器管理
GLMatrixStack modelViewMatrix;//模型视图矩阵堆栈
GLMatrixStack projectViewMatrix;//投影矩阵堆栈
GLFrame cameraFrame;//观察者
GLFrame objectFrame;

GLFrustum viewFrustum;//投影矩阵

//批次类(7种不同的图元,对应7种容器对象)
GLBatch pointBatch;//点
GLBatch lineBatch;//线
GLBatch lineStripBatch;//连线
GLBatch lineLoopBatch;//线环
GLBatch triangleBatch;//三级形
GLBatch triangleStripBatch;//三角形带
GLBatch triangleFanBatch;//三角形扇

//几何变换管道
GLGeometryTransform transformPipeline;
//颜色RGBA
GLfloat vGreen[] = {0.0f, 1.0f, 0.0f, 1.0f};
GLfloat vYellow[] = {1.0f,1.0f, 0.0f, 1.0f};
//设置按空格键来切换不同图元展示
//记录按了几次空格
int nStep = 0;

2、设置main函数

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("Triangle");
    
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);
    
    //注册点击空格会调用的函数
    glutKeyboardFunc(KeyPressFunc);
    
    GLenum err = glewInit();
    if(GLEW_OK != err) {
        fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));
        return 1;
    }
    
    SetupRC();
    glutMainLoop();
    return 0;
}

3、设置SetupRC函数,注释我都写在里面了

    //1、设置背景颜色
    glClearColor(0.7f, 0.7f, 0.7f,1.0);
    //2、初始化着色器管理器
    shaderManager.InitializeStockShaders();
    //3、开启深度测试
    glEnable(GL_DEPTH_TEST);
    //4、设置变换管道
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectViewMatrix);
    //5、设置观察者位置,便于观察效果
    cameraFrame.MoveForward(-15);
    
    //6、设置一些点
    //创建一维的六个点的数组
    GLfloat vCoast[18] = {
        3,3,0,
        -3,3,0,
        3,0,0,
        -3,0,0,
        3,-3,0,
        -3,-3,0
    };

    //设置图元渲染方式,点
    pointBatch.Begin(GL_POINTS, 6);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();

4、设置ChangeSize函数,需要注意的是,这个函数的调用时机,就是窗口已更改大小,或刚刚创建,无论是那种情况,我们都需要使用窗口维度设置视口和设置投影矩阵

    //1、设置视口
    glViewport(0, 0, w, h);
    //2、投影矩阵: 需要设置纵横比
    viewFrustum.SetPerspective(35, floorf(w) / floorf(h), 1.0, 500);
    //3、设置投影矩阵,加载投影矩阵
projectViewMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //4、模型视图视图矩阵堆栈,加载单元矩阵
    modelViewMatrix.LoadIdentity();

5、设置渲染RenderScence函数

    //1、清除缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //2、模型视图矩阵---压栈
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.MultMatrix(mCamera);//矩阵相乘,得到变换后的新矩阵
    
    M3DMatrix44f mObjectFrame;
    objectFrame.GetMatrix(mObjectFrame);    
    modelViewMatrix.MultMatrix(mObjectFrame);//矩阵相乘,得到变换后的新矩阵

    //3、挑选平面着色器渲染
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    
    
    //4、容器类提交绘制
    switch (nStep) {
        case 0:
            //设置点的大小
            glPointSize(20.0f);
            pointBatch.Draw();
            glPointSize(1.0f);
            break;
            
                default:
            break;
    }
    
    //5、模型视图矩阵---出栈,还原到以前的模型视图矩阵
    modelViewMatrix.PopMatrix();
    //6、交换缓冲区
    glutSwapBuffers();

6、设置特殊键位函数

void SpecialKeys (int key , int x, int y){
    //点击键盘上的上下左右键
    if (key == GLUT_KEY_UP) {
        //往上移动,围绕X轴,是顺时针方向,由于逆时针为正,所以是-5.0
        //m3dDegToRad() 角度转弧度函数
        objectFrame.RotateWorld(m3dDegToRad(-5.0), 1.0, 0, 0);//其中的1.0表示YES,0.0表示NO
    }else if (key == GLUT_KEY_DOWN) {
        //往下移动,围绕X轴,是逆时针方向,由于逆时针为正,所以是5.0
        objectFrame.RotateWorld(m3dDegToRad(5.0), 1.0, 0, 0);
    }else if (key == GLUT_KEY_LEFT) {
        //往左移动,围绕Y轴,是顺时针方向,由于逆时针为正,所以是-5.0
        objectFrame.RotateWorld(m3dDegToRad(-5.0), 0.0, 1.0, 0);
    }else if (key == GLUT_KEY_RIGHT) {
        //往右移动,围绕Y轴,是逆时针方向,由于逆时针为正,所以是5.0
        objectFrame.RotateWorld(m3dDegToRad(5.0), 0.0, 1.0, 0);
    }
   
    //发生变化需要重新渲染
    glutPostRedisplay();
}

7、设置空格切换函数KeyPressFunc(这个主要是用来切换不同图元)

void KeyPressFunc(unsigned char key, int x, int y){
    //不断点击空格修改nStep记录步数
    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();
}


二、绘制图元线段/连续线段/线环


绘制图元点的写完后,设置绘制图元线段/连续线段/线环 ,只需要配置SetupRC里面设置对应图元,然后再RenderScence修改图元绘制就行了,对应修改的位置如下

1、修改SetupRC部分代码

    //1/设置图元渲染方式,点
    pointBatch.Begin(GL_POINTS, 6);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();
    
    //2/设置图元渲染方式,线段
    lineBatch.Begin(GL_LINES, 6);
    lineBatch.CopyVertexData3f(vCoast);
    lineBatch.End();
    
    //3/设置图元渲染方式,连续线段
    lineStripBatch.Begin(GL_LINE_STRIP, 6);
    lineStripBatch.CopyVertexData3f(vCoast);
    lineStripBatch.End();
    
    //4/设置图元渲染方式,线环
    lineLoopBatch.Begin(GL_LINE_LOOP, 6);
    lineLoopBatch.CopyVertexData3f(vCoast);
    lineLoopBatch.End();

2、修改RenderScence对应部分代码

switch (nStep) {
        case 0:
            //设置点的大小
            glPointSize(20.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;
            
        default:
            break;
    }


三、渲染图元金字塔


接下来我们绘制金字塔,金字塔是由一个底面和四个侧面组成,金字塔没有底面的话,其实侧面是由4个三角形组成的,如果有底面的话,底面是两个三角形组成的,和上面不同图元绘制的方式类似,只需要修改SetupRC和RenderScene对应的代码就行了

1、SetupRC函数中设置金字塔四个顶点坐标数据和图元装配方式

    //设置金字塔的顶点数据
    //通过三角形创建金字塔
    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();


2、RenderScence绘制金字塔

    case 4:
        triangleBatch.Draw();
        break;

四、图元渲染三角形扇/图元渲染三角形带


1、绘制六边形三角形扇
修改SetupRC,设置顶点数据,该顶点的坐标数据是通过半径和角度求来的,下面这段代码的循环,就是为了求六边形七个顶点的位置坐标,六个顶点和一个中心点

// 三角形扇形--六边形
    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个顶点(包括圆心)
    //添加闭合的终点
    //如果屏蔽173-177行代码,并把绘制节点改为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();

2、绘制六边形三角形扇,修改RenderScence

    case 6:   
        triangleFanBatch.Draw();
        break;

3、绘制三角形带,配置顶点坐标以及图元装配方式

//三角形条带,一个小环或圆柱段
    //顶点下标
    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++;
    }
    
    // 关闭循环
    //结束循环,在循环位置生成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();

4、绘制三角形带,修改RenderScence

    case 5:
        triangleStripBatch.Draw();
        break;

运行得到所有的样式,如下图:


图元绘制实战.png

溪浣双鲤的技术摸爬滚打之路

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

推荐阅读更多精彩内容