缓冲区与glVertexAttribPointer之间的关系

时间:2018-12-11 10:18:47

标签: android opengl-es opengl-es-2.0

我试图理解[glGenBuffersglBindDataglBufferDataglBufferSubData]和glVertexAttribPointer之间的关系。

我看到您实际上并不需要使用buffers方法(至少在Android中不是),而是直接调用glVertexAttribPointer,将指向缓冲区对象的指针作为最后一个参数传递。 (在这种情况下,缓冲区为FloatBuffer

例如

GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer);
GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, 16, colorBuffer);
GLES20.glVertexAttribPointer(normalHandle, 3, GLES20.GL_FLOAT, false, 12, normalsBuffer);
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer);

1)为什么要使用缓冲方法?仅当我的数据碰巧堆放在一个阵列中时?

2)openGL如何在后台处理这些直接(非缓冲)功能?我看到它调用了glVertexAttribPointerBounds-实际上将它堆叠到一个字节的单个数组中吗?

3)如果我确实将数组绑定到GL_ARRAY_BUFFER-这意味着我必须使用堆叠的数组版本,并且我将无法再传递直接对象,对吗?即我不能这样做:

int[] bufferVertices = new int[1];
GLES20.glGenBuffers(1, bufferVertices, 0);
vertexBufferId = bufferVertices[0];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cubeCoords.length * 4 , vertexBuffer, GLES20.GL_STATIC_DRAW);

GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride,0);
GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, 16, colorBuffer);
GLES20.glVertexAttribPointer(normalHandle, 3, GLES20.GL_FLOAT, false,12, normalsBuffer);
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false,8, textureBuffer);

2 个答案:

答案 0 :(得分:1)

您可以有多个缓冲区对象。如果绑定了顶点数组缓冲区,则在调用glVertexAttribPointer时,会将glVertexAttribPointer的最后一个参数视为此缓冲区的偏移量,但是如果没有绑定顶点缓冲区对象,则将处理最后一个参数作为指向数据的指针:

请参见OpenGL ES 2.0 specification; 2.8. VERTEX ARRAYS; 21

void VertexAttribPointer( uint index, int size, enum type, 
                          boolean normalized,  sizei stride, 
                          const void *pointer );
     

...   对于每个命令, pointer 都指定要指定的数组第一个元素的第一个值在内存中的位置。

请参见OpenGL ES 2.0 specification; 2.9. BUFFER OBJECTS; 25

  

...当数组来源于   缓冲区对象,该数组的 pointer 值用于以基本机器单位计算缓冲区对象数据存储区中的偏移量。 ...

这意味着可以做到:

int[] bufferVertices = new int[4];
GLES20.glGenBuffers(4, bufferVertices, 0);

vertexBufferId = bufferVertices[0];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cubeCoords.length * 4, vertexBuffer, GLES20.GL_STATIC_DRAW);

colorBufferId = bufferVertices[1];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, colorBufferId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cubeCoords.length * 4, colorBuffer, GLES20.GL_STATIC_DRAW);

normlBufferId = bufferVertices[2];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, normlBufferId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cubeCoords.length * 4, normalsBuffer, GLES20.GL_STATIC_DRAW);

textureBufferId = bufferVertices[3];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, textureBufferId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cubeCoords.length * 4, textureBuffer, GLES20.GL_STATIC_DRAW);

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, 0);

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, colorBufferId);
GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, 0, 0);

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, normlBufferId);
GLES20.glVertexAttribPointer(normalHandle, 3, GLES20.GL_FLOAT, false, 0, 0);

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, textureBufferId);
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 0, 0);

答案 1 :(得分:0)

1)首选方法是尽可能使用缓冲区对象。这样可以确保提前分配必要的GPU资源。但是,对于动态几何,有时更方便地调用无缓冲方法。以我的经验,与使用带有GLES20.GL_DYNAMIC_DRAW的缓冲区相比,某些驱动程序在使用无缓冲区方法时表现出更好的性能。但是对于静态几何,最好的方法是使用GL缓冲区。这样,驱动程序可以避免在每次绘制调用之前必须复制顶点数据。

2)这里的常识是,当您将CPU端数组传递给glVertexAttribPointer时,驱动程序必须在随后的glDraw *调用期间将其复制。然后,您可以修改数组,它将在下一次glDraw *调用期间再次复制。至于顶点数据在内存中的布局,我敢打赌,它是按原样保留的,因为AFAIK GPU可以原生处理非交错流。因此,在GL绘制调用之前重新安排它们将需要毫无意义的收集操作。

3)GL缓冲区和CPU阵列流的混合应该可以正常工作。这是一种常见的情况,其中顶点数据的一部分是静态的(例如texcoords),而另一部分是动态的(例如位置),例如,一部分是由CPU转换的。