1
0
mirror of https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git synced 2025-08-23 12:45:29 +08:00

校对 04/07

This commit is contained in:
Geequlim
2015-08-08 14:33:48 +08:00
parent f6aa2b4470
commit c1c3449d87

View File

@@ -1,20 +1,26 @@
## 高级数据
# 高级数据
本文作者JoeyDeVries由Django翻译自http://learnopengl.com
原文 | [Advanced Data](http://learnopengl.com/#!Advanced-OpenGL/Advanced-Data)
---|---
作者 | JoeyDeVries
翻译 | [Django](http://bullteacher.com/)
校对 | [Geequlim](http://geequlim.com)
## 缓冲数据写入
我们在OpenGL中大量使用缓冲来储存数据已经有一会儿了。有一些有趣的方式来操纵缓冲也有一些有趣的方式通过纹理来向着色器传递大量数据。本教程中我们会讨论一些更加有意思的缓冲函数以及如何使用纹理对象来储存大量数据教程中纹理部分还没写
OpenGL中缓冲知识一个管理一块儿内存区域的对象除此没有更多点的了。当把缓冲绑定到一个特定缓冲对象的时候我们就给缓冲赋予了一个特殊的意义。当我们绑定到GL_ARRAY_BUFFER的时候这个缓冲就是一个顶点数组缓冲我们也可以简单地绑定到GL_ELEMENT_ARRAY_BUFFER。OpenGL内部为每个目标target储存一个缓冲基于目标来处理不同的缓冲。
OpenGL中缓冲只是一块儿内存区域的对象,除此没有更多点的了。当把缓冲绑定到一个特定缓冲对象的时候,我们就给缓冲赋予了一个特殊的意义。当我们绑定到`GL_ARRAY_BUFFER`的时候,这个缓冲就是一个顶点数组缓冲,我们也可以简单地绑定到`GL_ELEMENT_ARRAY_BUFFER`。OpenGL内部为每个目标target储存一个缓冲基于目标来处理不同的缓冲。
到目前为止,我们已经使用glBufferData函数填充缓冲对象管理的内存,这个函数分了一块内存空间,然后把数据存入内存。如果我们向它的数据(data这个参数传递的是NULL只会分类内存,而不会填充它。如果我们先打算开辟一些内存,稍后回到这个缓冲一点一点的填充内存的时候很有用。
到目前为止,我们使用`glBufferData`函数填充缓冲对象管理的内存,这个函数分了一块内存空间,然后把数据存入其中。如果我们向它的`data`这个参数传递的是NULL那么OpenGL只会帮我们分配内存,而不会填充它。如果我们先打算开辟一些内存,稍后回到这个缓冲一点一点的填充数据,有些时候很有用。
我们还可以调用glBufferSubData函数填充特定区域的缓冲而不是一次填充整个缓冲。这个函数需要一个缓冲目标target一个偏移量offset数据的大小以及数据本身作为参数。这个函数新的功能是我们可以给它一个偏移量offset来指定我们打算填充缓冲的位置与起始位置之间的偏移量。这样我们就可以插入/更新指定区域的缓冲内存空间了。一定要记住缓冲要有足够的内存分配所以在调用glBufferSubData之前调用glBufferData是必须的。
我们还可以调用`glBufferSubData`函数填充特定区域的缓冲而不是一次填充整个缓冲。这个函数需要一个缓冲目标target一个偏移量offset数据的大小以及数据本身作为参数。这个函数新的功能是我们可以给它一个偏移量offset来指定我们打算填充缓冲的位置与起始位置之间的偏移量。这样我们就可以插入/更新指定区域的缓冲内存空间了。一定要确保修改的缓冲要有足够的内存分配,所以在调用`glBufferSubData`之前,调用`glBufferData`是必须的。
```c++
glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // 范围: [24, 24 + sizeof(data)]
```
把数据传进缓冲另一个方式是缓冲内存请求一个指针你自己直接把数据复制到缓冲中。调用glMapBuffer函数OpenGL会返回一个当前绑定缓冲的内存的地址供我们操作
把数据传进缓冲另一个方式是缓冲内存请求一个指针,你自己直接把数据复制到缓冲中。调用`glMapBuffer`函数OpenGL会返回一个当前绑定缓冲的内存的地址供我们操作
```c++
float data[] = {
@@ -23,37 +29,37 @@ float data[] = {
};
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Get pointer
// 获取当前绑定缓存buffer的内存地址
void* ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// Now copy data into memory
// 向缓冲中写入数据
memcpy(ptr, data, sizeof(data));
// Make sure to tell OpenGL we're done with the pointer
// 完成够别忘了告诉OpenGL我们不再需要它了
glUnmapBuffer(GL_ARRAY_BUFFER);
```
调用glUnmapBuffer函数可以告诉OpenGL我们已经用完指针了OpenGL会知道你已经做完了。通过解映射unmapping指针会不再可用如果OpenGL可以把你的数据映射到缓冲上就会返回GL_TRUE。
调用`glUnmapBuffer`函数可以告诉OpenGL我们已经用完指针了OpenGL会知道你已经做完了。通过解映射unmapping指针会不再可用如果OpenGL可以把你的数据映射到缓冲上就会返回`GL_TRUE`
把数据直接映射到冲上使用glMapBuffer很有用因为不用把它储存在临时内存里。你可以从文件读取数据然后直接复制到缓冲的内存里。
把数据直接映射到冲上使用`glMapBuffer`很有用,因为不用把它储存在临时内存里。你可以从文件读取数据然后直接复制到缓冲的内存里。
### 分批处理顶点属性
## 分批处理顶点属性
使用glVertexAttribPointer函数我们可以指定缓冲内容的顶点数组的属性layout(布局)。顶点数组缓冲,使我们可以交叉属性也就是说我们把每个顶点的位置、法线、纹理坐标放在彼此挨着的地方。现在我们了解了更多的缓冲的内容,我们可以采取另一种方式了。
使用`glVertexAttribPointer`函数可以指定缓冲内容的顶点数组的属性的布局(layout)。我们已经知道,通过使用顶点属性指针我们可以交叉属性也就是说我们可以把每个顶点的位置、法线、纹理坐标放在彼此挨着的地方。现在我们了解了更多的缓冲的内容,可以采取另一种方式了。
我们可以做的是把每种类型的属性的所有向量数据批量保存在一个布局而不是交叉布局。与交叉布局123123123123不同我们采取批量方式111122223333。
当从文件加载顶点数据时你通常获取一个位置数组一个法线数组和一个纹理坐标数组。需要花点力气才能把它们结合为交叉数据。使用glBufferSubData可以简单的实现分批处理方式
当从文件加载顶点数据时你通常获取一个位置数组,一个法线数组和一个纹理坐标数组。需要花点力气才能把它们结合为交叉数据。使用`glBufferSubData`可以简单的实现分批处理方式:
```c++
GLfloat positions[] = { ... };
GLfloat normals[] = { ... };
GLfloat tex[] = { ... };
// Fill buffer
// 填充缓冲
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是更好的选择。
这样我们可以把属性数组当作一个整体直接传输给缓冲,不需要再处理它们了。我们还可以把它们结合为一个更大的数组然后使用`glBufferData`立即直接填充它,不过对于这项任务使用`glBufferSubData`是更好的选择。
我们还要更新顶点属性指针来反应这些改变:
@@ -64,23 +70,23 @@ glVertexAttribPointer(
2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)(sizeof(positions) + sizeof(normals)));
```
注意stride参数等于顶点属性的大小由于下一个顶点属性向量可以在它的后面3或2的元素那儿找到。
注意,`stride`参数等于顶点属性的大小,由于同类型的属性是连续储存的,所以下一个顶点属性向量可以在它的后面3或2的元素那儿找到。
这是我们有了另一种设置和指定顶点属性的方式。使用哪个方式对OpenGL来说也不会有立竿见影的效果这只是一种采用更加组织化的方式去设置顶点属性。选用哪种方式取决于你的偏好和应用类型。
### 复制缓冲
## 复制缓冲
当你的缓冲被数据填充以后你可能打算让其他缓冲能分享这些数据或者打算把缓冲的内容复制到另一个缓冲里。glCopyBufferSubData函数让我们能够相对容易地把一个缓冲的数据复制到另一个缓冲里。函数的原型是
当你的缓冲被数据填充以后,你可能打算让其他缓冲能分享这些数据或者打算把缓冲的内容复制到另一个缓冲里。`glCopyBufferSubData`函数让我们能够相对容易地把一个缓冲的数据复制到另一个缓冲里。函数的原型是:
```c++
void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);
```
readtargetwritetarget参数是复制的来源和目的的缓冲目标。例如我们可以从一个VERTEX_ARRAY_BUFFER复制到一个VERTEX_ELEMENT_ARRAY_BUFFER各自指定源和目的的缓冲目标。当前绑定到这些缓冲目标上的缓冲会被影响到。
`readtarget`和`writetarget`参数是复制的来源和目的的缓冲目标。例如我们可以从一个`VERTEX_ARRAY_BUFFER`复制到一个`VERTEX_ELEMENT_ARRAY_BUFFER`,各自指定源和目的的缓冲目标。当前绑定到这些缓冲目标上的缓冲会被影响到。
但如果我们打算读写的两个缓冲都是顶点数组缓冲(译注:GL_VERTEX_ARRAY_BUFFER怎么办?我们不能用一个缓冲目标用两次。于这个理由,这是个例外,OpenGL给了我们另外两个缓冲目标叫做GL_COPY_READ_BUFFERGL_COPY_WRITE_BUFFER。这样我们就可以把我们选择的缓冲用上面二者作为readtargetwritetarget的参数绑定到新的缓冲目标上了。
但如果我们打算读写的两个缓冲都是顶点数组缓冲(`GL_VERTEX_ARRAY_BUFFER`)怎么办?我们不能用一个缓冲作为操作的读取和写入目标次。于这个理由OpenGL给了我们另外两个缓冲目标叫做`GL_COPY_READ_BUFFER`和`GL_COPY_WRITE_BUFFER`。这样我们就可以把我们选择的缓冲,用上面二者作为`readtarget`和`writetarget`的参数绑定到新的缓冲目标上了。
接着glCopyBufferSubData函数会从readoffset处读取的size大小的数据写入到writetarget缓冲的writeoffset位置。下面是一个复制两个顶点数组缓冲的例子
接着`glCopyBufferSubData`函数会从readoffset处读取的size大小的数据写入到writetarget缓冲的writeoffset位置。下面是一个复制两个顶点数组缓冲的例子
```c++
GLfloat vertexData[] = { ... };
@@ -89,7 +95,7 @@ glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
```
我们也可以把writetarget缓冲绑定为新缓冲目标类型其中之一
我们也可以把`writetarget`缓冲绑定为新缓冲目标类型其中之一:
```c++
GLfloat vertexData[] = { ... };
@@ -98,4 +104,4 @@ glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
```
有了这些额外的关于如何操纵缓冲的知识,我们已经可以以更有意思的方式来使用它们了。当你对OpenGL更熟悉这些新缓冲方法就变得更有用。下个教程中我们会讨论unform缓冲对象彼时我们会充分利用glBufferSubData。
有了这些额外的关于如何操纵缓冲的知识,我们已经可以以更有的方式来使用它们了。当你对OpenGL更熟悉这些新缓冲方法就变得更有用。下个教程中我们会讨论unform缓冲对象彼时我们会充分利用`glBufferSubData`