第二十章 opengl之高级OpenGL(高级数据)

高级数据

前面都是使用缓冲来储存数据。
本节学习一些缓冲函数,使用纹理对象来存储大量数据等内容。

OpenGL中的缓冲只是一个管理特定内存块的对象,没有其它更多的功能了。在我们将它绑定到一个缓冲目标(Buffer Target)时,我们才赋予了其意义。
当我们绑定一个缓冲到GL_ARRAY_BUFFER时,它就是一个顶点数组缓冲,但我们也可以很容易地将其绑定到GL_ELEMENT_ARRAY_BUFFER。
OpenGL内部会为每个目标储存一个缓冲,并且会根据目标的不同,以不同的方式处理缓冲。

我们一直是调用glBufferData函数来填充缓冲对象所管理的内存,这个函数会分配一块内存,并将数据添加到这块内存中。如果我们将它的data参数设置为NULL,那么这个函数将只会分配内存,但不进行填充。这在我们需要预留(Reserve)特定大小的内存,之后回到这个缓冲一点一点填充的时候会很有用。

除了使用一次函数调用填充整个缓冲之外,我们也可以使用glBufferSubData,填充缓冲的特定区域。这个函数需要一个缓冲目标、一个偏移量、数据的大小和数据本身作为它的参数。这个函数不同的地方在于,我们可以提供一个偏移量,指定从何处开始填充这个缓冲。这能够让我们插入或者更新缓冲内存的某一部分。要注意的是,缓冲需要有足够的已分配内存,所以对一个缓冲调用glBufferSubData之前必须要先调用glBufferData。

glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // 范围: [24, 24 + sizeof(data)]

将数据导入缓冲另外一种方法是:请求缓冲内存的指针,直接将数据复制到缓冲中。通过调用glMapBuffer函数,OpenGL会返回当前绑定缓冲的内存指针:

float data[] = {
  0.5f, 1.0f, -0.35f
  ...
};
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// 获取指针
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// 复制数据到内存
memcpy(ptr, data, sizeof(data));
// 记得告诉OpenGL我们不再需要这个指针了
glUnmapBuffer(GL_ARRAY_BUFFER);

使用glUnmapBuffer函数后,OpenGL知道已经完成指针操作了。解除映射后,指针就不可用了,如果能够将数据映射到缓冲中,则函数会返回GL_TRUE。

用处:
要直接映射数据到缓冲,而不是将其存储到临时内存中,则glMapBuffer函数很重要。可以直接从文件中读取数据,并直接将它们复制到缓冲内存中。

分批顶点属性

使用glVertexAttribPointer函数,能够制定顶点数组缓冲内容的属性布局。在顶点数组缓冲中,对属性进行了交错处理,意思是:将每一个顶点的位置,法线或纹理坐标紧密放到一起。现在要说下另外一种方法:分批顶点属性
做法:将每一种属性类型的向量数据打包为一个大的区块,而不是对它们进行交错存储。与交错布局(12341234)不同,分批是(11223344)
当从文件中加载顶点属性时,通常获取一个位置数组,一个法线数组,或一个纹理做标书组。需要将这些数组转化为一个大的交错数据数组。使用分批的方式会是一个简单的解决方法:
glBufferSubData函数:

float positions[] = { ... };
float normals[] = { ... };
float tex[] = { ... };
// 填充缓冲
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), &positions);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(tex), &tex);

这样子我们就能直接将属性数组作为一个整体传递给缓冲,而不需要事先处理它们了。另外一个不太方便的方法是:将它们合并为一个大的数组,再使用glBufferData来填充缓冲,但对于这种工作,使用glBufferSubData会更合适一点。

另外需要更新顶点属性指针来反应改变:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);  
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(sizeof(positions)));  
glVertexAttribPointer(
  2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(sizeof(positions) + sizeof(normals)));

这给了我们设置顶点属性的另一种方法。两种方法都可以使用

复制缓冲

当缓冲填充好数据后,如果想与其他的缓冲共享其中的数据,或者想将缓冲的内容复制到另外一个缓冲中。glCopyBufferSubData函数能够容易的从一个缓冲中复制数据到另外一个缓冲中。函数原型如下:

void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset,
                         GLintptr writeoffset, GLsizeiptr size);

readtarget和writetarget参数需要填入复制源和复制目标的缓冲目标
举例:可以将VERTEX_ARRAY_BUFFER缓冲复制到VERTEX_ELEMENT_ARRAY_BUFFER缓冲,分别将这些缓冲目标设置为读和写的目标。当前绑定到这些缓冲目标的缓冲将会被影响到。

如果想读写数据的两个不同缓冲都为顶点数组缓冲。我们不能同时将两个缓冲绑定到同一个缓冲目标上。
正是出于这个原因,OpenGL提供给我们另外两个缓冲目标,叫做GL_COPY_READ_BUFFER和GL_COPY_WRITE_BUFFER。我们接下来就可以将需要的缓冲绑定到这两个缓冲目标上,并将这两个目标作为readtarget和writetarget参数。

接下来glCopyBufferSubData会从readtarget中读取size大小的数据,并将其写入writetarget缓冲的writeoffset偏移量处。下面举例 复制两个顶点数组缓冲:

float vertexData[] = { ... };
glBindBuffer(GL_COPY_READ_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));

我们也可以只将writetarget缓冲绑定为新的缓冲目标类型之一:

float vertexData[] = { ... };
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值