准备知识

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

/*

    Copyright 2010 Etay Meiri

    This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. Tutorial 10 - Indexed draws
*/
#include "stdafx.h"
#include <string.h>
#include <assert.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include "ogldev_math_3d.h"
GLuint VBO;
GLuint IBO;
GLuint gWorldLocation; const char* pVSFileName = "shader.vs";
const char* pFSFileName = "shader.fs"; static void RenderSceneCB()
{
glClear(GL_COLOR_BUFFER_BIT); static float Scale = 0.0f; Scale += 0.01f; Matrix4f World; World.m[0][0] = cosf(Scale); World.m[0][1] = 0.0f; World.m[0][2] = -sinf(Scale); World.m[0][3] = 0.0f;
World.m[1][0] = 0.0; World.m[1][1] = 1.0f; World.m[1][2] = 0.0f ; World.m[1][3] = 0.0f;
World.m[2][0] = sinf(Scale); World.m[2][1] = 0.0f; World.m[2][2] = cosf(Scale) ; World.m[2][3] = 0.0f;
World.m[3][0] = 0.0f; World.m[3][1] = 0.0f; World.m[3][2] = 0.0f ; World.m[3][3] = 1.0f; glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &World.m[0][0]); glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0); glDisableVertexAttribArray(0); glutSwapBuffers();
} static void InitializeGlutCallbacks()
{
glutDisplayFunc(RenderSceneCB);
glutIdleFunc(RenderSceneCB);
} static void CreateVertexBuffer()
{
Vector3f Vertices[4];
Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
Vertices[1] = Vector3f(0.0f, -1.0f, 1.0f);
Vertices[2] = Vector3f(1.0f, -1.0f, 0.0f);
Vertices[3] = Vector3f(0.0f, 1.0f, 0.0f); glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
} static void CreateIndexBuffer()
{
unsigned int Indices[] = { 0, 3, 1,
1, 3, 2,
2, 3, 0,
0, 1, 2 }; glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
} static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
{
GLuint ShaderObj = glCreateShader(ShaderType); if (ShaderObj == 0) {
fprintf(stderr, "Error creating shader type %d\n", ShaderType);
exit(1);
} const GLchar* p[1];
p[0] = pShaderText;
GLint Lengths[1];
Lengths[0]= strlen(pShaderText);
glShaderSource(ShaderObj, 1, p, Lengths);
glCompileShader(ShaderObj);
GLint success;
glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
if (!success) {
GLchar InfoLog[1024];
glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
exit(1);
} glAttachShader(ShaderProgram, ShaderObj);
} static void CompileShaders()
{
GLuint ShaderProgram = glCreateProgram(); if (ShaderProgram == 0) {
fprintf(stderr, "Error creating shader program\n");
exit(1);
} string vs, fs; if (!ReadFile(pVSFileName, vs)) {
exit(1);
}; if (!ReadFile(pFSFileName, fs)) {
exit(1);
}; AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER);
AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER); GLint Success = 0;
GLchar ErrorLog[1024] = { 0 }; glLinkProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
if (Success == 0) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
exit(1);
} glValidateProgram(ShaderProgram);
glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
if (!Success) {
glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
exit(1);
} glUseProgram(ShaderProgram); gWorldLocation = glGetUniformLocation(ShaderProgram, "gWorld");
assert(gWorldLocation != 0xFFFFFFFF);
}
int _tmain(int argc, _TCHAR* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
glutInitWindowSize(1024, 768);
glutInitWindowPosition(100, 100);
glutCreateWindow("Tutorial 10"); InitializeGlutCallbacks(); // Must be done after glut is initialized!
GLenum res = glewInit();
if (res != GLEW_OK) {
fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
return 1;
} printf("GL version: %s\n", glGetString(GL_VERSION)); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); CreateVertexBuffer();
CreateIndexBuffer(); CompileShaders(); glutMainLoop(); return 0;
}

代码解读

GLuint IBO;

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

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

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

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

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

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

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

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

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

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

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. android drawable资源调用使用心得

    1. 调用顺序 android 调用应用图片资源时,会优先选择当前手机屏幕dpi对应的的文件夹(如drawable-ldpi, drawable-mdpi, drawable-hdpi, drawab ...

  2. 高斯滤波及高斯卷积核C++实现

    高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,在图像处理的降噪.平滑中应用较多,特别是对抑制或消除服从正态分布的噪声非常有效. 高斯滤波的过程其实就是对整幅图像进行加权平均操作的过程.滤波后图像上每 ...

  3. AngularJS1 学习笔记1

    什么是 AngularJS? AngularJS 使得开发现代的单一页面应用程序(SPAs:Single Page Applications)变得更加容易. AngularJS 把应用程序数据绑定到 ...

  4. 百度编辑器UEditor修改成支持物理路径

    一.前言 我虽然工作了2年.有快1年没有做后台的开发了.最近要写个新项目用到富文本编辑器,然后选择用了百度的UEditor.在使用过程中感觉有些不太好.然后就自己手动改一下源码,写得不好请见谅.这只是 ...

  5. Android 对话框黑色边框的解决

    代码解决 : Dialog dialog = new Dialog(this); Window win = dialog.getWindow(); win.setBackgroundDrawableR ...

  6. 阿里巴巴和印度最大移动支付和商务平台Paytm

    2015年9月29日,阿里巴巴和印度最大移动支付和商务平台Paytm发布联合声明,宣布阿里巴巴集团及其旗下金融子公司蚂蚁金服将向Paytm注入新资金.阿里称这是一项“战略性的”投资. 蚂蚁金服已经在2 ...

  7. c#学习0216

    2017-03-02 out  关键字指定所给的参数为一个输出参数 该参数的值将返回给函数调用中使用的变量 注意事项 1未赋值的变量用作ref参数是非法的,但是可以把未赋值的变量用作out参数 2 在 ...

  8. FCC高级编程篇之Validate US Telephone Numbers

    Validate US Telephone Numbers Return true if the passed string is a valid US phone number. The user ...

  9. wordpress 后台登录增加访问效验,优化退出效果

    之前记录了 wordpress 后台登录增加访问效验, 记录了增加后台访问地址被直接访问的困难性的修改步骤. 还有一个地方需要补充一下,就是退出. 退出的时候不做调整会直接跳到首页,这样体验很不好. ...

  10. python中*号用法总结

    python 中有很多地方用到星号,有时候会想知道这个*是干嘛用的,总结如下,有不当之处,还望不吝指出,谢谢.1.乘法: 在很多时候是用作乘法的,例如: In [90]: 2*7 Out[90]: 1 ...