I have a problem with rendering multiple instances of an object, using one VertexArrayObject and four VertexBufferObjects.
I cannot get my head around what's wrong with my approach. Here are the basics:
My relatively simple Vertex-Shader-code:
#version 330 core
precision highp float;
layout (location=0) in vec3 position;
layout (location=1) in vec2 texcoord;
layout (location=3) in mat4 modelViewMatrix;
out vec2 textureCoord;
uniform mat4 pr_matrix;
void main() {
textureCoord = vec2(texcoord.x, texcoord.y);
vec4 mvPos = modelViewMatrix * vec4(position, 1.0);
gl_Position = pr_matrix * mvPos;
}
As you can see, I try to pass the model view matrix (model and camera_view combined) as an VertexAttribute.
As far as I know, a VertexAttribute is limited to a max of vec4, which means my mat4 will actually take up 4 * vec4 locations.
Each VAO and VBO exists only once. I do not use a separate one for each "gameobject", as some online-tutorials do. Therefore I update each of the Buffers at specific positions. Buf first, let me show you the following code, which initializes them:
// VAO
this.vao = glGenVertexArrays();
glBindVertexArray(this.vao);
// buffer for vertex positions
this.positionVBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, this.positionVBO);
// upload null data to allocate vbo storage in memory
glBufferData(GL_ARRAY_BUFFER, vertexpoints * Float.BYTES, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
// buffer for texture coordinates
this.textureVBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, this.textureVBO);
glBufferData(GL_ARRAY_BUFFER, texturepoints * Float.BYTES, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
// buffer for transform matrices
this.matricesVBO = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, this.matricesVBO);
glBufferData(GL_ARRAY_BUFFER, mtrxsize * Float.BYTES, GL_DYNAMIC_DRAW);
// Byte size of one vec4
int vec4Size = 4 * Float.BYTES;
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_FLOAT, false, 4 * vec4Size, 0);
glVertexAttribDivisor(3, 1);
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 4, GL_FLOAT, false, 4 * vec4Size, 1 * vec4Size);
glVertexAttribDivisor(4, 1);
glEnableVertexAttribArray(5);
glVertexAttribPointer(5, 4, GL_FLOAT, false, 4 * vec4Size, 2 * vec4Size);
glVertexAttribDivisor(5, 1);
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, false, 4 * vec4Size, 3 * vec4Size);
glVertexAttribDivisor(6, 1);
//buffer for indices
this.indicesVBO = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.indicesVBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indsize * Integer.BYTES, GL_DYNAMIC_DRAW);
//unbind buffers and array
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
As far as I'm aware, this initialization should correspond to the 4 VertexAttributes, defined in the Vertex-Shader.
For test purposes, I initialize 4 gameobjects (A1, A2, B1, B2), each with:
- 4 x vec3 points for the position-attribute, resulting in 4 * 3 = 12 floating-points pushed to the positionVBO
- 4 x vec2 points for the texture-attribute, resulting in 4 * 2 = 8 floating-points pushed to the textureVBO
- 6 x int points for the indices to draw (0, 1, 2, 2, 3, 0), pushed to the indicesVBO
- 1 x mat4 for the modelViewMatrix-attribute, resulting in 4 * vec4 = 4 * 4 = 16 floating-points pushed to the matricesVBO
for each gameobject I push its data to the VBOs, using the following logic:
long vertoff = gameObjectVertOffset; // offset of the current gameobject's vertex points in position data
long texoff = gameObjectTexOffset; // offset of the current gameobject's texture points in texture data
long indoff = gameObjectIndOffset; // offset of the current gameobject's indices in index data
long instoff = gameObjectMatrixOffset; // offset of the current gameobject's matrix (vec4) in matrices data
// upload new position data
if(gameObjectVertBuf.capacity() > 0) {
gameObjectVertBuf.flip();
glBindBuffer(GL_ARRAY_BUFFER, this.positionVBO);
glBufferSubData(GL_ARRAY_BUFFER, vertoff * Float.BYTES, gameObjectVertBuf);
}
// upload new texture data
if(gameObjectTexBuf.capacity() > 0) {
gameObjectTexBuf.flip();
glBindBuffer(GL_ARRAY_BUFFER, this.textureVBO);
glBufferSubData(GL_ARRAY_BUFFER, texoff * Float.BYTES, gameObjectTexBuf);
}
// upload new indices data
if(gameObjectIndBuf.capacity() > 0) {
gameObjectIndBuf.flip();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.indicesVBO);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indoff * Integer.BYTES, gameObjectIndBuf);
}
// upload new model matrix data
if(gameObjectMatrixBuf.capacity() > 0) {
gameObjectMatrixBuf.flip();
glBindBuffer(GL_ARRAY_BUFFER, this.matricesVBO);
glBufferSubData(GL_ARRAY_BUFFER, instoff * Float.BYTES, gameObjectMatrixBuf);
}
Now to the actual rendering:
I want to draw element A 2 times and after that, element B 2 times. for the instanced rendering, I group together the gameobjects, I knew i could render in one call, inside lists.
I now have two lists, each with two elements in them:
- List1[A1, A2]
- List2[B1, B2]
Once per list I now do:
numInstances = 2;
this.vao.bind();
shaderprogram.useProgram();
glDrawElementsInstanced(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, (int) (offset * Integer.BYTES), numInstances);
offset += 6 * numInstances; // 6 indices * 2 instances
The Problem:
This results in the first two elements being rendered correctly, but the second two (from the second list / glDrawElementsInstanced() call) are rendered with the transformation matrices of the first two elements.
It does not matter, which list of objects are rendered first. The second iteration always seems to use the modelViewMatrix attributes from the first ones.
As far as I understood, the glVertexAttribDivisor() call should limit the iteration of the matrices per instance instead of per vertex.
What am I missing here?