注意:需要在配置好OpenGL的编程环境中运行下列代码,环境配置文章可参考:
OpenGL在Mac项目上的配置
下面的代码,直接放置在main.cpp文件中即可:
#pragma mark - 基本概念 /* GLUT 全名OpenGL Uitility Tools,一种跨平台(Windows、Mac、Linux)的GUI编程框架,用于辅助OpenGL显示GUI界面。 GLEW 一种维护最好的开源OpenGL扩展加载库(自动初始化所有函数指针并包含所需类型定义、常量和枚举值),GLEW被预先封装在了GLTools库中。 GLTools OpenGL快捷开发工具,包含一个用于操作矩阵和向量的3D数学库,一些产生和渲染简单的3D对象的函数,以及对视觉平截头体、相机类和变换矩阵进行管理的函数的充分支持。 OpenGL数据类型 为了OpenGL的移植性,OpenGL定义了各种数据类型,这些数据类型可以映射到所有平台上的特定最小格式。 */ #pragma mark - 代码解析 #include <iostream> #include "GLShaderManager.h" #include "GLTools.h" #include <glut/glut.h> GLBatch triangleBatch; GLShaderManager shaderManager; // 2 为程序做一次性的设置 void SetupRC() { /// glew // 设置背景颜色 glClearColor(0.0f, 0.0f, 1.0f, 1.0f); /// include-GLShaderManager // 初始化着色管理器 shaderManager.InitializeStockShaders(); /// glew // 构建三角形顶点数组,vVert包含3个顶点的(x,y,z)笛卡尔坐标 GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f }; /// include-GLBatch // 开始构建批次,GL_TRIANGLES表示三角形,后面参数是顶点数 triangleBatch.Begin(GL_TRIANGLES, 3); /* 批次增加存储属性 CopyVertexData3f(XYZ顶点数据) CopyNormalDataf(表面法线) CopyColorData4f(RGBA颜色) CopyTexCoordData2f(纹理坐标) */ triangleBatch.CopyVertexData3f(vVerts); // 结束批次属性设置,表明完成数据复制操作,不能再添加新属性 triangleBatch.End(); } // 3 窗口大小改变时接受新的宽度和高度 void ChangeSize(int w, int h) { /// glew // 设置视口,(x, y, w, h),其中x,y代表窗口中视口的左下角坐标 glViewport(0, 0, w, h); } // 4 开始渲染 void RenderScene(void) { /// glew // 清楚一个或一组特定的缓冲区 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); /// glew // 设置一组浮点数表示红色 GLfloat vRed[] = {1.0f, 0.0f, 0.0f, 1.0f}; /// include-GLShaderManager // 传递到存储着色器,即GLT_SHADER_IDENTITY着色器,这个着色器只是使用指定颜色以默认笛卡尔坐标系在屏幕上渲染几何图形 shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed); /// include-GLBatch // 根据批次里的顶点数据利用当前设置的着色器进行画图 triangleBatch.Draw(); /// GLUT /* 当glutInitDisplayMode有传入双缓冲模式时, 将在后台缓冲区进行渲染,然后在结束时交换到前台 为了防止观察者看到可能随着动画帧和动画帧之间闪烁的渲染过程 */ glutSwapBuffers(); } // 1 程序入口 int main(int argc, char *argv[]) { /// GLTools // 设置当前工作目录,针对Mac OS X gltSetWorkingDirectory(argv[0]); /// GLUT // 初始化GLUT库 glutInit(&argc, argv); /// GLUT // 初始化渲染模式,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); /// GLUT // 初始化窗口大小 glutInitWindowSize(800, 720); /// GLUT // 创建窗口 glutCreateWindow("Triangle"); /// GLUT // 注册回调函数,拦截窗口大小变化消息,ChangeSize是自定义方法名 glutReshapeFunc(ChangeSize); /// GLUT // 注册回调函数,拦截窗口渲染消息,RenderScene是自定义方法名 glutDisplayFunc(RenderScene); /// glew // 确保驱动程序的初始化中没有出现任何问题 GLenum err = glewInit(); if (GLEW_OK != err) { /// glew fprintf(stderr, "glew erroe:%s ", glewGetErrorString(err)); return 1; } /// (自定义函数) // 初始化设置 SetupRC(); /// GLUT // 进入调用循环 glutMainLoop(); return 0; } #pragma mark - 启动程序,调用函数的顺序 /* 1 主函数 int main(int argc, char *argv[]) 2 为程序做一次性的设置 void SetupRC() 3 窗口大小改变时接受新的宽度和高度 void ChangeSize(int w, int h) 4 开始渲染 void RenderScene(void) */ #pragma mark - 拖动窗口,调用函数的顺序 /* 1 窗口大小改变时接受新的宽度和高度 void ChangeSize(int w, int h) 2 开始渲染 void RenderScene(void) */ #pragma mark - 全局变量的操作 /* 1 定义批次 GLBatch triangleBatch; 2 属性设置 triangleBatch.Begin(GL_TRIANGLES, 3); triangleBatch.CopyVertexData3f(vVerts); triangleBatch.End(); 3 启用绘制功能 triangleBatch.Draw(); */ /* 1 定义着色管理器 GLShaderManager shaderManager; 2 初始化着色管理器 shaderManager.InitializeStockShaders(); 3 使用着色管理器渲染 shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed); */
代码运行后的效果图如下图所示:
扩展:
如果在其他配置不变的情况下,由目前的三角形改为一个四边形,如何实现呢?
只需要将下列代码更改就行了:
// 2 为程序做一次性的设置 void SetupRC() { /// glew // 设置背景颜色 glClearColor(0.0f, 0.0f, 1.0f, 1.0f); /// include-GLShaderManager // 初始化着色管理器 shaderManager.InitializeStockShaders(); /// glew // 构建四边形顶点数组,vVert包含4个顶点的(x,y,z)笛卡尔坐标 GLfloat vVerts[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.5f, 0.5f, 0.0f, -0.5f, 0.5f, 0.0f, }; /// include-GLBatch // 开始构建批次,GL_TRIANGLE_FAN表示四边形,后面参数是顶点数 triangleBatch.Begin(GL_TRIANGLE_FAN, 4); /* 批次增加存储属性 CopyVertexData3f(XYZ顶点数据) CopyNormalDataf(表面法线) CopyColorData4f(RGBA颜色) CopyTexCoordData2f(纹理坐标) */ // 批次增加存储属性,顶点数据 triangleBatch.CopyVertexData3f(vVerts); // 结束批次属性设置,表明完成数据复制操作,不能再添加新属性 triangleBatch.End(); }
改动的地方标记如下:
效果图如下所示:
这样看来,绘制不同的图形这块,
类似GL_TRIANGLE_FAN这样的参数是关键。
有如下枚举值:
GL_POINTS: 点
GL_LINES: 线
GL_LINE_STRIP: 条带线
GL_LINE_LOOP: 循环线
GL_TRIANGLES: 独立三角形
GL_TRIANGLE_STRIP: 三角形条带
GL_TRIANGLE_FAN: 三角形扇面
接下来,使用各个枚举值看看效果,为了看得清晰,增加了下列代码:
// 开始构建批次,GL_TRIANGLE_FAN表示四边形,后面参数是顶点数 triangleBatch.Begin(GL_TRIANGLE_STRIP, 4); // 点的大小 glPointSize(9.f); // 线条的宽度 glLineWidth(5.f); // 正面和反面都使用线框方式绘制 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
(1)GL_POINTS: 点
triangleBatch.Begin(GL_POINTS, 4);
(2)GL_LINES: 线
triangleBatch.Begin(GL_LINES, 4);
(3)GL_LINE_STRIP: 条带线
triangleBatch.Begin(GL_LINE_STRIP, 4);
(4)GL_LINE_LOOP: 循环线
triangleBatch.Begin(GL_LINE_LOOP, 4);
(5)GL_TRIANGLES: 独立三角形
triangleBatch.Begin(GL_TRIANGLES, 4);
(6)GL_TRIANGLE_STRIP: 三角形条带
triangleBatch.Begin(GL_TRIANGLE_STRIP, 4);
(7)GL_TRIANGLE_FAN: 三角形扇面
triangleBatch.Begin(GL_TRIANGLE_FAN, 4);
对各个枚举值的理解,结合上面的效果图和下面的文字材料,便能轻松理解:
/* 由于OPENGL ES 取消了QUAD 等的支持,所以所有多边形都得需要从三角面片来组合成。这样就需要通过GL_TRIANGLE_FAN和GL_TRIANGLE_STRIP来组织顶点,通过画多个三角面片来组成一个需要得形状。 不同元素决定了定点的不同组织方式: 1、GL_POINTS: 绘制的是点,也就是单个的点,注意调整点的大小gl。 2、GL_LINES: 绘制的是线段,只在每两个点之间画。 3、GL_LINE_LOOP: 绘制出来的点将做到首尾相连,试想,如果不是用的这个而是直接用line的话,那么就要手动连接,即在最后一个点的是很放一个首节点连起来。 4、GL_LINE_STRIP: 连起来的是一整条的,与line不同,连接的是line的话中间有间隔点就不会连起来,而用这个的话会把相邻的点连起来。 5、GL_TRIANGLE: 利用给定3个点,来绘制三角形。每三个顶点绘制一个三角形,若给定数据点个数不是3的整数倍,则自动忽略剩余的点。 6、GL_TRIANGLE_STRIP: GL_TRIANGLE_STRIP则稍微有点复杂。 其规律是: 构建当前三角形的顶点的连接顺序依赖于要和前面已经出现过的2个顶点组成三角形的当前顶点的序号的奇偶性(如果从0开始): 如果当前顶点是奇数: 组成三角形的顶点排列顺序:T = [n-1 n-2 n]. 如果当前顶点是偶数: 组成三角形的顶点排列顺序:T = [n-2 n-1 n]. 以上图为例,第一个三角形,顶点v2序号是2,是偶数,则顶点排列顺序是v0,v1,v2。第二个三角形,顶点v3序号是3,是奇数,则顶点排列顺序是v2,v1,v3,第三个三角形,顶点v4序号是4,是偶数,则顶点排列顺序是v2,v3,v4,以此类推。 这个顺序是为了保证所有的三角形都是按照相同的方向绘制的,使这个三角形串能够正确形成表面的一部分。对于某些操作,维持方向是很重要的,比如剔除。 注意:顶点个数n不能小于3,否则不能绘制任何三角形。 7、GL_TRIANGLE_FAN: 在跳过开始的2个顶点,然后遍历每个顶点,让OpenGL将这些顶点和它前一个,以及数组的第一个顶点一起组成一个三角形 */