当前位置: 首页 > news >正文

songho——OpenGL的顶点缓冲对象

http://www.songho.ca/opengl/gl_vbo.html
另外参考网址:http://dev.gameres.com/Program/Visual/3D/vbo.htm

GL_ARB_vertex_buffer_object扩展的目的是提高OpenGL的性能,通过利用顶点数组和显示列表。避免了实现的缺点。顶点缓冲对象VBO允许顶点数据存储在高性能的图形显卡的内存中,提高数据的传输。如果缓冲对象用来存储像素数据,它要调用 Pixel Buffer Object (PBO).

使用顶点数组,可以减少函数的调用,重复利用共享的顶点。但是,使用顶点数组的弊端是,顶点数组函数是在客户端状态,数组中的数据必须在每次需要使用的时候重新在服务端设置。

另外一个方面,显示列表是服务器端的函数,它不受数据传输的影响。但是,一旦显示列表被编译,显示列表中的数据就不能被修改。

顶点缓冲对象为顶点属性创建缓冲对象,它是在服务器端,在高性能的内存中。提供相同的访问函数来引用数组,这个被用于顶点数组中,如glVertexPointer,glNormalPointer,glTexCoordPointer等等。

在顶点缓冲对象中内存管理将会把缓冲对象到最合适的内存空间,这个基于用户的提示:目标和使用方式。因此,内存管理者可用优化缓冲,通过在3中内存中权衡:系统、AGP和视频内存。

和显示列表不同,在顶点缓冲中的数据,可用被读,可用通过映射用户的内存来更新。

VBO的另外一个重要的优点,多个用户共享缓冲对象,像显示列表和纹理。由于VBO是服务端的,多个客户端可用通过对应的标识符来访问同一个缓冲。

创建顶点缓冲对象:
创建顶点缓冲对象,需要三步:
1、产生新的缓冲对象glGenBuffers
2、绑定缓冲对象glBindBuffer
3、拷贝数据到缓冲对象glBufferData

glGenBuffers
glGenBuffers创建缓冲对象,返回的是缓冲对象的id。它需要两个参数:第一个是缓冲对象的个数,第二个参数是GLuint的数组,包含一个ID或者多个ID。

void glGenBuffers(GLsizei n, GLuint* ids)

glBindBuffer()
一旦缓冲对象被创建,我们需要缓冲对象的对应的ID,glBindBuffer()有两个参数:目标和ID:

void glBindBuffer(GLenum target, GLuint id)

目标是一个提示告诉VBO,这个缓冲对象将会用来存储顶点数组数据还是索引数组数据:GL_ARRAY_BUFFER 或者
GL_ELEMENT_ARRAY_BUFFER。任何一个顶点属性,比如顶点坐标,纹理坐标,法线或者颜色分量数组都应该使用GL_ARRAY_BUFFER。索引数组是用在这个函数glDraw[Range]Elements(),它将绑定的是GL_ELEMENT_ARRAY_BUFFER。注意这个目标标记帮助VBO来最有效率决定缓冲对象的位置,比如,有些系统更偏向于在AGP或者是系统内存中使用索引,而在视频内存中使用顶点数据。

一旦,glBindBuffer()第一次被调用,VBO会使用零来初始化缓冲,设置VBO最初的状态,比如使用和访问属性。

glBufferData()
你可以在缓冲被初始化之后,使用glBufferData()来拷贝数据到缓冲对象。

void glBufferData(GLenum target, GLsizei size, const void* data, GLenum usage)

第一个参数,为目标,其值可以为GL_ARRAY_BUFFER 或则GL_ELEMENT_ARRAY_BUFFER。第二个参数是将要传送的数据的字节个数。第三个参数是指向源数据的指针。如果数据是NULL指针,那么VBO将保留给定的数据大小内存空间。最后一个参数,使用标记参数,用来标记VBO的使用缓冲对象的方式,它可以是static、dynamic或者是istream、read、copy或者是draw。

VBO定义8个枚举值,用来标记如何使用缓冲数据。

GL_STATIC_DRAW
GL_STATIC_READ
GL_STATIC_COPY
GL_DYNAMIC_DRAW
GL_DYNAMIC_READ
GL_DYNAMIC_COPY
GL_STREAM_DRAW
GL_STREAM_READ
GL_STREAM_COPY

"static"意味着VBO中的数据不会被改变(定义一次使用多次),"dynamic"意味着数据将会改变的很频繁(指定一次,重复使用),"stream"意味着数据会在每帧改变(指定一次使用一次)。"draw"意味着数据将会被送到GPU以备绘制(应用到GL),"read"意味着数据将会被客户端应用读取(GL到应用),"copy"意味着数据将会被用来绘制和读取(GL
到GL)。

注意只有绘制对VBO有用,拷贝和读取只对每像素/每帧的缓冲对象有意义(PBO或者FBO)。

VBO内存管理将会根据这些应用标记,选择最好的内存来存放缓冲对象,比如GL_STATIC_DRAW 和GL_STREAM_DRAW 会用来视频内存,GL_DYNAMIC_DRAW 会用AGP内存,任何_READ_相关的内存会在系统或者AGP内存,因为这些数据将会很容易访问。

glBufferSubData()

void glBufferSubData(GLenum target, GLint offset, GLsizei size, void* data)

和glBufferData()类似,glBufferSubData()被用来拷贝数据到VBO,但是它只替换一部分数据到已存在的缓冲,从给定的开始位置。(缓冲的总大小需要在使用glBufferSubData之前使用glBufferData设置)。

glDeleteBuffers()

void glDeleteBuffers(GLsizei n, const GLuint* ids)

你可以使用void glDeleteBuffers来删除一个VBO或者VBOs。当一个缓冲对象被删除之后,它的内容将会被丢失。

下面代码是对于每个顶点坐标,来创建一个VBO的例子。注意你可以在你的应用拷贝数据到VBO之后,删除顶点数组的内存分配。


GLuint vboId;                              // ID of VBO
GLfloat* vertices = new GLfloat[vCount*3]; // create vertex array
...

// generate a new VBO and get the associated ID
glGenBuffers(1, &vboId);

// bind VBO in order to use
glBindBuffer(GL_ARRAY_BUFFER, vboId);

// upload data to VBO
glBufferData(GL_ARRAY_BUFFER, dataSize, vertices, GL_STATIC_DRAW);

// it is safe to delete after copying data to VBO
delete [] vertices;
...

// delete VBO when program terminated
glDeleteBuffers(1, &vboId);

绘制VBO
由于VBO在存在的顶点数组实现之上,渲染VBO和使用顶点数组一样。唯一的不同是,指向顶点数组的指针是当前绑定的缓冲对象的开始位置。因此除了glBindBuffer()之外,没有额外的函数需要用来绘制一个VBO。

绑定缓冲对象用0,就会关闭VBO的操作。这是个很好的主意用来关闭VBO,在使用之后。所以正常的顶点数组操作用决定的指针将会被激活。

// bind VBOs for vertex array and index array
glBindBuffer(GL_ARRAY_BUFFER, vboId1);            // for vertex attributes
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId2);    // for indices

glEnableClientState(GL_VERTEX_ARRAY);             // activate vertex position array
glEnableClientState(GL_NORMAL_ARRAY);             // activate vertex normal array
glEnableClientState(GL_TEXTURE_COORD_ARRAY);      // activate texture coord array

// do same as vertex array except pointer
glVertexPointer(3, GL_FLOAT, stride, offset1);    // last param is offset, not ptr
glNormalPointer(GL_FLOAT, stride, offset2);
glTexCoordPointer(2, GL_FLOAT, stride, offset3);

// draw 6 faces using offset of index array
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);

glDisableClientState(GL_VERTEX_ARRAY);            // deactivate vertex position array
glDisableClientState(GL_NORMAL_ARRAY);            // deactivate vertex normal array
glDisableClientState(GL_TEXTURE_COORD_ARRAY);     // deactivate vertex tex coord array

// bind with 0, so, switch back to normal pointer operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

OpenGL 版本2.0将会增加glVertexAttribPointer()、glEnableVertexAttribArray()和glDisableVertexAttribArray()函数用来指定通用的顶点属性。比如,你可以指定所有的顶点属性:位置、法线、颜色、纹理坐标,使用单个API:

// bind VBOs for vertex array and index array
glBindBuffer(GL_ARRAY_BUFFER, vboId1);            // for vertex coordinates
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId2);    // for indices

glEnableVertexAttribArray(attribVertex);          // activate vertex position array
glEnableVertexAttribArray(attribNormal);          // activate vertex normal array
glEnableVertexAttribArray(attribTexCoord);        // activate texture coords array

// set vertex arrays with generic API
glVertexAttribPointer(attribVertex, 3, GL_FLOAT, false, stride, offset1);
glVertexAttribPointer(attribNormal, 3, GL_FLOAT, false, stride, offset2);
glVertexAttribPointer(attribTexCoord, 2, GL_FLOAT, false, stride, offset3);

// draw 6 faces using offset of index array
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);

glDisableVertexAttribArray(attribVertex);         // deactivate vertex position
glDisableVertexAttribArray(attribNormal);         // deactivate vertex normal
glDisableVertexAttribArray(attribTexCoord);       // deactivate texture coords

// bind with 0, so, switch back to normal pointer operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

更新VBO
VBO比显示列表的优点是,客户端可以读取和修改缓冲对象数据,但是显示列表不能。更新VBO最简单的方法是,拷贝新的数据到指定的VBO,使用glBufferData()或者glBufferSubData()。对于这个情况,你的应用该有一个有用的顶点数组。这就意味着你需要有两份顶点数据,一个是应用,另外一个是VBO。

另外一个修改缓冲对象的方法是,应用缓冲对象到你的客户端内存,客户端可以更新数据,使用指针指向映射对象。下面描述如何映射VBO到客户端内存,如何访问映射数据。

glMapBuffer()
VBO提供glMapBuffer()为了映射缓冲对象到客户端内存。

void* glMapBuffer(GLenum target, GLenum access)

如果OpenGL能用来映射缓冲对象到客户端地址空间,glMapBuffer()返回指针到缓冲。否则返回NULL。
第一个参数,目标,早先提到过,在glBindBuffer()中。第二个参数,访问标记指定对映射数据做什么,读、写后者两者都有。

GL_READ_ONLY
GL_WRITE_ONLY
GL_READ_WRITE

注意glMapBuffer阴影同步问题。如果GPU还是工作在缓冲对象,glMapBuffer将不会返回直到GPU在对应的缓冲对象上完成工作。

为了避免等待,你可以首先调用glBufferData()使用NULL指针,然后调用glMapBuffer()。在这种情况下,之前的数据会被丢弃,glMapBuffer()返回新开辟的内存指针立刻,甚至是GPU依然和之前的数据在工作,也会及时返回。

但是,这个方法是仅仅是在我们想要更新整个数据集合时有用,因为你丢弃了之前的数据。如果你想要改变一部分数据或者是读取数据,你最好不要释放之前的数据。

glUnmapBuffer()

GLboolean glUnmapBuffer(GLenum target)

在修改了VBO的数据之后,需要从客户端内存中解映射缓冲对象。glUmapBuffer()返回GL_TRUE,如果成功。当返回的是GL_FALSE,VBO的内容在被映射的时候被破坏。崩溃的结果来源于屏幕分辨率的改变或者是窗口系统特定的事件。这种情况下,数据必须重新提交。

这里有一个段代码是使用映射方法修改VBO。


// bind then map the VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
float* ptr = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);

// if the pointer is valid(mapped), update VBO
if(ptr)
{
    updateMyVBO(ptr, ...);             // modify buffer data
    glUnmapBuffer(GL_ARRAY_BUFFER);    // unmap it after use
}

// you can draw the updated VBO
...

相关文章:

  • OGL(教程35)——延迟渲染1——代码结构梳理
  • 安装MySQL_安装Navicat_启动数据库服务
  • android studio查看android手机日志
  • Python02(Linux命令)
  • luaforwindws安装与调用方式
  • vs添加链接文件
  • httpclient
  • 继成ScriptableObject的静态类对象竟然没有销毁
  • 国内镜像站
  • unity地形
  • Excel催化剂开源第5波-任务窗格在OFFICE2013中新建文档不能同步显示问题解决
  • shader 顶点属性——颜色
  • unity shader projector使用
  • python os walk 讲的太清楚了 有图片
  • 日志logging
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 2017前端实习生面试总结
  • Angular 响应式表单之下拉框
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • ES6系列(二)变量的解构赋值
  • git 常用命令
  • Rancher-k8s加速安装文档
  • ReactNative开发常用的三方模块
  • React中的“虫洞”——Context
  • vue:响应原理
  • 爱情 北京女病人
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 浮动相关
  • 简析gRPC client 连接管理
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 听说你叫Java(二)–Servlet请求
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 在Unity中实现一个简单的消息管理器
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 我们雇佣了一只大猴子...
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • ​中南建设2022年半年报“韧”字当头,经营性现金流持续为正​
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (C++17) std算法之执行策略 execution
  • (动态规划)5. 最长回文子串 java解决
  • (二)springcloud实战之config配置中心
  • (四)图像的%2线性拉伸
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转)关于多人操作数据的处理策略
  • (转载)CentOS查看系统信息|CentOS查看命令
  • *** 2003
  • .NET CF命令行调试器MDbg入门(一)
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .NET导入Excel数据
  • /proc/interrupts 和 /proc/stat 查看中断的情况