1
votes

I'm trying to understand the relations between [glGenBuffers, glBindData, glBufferData, glBufferSubData] and glVertexAttribPointer.

I saw that you don't really have to use the buffers method (at least not in Android) but rather call glVertexAttribPointer directly, passing a pointer to the buffer object as the last parameter. (the buffers being a FloatBuffer in this case)

e.g.

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) Why would I want to use the buffer methods? Only if my data is happened to be stacked together in one array?

2) How do openGL handle these direct (non buffer) functions behind the scenes? I saw that it calls glVertexAttribPointerBounds - does it actually stack it together to a single array of bytes?

3) If I do bind an array to the GL_ARRAY_BUFFER - it means I have to use the stacked array version, and I won't be able to pass anymore direct objects, correct? i.e. I cannot do this:

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

2 Answers

1
votes

You can have multiple buffer objects. If a vertex array buffer is bound, when glVertexAttribPointer is called then the last parameter of glVertexAttribPointer is treated as an offset to this buffer, but if no vertex buffer object is bound, then the last parameter is treated as a pointer to the data:

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

... For each command, pointer specifies the location in memory of the first value of the first element of the array being specified.

See OpenGL ES 2.0 specification; 2.9. BUFFER OBJECTS; 25:

... When an array is sourced from a buffer object, the pointer value of that array is used to compute an offset, in basic machine units, into the data store of the buffer object. ...

This means it is possible to do:

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

1) The preferred way is to use buffer objects whenever possible. This ensures allocation of necessary GPU resources in advance. However, for dynamic geometry it is sometimes more convenient to call bufferless methods. In my experience, some drivers have shown better perfomance with bufferless methods even compared to using a buffer with GLES20.GL_DYNAMIC_DRAW. But for static geometry the best approach is to use a GL buffer. This way the driver can avoid having to copy the vertex data before each draw call.

2) The common knowledge here is that when you pass a CPU side array to glVertexAttribPointer, it has to be copied by the driver during the subsequent glDraw* call. You can then modify the array and it will be copied during the next glDraw* call again. As for the layout of the vertex data in memory, I would bet that it is left as it is, since AFAIK GPUs can handle non-interleaved streams natively. Thus re-arranging them before a GL draw call would require a meaningless gather operation.

3) The mix of GL buffer and CPU array streams should work OK. This is a common scenario, where one part of the vertex data is static (e.g. texcoords), and another is dynamic (e.g. positions), the latter being transformed by CPU, for example.