一、缓冲数据
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