I want to draw lots of spheres in different locations and orientations with Opengl4 and JOGL. As the vertexes and colours are the same for all of them, I have just one array for vertexes and another for colours. For the positions and orientations, I have another big matrix where I have all data for all spheres.

In principle, drawing one with glDrawArrays is not a problem but for severals, I have read that I should use glDrawArraysInstanced instead. My problem is that I am a bit confused about how to apply each transformation for my particles. How should I introduce this array into the shader? Should I send the matrix model after doing the transformations in the cpu or should I send the positions and orientations and transform them inside the shader? How do I connect the data to the shader? How should the shader look like?

  • 1
    Sphere's with different orientations? That shouldn't change it. – Bálint Jun 9 '17 at 13:16
  • 2
    yes if you have a texture that is not just a solid colour ;) – Learning from masters Jun 9 '17 at 13:17
  • 1
    Right, forgot about that – Bálint Jun 9 '17 at 13:27

Yes, in instanced rendering, you typically supply transformation matrices. And this is how you do that in OpenGL:

In the vertex shader, you supply vertex position and normal as you usually do, but now you also have a vertex attribute for the transformation like this:

in mediump vec4 position;
in mediump vec4 surfacenormal;
in mediump mat4 trf;

And then you multiply your position and surfacenormal with the transformation matrix like so:

mediump vec4 transformedpos = trf * position;
mediump vec4 sn = surfacenormal;
sn.w = 0.0;
mediump vec4 transformedsurfacenormal = trf * sn;

Next, use the transformed version in your vertex shader as you usually do.

The matrix on the application side, is treated as 4 consecutive vertex attributes of 4 floats each.

You need to tell OpenGL that these vertex attributes need to advance to the next value, not for each vertex, but once for each instance. You do this with glVertexAttribDivisor() calls like so:

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

And tell OpenGL where to find these 4 consecutive attributes in the VBO:

glVertexAttribPointer( ATTRIB_TRF+0,  4, GL_FLOAT, 0, 16 * sizeof(float), (void*) ( off +  0*sizeof(float) ) );
glVertexAttribPointer( ATTRIB_TRF+1,  4, GL_FLOAT, 0, 16 * sizeof(float), (void*) ( off +  4*sizeof(float) ) );
glVertexAttribPointer( ATTRIB_TRF+2,  4, GL_FLOAT, 0, 16 * sizeof(float), (void*) ( off +  8*sizeof(float) ) );
glVertexAttribPointer( ATTRIB_TRF+3,  4, GL_FLOAT, 0, 16 * sizeof(float), (void*) ( off + 12*sizeof(float) ) );

Note that you will want to put the per-instance data in the VBO, after all the normal model (per-vertex) data, which in my code sample has size 'off'.

Typically, you will want to change the values for these transformation each frame. This means you need to update a part of your VBO and put in the new versions of the matrices. This is done with glBufferSubData()

const size_t sz = 16 * sizeof( float ) * objCnt;
glBufferSubData( GL_ARRAY_BUFFER, off, sz, object_matrices );

And finally draw all instances with a call to glDrawInstancedArrays()

glDrawArraysInstanced( GL_TRIANGLES, 0, numVerts, objCnt );

Here, numverts is the number of vertices in the model itself, not the total nr of verts drawn for all instances.

To sum it all up: the instance transformation matrix is treated as a per vertex attribute in your GLSL vertex shader. Yet, the data is fed not as a value per vertex, but only a new value per (typically 1) instance using glVertexAttribDivisor() which you need to call four times, once for each row in the matrix.

For instanced rendering there's GLSL variable called gl_InstanceID, which stores the id of the current instance. You can do 2 things:

1.) You create an array of matrices and you load each transformation matrix in it, then use the instance id to get the correct one. This way you don't have to create matrices on the fly (you only have to do it once, not as for each vertex), but you'll need to have a max number of spheres, because OpenGL doesn't support dynamic arrays.

2.) You load the positions and rotations as vec3-s in a dynamic buffer, then you reconstruct the matrices on the fly. If the spheres don't have too many vertices, then this may be a better solution.

As i understood you can draw them by this way.

you should have a vec3 list which you hold the positions of spheres.(another list for rotations)

using for loop

for(int i = 0;i < count_of_positions;i++)
{
    modelMatrix.translate(positions[i]);
    (same for rotations)
    shader.setUniformMat4("modelmat", modelMatrix);(example shader code)
    glDrawArrays(params);
}

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.