OpenGL基本图元练习

背景

本编文章主要是为了实践图元的应用和坐标系的转换过程

声明的全局变量

// 各种需要的类
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;
GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };

// 跟踪效果步骤
int nStep = 0;

main函数

int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    //申请一个颜色缓存区、深度缓存区、双缓存区、模板缓存区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //设置window 的尺寸
    glutInitWindowSize(800, 600);
    //创建window的名称
    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();
    
    //runloop运行循环
    glutMainLoop();
    return 0;
}

main方法中各函数的详细解释看这里

SetupRC函数

// 灰色的背景
   glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
   shaderManager.InitializeStockShaders();
   glEnable(GL_DEPTH_TEST);
   //设置变换管线以使用两个矩阵堆栈
   transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
   cameraFrame.MoveForward(-15.0f);
   
   /*
    常见函数:
    void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
     参数1:表示使用的图元
     参数2:顶点数
     参数3:纹理坐标(可选)
    
    //负责顶点坐标
    void GLBatch::CopyVertexData3f(GLFloat *vNorms);
    
    //结束,表示已经完成数据复制工作
    void GLBatch::End(void);
    
    
    */
   //定义一些点,三角形形状。
  
   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个顶点(包括圆心)
   //添加闭合的终点
   //课程添加演示:屏蔽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();

glEnable(GL_DEPTH_TEST);

开启深度测试功能,启用之后OpenGL在绘制的时候就会检查,当前像素前面是否有别的像素,如果别的像素挡道了它,那它就不会绘制,也就是说,OpenGL就只绘制最前面的一层。

transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

transformPipeline 转换管道,用来管理模型视图矩阵栈和投影矩阵栈。

cameraFrame.MoveForward(-15.0f);

观察者(相机)所在的位置
MoveForward 沿着Z前进
MoveUp沿着Y移动
MoveRight沿着X移动
其他的可以参考这个

ChangeSize函数

 glViewport(0, 0, w, h);
    //创建投影矩阵,并将它载入投影矩阵堆栈中
    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //调用顶部载入单元矩阵
    modelViewMatrix.LoadIdentity();

viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);是设置透视投影
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix())
把透视投影矩阵压栈道projectionMatrix

KeyPressFunc点击空格键调用的方法

  if(key == 32) //空格键的ASCII码是32
    {
        nStep++; //通过nStep来区分绘制不能的视图
        
        if(nStep > 6)
            nStep = 0;
    }
    //根据nStep 来给窗口设定特定的窗口名
    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();//重新渲染

SpecialKeys函数

if(key == GLUT_KEY_UP)
        //围绕一个指定的X,Y,Z轴旋转。
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_DOWN)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
    
    if(key == GLUT_KEY_LEFT)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
    
    if(key == GLUT_KEY_RIGHT)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
    
    glutPostRedisplay();

objectFrame就是记录模型数据的类

RenderScene

  // Clear the window with current clearing color
    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);
    
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
     --transformPipeline.GetModelViewProjectionMatrix() 获取的
     GetMatrix函数就可以获得矩阵堆栈顶部的值
     参数3:颜色值(黑色)
     */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    
    switch(nStep) {
        case 0:
            //设置点的大小
            glPointSize(4.0f);
            pointBatch.Draw();
            glPointSize(1.0f);
            break;
        case 1:
            //设置线的宽度
            glLineWidth(2.0f);
            lineBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 2:
            glLineWidth(2.0f);
            lineStripBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 3:
            glLineWidth(2.0f);
            lineLoopBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 4:
            DrawWireFramedBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleFanBatch);
            break;
    }
    
    //还原到以前的模型视图矩阵(单位矩阵)
    modelViewMatrix.PopMatrix();
    
    // 进行缓冲区交换
    glutSwapBuffers();

关于modelViewMatrix的压栈和出栈请到文章的最后观看

  M3DMatrix44f mCamera;
   cameraFrame.GetCameraMatrix(mCamera);

cameraFrame是记录观察者变化的对象,通过 cameraFrame.GetCameraMatrix(mCamera);,获取观察者矩阵

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

GLT_SHADER_FLAT为平面着色器

transformPipeline.GetModelViewProjectionMatrix()

transformPipeline管理者两个矩阵栈,一个是投影矩阵栈,一个是模型视图矩阵栈。模型视图矩阵栈管理者模型矩阵和视图矩阵。坐标转化过程,投影矩阵栈管理者投影矩阵。

DrawWireFramedBatch

 /*------------画绿色部分----------------*/
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
          --transformPipeline 变换管线(指定了2个矩阵堆栈)
     参数3:颜色值
    */
    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:颜色值(黑色)
     */
    
    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);

modelViewMatrix的大致执行过程

modelViewMatrixDiagram.png

纠错 modelViewMatrix初始化出来不是空的栈,是和LoadIdentity一样,栈底有一个单元矩阵

demo

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

推荐阅读更多精彩内容