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

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

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

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

一、绘制图元点


先上绘制的总结思路图


6、OpenGL初探之OpenGL渲染基础综合实战_第1张图片
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;

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


6、OpenGL初探之OpenGL渲染基础综合实战_第2张图片
图元绘制实战.png

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

你可能感兴趣的:(6、OpenGL初探之OpenGL渲染基础综合实战)