3.绘制基本图元、环、金字塔与矩阵的压栈出栈

2020-07-10  本文已影响0人  脩脩脩

一、案例

基本的图元装配方式 在之前的文章介绍过。
这个案例是对这些基本的图元进行操作 以及用这些图元完成一些简单的图形。


gif1.gif

流程:

程序流程图

main 函数:程序入口

int main(int argc,char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    //申请一个颜色缓存区,深度缓存区,双缓存区,模板缓存区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //设置windo的名称
    glutCreateWindow("GL_POINTS");
    //注册回调函数
    glutReshapeFunc(changeSize);
    //点击空格 调用的函数
    glutKeyboardFunc(keyPressFunc);
    //上下左右调用的函数
    glutSpecialFunc(SpecialKeys);
    //显示函数
    glutDisplayFunc(RenderScene);
    //判断一下是否能初始化glew库,确保项目能正常使用OpenGL 框架
      GLenum err = glewInit();
      if (GLEW_OK != err) {
          fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
          return 1;
      }
    setupRC();
    glutMainLoop();
    return 0;
}

changeSize():用于设置视口和投影方式

void changeSize(int w, int h)
{
    glViewport(0, 0, w, h);

    //创建投影矩阵
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
    //将它载入投影矩阵堆栈中。
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //初始化模型视图矩阵堆栈,压入单元矩阵
    modelViewMatrix.LoadIdentity();
}

setupRC():初始化图形数据配置(顶点数据,图元链接方式,矩阵观察者,相应的矩阵设置)

void setupRC()
{
    //清屏
    glClearColor(0.9, 0.9, 0.9, 1.0);
    //初始化着色管理器
    shaderManager.InitializeStockShaders();
   //开启深度测试:这个功能在后面讲解。
    glEnable(GL_DEPTH_TEST);
    //将模型视图矩阵和投影矩阵放到变换管道中,能帮助快速进行矩阵相乘
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    //观察者视角往前移动 -15
    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;


    //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个顶点(包括圆心)
      //添加闭合的终点
       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();
}

RenderScene():处理图形渲染,系统和手动触发,每次渲染前需要清除缓存。

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;
//只要使用GetMatrix 函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载, 用来使用GLShaderManager的使用,或者是获取顶部矩阵的顶点副本数据.
    objectFrame.GetMatrix(mObjectFrame);
    //矩阵乘以矩阵.
    modelViewMatrix.MultMatrix(mObjectFrame);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    switch (nStep) {
        case 0:
            glPointSize(4.0f);
            pointBatch.Draw();
            break;
        case 1:
            glLineWidth(4.0f);
            lineBatch.Draw();
            break;
        case 2:
            lineStripBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 3:
            lineLoopBatch.Draw();
            glLineWidth(1.0);
            break;
        case 4:
            DrawWireFramedBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleFanBatch);
            break;
    }
    //还原到以前的模型视图矩阵(单位矩阵)
    modelViewMatrix.PopMatrix();
    //缓冲区交换
    glutSwapBuffers();
}
//处理图形的填充和边框的处理。
void DrawWireFramedBatch(GLBatch *pBatch)
{
    //绿色
    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(GL_FRONT_AND_BACK, GL_LINE);

    glLineWidth(2.0f);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();
    
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
}

SpecialKeys(): 监听键盘上下左右

void SpecialKeys(int key, int x, int y)
{
    if (key == GLUT_KEY_UP) {
        //绕x轴转
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0, 0.0, 0.0);
    }
    if (key == GLUT_KEY_DOWN) {
        //绕x轴转
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0, 0.0, 0.0);
    }
    if (key == GLUT_KEY_LEFT) {
        //绕x轴转
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0, 1.0, 0.0);
    }
    if (key == GLUT_KEY_RIGHT) {
        //绕x轴转
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0, 1.0, 0.0);
    }
    glutPostRedisplay();
}

keyPressFunc():监听空格键

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();
}

二、关于矩阵的堆栈

这个案例相比于之前的案例多出了一个新的知识点:矩阵的压栈与出栈。

2.1:初始化一个单元矩阵,矩阵堆栈中默认是存在一个单元矩阵的,但是也可以自己初始化一个。此时堆栈中存在一个矩阵。
modelViewMatrix.LoadIdentity();
2.2:压栈:把栈顶的矩阵复制一份后再压到栈顶。此时堆栈中有两个单元矩阵
modelViewMatrix.PushMatrix();
2.3:矩阵相乘:
 M3DMatrix44f mCamera;
 cameraFrame.GetCameraMatrix(mCamera);
//矩阵乘以矩阵.
modelViewMatrix.MultMatrix(mCamera);
M3DMatrix44f mObjectFrame;
/*只要使用GetMatrix 函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载, 
用来使用GLShaderManager的使用,或者是获取顶部矩阵的顶点副本数据.*/
objectFrame.GetMatrix(mObjectFrame);
//矩阵乘以矩阵.
modelViewMatrix.MultMatrix(mObjectFrame);

2.4:出栈:将视图模型矩阵pop出栈
//还原到以前的模型视图矩阵(单位矩阵,就是恢复成原来还没有处理矩阵前的状态)

modelViewMatrix.PopMatrix();
图1

图1 by Style_月月

上一篇下一篇

猜你喜欢

热点阅读