0
\$\begingroup\$

I've been looking over my code and I'm just wondering, when I set a texture for say, 20 quads that need to use the same texture, it seems as though I'm creating a new texture each time...... surely this isn't efficient?

How can I make sure that 20 quads that use the same texture do just that, use only 1 rather than creating 20 copies of it?

I just can't work out how I can do this. I know I can change the texID after applying the texture so that all quads that use the same texture, will have the same Texture ID, but this isn't really the issue I'm facing.

Of course, I may be completely misunderstanding how OpenGL deals with textures and my code may be OK :-/

Code:

    public void setTexture(GLSurfaceView view, Bitmap imgTexture){
        this.imgTexture=imgTexture;                 
        //Create program from Utils class
        iProgId = Utils.LoadProgram(strVShader, strFShader);
        //Return location of u_basemap
        iBaseMap = GLES20.glGetUniformLocation(iProgId, "u_baseMap");
            //Return location of attribute variables
        iPosition = GLES20.glGetAttribLocation(iProgId, "a_position");
        iTexCoords = GLES20.glGetAttribLocation(iProgId, "a_texCoords");
        //Return usable texture ID from Utils class
                texID = Utils.LoadTexture(view, imgTexture);

}

And....

public static int LoadTexture(GLSurfaceView view, Bitmap imgTex){

    //Array for texture
    int textures[] = new int[1];
    try {
        //Return texture name in textures Array
        GLES20.glGenTextures(1, textures, 0);
        //Bind textures
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
        //Set parameters
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);

        //Apply the texture to the image loaded
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, imgTex, 0);

        //clamp the texture
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);        
    } catch (Exception e){          
    }
    //Increase texture count by one
    textureCount++;
    return textures[0];
}

Edit

I've attempted the suggestion below (just calling glBindTextures()) however, I'm not having any luck. Clearly I'm not understanding this correctly.

Lets say I have 2 sprites, Object1 and Object2.

I call the following:

object1.setTexture(this, myAtlasTexture); //Calls setTexture (See above)

This works and sets the texture on this sprite, so I can now draw it.

Now I want object2 to use the same texture. So I don't want to call this again and create a new texture (I believe this is a bad idea) - I simply want to use the texture that was previously created when I called setTexure on object1.

So, instead of calling setTexture on object 2, I do the following:

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIDOfObject1);

(Note textureIDOfObject1 is just that, I just query object1 to obtain its texture ID).

\$\endgroup\$
  • \$\begingroup\$ Is this using the Android SDK? You should usually tag questions with the language/framework you're using, not just opengl-es. Also, you haven't given us enough information to know what's going on. Where is setTexture being called from? What does your draw function look like? \$\endgroup\$ – bcrist Jun 21 '14 at 22:17
  • \$\begingroup\$ Hi @bcrist, yes this is a 2d engine I'm writing with the standard SDK/Java - I've updated the tag to include Java. setTexture() is being called from my custom Resource class (where all of my objects are created and properties applied etc) - I didn't post the draw routine because I didn't think it was relevant as this was purely about applying the texture and not rendering it. But I can add if you think it's required. \$\endgroup\$ – BungleBonce Jun 21 '14 at 23:14
  • \$\begingroup\$ If by "applying the texture" you mean binding a GL texture object (identified above by texID) to a texture image unit and binding that TIU to a sampler uniform in a program somewhere, then the code you posted does not do that either. All that we can see happening here is that when setTexture is called, a new GL program object is created, some attribute/uniform locations are queried, and a new GL texture object is created/uploaded. Nothing else is done with the texture object. \$\endgroup\$ – bcrist Jun 21 '14 at 23:43
  • \$\begingroup\$ If you're only calling setTexture once per material, then you're probably fine. Creating a different program for each material is probably unnecessary, as you can probably use the same program for most if not all of them, but it may not be a huge issue if you don't have more than a handful of materials. On the other hand if setTexture is being called every frame, for every single quad, you have a big problem. \$\endgroup\$ – bcrist Jun 21 '14 at 23:47
  • \$\begingroup\$ Thanks @bcrist, no I don't call this every frame, this is a one-time setup after all my quad objects have been created. What was worrying me was that as you say, a new program is being created every time I call it and a new 'texID'. However, I have 71 objects and I'm calling this once per object. 50 of these objects use the same texture (which is an atlas of smaller textures), so I thought it was inefficient and thought I could get away with somehow calling it once for all objects that use the same texture. Sorry, I'm probably wrong and misunderstanding it! Cheers. \$\endgroup\$ – BungleBonce Jun 21 '14 at 23:55
2
\$\begingroup\$

So I worked out how to do this:

Lets say I have object1, object2, object3 etc.... and these objects all use a common texture.

I call my setTexture method (as detailed in my question) for object1 and then, instead of calling setTextures on the remaining objects, I created a new method and called that instead.

public void bindExistingTexture(int textureToBind){

        //As before
        iProgId = Utils.LoadProgram(strVShader, strFShader);
        iBaseMap = GLES20.glGetUniformLocation(iProgId, "u_baseMap");
        iPosition = GLES20.glGetAttribLocation(iProgId, "a_position");
        iTexCoords = GLES20.glGetAttribLocation(iProgId, "a_texCoords");

        //Here, instead of creating and loading another texture, we simply bind an existing texture
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureToBind);
        //And update the texID value used in rendering methods
        texID = textureToBind;

}

So, it's pretty much the same method, but instead of:

texID = Utils.LoadTexture(view, imgTexture);

I simply re-bind an existing texture and update the local variable to match.

My call would look something like this:

    object1.setTexture(this, myAtlasGraphic);
    object2.bindExistingTexture(object1.texID);

This has greatly reduced my app loading time.

\$\endgroup\$
  • \$\begingroup\$ Glad to see that you found a solution! Don't forget to mark it as accepted - it helps others who may run into the same or similar problems. \$\endgroup\$ – Pikalek Aug 29 '18 at 13:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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