OpenGL基础38:数据存储

 

一、缓冲数据

OpenGL中的缓冲就是一块内存区域的对象,将缓冲绑定到一个特定缓冲对象的时候,就给缓冲赋予了一个特殊的意义,例如绑定到GL_ARRAY_BUFFER的时候,这个缓冲就是一个顶点数组缓冲,绑定到GL_ELEMENT_ARRAY_BUFFER的时候,这个缓冲就是个顶点索引缓冲

之前的用法一直都是这样:

GLuint skyboxVAO, skyboxVBO;
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);

可以看到,上面的缓冲都是即绑即用的,如果说每个缓冲类型中的各个对象就像不同的轨道,那么glBindbuffer就指定了正确的铁路轨道,在此之后,使用glBufferData填充缓冲对象管理的内存,这个函数分配了一块内存空间,然后把数据存入其中

当然了,也可以将第三个参数(data)传递为NULL,以让OpenGL只帮我们分配内存,而不去填充它,之后我们再用其它的方法把数据存进去,一个例子就是glMapBuffer():

  • glMapBuffer():传入当前的缓存对象数据和读写规则,函数会返回一个当前绑定缓冲的内存的地址,供我们操作,拿到这个内存地址后,就可以直接使用memcpy拷贝数据了
  • glUnmapBuffer():释放缓冲区对象与客户端地址空间的关系

下面这份代码就可以起到和上面一样的效果

glMapBuffer 需要将数据从GPU上Copy到主机上,不但多一次内存拷贝,而且需要CPU和GPU之间同步,所以效率并不是很高,大部分情况下推荐使用glBufferSubData

GLuint skyboxVAO, skyboxVBO;
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), NULL, GL_STATIC_DRAW);
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(ptr, skyboxVertices, sizeof(skyboxVertices));
glUnmapBuffer(GL_ARRAY_BUFFER);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);

 

二、分批顶点属性

使用glVertexAttribPointer函数可以指定缓冲内容的顶点数组的属性的布局(Layout),之前一直用的也确实是glVertexAttribPointer方式,通过使用顶点属性指针,可以支持交叉(Interleave)属性,确实,之前的数据也就是这么定义的,假设"1"为位置属性,"2"为颜色属性,"3"为纹理属性,那么之前的定义方法就是"123123123……"

如果是从其它地方读取数据,那么有些情况下会是每种属性单独一块,而不是交叉,也就是"111122223333……"这样的分批处理方式,这个时候想要将其处理为交叉形式并不容易,还没有必要,使用glBufferSubData即可简单的实现分批处理方式

  • glBufferSubData(target, offset, size, data):填充特定区域的缓冲,偏移为offset,大小为size,数据为data,和glMapBuffer()一样,在此之前空间需要已经被申请好
GLfloat positions[] = { ... };
GLfloat normals[] = { ... };
GLfloat 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(GLfloat), 0);  
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)(sizeof(positions)));  
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)(sizeof(positions) + sizeof(normals)));

两个方法差别不是很大

 

三、复制缓冲

字面意思:把缓冲的内容复制到另一个缓冲里

  • glCopyBufferSubData(readtarget, writetarget, readoffset, writeoffset, size): 从左到右参数分别为:源数据的缓冲区类型、目标数据的缓冲区类型、源数据缓冲区中的数据偏移量、目标数据缓冲区中的数据偏移量、拷贝的字节数

注意readtarget和writetarget不能是同一个

一个例子如下:

  • GL_COPY_READ_BUFFER、GL_COPY_WRITE_BUFFER:两个只用于拷贝的缓冲,不能用于其他OpenGL的操作,如果不影响OpenGL的状态也不需要记录拷贝之前的目标区域信息的话,那么整个操作过程都是可以保证安全的
GLuint stoneVAO, x;
glGenVertexArrays(1, &stoneVAO);
glGenBuffers(1, &x);
glBindVertexArray(stoneVAO);
glBindBuffer(GL_ARRAY_BUFFER, x);
glBufferData(GL_ARRAY_BUFFER, sizeof(stoneVertices), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_COPY_READ_BUFFER, x);

GLuint stoneVBO;
glGenBuffers(1, &stoneVBO);
glBindBuffer(GL_ARRAY_BUFFER, stoneVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(stoneVertices), NULL, GL_STATIC_DRAW);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_ARRAY_BUFFER, 0, 0, sizeof(stoneVertices));
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
GLuint stone = loadTexture("Texture/stone3.png", true);

///等同于

GLuint stoneVAO, stoneVBO;
glGenVertexArrays(1, &stoneVAO);
glGenBuffers(1, &stoneVBO);
glBindVertexArray(stoneVAO);
glBindBuffer(GL_ARRAY_BUFFER, stoneVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(stoneVertices), stoneVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
GLuint stone = loadTexture("Texture/stone3.png", true);

参考文章:https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL2RenderingContext/copyBufferSubData

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值