准备知识

OpenGl提供了一些绘图函数。到目前为止我们使用的glDrawArrays绘图函数属于”顺序绘制”。这意味着顶点缓冲区从指定的偏移量开始被扫描,每X(点为1,直线为2等)个顶点构成一个图元。这样使用起来非常方便,缺点是当多个图元共用一个顶点时,这个顶点必须在顶点缓冲区中出现多次。也就是说,这些顶点没有共享的概念。属于”索引绘制”的函数则提供这种共享机制。我们除了一个顶点缓存区外,还有一个索引缓存区用来存放顶点的索引值。索引缓存区的扫描和顶点缓存区类似,以每X个索引对应的顶点构成一个基本图元。共享机制在提高内存使用效率上非常重要,因为计算机中的绝大多数图形对象都是三角形网格构成的,这些三角形有很多都是共用顶点。

我们来看一下顺序绘制:

如果是绘制三角形,GPU会将这些顶点分成以下几组:V0/1/2, V3/4/5, V6/7/8。

接下来看一下索引绘制:



这种情况下,GPU会使用这几组顶点来绘制三角形:V2/0/1, V5/2/4, V6/5/7。

使用索引绘制方式需要创建索引缓冲区,索引缓存区中的数据还要受到顶点缓冲区的限制,绘图调用的API函数也和之前不同。

项目配置

参见前面的教程。

程序代码

清单1.主程序代码tutorial10.cpp

  1. /*
  2. Copyright 2010 Etay Meiri
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. Tutorial 10 - Indexed draws
  14. */
  15. #include "stdafx.h"
  16. #include <string.h>
  17. #include <assert.h>
  18. #include <math.h>
  19. #include <GL/glew.h>
  20. #include <GL/freeglut.h>
  21. #include "ogldev_math_3d.h"
  22. GLuint VBO;
  23. GLuint IBO;
  24. GLuint gWorldLocation;
  25. const char* pVSFileName = "shader.vs";
  26. const char* pFSFileName = "shader.fs";
  27. static void RenderSceneCB()
  28. {
  29. glClear(GL_COLOR_BUFFER_BIT);
  30. static float Scale = 0.0f;
  31. Scale += 0.01f;
  32. Matrix4f World;
  33. World.m[0][0] = cosf(Scale); World.m[0][1] = 0.0f; World.m[0][2] = -sinf(Scale); World.m[0][3] = 0.0f;
  34. World.m[1][0] = 0.0; World.m[1][1] = 1.0f; World.m[1][2] = 0.0f ; World.m[1][3] = 0.0f;
  35. World.m[2][0] = sinf(Scale); World.m[2][1] = 0.0f; World.m[2][2] = cosf(Scale) ; World.m[2][3] = 0.0f;
  36. World.m[3][0] = 0.0f; World.m[3][1] = 0.0f; World.m[3][2] = 0.0f ; World.m[3][3] = 1.0f;
  37. glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &World.m[0][0]);
  38. glEnableVertexAttribArray(0);
  39. glBindBuffer(GL_ARRAY_BUFFER, VBO);
  40. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
  41. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
  42. glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
  43. glDisableVertexAttribArray(0);
  44. glutSwapBuffers();
  45. }
  46. static void InitializeGlutCallbacks()
  47. {
  48. glutDisplayFunc(RenderSceneCB);
  49. glutIdleFunc(RenderSceneCB);
  50. }
  51. static void CreateVertexBuffer()
  52. {
  53. Vector3f Vertices[4];
  54. Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
  55. Vertices[1] = Vector3f(0.0f, -1.0f, 1.0f);
  56. Vertices[2] = Vector3f(1.0f, -1.0f, 0.0f);
  57. Vertices[3] = Vector3f(0.0f, 1.0f, 0.0f);
  58. glGenBuffers(1, &VBO);
  59. glBindBuffer(GL_ARRAY_BUFFER, VBO);
  60. glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
  61. }
  62. static void CreateIndexBuffer()
  63. {
  64. unsigned int Indices[] = { 0, 3, 1,
  65. 1, 3, 2,
  66. 2, 3, 0,
  67. 0, 1, 2 };
  68. glGenBuffers(1, &IBO);
  69. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
  70. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
  71. }
  72. static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
  73. {
  74. GLuint ShaderObj = glCreateShader(ShaderType);
  75. if (ShaderObj == 0) {
  76. fprintf(stderr, "Error creating shader type %d\n", ShaderType);
  77. exit(1);
  78. }
  79. const GLchar* p[1];
  80. p[0] = pShaderText;
  81. GLint Lengths[1];
  82. Lengths[0]= strlen(pShaderText);
  83. glShaderSource(ShaderObj, 1, p, Lengths);
  84. glCompileShader(ShaderObj);
  85. GLint success;
  86. glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
  87. if (!success) {
  88. GLchar InfoLog[1024];
  89. glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
  90. fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
  91. exit(1);
  92. }
  93. glAttachShader(ShaderProgram, ShaderObj);
  94. }
  95. static void CompileShaders()
  96. {
  97. GLuint ShaderProgram = glCreateProgram();
  98. if (ShaderProgram == 0) {
  99. fprintf(stderr, "Error creating shader program\n");
  100. exit(1);
  101. }
  102. string vs, fs;
  103. if (!ReadFile(pVSFileName, vs)) {
  104. exit(1);
  105. };
  106. if (!ReadFile(pFSFileName, fs)) {
  107. exit(1);
  108. };
  109. AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER);
  110. AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER);
  111. GLint Success = 0;
  112. GLchar ErrorLog[1024] = { 0 };
  113. glLinkProgram(ShaderProgram);
  114. glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
  115. if (Success == 0) {
  116. glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
  117. fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
  118. exit(1);
  119. }
  120. glValidateProgram(ShaderProgram);
  121. glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
  122. if (!Success) {
  123. glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
  124. fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
  125. exit(1);
  126. }
  127. glUseProgram(ShaderProgram);
  128. gWorldLocation = glGetUniformLocation(ShaderProgram, "gWorld");
  129. assert(gWorldLocation != 0xFFFFFFFF);
  130. }
  131. int _tmain(int argc, _TCHAR* argv[])
  132. {
  133. glutInit(&argc, argv);
  134. glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
  135. glutInitWindowSize(1024, 768);
  136. glutInitWindowPosition(100, 100);
  137. glutCreateWindow("Tutorial 10");
  138. InitializeGlutCallbacks();
  139. // Must be done after glut is initialized!
  140. GLenum res = glewInit();
  141. if (res != GLEW_OK) {
  142. fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
  143. return 1;
  144. }
  145. printf("GL version: %s\n", glGetString(GL_VERSION));
  146. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  147. CreateVertexBuffer();
  148. CreateIndexBuffer();
  149. CompileShaders();
  150. glutMainLoop();
  151. return 0;
  152. }

代码解读

  1. GLuint IBO;

创建索引缓存句柄,对索引缓冲区的操作通过该句柄完成。

  1. Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
  2. Vertices[1] = Vector3f(0.0f, -1.0f, 1.0f);
  3. Vertices[2] = Vector3f(1.0f, -1.0f, 0.0f);
  4. Vertices[3] = Vector3f(0.0f, 1.0f, 0.0f);

为了演示顶点共享,我们需要更复杂的网格模型。很多教程都使用旋转立方体来演示这个知识点,这需要8个顶点和12个三角形。这里我们使用旋转四面体代替,它只需要4个顶点和4个三角形。

当我们从上面(即沿着Y轴)观看这些顶点时,顶点布局如下图所示:

  1. unsigned int Indices[] = { 0, 3, 1,
  2. 1, 3, 2,
  3. 2, 3, 0,
  4. 0, 1, 2 };

索引缓冲区由一个索引数组组成,每个索引对应顶点缓冲区中的一个顶点。同时观察索引数组和上面的顶点分布图会发现最后一个三角形构成四面体的底面,另外三个三角形作为侧表面(这个例子中四面体并不是对称的)。

  1. glGenBuffers(1, &IBO);
  2. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
  3. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);

上面代码用于创建索引缓冲区并向其中填充数据,不同的是这里使用的参数是GL_ELEMENT_ARRAY_BUFFER,而创建顶点缓冲区使用的参数为GL_ARRAY_BUFFER。

  1. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

和顺序绘制方式一样,在绘制之前我们也要调用glBindBuffer,同时使用GL_ELEMENT_ARRAY_BUFFER作为参数。

  1. glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

这里我们需要使用glDrawElements代替glDrawArrays。第一个参数指定要绘制的图元类型,第二个参数指定用来生成图元的索引数量,第三个参数是每个索引的数据类型,这里可供选择的有GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT。最后一个参数用来告诉GPU开始扫描索引缓冲区的偏移量,这里指定为0,表明从第一个索引开始。这个参数是非常有用的,因为有时候多个模型的顶点索引存放在同一个索引缓冲区。

运行效果

可以看到一个四面体在窗口中旋转。

OpenGL编程逐步深入(十)索引绘制的更多相关文章

  1. NeHe OpenGL教程 第三十五课:播放AVI

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. 用MFC实现OpenGL编程

    一.OpenGL简介 众所周知,OpenGL原先是Silicon Graphics Incorporated(SGI公司)在他们的图形工作站上开发高质量图像的接口.但最近几年它成为一个非常优秀的开放式 ...

  3. Win32 OpenGL 编程( 1 ) Win32 下的 OpenGL 编程必须步骤

    http://blog.csdn.net/vagrxie/article/details/4602961 Win32 OpenGL 编程( 1 ) Win32 下的 OpenGL 编程必须步骤 wri ...

  4. OpenGL编程逐步深入(二)在窗口中显示一个点

    准备知识 在本文中我们将会接触到OpenGl的扩展库GLEW( OpenGL Extension Wrangler Library),GLEW可以帮助我们处理OpenGl中繁琐的扩展管理.一旦初始化后 ...

  5. OpenGL编程指南(第七版)

    OpenGL编程指南(第七版) 转自:http://blog.csdn.net/w540982016044/article/details/21287645 在接触OpenGL中,配置显得相当麻烦,特 ...

  6. C#编程总结(十四)dynamic

    http://www.cnblogs.com/yank/p/4177619.html C#编程总结(十四)dynamic 介绍 Visual C# 2010 引入了一个新类型 dynamic. 该类型 ...

  7. C#编程总结(十)字符转码

    C#编程总结(十)字符转码 为了适应某种特殊需要,字符需要根据规则进行转码,便于传输.展现以及其他操作等. 看看下面的转码,就知道他的用处了. 1.字符串转码 根据原编码格式与目标编码格式,完成转换. ...

  8. [转]VS 2012环境下使用MFC进行OpenGL编程

    我就不黏贴复制了,直接给出原文链接:VS 2012环境下使用MFC进行OpenGL编程 其它好文链接: 1.OpenGL系列教程之十二:OpenGL Windows图形界面应用程序

  9. VS2010/MFC编程入门之五十(图形图像:GDI对象之画笔CPen)

    上一节中鸡啄米讲了CDC类及其屏幕绘图函数,本节的主要内容是GDI对象之画笔CPen. GDI对象 在MFC中,CGdiObject类是GDI对象的基类,通过查阅MSDN我们可以看到,CGdiObje ...

随机推荐

  1. 【BZOJ3270】博物馆 概率DP 高斯消元

    链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网 ...

  2. 王立平--自己定义TitleBar

    效果: 1.自己定义titleBar的布局. <?xml version="1.0" encoding="utf-8"?> <Relative ...

  3. 集群节点Elasticsearch升级

    集群节点Elasticsearch升级 操作流程 1.首先执行Elasticsearch-1.2.2集群的索引数据备份 2.关闭elasticsearch-1.2.2集群的recovery.compr ...

  4. 判断一个整数是否是回文数C++实现 leetcode系列(九)

    判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121 输出: true 示例 2: 输入: -121 输出: false 解释: 从左向 ...

  5. 解决JavaScript浮点数(小数) 运算出现Bug的方法

    解决JS浮点数(小数) 运算出现Bug的方法例如37.2 * 5.5 = 206.08 就直接用JS算了一个结果为: 204.60000000000002 怎么会这样, 两个只有一位小数的数字相乘, ...

  6. ibatis annotations 注解方式返回刚插入的自增长主键ID的值--转

    原文地址:http://www.blogs8.cn/posts/WWpt35l mybatis提供了注解方式编写sql,省去了配置并编写xml mapper文件的麻烦,今天遇到了获取自增长主键返回值的 ...

  7. SSIS故障排除

    1.2015.09.10 SSIS部署到SQL Server上 JOB任务无法执行 说是sa账户没有执行权限 解决办法:1)SQL Server 启动时使用windows管理员账户登录.2)部署的数据 ...

  8. 用js将CheckBox的值存入数据库和将数据库字符串的值转为数组选中CheckBox

    Index @{ ViewBag.Title = "测试"; } <script src="~/Scripts/jquery-1.10.2.js"> ...

  9. 我的Java历程_Java对象类型的转换

    向上转型: 可以将子类对象看作是父类对象叫做“向上转型”,由于向上转型是从一个较为具体的类向较为抽象的类的转换,所以它总是安全的. 例如:可以将正方形.长方形叫做是四边形,但是不能说四边形是正方形或长 ...

  10. URL正则

    现有需求 表单填写域名只能填写 baseURL 或者 baseURL+端口 不带协议 否则为不合法 String url1 = ".com:90"; String url2 = & ...