0
\$\begingroup\$

I've been learning LWJGL and was originally using instancing and using a uniform to send all the mat4 data to the shader for the individual positions. I'm trying to switch to using a VBO for this since I had a batch size limitation of just 128, but have run into a problem with sending the data to the shader. I've spend a few days looking over the instancing sections of both https://learnopengl.com/ and https://lwjglgamedev.gitbooks.io/ but haven't been able to find anything that could be the cause of this.

Nothing was changed aside from the code presented here, the creation of any needed variables, and the conversion from uniform to VBO in the shader itself.

This was the original code using the uniform that worked just fine. First is what was used to actually make the draw calls, and the second part is what was used to pass the mat4s to the shader:

// Prepare shader

GL40.glEnableVertexAttribArray(0);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, vertexID);
GL40.glVertexAttribPointer(0, 3, GL40.GL_FLOAT, false, 0, 0);

GL40.glEnableVertexAttribArray(1);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, textureID);
GL40.glVertexAttribPointer(1, 2, GL40.GL_FLOAT, false, 0, 0);

GL40.glEnableVertexAttribArray(2);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, normalID);
GL40.glVertexAttribPointer(2, 3, GL40.GL_FLOAT, false, 0, 0);

GL40.glEnableVertexAttribArray(3);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, shadeID);
GL40.glBufferData(GL40.GL_ARRAY_BUFFER, createFloatBuffer(shade.toArray(new Vector4f[shade.size()])), GL40.GL_STATIC_DRAW);
GL40.glVertexAttribPointer(3, 4, GL40.GL_FLOAT, false, 0, 0);
GL40.glVertexAttribDivisor(3, 1);

GL40.glBindBuffer(GL40.GL_ELEMENT_ARRAY_BUFFER, indexID);
for(int i = 0; i < amount / 128 + 1; i++) {
    int draws = Math.min(128, amount - i * 128);
    for(int j = 0; j < draws; j ++) {
        int loc = i * 128 + j;
        shader.setUniform("model[" + j + "]", Matrix4f.transform(position.get(loc), rotation.get(loc), scale.get(loc)));
    }
    GL40.glDrawElementsInstanced(GL40.GL_TRIANGLES, drawCount, GL11.GL_UNSIGNED_INT, 0, draws);
}

GL40.glBindBuffer(GL40.GL_ELEMENT_ARRAY_BUFFER, 0);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, 0);

GL40.glDisableVertexAttribArray(0);
GL40.glDisableVertexAttribArray(1);
GL40.glDisableVertexAttribArray(2);
GL40.glDisableVertexAttribArray(3);

Setting the mat4 uniform:

public void setUniform(String name, Matrix4f value) {
    int location = GL20.glGetUniformLocation(programID, name);
    if (location != -1) {
        try (MemoryStack stack = MemoryStack.stackPush()) {
            final FloatBuffer matrixBuffer = stack.mallocFloat(16);
            matrixBuffer.put(value.getAll()).flip();
            GL20.glUniformMatrix4fv(location, true, matrixBuffer);
        }
    }
}

And this is the code that's been causing issues:

// Prepare shader

GL40.glEnableVertexAttribArray(0);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, vertexID);
GL40.glVertexAttribPointer(0, 3, GL40.GL_FLOAT, false, 0, 0);

GL40.glEnableVertexAttribArray(1);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, textureID);
GL40.glVertexAttribPointer(1, 2, GL40.GL_FLOAT, false, 0, 0);

GL40.glEnableVertexAttribArray(2);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, normalID);
GL40.glVertexAttribPointer(2, 3, GL40.GL_FLOAT, false, 0, 0);

GL40.glEnableVertexAttribArray(3);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, shadeID);
GL40.glBufferData(GL40.GL_ARRAY_BUFFER, createFloatBuffer(shade.toArray(new Vector4f[shade.size()])),
        GL40.GL_STATIC_DRAW);
GL40.glVertexAttribPointer(3, 4, GL40.GL_FLOAT, false, 0, 0);
GL40.glVertexAttribDivisor(3, 1);

GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, modelID);
GL40.glBufferData(GL40.GL_ARRAY_BUFFER, createFloatBuffer(position.toArray(new Vector3f[position.size()]), rotation.toArray(new Vector3f[rotation.size()]), scale.toArray(new Vector3f[scale.size()])), GL40.GL_STATIC_DRAW);

int size = 4;
for (int i = 0; i < 4; i++) {
    GL40.glEnableVertexAttribArray(4 + i);
    GL40.glVertexAttribPointer(4 + i, 4, GL40.GL_FLOAT, false, 4 * size, i * size);
    GL40.glVertexAttribDivisor(4 + i, 1);
}

GL40.glBindBuffer(GL40.GL_ELEMENT_ARRAY_BUFFER, indexID);
GL40.glDrawElementsInstanced(GL40.GL_TRIANGLES, drawCount, GL11.GL_UNSIGNED_INT, 0, amount);

GL40.glBindBuffer(GL40.GL_ELEMENT_ARRAY_BUFFER, 0);
GL40.glBindBuffer(GL40.GL_ARRAY_BUFFER, 0);

GL40.glDisableVertexAttribArray(0);
GL40.glDisableVertexAttribArray(1);
GL40.glDisableVertexAttribArray(2);
GL40.glDisableVertexAttribArray(3);
GL40.glDisableVertexAttribArray(4);
GL40.glDisableVertexAttribArray(5);
GL40.glDisableVertexAttribArray(6);
GL40.glDisableVertexAttribArray(7);

Storing the mat4s into the VBO:

public static FloatBuffer createFloatBuffer(Vector3f[] positions, Vector3f[] rotation, Vector3f[] scales {
    FloatBuffer buffer = BufferUtils.createFloatBuffer(positions.length * 16);
    for (int i = 0; i < positions.length; i++) {
        buffer.put(Matrix4f.transform(positions[i], rotation[i], scales[i]).getAll());
    }
    buffer.flip();
    return buffer;
}

I've messed around with this a ton over the past couple days which leads me to believe that it's not a problem with the data being sent, but rather how the data itself is formatted. That doesn't make much sense though, since both examples use Matrix4f.transform() to get the needed data. I tried transposing the matrices before adding them to the buffer but to no avail.

And just for reference, here is an example of what Matrix4f.getAll() produces:

[0.5, 0.0, 0.0, -40.0,

0.0, 0.5, 0.0, 12.0,

0.0, 0.0, 0.5, 88.0,

0.0, 0.0, 0.0, 1.0]

Here are two screen shots of what it's suppose to look like and the results I'm getting from using the VBO approach: enter image description here

enter image description here

\$\endgroup\$
4
  • \$\begingroup\$ I have no idea about java, but you'd need a uniform buffer for sending in uniform data and a vertex buffer for stream data. Also, it seems like state bashing and slightly confusing to me to bind and unbind all those buffers and enable and disable all those attributes and asking for locations every time a uniform is set. Have you considered a single buffer with interleaved data, set up once if it doesn't change, and storing locations for quick access ? \$\endgroup\$
    – user144188
    Commented Dec 6, 2020 at 11:51
  • \$\begingroup\$ A buffer (uniform or vertex) that's updated every frame contains dynamic data and needs to be synchronized ... \$\endgroup\$
    – user144188
    Commented Dec 6, 2020 at 12:07
  • \$\begingroup\$ @a_donda Isn't that taken care of by glBufferData()? Its the buffer attached to modelID that's causing this to happen, but that should overwrite any data already present since its right before the draw call, right? \$\endgroup\$ Commented Dec 6, 2020 at 20:43
  • \$\begingroup\$ You're using the wrong tool: you need a uniform buffer for uniform data, not a vertex buffer. And glBufferData() on a static buffer is not the right API to update per-frame data (it works, but is terribly imperformant), you want a dynamic buffer that you can map and synchronize. \$\endgroup\$
    – user144188
    Commented Dec 6, 2020 at 22:21

0

You must log in to answer this question.

Browse other questions tagged .