OpenGL 入门 18:数据存储

一、缓冲的填充与修改

首先需要调用glBufferData给缓冲分配足够的内存。

  1. glBufferSubData

void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
// example:
glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data);
  • target: 缓冲区对象的类型,取值可以是GL_ARRAY_BUFFER,GL_ELEMENT_ARRAY_BUFFER或者GL_UNIFORM_BUFFER等。

  • offset: 相对于缓冲区起始位置的偏移量,指定了数据将要传输到缓冲区的哪个位置。

  • size: 需要传输的数据大小,以字节为单位。

  • data: 指向包含要传输的数据的指针。

须知:

1. 将数据直接拷贝到GPU的内存中。
2. 适用于数据量小、数据频繁变化或只需要部分数据更新
3. 频繁调用对CPU之间GPU带宽有一定影响

2. glMapBuffer

void* glMapBuffer(GLenum target, GLenum access);
  • target:要映射的缓冲区目标,可以是以下任一值之一:

  • GL_ARRAY_BUFFER:用于顶点数据的缓冲区对象。

  • GL_ELEMENT_ARRAY_BUFFER:用于索引数据的缓冲区对象。

  • GL_UNIFORM_BUFFER:用于Uniform数据的缓冲区对象。

  • GL_TRANSFORM_FEEDBACK_BUFFER:用于Transform Feedback数据的缓冲区对象。

  • GL_SHADER_STORAGE_BUFFER:用于Shader存储数据的缓冲区对象。

  • access:映射缓冲区的访问方式,可以是以下任一值之一:

  • GL_READ_ONLY:只读访问。

  • GL_WRITE_ONLY:只写访问。

  • GL_READ_WRITE:读写访问。

原理:

1. 调用glMapBuffer后,使用 映射操作,会将GPU上指定缓冲区的内容一模一样拷贝一份至CPU的内存上(也称为 映射区域),并返回 内存指针。之后的可通过指针填充或修改缓冲数据,当然这些修改都只是在CPU的内存之上,并且OpenGL实现会将应用程序所做的修改缓存下来,等到 解除映射(调用glUnmapBuffer)时一次性拷贝到GPU内存。
2. 当缓冲区对象被映射时,OpenGL会将该缓冲区对象的使用限制为GL_READ_WRITE。这样做是为了方便读取和修改相应缓冲区对象的数据。
3. 解除映射之前,其他OpenGL API要是想读取或修改缓冲区的内容,就必须使用 同步机制以保证数据的一致性。因此会在他们进行读取和修改时,OpenGL就会强制提前将 整个映射区域的数据拷贝到GPU内存中,这样就会导致多次数据拷贝,从而降低性能。通常在映射期间,应该尽量避免其他OpenGL API对相应的缓冲区对象进行修改。

例子:

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);

二、分批顶点属性

之前的加载顶点数据时,通过glBufferData一次性将所有数据将在到顶点缓冲中。且其中的数据采用了交错(Interleave)处理,即将每一个顶点的位置、法线和/或纹理坐标紧密放置在一起。如123123的形式。

但当从文件中加载顶点数据时,通常获得是一个位置数组、一个法线数组和/或一个纹理坐标数组。通常是11122233的形式。因此我们可以使用glBufferSubData分批将这些属性加载到顶点缓冲。当然前提是需要glBufferData预先分配好足够的内存。

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);
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)));

三、复制缓冲

void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset,
                         GLintptr writeoffset, GLsizeiptr size);
  • readTarget:指定源缓冲区对象的目标类型,可以是GL_ARRAY_BUFFER、GL_COPY_READ_BUFFER、GL_ELEMENT_ARRAY_BUFFER、GL_PIXEL_PACK_BUFFER中的任意一个。

  • writeTarget:指定目标缓冲区对象的目标类型,可以是GL_ARRAY_BUFFER、GL_COPY_WRITE_BUFFER、GL_ELEMENT_ARRAY_BUFFER、GL_PIXEL_UNPACK_BUFFER中的任意一个。

  • readOffset:指定源缓冲区对象中的偏移量,以字节为单位。

  • writeOffset:指定目标缓冲区对象中的偏移量,以字节为单位。

  • size:指定拷贝数据的字节数。

该函数会将源缓冲区对象中从偏移量readOffset开始的size个字节的数据拷贝到目标缓冲区对象中的偏移量writeOffset处,两个缓冲区对象的数据类型和大小必须一致。

缓冲目标相同时

使用OpenGL提供给我们另外两个缓冲目标,叫做GL_COPY_READ_BUFFERGL_COPY_WRITE_BUFFER。

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));

or

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值