Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Background

In my app I use the following code to rotate my quad:

Code

    //Rotate the quad

Matrix.setIdentityM(mRotationMatrix, 0);
Matrix.translateM(mRotationMatrix, 0, centreX, centreY, 0f);
Matrix.rotateM(mRotationMatrix, 0, -angle, 0, 0, 0.1f);
Matrix.translateM(mRotationMatrix, 0, -centreX, -centreY, 0f);

And then apply the matrices:

// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(mvpMatrix2, 0, mvpMatrix, 0,  mRotationMatrix, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(iProgId, "uMVPMatrix");
 // Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix2, 0);

The above code works great when I'm drawing single quads.

Problem

However, if I have a few quads with the same texture to draw, I batch them up and draw them with one call to glDrawArrays.

I can't work out if it is possible to rotate each individual quad before drawing them (or how to do it if it is possible) - I realise, they will all be rotated the same amount at the same time but this isn't an issue).

Grateful for any advise.

EDIT Rotation method:

public void rotateBatchQuads(int[] coordinates, int angle){

for (x=0;x<coordinates.length;x+=2){


//Center of quad (Along the x)
float centreX = coordinates[x]+quadWidth/2;  //Pseudo code
float centreY = coordinates[x+1]+quadHeight/2 //Pseudo code
//Center of quad (Along the y)

//Rotate the quad

Matrix.setIdentityM(mRotationMatrix, 0);
Matrix.translateM(mRotationMatrix, 0, centreX, centreY, 0f);
Matrix.rotateM(mRotationMatrix, 0, -angle, 0, 0, 0.1f);
Matrix.translateM(mRotationMatrix, 0, -centreX, -centreY, 0f);

}

}
share|improve this question
add comment

3 Answers

You can rotate the vertices before copying them into a vertex buffer. Something like:

batch = vertex[SIZE]
vbo = gl.CreateBuffer(SIZE * sizeof(vertex))
default_quad = { (-1,-1), (-1,1), (1,1), (1,-1) }
transformed_vertices = vertex[4]

draw(sprites)
{
  for each sprite in sprites
  {
    matrix = sprite.CalculateModelWorldMatrix()
    matrix.ApplyTo(default_quad_vertices, transformed_vertices)
    batch.Append(transformed_vertices)

    if batch.IsFull
    {
      gl.BufferData(vbo, batch)
      gl.Draw(vbo)
      batch.Clear()
    }
  }

  if not batch.IsEmpty
  {
    gl.BufferData(vbo, batch)
    gl.Draw(vbo)
    batch.Clear()
  }
}

Multiply the standard quad vertices by the sprite's model-world matrix in your CPU code then append them to an array. Copy the array to your VBO. Draw. Give the shader just the world-view (camera) and view-projection matrices (as your vertices will have already been transformed to world space so the GPU has no use for a model-world matrix).

Applying the transform simply means to multiply each vertex of a quad with the sprite's model-view matrix on the CPU. Each sprite can have a completely different transform, there's no need for all rendered sprites to have the same rotation (or scale or position).

Note that in this approach, you should NOT create a new array of vertices for your batch or a new VBO each frame. Create these once and cache them. Creating them again wastes processing time and produces excess garbage for the GC. Create them once at a reasonable size and reuse them. Never ever allocate (create new objects) inside your rendering loop unless you literally can't avoid it. If you end up with too many quads to fit into the sizes you chose, you can process the quads in chunks. e.g. if your buffer can only hold 100 quads and you have 120 of them, draw 100 quads in one batch and the remaining 20 in a second batch.

Once GLES 3.0 is available on all your target platforms and hardware you should look into instancing which is a more efficient way of doing the above on the hardware. With instancing you can create a buffer filled with the transformation matrices of X quads into a buffer (or even the raw position/rotation/scale data used to compute the matrix) and then ask the GPU to draw a generic quad X number of times using that buffer as input data for each individual instance.

share|improve this answer
    
Hey @SeanMiddleditch thanks for this. Still a little confused after reading your answer. When do I actually apply the rotation matrix? Every time I apply it (Matrix.rotateM(mRotationMatrix, 0, -angle, 0, 0, 0.1f);) it rotates the entire batch and not just the individual quad. I've added my rotation method to my code, would be grateful if you could advise how I would implement your answer based on my code. I'm not using VBO's by the way if that makes any difference. Thanks! :-) –  user22241 Jun 12 '13 at 11:57
    
Copy the default quad vertices (4 of them) into a buffer. Apply the matrix to those four copies. Now you have a single rotated quad. Note you will want to perform your translation and scale here as well, the entire model->world transformation must be done on the CPU before copying the vertices into the batch VBO. –  Sean Middleditch Jun 12 '13 at 17:09
    
I'm not using VBO's - does this matter, also sorry but I don't understand how to apply the rotation to the 4 vertices - using Matrix.rotateM(mRotationMatrix, 0, -angle, 0, 0, 0.1f);? How is that achieved? Thanks. –  user22241 Jun 12 '13 at 21:26
    
No. Just update the arrays and don't worry about the VBO part. I can't help you with the API for your chosen toolkit, I don't know what Matrix library you're using (and even if I did, I don't know how to use any of the Android Java libraries); the documentation should cover how to multiply vertices by a matrix. –  Sean Middleditch Jun 12 '13 at 21:48
add comment

You can also use an array of uniform matrices and draw in groups based on the number of uniform matrices you are able to store in the GLSL shader.

You will hit uniform limits fairly quickly with this approach.

share|improve this answer
add comment

In this case you'd consider multiplying each quad's base position by your mRotationMatrix on the CPU, then loading the result into a dynamic VBO for drawing. That's a clear tradeoff - the overhead of CPU-side matrix multiplications, together with the overhead of having to transfer the quad data each frame, and that together with the fact that dynamic VBOs just aren't going to be as fast as static would mean that you may or may not come out on the right side of it being faster versus a single draw call per quad.

Another option for you is to rebuild the rotation matrix in your vertex shader from 3 extra input attributes (centerX, centerY, angle); if you can simplify this well enough it may turn out to be the fastest option of all.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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