Reputation: 427
Good evening, following scenario:
I'm implementing a label rendering algorithm. The input data consists of a number of strings and a texture atlas that has texture data for every possible string glyph. Every string is supposed to generate a number of quads, one for every instance of every glyph, whose texture coordinates reference the texture atlas.
The only thing that can change between the frames is the on screen position of the labels and their glyphs. So, my first thought was to create a huge buffer of quads that make up the glyphs in local coordinates, then just read the position from a separate buffer that I update every frame.
Now my question: What would be the currently fastest method to do this in OpenGL ES 3.1? My first thought was using glDrawElements with GL_TRIANGLE_STRIP using a primitive restart index, then convert the index to some entry in my buffer inside the vertex shader and read the position from that.
However, I wonder if a faster method has popped up in the meantime.
Thank you in advance!
Upvotes: 0
Views: 1596
Reputation: 11
I've written a small sprite shader which is a clone of the sprite shader from here http://learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites
The only difference between straightforward OpenGL and OpenGL ES 3.1 would be a few lines in the shaders. You need to change the version in both shaders from:
#version 330 core
to
#version 300 es
And, to the fragment shader, add the line
precision mediump float;
somewhere near the top.
Then, assuming your shader has a model and projection matrix uniforms, you just use a 2d vector position to create a 4d vector, substituting 0.0 and 1.0 for the last two parameters in your vertex shader:
gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);
I feel this is explained better in Joey's LearnOpenGL tutorials than I can explain in detail here.
Your draw function might look something like this (if you use C++11):
mShader( [ & ] ( ) // Same as glUseProgram( shaderID ) but automatically un-Uses it when it goes out of scope
{
glm::mat4 model;
model = glm::translate( model, glm::vec3( xPosition, 0.0f ) );
model = glm::translate( model, glm::vec3( 0.5f * xSize.x, 0.5f * xSize.y, 0.0f ) );
model = glm::rotate( model, xRotate, glm::vec3( 0.0f, 0.0f, 1.0f ) );
model = glm::translate( model, glm::vec3( -0.5f * xSize.x, -0.5f * xSize.y, 0.0f ) );
model = glm::scale( model, glm::vec3( xSize, 1.0f ) );
mShader.getUniforms( ).setUniformMatrix4fv( "model", model );
mShader.getUniforms( ).setUniformVector3f( "spriteColor", xColor );
glActiveTexture( GL_TEXTURE0 );
xTexture( [ & ]( ) // Does the same as glBindTexture but automatically unbinds when out of scope
{
glBindVertexArrayOES( mQuadVAO );
glDrawArrays( GL_TRIANGLES, 0, 6 );
glBindVertexArrayOES( 0 );
} );
} );
The only difference in my implementation from the tutorial that is I use SDL to get an OpenGL ES 3.0 context (for portability and other functions, such as keyboard, mouse). I can confirm that Joey's tutorial runs on Android, embedded Linux Intel integrated graphics and Windows with the AMD GLES3 SDK with just the few minor changes I mentioned above.
You might want to augment the implementation by using a PBO; an OpenGL version is in the same repo.
It uses PBOs. This can be used to update the quad textures more rapidly than glTexImage2D
. I don't know if PBOs are supported in OpenGL ES but they might be by extension. If you just need to load a bunch of quads ahead of time and render them, you don't need to go that far.
To update the quad in the straightforward way, it's just:
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE );
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, mW, mH, GL_RGBA, GL_UNSIGNED_BYTE, xData );
Using the PBOs, you can use two PBOs to enable a "double buffering feature" where you clear a PBO and map the other PBO each turn. See the OpenGL ES specifications for glBindBufferARB
, glBufferDataARB
, glMapBufferARB
, glUnmapBufferARB
for more information. Apparently, as of 3.0, OpenGL ES does support PBOs so if you need to update the textures rapidly, this is the way to go.
Upvotes: 1
Reputation: 2779
OpenGL ES based devices are mostly limited on number of draw calls for each frame. So you need to batch all triangles of every character into one single mesh and draw it once. This will give you the best performance in most cases.
There are some limitations in this method.
Upvotes: 0