androidopengl-es3dtexture-mappinggles20

GLES20 Why texture data is not linked with vertices indexes?


I was trying to render a cube and apply a single texture on all faces. as well as using as little vertices as i can by passing up indexes of the vertices of the face. example:

Vertices:

static final float FACE_VERTEX[] = {
        // front
        0.0f,  1.0f, 1.0f,     //0
        0.0f, 0.0f, 1.0f,     //1
        1.0f, 0.0f, 1.0f,      //2
        1.0f,  1.0f, 1.0f,      //3

        //back
        1.0f,  1.0f, 0.0f,      //4 - 3
        1.0f, 0.0f, 0.0f,      //5 - 2
        0.0f, 0.0f, 0.0f,     //6 - 1
        0.0f,  1.0f, 0.0f,     //7 - 0
}; 

Indexes:

static final int FACE_INDEX[] = {
        //front
        0,1,2, 0,2,3,
        //back
        4,5,6, 4,6,7,

        //left
        7,6,1, 7,1,0,

        //right
        3,2,5, 3,5,4,

        //top
        4,7,0, 4,0,3,

        //bottom
        1,6,5, 1,5,2

};

texture mapping data:

final int textureCoordinateData[] =
        {
                // Front face
                0,0, 0,1, 1,1, 1,0,
                //back
                0,0, 0,1, 1,1, 1,0,
                //left
                0,0, 0,1, 1,1, 1,0,
                //right
                0,0, 0,1, 1,1, 1,0,
                //top
                0,0, 0,1, 1,1, 1,0,
                //bottom
                0,0, 0,1, 1,1, 1,0,
                //top
                0,0, 0,1, 1,1, 1,0,
                //bottom
                0,0, 0,1, 1,1, 1,0,


        };

The texture is rendered on all sides of the cube, except top and bottom. only the first row of the pixels of the front face are rendered along the whole top and bottom faces (see screenshot):

enter image description here

I am using VBOs to store the vertex/index/texture data in the GPU, and rendering with

glDrawElements(GL_TRIANGLES, indexLength, GL_UNSIGNED_INT, 0);

However this issue is because the texture data should be mapped to the passed vertex data (which is kinda annoying for a cube model) and is not calculated by the index.

My questions will be: - Is there any way to keep the vertices data as low as possible and map the texture to the index data? - If I create 36 vertices (some are repeated) to solve the texture mapping issue, but I created the correct indexes to render the cube, would it still be faster than using glDrawArrays? or shall i go with glDrawArrays and trash the indexing data anyway?

related question (that didn't answer my question):

OpenGL ES - texture map all faces of an 8 vertex cube?

If you read the first comment on the answer:

What do you mean you have to use 24 vertexes? If you have to duplicate the vertexes what is the point of using an index buffer then if you are still sending repeat data to the GPU?


Solution

  • There is really no reasonable way to draw a cube with texture coordinates with less than 24 vertices. That's because... it has 24 vertices, when using the definition of what forms a vertex in OpenGL, which is each unique combination of vertex attributes (position, texture coordinates, normal, etc).

    You might be able to reduce the number slightly with only positions and texture coordinates, since texture coordinates in your case are only combinations of 0 and 1 values. If you don't care about the orientation of the texture on each face, you could for example have 1 vertex that uses (0, 0) for the texture coordinates of all 3 adjacent faces. Of course you can't do that for all vertices, but you could trim down the number of vertices somewhat.

    So is using indices worth it? As you already found by now, you can easily stick with 24 vertices when using indices, but need 36 vertices with the most straightforward use of glDrawArrays(), where you use a single draw call with GL_TRIANGLES as the primitive type. So it does reduce the number of vertices.

    You could reduce it to 24 vertices with glDrawArrays() as well, if you make a separate draw call for each face, and draw the face using the GL_TRIANGLE_STRIP primitive type with 4 vertices each. This will result in 6 draw calls, though, and having many small draw calls is not desirable either.

    Even if it might look questionable in the case of a cube, index buffers are highly useful in general. A cube is just such a small and simple shape that the way you send the vertices won't make much of a difference anyway.

    In most use cases, you will have much more complex shapes, with many more vertices that often define smooth surfaces. In this case, the same vertex (with the same normal and texture coordinates) is mostly shared by multiple triangles. Sketching part of a regular mesh:

    _____________
    |  /|  /|  /|
    | / | / | / |
    |/__|/__|/__|
    |  /|  /|  /|
    | / | / | / |
    |/__|/__|/__|
    

    you can see that the interior vertices are shared by 6 triangles each. So for a relatively large, smooth surface, you can typically reduce the number of vertices by about a factor of 6 by sharing vertices, which is what using an index array allows you to do. Reducing memory usage by almost a factor of 6 can be a substantial gain if your geometry is sufficiently large.