This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion
  • Note: This was originally posted on 21st March 2013 at http://forums.arm.com

    Hi Irfan,

    I don't think it will make any difference moving to OpenGL ES 3.0 unless you *need* to use ETC2 since OpenGL ES 2.0 only supports ETC1.

    I suspect the problem must be in the texture loader code, not recognizing the output file from the Mali Texture Compression Tool.

    Can you tell us more about what engine or library code you are calling to load the textures? If I were investigating this I would try and debug the code to step through and find where the error message is generated, and which test is performed just before the error message is reported - this should narrow down which part of the file format the loader thinks is wrong.

    HTH, Pete
  • Note: This was originally posted on 21st March 2013 at http://forums.arm.com

    Hey Pete thanks for responding to my question.  Actually I accidently sumbitted two posts on this forum, one this one and the other with the same title and question but also with parts and pieces of what I thought was relevant code that involved loading textures using ETC1. Feel free to have a look at what I posted in that other post with code and see if you could figure something out.   I have several times tried debugging my code line by line and trying to follow what Logcat was telling me.  I have double checked all my code, from the shaders to the rendering code but to no avail.  As far as moving to  openGL ES 3.0, there is in my android game that I plan on using and that is a water environment.  From what I hear, if you want to incorporate translucent water effects you should switch to ES 3.0 rather than ES 2.0 as ES 2.0 doesn't really support much the alpha channel at least if you're planning on using ETC1 (the most common Android texture format). 


    Hi Irfan,

    I don't think it will make any difference moving to OpenGL ES 3.0 unless you *need* to use ETC2 since OpenGL ES 2.0 only supports ETC1.

    I suspect the problem must be in the texture loader code, not recognizing the output file from the Mali Texture Compression Tool.

    Can you tell us more about what engine or library code you are calling to load the textures? If I were investigating this I would try and debug the code to step through and find where the error message is generated, and which test is performed just before the error message is reported - this should narrow down which part of the file format the loader thinks is wrong.

    HTH, Pete
  • Note: This was originally posted on 22nd March 2013 at http://forums.arm.com

    Hi Irfan,

    I didn't spot you posted about the same issue twice - I've merged the threads and deleted the duplicate post without the code.

    You are right that the ETC1 format used by OpenGL ES 2.0 doesn't contain an alpha channel, but plenty of OpenGL ES 2.0 games and examples manage translucent water effects, so you don't need to move to OpenGL ES 3.0 just for that reason. Since there are not many commercial devices supporting OpenGL ES 3.0 yet perhaps aim to do the effect under OpenGL ES 2.0 to maximise the target market?

    There are a few ways to use both ETC1 and have an alpha channel, essentially it comes down to 2 choices.

    1) you could double the height of the texture, and keep only RGB color data in the top half, and put the alpha channel in the bottom half in grayscale. Then, your fragment shader changes to sample the top half of the texture to get the color data and samples the same texel in the bottom half to read the alpha value.

    2) you could have 2 ETC1 textures, one holding color data and one holding alpha data coded as grayscale. You sample both textures in your fragment shader to form the complete RGBA texture data.

    We have some sample code and a document that goes into more depth on these techniques.

    From the code posted, it looks like if you see the message "Unable to read PKM file header" then the code wasn't able to read even the ETC1 header bytes. Could it be a problem with the InputStream itself? You could try changing the code to print how many bytes were able to be read (we only know it's not the number that are in an ETC1 header). If for instance it comes back zero then something has gone wrong trying to read the stream at all. If it reads some but not enough for an ETC1 header then perhaps the file has become corrupt and truncated somehow.

    HTH, Pete
  • Note: This was originally posted on 22nd March 2013 at http://forums.arm.com

    After hearing what you said about switching entirely to openGL ES 3.0, probably does not sound like a good alternative due to the fact that you reminded me that very limited devices right now are on that openGL ES 3.0. In fact, it may be more interesting and educational to see how the alpha channel is applied to textures that are not meant for it, so I'm sticking to openGL ES 2.0 for now.As far as the errors are concerned, as you may have remembered from my original post that I was having both problems in non stopping loop in LogCat:1) Unable to read PKM file header 2) Not a PKM file.  For error 1 I've found out that if I had reading problems from the InputStream as you suggested I would only have this error.  I actually solved this error by changing the size of the array buffer as follows:  byte[] buf = new byte[4096];

    to

    byte[] buf = new byte[ETC1.ETC_PKM_HEADER_SIZE];

    I guess I had 4096 as an array size because I was following an example online and that's what they had in that example, which I never understood why. It makes more sense to have the ETC1.ETC_PKM_HEADER_SIZE as the buffer size because you're trying to put in the whole header (16 bytes) into the buffer, so you need to make sure you have enough space. The second error after some clever searching, reading pointer pop up tips at the part of the code that involves that error lead me to a clue. When I pointed to " if (!ETC1.isValid(headerBuffer)) " line of the code, the pop up tip told me that this line checks to see if the headerBuffer is properly. This gave me a hint to include ETC1.formatHeader (headerBuffer, width, height) in my code to properly format the header, and this is why I've been getting that error. Although I've solved both errors and made much progress working through my problems, I'm still having to deal with the problem of not being able to see anything on my screen and giving the black screen.  I'll keep you posted if I see some new progress.  Thanks

    Hi Irfan,

    I didn't spot you posted about the same issue twice - I've merged the threads and deleted the duplicate post without the code.

    You are right that the ETC1 format used by OpenGL ES 2.0 doesn't contain an alpha channel, but plenty of OpenGL ES 2.0 games and examples manage translucent water effects, so you don't need to move to OpenGL ES 3.0 just for that reason. Since there are not many commercial devices supporting OpenGL ES 3.0 yet perhaps aim to do the effect under OpenGL ES 2.0 to maximise the target market?

    There are a few ways to use both ETC1 and have an alpha channel, essentially it comes down to 2 choices.

    1) you could double the height of the texture, and keep only RGB color data in the top half, and put the alpha channel in the bottom half in grayscale. Then, your fragment shader changes to sample the top half of the texture to get the color data and samples the same texel in the bottom half to read the alpha value.

    2) you could have 2 ETC1 textures, one holding color data and one holding alpha data coded as grayscale. You sample both textures in your fragment shader to form the complete RGBA texture data.

    We have some sample code and a document that goes into more depth on these techniques here:

    http://malideveloper...alpha-channels/

    From the code posted, it looks like if you see the message "Unable to read PKM file header" then the code wasn't able to read even the ETC1 header bytes. Could it be a problem with the InputStream itself? You could try changing the code to print how many bytes were able to be read (we only know it's not the number that are in an ETC1 header). If for instance it comes back zero then something has gone wrong trying to read the stream at all. If it reads some but not enough for an ETC1 header then perhaps the file has become corrupt and truncated somehow.

    HTH, Pete
  • Note: This was originally posted on 23rd March 2013 at http://forums.arm.com

    Hi,

    sounds like good progress! If the rest of the code is running without errors but you aren't seeing textures then my next guess is that OpenGL ES does not believe the texture is "complete". For instance, OpenGL ES defaults to using a minifying texture filtering mode using mipmaps. If you upload only the original base level of the texture, OpenGL ES will believe the other levels are missing, and refuse to render anything.

    There are 2 ways to solve this. The quickest and easiest is probably to change the filtering mode so OpenGL ES does not expect to find mipmap levels:


    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);


    though you should note that if you display many of your textures on geometry smaller than the original texture size this will mean less efficient usage of the texture cache and may be sub optimal.

    The alternative (which makes better use of the texture cache) is to ask the Mali Texture Compression Tool to generate compressed textures for each mipmap level (e.g. 8x8, 4x4, 2x2 and 1x1) and load each one into the appropriate mipmap level:


    glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_ETC1_RGB8_OES, 8, 8, 0, 32, pMipmapLevel0);
    glCompressedTexImage2D(GL_TEXTURE_2D, 1, GL_ETC1_RGB8_OES, 4, 4, 0,  8, pMipmapLevel1);
    glCompressedTexImage2D(GL_TEXTURE_2D, 2, GL_ETC1_RGB8_OES, 2, 2, 0,  2, pMipmapLevel2);
    glCompressedTexImage2D(GL_TEXTURE_2D, 3, GL_ETC1_RGB8_OES, 1, 1, 0,  1, pMipmapLevel3);


    Then OpenGL ES will select the most appropriate mipmap level for the size of the textured triangle on screen, and not waste space in the texture cache loading lots of texel data that will never be sampled.

    HTH, Pete
  • Note: This was originally posted on 26th March 2013 at http://forums.arm.com

    Hey Pete, well I'm currently undergoing the second option in which I need to utilize mipmaps, but it may take some time to see the result of using them as I have to apply them to 5 different environments each with their own texture patterns as you may imagine. However, I had a question in regards to the first option in which I have to utilize the filters because I don't see why you need to use them in the first place. The reason why I'm puzzled is because if you utilize power of two textures for both dimensions that you actually don't need them as far as I can understand. I did some calculations trying to understand why using power of twos are very important. The calculations that I did are as follows:
    Target Device Specs:
    1) Resolution: 1280 * 800
    2) Screen Size: 9.4 inches
    3) aspect ratio: 16:9

    Texture Size
    1) POT: 128 * 128, the size that I used for all my actual textures
    2) NPOT: 135 * 128, just for calculation and comparison purposes

    With this information I calculated pixels/in like this:
    Thanks to Wikipedia, I was able to find out the formula to relate screen size to width and height of a triangle of the screen (I don't have the capabilities to show the calculations of width and height of the screen here because it involves square root) so you have to believe me that
    height of the screen = 4.6 inches
    width of the screen = 8.2 inches

    Assuming this is correct and we only look at the width, I calculated the pixels/in of the screen as:

    pixels/in of screen = 1280 pixels / 8.2 inches = 156.1 pixels / in.

    The texture's width pixel/in calculations = 128 pixels / 8.2 inches = 15.61 pixels / inch (POT)
    The texture's width pixel/in calculations = 135 pixels / 8.2 inches = 16.46 pixels / inch (NPOT)

    Assuming we look at 1 inch of the screen,

    Number of screen pixels = 156.1 pixels
    Number of POT pixels = 15.61 pixels
    Number of NPOT pixels = 16.46 pixels

    Finally I did a ratio comparison between texture pixels to screen pixels and found:

    POT

    156.1 pixels / 15.61 pixels = 10 / 1

    NPOT

    156.1 pixels / 16.46 pixels = 9.48 / 1

    After doing these calculations, I've come to a conclusion that if you use POT textures there is no need to incorporate such filters because the number of pixels evens out (10/1).  The opposite is true for using NPOT because you end up with uneven number of pixels (9.48/1), and thus having to use filters such as:

    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

    Actually in the NPOT situation, it looks like I would only have to utilize the magnification filter, and the wrap filters.

    Then again, when I tried doing this without compression, I had POT textures (.png 128 x 128) and all four filters and although the cube and the textures were drawn out I still had the problem that the texture did not line up correctly in the cube faces. The textures appeared like they were stretched out. In that situation I had all three dimensions as:

    x-range : -3.0(left) to 3.0(right)
    y-range: -3.0(bottom) to 3.0(top)
    z-range: -3.0 (left)to 3.0 (right)

    Hi,

    sounds like good progress! If the rest of the code is running without errors but you aren't seeing textures then my next guess is that OpenGL ES does not believe the texture is "complete". For instance, OpenGL ES defaults to using a minifying texture filtering mode using mipmaps. If you upload only the original base level of the texture, OpenGL ES will believe the other levels are missing, and refuse to render anything.

    There are 2 ways to solve this. The quickest and easiest is probably to change the filtering mode so OpenGL ES does not expect to find mipmap levels:


    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);


    though you should note that if you display many of your textures on geometry smaller than the original texture size this will mean less efficient usage of the texture cache and may be sub optimal.

    The alternative (which makes better use of the texture cache) is to ask the Mali Texture Compression Tool to generate compressed textures for each mipmap level (e.g. 8x8, 4x4, 2x2 and 1x1) and load each one into the appropriate mipmap level:


    glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_ETC1_RGB8_OES, 8, 8, 0, 32, pMipmapLevel0);
    glCompressedTexImage2D(GL_TEXTURE_2D, 1, GL_ETC1_RGB8_OES, 4, 4, 0,  8, pMipmapLevel1);
    glCompressedTexImage2D(GL_TEXTURE_2D, 2, GL_ETC1_RGB8_OES, 2, 2, 0,  2, pMipmapLevel2);
    glCompressedTexImage2D(GL_TEXTURE_2D, 3, GL_ETC1_RGB8_OES, 1, 1, 0,  1, pMipmapLevel3);


    Then OpenGL ES will select the most appropriate mipmap level for the size of the textured triangle on screen, and not waste space in the texture cache loading lots of texel data that will never be sampled.

    HTH, Pete
  • Note: This was originally posted on 26th March 2013 at http://forums.arm.com

    If you ensure that your texture rendering is a 1:1 mapping with the screen pixels (i.e. middle of pixel = middle of texel, 1 texel exists for each pixel), then you probably don't need filters. As soon as you have any "3D" in the scene or a texture which does not have a 1:1 mapping then you will get some improvement in visual quality from filtering the texture samples.

    HTH,
    Iso
  • Note: This was originally posted on 28th March 2013 at http://forums.arm.com

    Hey Pete,

    Unfortunately I've tried to implement the below techniques, and now it both won't draw the cube and Console shows up with this repeated error:


    [2013-03-28 12:35:11 - Emulator] sdk/emulator/opengl/host/libs/Translator/GLcommon/TextureUtils.cpp:doCompressedTexImage2D:60 error 0x501
    [2013-03-28 12:35:11 - Emulator] sdk/emulator/opengl/host/libs/Translator/GLcommon/TextureUtils.cpp:doCompressedTexImage2D:60 error 0x501
    [2013-03-28 12:35:11 - Emulator] sdk/emulator/opengl/host/libs/Translator/GLcommon/TextureUtils.cpp:doCompressedTexImage2D:60 error 0x501
    [2013-03-28 12:35:11 - Emulator] sdk/emulator/opengl/host/libs/Translator/GLcommon/TextureUtils.cpp:doCompressedTexImage2D:60 error 0x501
    [2013-03-28 12:35:11 - Emulator] sdk/emulator/opengl/host/libs/Translator/GLcommon/TextureUtils.cpp:doCompressedTexImage2D:60 error 0x501
    [2013-03-28 12:35:11 - Emulator] sdk/emulator/opengl/host/libs/Translator/GLcommon/TextureUtils.cpp:doCompressedTexImage2D:60 error 0x501
    [2013-03-28 12:35:11 - Emulator] sdk/emulator/opengl/host/libs/Translator/GLcommon/TextureUtils.cpp:doCompressedTexImage2D:60 error 0x501

    Well here is my changed loadTexture code:


    public static int loadCubeTexture(ETC1Texture landETC)
    {
      int[] landTexEnvironments = new int[1];
     
      GLES20.glGenTextures(1, landTexEnvironments, 0);
       
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
     
       int width, height;
          width = landETC.getWidth();
       height = landETC.getHeight();
      
       Buffer land_data_mip0 = landETC.getData();
       Buffer land_data_mip1 = landETC.getData();
       Buffer land_data_mip2 = landETC.getData();
       Buffer land_data_mip3 = landETC.getData();
       Buffer land_data_mip4 = landETC.getData();
       Buffer land_data_mip5 = landETC.getData();
       Buffer land_data_mip6 = landETC.getData();
       Buffer land_data_mip7 = landETC.getData();
      
      
      
       int imageSize0 = land_data_mip0.remaining();
       int imageSize1= land_data_mip1.remaining();
       int imageSize2 = land_data_mip2.remaining();
       int imageSize3 = land_data_mip3.remaining();
       int imageSize4 = land_data_mip4.remaining();
       int imageSize5 = land_data_mip5.remaining();
       int imageSize6 = land_data_mip6.remaining();
       int imageSize7 = land_data_mip7.remaining();
      
       if (ETC1Util.isETC1Supported())
       {
         GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 0, ETC1.ETC1_RGB8_OES, 128, 128, 0, imageSize0, land_data_mip0);
               //ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 0, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
              
               GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 1, ETC1.ETC1_RGB8_OES, 64, 64, 0, imageSize1, land_data_mip1);
               //ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 1, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
              
               GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 2, ETC1.ETC1_RGB8_OES, 32, 32, 0, imageSize2, land_data_mip2);
               //ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 2, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
              
               GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 3, ETC1.ETC1_RGB8_OES, 16, 16, 0, imageSize3, land_data_mip3);
               //ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 3, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
              
               GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 4, ETC1.ETC1_RGB8_OES, 8, 8, 0, imageSize4, land_data_mip4);
               //ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 4, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
              
               GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 5, ETC1.ETC1_RGB8_OES, 4, 4, 0, imageSize5, land_data_mip5);
              // ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 5, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
              
               GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 6, ETC1.ETC1_RGB8_OES, 2, 2, 0, imageSize6, land_data_mip6);
              // ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 6, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
              
               GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 7, ETC1.ETC1_RGB8_OES, 1, 1, 0, imageSize7, land_data_mip7);
               //ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 7, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, landTexEnvironments[0]);
         
       } else
       {
         
           int pixelSize = 2;
           int stride = pixelSize * width;
           ByteBuffer decodedData = ByteBuffer.allocateDirect(stride*height)
               .order(ByteOrder.nativeOrder());
           ETC1.decodeImage(land_data_mip0, decodedData, 128, 128, pixelSize, stride);
           GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, 128, 128, 0,
                   GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, decodedData);
           ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 0, 0,GLES20.GL_RGB , GLES20.GL_UNSIGNED_BYTE, landETC);
       }
     
      //GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_COMPRESSED_TEXTURE_FORMATS, new_texture_air.getWidth(), new_texture_air.getHeight(), 0, image_size, imageBuffer1);          
     
          
           
           
               
            
               
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
            //GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
           // GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
            //GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
      
                  
           

            return landTexEnvironments[0];
     
     
    }

    I've made the loadCubeTexture (ETC1Texture landETC) function to return an int because the result of the function must be assigned to a textureHandle.  Then  the textureHandle is sent back to the shader code like this:


    // face 0
      
      
       mTextureHandle = LandRenderer.loadCubeTexture(land2ETC1);
       GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ibo[0]);
       GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_BYTE, 0);

    The other faces of my cube are similar to the above code except the last face # 5 which uses a second textureHandle called mTextureHandle2. This is because I wanted 5/6 sides of the cube to have the same textures and the last side, which is the ground in my game, to have a different texture.


    // face 5
      
      
       mTextureHandle2 = LandRenderer.loadCubeTexture(land1ETC1);
       GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ibo[5]);
       GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_BYTE, 0);



    mTextureUniformHandle = GLES20.glGetUniformLocation(env.mProgram, "uTexture");
       
       if (mTextureUniformHandle == -1)
       {
        throw new RuntimeException ("could not find location for uTexture");
       }
      
       GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
       
       GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, LandEnvironment.mTextureHandle);
      
       GLES20.glUniform1i(mTextureUniformHandle, 0);

    Finally my shader code is as follows:


    private final String vertexShaderCode =
          "uniform mat4 uMVPMatrix;      \n"     // A constant representing the combined model/view/projection matrix.
       + "uniform mat4 uAirMVPMatrix; \n"
       + "uniform mat4 uFireMVPMatrix; \n"
       + "uniform mat4 uLandMVPMatrix; \n"
       + "uniform mat4 uWaterMVPMatrix; \n"
      
        + "attribute vec4 aPosition;     \n"     // Per-vertex position information we will pass in.
       // + "attribute vec4 aColor;        \n"     // Per-vertex color information we will pass in.
        + "attribute vec2 aTexCoordinate; \n"
       
       // + "varying vec4 vColor;          \n"     // This will be passed into the fragment shader.
        + "varying vec2 vTexCoordinate;  \n"
       
        + "void main()                    \n"     // The entry point for our vertex shader.
        + "{                              \n"
        //+ "   vColor = aColor;          \n"     // Pass the color through to the fragment shader.
        + "   vTexCoordinate = aTexCoordinate; \n"
                                                  // It will be interpolated across the triangle.
        + "   gl_Position =  uMVPMatrix * uAirMVPMatrix * uFireMVPMatrix * uLandMVPMatrix * uWaterMVPMatrix * aPosition;   \n"     // Multiply the vertex by the matrix to get the final point in
        + "}                              \n";    // normalized screen coordinates.

      private final String fragmentShaderCode =
          "precision mediump float;       \n"     // Set the default precision to medium. We don't need as high of a
                                                  // precision in the fragment shader.
        + "uniform sampler2D uTexture;    \n"
        + "uniform sampler2D uTexture2; \n"
        + "uniform sampler2D uTexture3; \n"
        + "uniform sampler2D uTexture4; \n"
        + "uniform sampler2D uTexture5; \n"
        + "uniform sampler2D uTexture6; \n"
       
       
       
      
       
        + "varying vec2 vTexCoordinate;"
       // + "varying vec4 vColor;          \n"     // This is the color from the vertex shader interpolated across the
                                                  // triangle per fragment.
        + "void main()                    \n"     // The entry point for our fragment shader.
        + "{                              \n"
        + "   gl_FragColor = texture2D(uTexture, vTexCoordinate) * texture2D(uTexture2, vTexCoordinate) * texture2D(uTexture3, vTexCoordinate) * texture2D(uTexture4, vTexCoordinate) * texture2D(uTexture5, vTexCoordinate) * texture2D(uTexture6, vTexCoordinate);     \n"     // Pass the color directly through the pipeline.
        + "}                              \n";


    Hi,

    sounds like good progress! If the rest of the code is running without errors but you aren't seeing textures then my next guess is that OpenGL ES does not believe the texture is "complete". For instance, OpenGL ES defaults to using a minifying texture filtering mode using mipmaps. If you upload only the original base level of the texture, OpenGL ES will believe the other levels are missing, and refuse to render anything.

    There are 2 ways to solve this. The quickest and easiest is probably to change the filtering mode so OpenGL ES does not expect to find mipmap levels:


    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);


    though you should note that if you display many of your textures on geometry smaller than the original texture size this will mean less efficient usage of the texture cache and may be sub optimal.

    The alternative (which makes better use of the texture cache) is to ask the Mali Texture Compression Tool to generate compressed textures for each mipmap level (e.g. 8x8, 4x4, 2x2 and 1x1) and load each one into the appropriate mipmap level:


    glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_ETC1_RGB8_OES, 8, 8, 0, 32, pMipmapLevel0);
    glCompressedTexImage2D(GL_TEXTURE_2D, 1, GL_ETC1_RGB8_OES, 4, 4, 0,  8, pMipmapLevel1);
    glCompressedTexImage2D(GL_TEXTURE_2D, 2, GL_ETC1_RGB8_OES, 2, 2, 0,  2, pMipmapLevel2);
    glCompressedTexImage2D(GL_TEXTURE_2D, 3, GL_ETC1_RGB8_OES, 1, 1, 0,  1, pMipmapLevel3);


    Then OpenGL ES will select the most appropriate mipmap level for the size of the textured triangle on screen, and not waste space in the texture cache loading lots of texel data that will never be sampled.

    HTH, Pete