[SOLVED]Instancing, Passing Changing Data to be Instanced

Hi,

First off, I would like to mention that I feel that I understand what instancing is, and somewhat when the best times to use is would be, more or less.
And I’ve come to the conclusion that instancing would be better overall than looping several hundred/thousand times in my case.

How ever, I’m a bit stumped when it comes to passing certain data. Matrix data for each instance.

The book I’m reading is using an example of drawing four squares, with instanced positions and colors.
The colors and positions are hard coded and passed to the GPU during the stage in which you create a VAO and VBO, and pass the relevant data. It doesn’t seem to use interleaving, where as for my data, I am.

In my situation, with what I’m thinking (of which may not be the best solution).
Is that I have the same space ship model, but I want it rendered in different places, with different rotations, so I figured the easiest way is to have everything permanent (vert positions, normals, uvs), but have a model matrix be instanced. But I don’t create and initialize all ships at the beginning (when I create the VAO and VBO for that object).

So overall, I’m unsure with how I go about it. When it’s not hard-coded, but dynamic.

If I’m unclear in what I’m stuck with, I apologize, it’s rather difficult to for me to put into words.

Withdrawn - I was talking about completely the wrong thing. Sorry for wasting peoples time.

Pretty sure Bogieman is talking about OpenGL instancing, not Java instances.

It’s possible to define per instance shader attributes using glVertexAttribDivisor():

Shader:


#version 150

uniform mat4 viewMatrix, projectionMatrix;
uniform mat3 normalMatrix;

//Per vertex attributes
in vec3 position;
in vec2 texCoords;
in vec3 normal;

//Per instance attribute
in vec3 instancePosition;

...

VAO setup:


			vao = glGenVertexArrays();
			glBindVertexArray(vao);

			glBindBuffer(GL_ARRAY_BUFFER, vbo);
			glVertexAttribPointer(positionLocation, 3, GL_FLOAT, false, VERTEX_SIZE, POSITION_INDEX);
			glVertexAttribPointer(texCoordsLocation, 2, GL_FLOAT, false, VERTEX_SIZE, TEX_COORD_INDEX);
			glVertexAttribPointer(normalLocation, 3, GL_SHORT, true, VERTEX_SIZE, NORMAL_INDEX);
			glEnableVertexAttribArray(positionLocation);
			glEnableVertexAttribArray(texCoordsLocation);
			glEnableVertexAttribArray(normalLocation);
			
			glBindBuffer(GL_ARRAY_BUFFER, instanceDataVBO);
			glVertexAttribPointer(instancePositionLocation, 3, GL_FLOAT, false, 0, 0);
			glVertexAttribDivisorARB(instancePositionLocation, 1); // <--- important
			glEnableVertexAttribArray(instancePositionLocation);
			
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
			
			glBindVertexArray(0);

You can do the same with an mat4 variable instead of a vec3 of course. In that case the matrix is split up into 4 separate vec4 attributes, with consecutive locations:


// model_matrix will be used as a per-instance transformation
// matrix. Note that a mat4 consumes 4 consecutive locations, so
// this will actually sit in locations, 3, 4, 5, and 6.
layout (location = 3) in mat4 model_matrix;

More information: http://www.informit.com/articles/article.aspx?p=2033340&seqNum=5

Yeah, that’s what I’m on about, I probably should have added that :confused:

Anyways, so from that example, does that mean that I’m creating a second VBO?

And would the “instancePositionLocation” have to be different, than all the previous ones?
Of which I have set to be from 0-4.
Meaning that it would have to be 5, with nothing in the slots for 6,7 and 8 as well as, since it’s like 4 separate vectors that contain 4 separate values.

 glVertexAttribPointer(instancePositionLocation, 3, GL_FLOAT, false, 0, 0);

Also, why is each attribute enabled after they are set.
I currently have it that I just enable them during the rendering part.
Is it optional, or do I have to do it in order to get instancing to work, or is it generally better to do it that way?

I’ll try and set this up, and I’ll come back if I have more questions, issues, or let you guys know that I got it working.

You usually create a second VBO for per-instance data since you’ll be updating that VBO each frame, while the per-vertex data is usually static.

Per-instance attributes work exactly the same as normal attributes, except that you call glVertexAttribDivisor() to tell OpenGL to go to the next attribute in the buffer after each instance has been processed, instead of after each vertex. You can use whatever code you already have for this and just add a call to glVertexAttribDivisor() where needed.

If you have a mat4, you’d instead do something like this:


glVertexAttribPointer(modelMatrixLocation + 0, 4, GL_FLOAT, false, 64,  0);
glVertexAttribPointer(modelMatrixLocation + 1, 4, GL_FLOAT, false, 64, 16);
glVertexAttribPointer(modelMatrixLocation + 2, 4, GL_FLOAT, false, 64, 32);
glVertexAttribPointer(modelMatrixLocation + 3, 4, GL_FLOAT, false, 64, 48);

glVertexAttribDivisor(modelMatrixLocation + 0, 1);
glVertexAttribDivisor(modelMatrixLocation + 1, 1);
glVertexAttribDivisor(modelMatrixLocation + 2, 1);
glVertexAttribDivisor(modelMatrixLocation + 3, 1);

glEnableVertexAttribArray(modelMatrixLocation + 0);
glEnableVertexAttribArray(modelMatrixLocation + 1);
glEnableVertexAttribArray(modelMatrixLocation + 2);
glEnableVertexAttribArray(modelMatrixLocation + 3);

In essence, a mat4 attribute is just treated as 4 consecutive vec4 attributes.

In the code I posted I created a VAO (vertex array object) and set up my vertex attributes once. When I want to render stuff, I simply bind the VAO again and all my vertex attribute settings are automatically restored so I don’t have to call all those functions again.

And to update the data, do I use the following?

glBufferSubData(GL_ARRAY_BUFFER, 0, getMatrixBuffer())

But would I set it up as, in my case, like the following?
(This would get called every time the game loop goes through the render part)


for(int j = 0; j < baseObject[i].getNumOf(); j++) {
	baseObject[i].getModelMatrix()[j].store(getMatrixBuffer()); 
}
getMatrixBuffer().flip();			
// IVbo stands for Instance Vertex Buffer Object, simply so I know it's separate from the normal Vbo
glBindBuffer(GL_ARRAY_BUFFER, baseObject[i].getModel().getIVbo());
// Would offset be 0, since it would be everything in the attribute being updated
glBufferSubData(GL_ARRAY_BUFFER, 0, getMatrixBuffer(); 
getMatrixBuffer().clear();
glBindBuffer(GL_ARRAY_BUFFER, 0);

This is what I have when I’m just doing it one by one.

for(int j = 0; j < baseObject[i].getNumOf(); j++) {
	baseObject[i].getModelMatrix()[j].store(getMatrixBuffer()); 
}
getMatrixBuffer().flip();			
GL20.glUniformMatrix4(getShaders()[0].getUniform(UniformType.MODEL).getLocation(), false, 
						     getMatrixBuffer());
getMatrixBuffer().clear();

Also, is it possible to have just one of the attributes instead of 4?
Like so?

// 3 is the last position used in the static VBO, so I put the location as 4
glVertexAttribPointer(4, 16, GL_FLOAT, false, 64,  0);

Or would OpenGL be unable to automatically divide the data so that it ends up getting 4 vec4s.

Your code looks fine.

You really need all 4 of each vertex attribute setup call though. From the perspective of OpenGL, the shader simply has 4 vec4 attributes. They’re not related or linked in any place, so if you only enable the first vec4 you’re only going to update the first of the four rows in your mat4.

[s]Hmm, I’m getting an error.

I’m using LWJGL.
It’s value is 1281.
And that seems to mean Invalid Value.

I don’t really know the issue, it seems to go through all of it, and then, due to my error checking class, terminate the program.
(I have it, so if an error is found that it cleans up and calls System.exit(-1); )[/s]

Helped if I read the replies properly, anyways, it now just simply crashes, seems it crashes at the point when I do the error checking.

More specifically, it seems when it gets to,

int val = GL11.glGetError();

When you get to glGetError(), your driver is forced to actually complete all the commands that happened before glGetError(), so the error isn’t due to glGetError(), but glGetError() is the trigger. Complete VM crashes are almost always related to access violations, e.g. that you’re telling OpenGL to read vertex data from somewhere illegal. Double-check that you haven’t forgotten a flip() and that your vertex attributes are correctly set up.

Ok, so I got it semi working, it renders and all with one instance, how-ever, should I try and have more than one instance, I now get java.nio.BufferOverflowException

I assume it may be due to my limited knowledge when it comes to buffer objects in Java.
Anyways, here’s a snippet where it happens, of which may look familiar.
(Note: I have it that the size is currently 64, but even if I up it to 1000, or even 1mil, it still happens.)

for(int j = 0; j < 2; j++) {
	// It happens when getModelMatrix()[j] gets to 1, or rather, the second element in the array.
	baseObject[i].getModelMatrix()[j].store(getMatrixBuffer()); 
}		
getMatrixBuffer().flip();			
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, baseObject[i].getModel().getIVbo());
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, getMatrixBuffer());
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
getMatrixBuffer().clear();

+  // always make sure we know/set the state of the buffer *before* we write into it.

+  getMatrixBuffer().clear();
for(int j = 0; j < 2; j++) {
	// It happens when getModelMatrix()[j] gets to 1, or rather, the second element in the array.
	baseObject[i].getModelMatrix()[j].store(getMatrixBuffer()); 
}		
getMatrixBuffer().flip();			
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, baseObject[i].getModel().getIVbo());
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, getMatrixBuffer());
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
-  getMatrixBuffer().clear();

Ah thank you, it doesn’t crash anymore, but it seems I have a bug else where lol

Also, for the code snippet, how do I make it so you have a + and it’s got a green highlight?
And the how do I it for the red as well?


+ put a "+" in front of the line and it's highlighted green
- or put a "-" in front of it.

I’m stumped, I can’t figure out why the other instances aren’t rendering.
I’ve got it so that a total of 3 instances of each object should appear, but only the first one appears.
Perhaps I didn’t input enough or some data correctly.

[s]This bit is what I’m only truly unsure about, but I’m unsure if this is the cause.

glDrawElementsInstanced(int mode, int indices_count, int type, long indices_buffer_offset, int primcount)
// Here's what I have
GL31.glDrawElementsInstanced(GL11.GL_TRIANGLES, baseObject[i].getModel().getNumOfIndeces(), 
						GL11.GL_UNSIGNED_INT, 0, baseObject[i].getNumOf());

Is it right that I have “indices_buffer_offset” at zero, messing with the value screws it all up.[/s]
I’ve figured out what the “indices_buffer_offset” does, doesn’t seem too relevant.

Also, when I create the second VBO, I didn’t use glBufferData(int target, java.nio.ByteBuffer data, int usage).
Instead, I put it in the rendering part, when I pass the matrix buffer like so.

getMatrixBuffer().clear();
for(int j = 0; j < baseObject[i].getNumOf(); j++) {
	baseObject[i].getModelMatrix()[j].store(getMatrixBuffer()); 
}		
getMatrixBuffer().flip();			
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, baseObject[i].getModel().getIVbo());
+GL15.glBufferData(GL15.GL_ARRAY_BUFFER, getMatrixBuffer(), GL15.GL_DYNAMIC_DRAW);
-GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, getMatrixBuffer());
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

Could this cause issues, beside performance.

I don’t think it’s the shaders, could be, but I don’t think it is.
As now I multiply the mvp matrix using the modelmatrix attribute and it works, instead of from a uniform.

Looking at the link theagentd provided.
They have a part like so.

// Map the buffer
mat4 * matrices = (mat4 *)glMapBuffer(GL_ARRAY_BUFFER,
                                      GL_WRITE_ONLY);

// Set model matrices for each instance
for (n = 0; n < INSTANCE_COUNT; n++)
{
    float a = 50.0f * float(n) / 4.0f;
    float b = 50.0f * float(n) / 5.0f;
    float c = 50.0f * float(n) / 6.0f;

    matrices[n] = rotation(a + t * 360.0f, 1.0f, 0.0f, 0.0f) *
                  rotation(b + t * 360.0f, 0.0f, 1.0f, 0.0f) *
                  rotation(c + t * 360.0f, 0.0f, 0.0f, 1.0f) *
                  translation(10.0f + a, 40.0f + b, 50.0f + c);
}

// Done. Unmap the buffer.
glUnmapBuffer(GL_ARRAY_BUFFER);

Should I be doing it similar to this instead of redoing the buffer data every-time.

Oh, and thanks matheus. And thanks for the tip Riven. :smiley:

I’ve still not made any progress. Anyone have any idea?
Only one instance will render. Although the frame rate dips as if multiple instances are rendering.
Or at the very least, I can only see one instance.

Got it sorted guys, seems like I have to do the following when initializing the model.

// This is just a small part, no point showing all of it.
GL20.glVertexAttribPointer(4, 4, GL11.GL_FLOAT, false, 64, 0);
GL20.glEnableVertexAttribArray(4);
GL33.glVertexAttribDivisor(4, 1);

Instead of leaving it the way I had it.
Where I would called “glVertexAttribDivisor(num ,num);” just before rendering.

Anyways, thanks for the help guys, love this forum for the help :smiley: