6

My question is quite trivial I believe, I'm using OpenGL ES 2.0 to draw a simple 2D scene.
I have a background texture that stretches the whole screen and another texture of a flower (or shel I say sprite?) that drawn at a specific location on screen.

So the trivial why i can think of doing it is to call glDrawArrays twice, one with the vertices of the background texture, and another one with the vertices of the flower texture.

Is that the right way? if so, is that mean that for 10 flowers i'll need to call glDrawArrays 10 times?

And what about blending? what if i want to blend the flower with the background, i need both the background and flower pixel colors and that may be a problem with two draws no?

Or is it possible to do it in one draw? if so how can I create a shader that knows if it now processing the background texture vertex or the flower texture vertex?

Or is it possible to do it in one draw?     

The problem with one draw is that the shader needs to know if the current vertex is a background vertex (than use the background texture color) or a flower vertex( than use the flower texture color), and I don't know how to do it.  

Here is how I use one draw call to draw the background image stretches the whole screen and the flower is half size centered.

- (void)renderOnce {
    //... set program, clear color..

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, backgroundTexture);
    glUniform1i(backgroundTextureUniform, 2);

    glActiveTexture(GL_TEXTURE3);
    glBindTexture(GL_TEXTURE_2D, flowerTexture);
    glUniform1i(flowerTextureUniform, 3);

    static const GLfloat allVertices[] = {
        -1.0f, -1.0f, // background texture coordinates
        1.0f, -1.0f,  // to draw in whole screen
        -1.0f,  1.0f, //
        1.0f,  1.0f,

        -0.5f, -0.5f, // flower texture coordinates
        0.5f, -0.5f,  // to draw half screen size
        -0.5f,  0.5f, // and centered
        0.5f,  0.5f,  //
    };

    // both background and flower texture coords use the whole texture
    static const GLfloat backgroundTextureCoordinates[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
    };

    static const GLfloat flowerTextureCoordinates[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
    };

    glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, 0, 0, allVertices);
    glVertexAttribPointer(backgroundTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, backgroundTextureCoordinates);
    glVertexAttribPointer(flowerTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, flowerTextureCoordinates);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
genpfault
  • 51,148
  • 11
  • 85
  • 139
Eyal
  • 10,777
  • 18
  • 78
  • 130

1 Answers1

5

You have two choices:

  1. Call glDrawArrays for every texture you want to draw, this will be slow if you have more than 10-20 textures, to speed it up thought you can use hardware vbo
  2. Batch the vertices(vertices,texture coords,color) of all the sprites you want to draw in one array and use a texture atlas(a texture that has all of the pictures you want to draw in it) and draw all this with one glDrawArrays

The second way is obviously the better and the right one.To get an idea of how to do it ,look at my awnser here

Community
  • 1
  • 1
SteveL
  • 3,331
  • 4
  • 32
  • 57
  • thanks for your reply, I updated my answer with an example of how I draw with one draw call. My problem is how to build a shader that will know to read the right color for the background/flower vertex. can u please check it out – Eyal Sep 30 '13 at 11:23
  • This is not possible in opengl es .On the time you call glDrawArrays ,only one shader can be active and the attributes of it will be the last values you passed to it before glDrawArrays.The only way to do what you want is to call glDrawArrays twice, one for the background(with the background shader) and one for all the flowers(with the flower's shader) – SteveL Sep 30 '13 at 12:17
  • I'm confused, so how do I do what u suggested (option 2)? – Eyal Sep 30 '13 at 12:33
  • If you really wanted to do it in a single draw call, you *could* store an ID of which texture to use in your VBO (duplicating the same ID for every vertex). This is entirely possible but not really practical. Lets say you have an animated sprite, you *could* implement it by binding 10 textures and choose the texture to sample inside your shader. A better way would be to pack the frames into the one texture. Similarly, as SteveL says, you could create a texture atlas with both the background and your flower. Then use texture coordinates to decide the texture rather than an ID. – jozxyqk Sep 30 '13 at 12:38
  • 1
    @Eyal , The only option if you want to use different shaders is to call glDrawArrays for every different shader.So the way to go is: 1)Draw background (with the background shader without batching ,if you have only one background) 2)batch and draw all the flowers(with the flower shader). – SteveL Sep 30 '13 at 13:15
  • @SteveL what about blending then? what if i want more than gl_blen blending option? – Eyal Sep 30 '13 at 13:19
  • @Eyal , One other way , to proccess diffent the vertices of background from the flower vertices is to do something similar to what jozxyqk saggested , store an id of what you are drawing(background or flower) at the VBO(in every vertice) and in your shader use an if statement like this : if(id==0)backgroundShader();else if(id==1)flowerShader(). But this is a solution that i would never use , calling glDrawArrays one time for every different shader is surprisingly faster than using complex shaders.I hope i didint confused you more with this comment. – SteveL Sep 30 '13 at 13:20
  • @Eyal ,Call glDrawArrays twice(as i said above) and change the blending mode before each call with glBlendFunc(), or do blending yourself inside each shader. – SteveL Sep 30 '13 at 13:24