5
\$\begingroup\$

I'm working my way through optimizing the rendering of sprites in a 2D game using OpenGL ES and I've hit the limit of my knowledge when it comes to GLSL and vertex shaders.

I have two large float buffers containing my vertex coordinates and texture coordinates (eventually this will be one buffer) for multiple sprites in order to perform a single glDrawArrays call. This works but I've hit a snag when it comes to passing the transformation matrix into the vertex shader.

My shader code is:

uniform mat4 u_MVPMatrix; 
attribute vec4 a_Position;      
attribute vec2 a_TexCoordinate; 
varying vec2 v_TexCoordinate; 

void main()
{
    v_TexCoordinate = a_TexCoordinate;
    gl_Position = u_MVPMatrix * a_Position;
}

In Java (Android) I am using a FloatBuffer to store the vertex/texture data and this is provided to the shader like so:

mGlEs20.glVertexAttribPointer(mVertexHandle, Globals.GL_POSITION_VERTEX_COUNT, GLES20.GL_FLOAT, false, 0, mVertexCoordinates);   
mGlEs20.glVertexAttribPointer(mTextureCoordinateHandle, Globals.GL_TEXTURE_VERTEX_COUNT, GLES20.GL_FLOAT, false, 0, mTextureCoordinates);

(The Globals.GL_POSITION_VERTEX_COUNT etc are just integers with the value of 2 right now)

And I'm passing the MVP (Model/View/Projection) matrix buffer like this:

GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mModelCoordinates); 

(mModelCoordinates is a FloatBuffer containing 16-float sequences representing the MVP matrix for each sprite)

This renders my scene but all the sprites share the same transformation, so it's obviously only picking the first 16 elements from the buffer which makes sense since I am passing in "1" as the second parameter. The documentation for this method says:

"This should be 1 if the targeted uniform variable is not an array of matrices, and 1 or more if it is an array of matrices."

So I tried modifying the shader with a fixed size array large enough to accomodate most of my scenarios:

uniform mat4 u_MVPMatrix[1000];

But this lead to an error in the shader:

cannot convert from 'uniform array of 4X4 matrix of float' to 'Position 4-component vector of float'

This just seems wrong anyway as it's not clear to me how the shader would know when to transition to the next matrix anyway.

Anyone have an idea how I can get my shader to pick up a different MVP matrix (i.e. the NEXT 16 floats) from my MVP buffer for every 4 vertices it encounters? (I am using GL_TRIANGLE_STRIP so each sprite has 4 vertices).

Thanks!

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

You could add a vertex attribute to identify the index of the matrix to be used. Something like:

attribute int a_iMVP;
...
gl_Position = u_MVPMatrix[a_iMVP] * a_Position;

Of course, this will consume extra space in your vertex buffer and need to be generated by your application code. If your hardware supports instancing, this attribute could be in a separate, instanced vertex buffer, so you'd only need one value per 4 verts instead of a value for every vert.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ I cannot find info about how to send an array of matrices to the vertex shader. Could you give me some examples how to do this? \$\endgroup\$
    – Winged
    Jun 19, 2014 at 15:59
  • \$\begingroup\$ @Winged You just declare an array of uniform matrices, as shown in the question: uniform mat4 u_MVPMatrix[1000]; You can use glUniformMatrix4fv to set them (all at once) from the CPU. \$\endgroup\$ Jun 19, 2014 at 21:28
  • \$\begingroup\$ How exactly should I call glUniformMatrix4fv? I'm using WebGL API and as far as I know, WebGL's version does not have count attribute, but this may not be the problem here. When I call gl.uniformMatrix4fv(program.mMatrixUniform, false, this.modelMatrices); where this.modelMatrices is an Float32Array of 4x4 Arrays, and program.mMatrixUniform is declared like this: program.mMatrixUniform = gl.getUniformLocation(program, 'uMMatrix'); (should I type 'uMMatrix[]' or 'uMMatrix[20]' instead?), it throws gl.INVALID_VALUE was caused by call to: uniformMatrix4fv. \$\endgroup\$
    – Winged
    Jun 24, 2014 at 18:06
  • \$\begingroup\$ @Winged Ah, I don't know if this is possible in WebGL. In vanilla OpenGL you'd pass a whole array of matrices by using the count parameter, as you mentioned. \$\endgroup\$ Jun 24, 2014 at 19:51
1
\$\begingroup\$

If all your sprites share the same View and Projection matrices (you are doing 2D sprites and not 3D billboarding), calculate the final quads via CPU and update your vertex buffer with their positions. Here is an answer that explains how XNA does its SpriteBatch magic.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your answer. I had started down this approach but was getting some weird results. Could have been my matrix manipulation that was janky. \$\endgroup\$ Oct 30, 2012 at 5:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .