Search code examples
opengltexturesmultitexturing

Texture units and vertex arrays in OpenGL


I'm trying to draw one cube with different textures for each face. I've come across many tutorials which state that in the display() routines, you need to enable all texture units before calling glDrawElements(). I do this my calling:

gl.glClientActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, getTexCoordBufferObject());
gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);

gl.glClientActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId+1);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, getTexCoordBufferObject());
gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);

...

gl.glClientActiveTexture(GL.GL_TEXTURE5);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId+5);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, getTexCoordBufferObject());
gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);

This all makes sense to me. What doesn't however is how I determine which texture I am referring to when I populate the buffers (e.g. when I load my model data). Thanks Chris


Solution

  • What you are looking for is a cube map. In OpenGL, you can define six textures at once (representing the size sides of a cube) and map them using 3D texture coordinates instead of the common 2D texture coordinates. For a simple cube, the texture coordinates would be the same as the vertices' respective normals. (If you will only be texturing plane cubes in this manner, you can consolidate normals and texture coordinates in your vertex shader, too!) Cube maps are much simpler than trying to bind six distinct textures simultaneously the way you are doing right now.

    GLuint mHandle;
    glGenTextures(1, &mHandle); // create your texture normally
    
    // Note the target being used instead of GL_TEXTURE_2D!
    glTextParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTextParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glTextParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTextParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    glBindTexture(GL_TEXTURE_CUBE_MAP, mHandle);
    
    // Now, load in your six distinct images. They need to be the same dimensions!
    // Notice the targets being specified: the six sides of the cube map.
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, width, height, 0,
        format, GL_UNSIGNED_BYTE, data1);
    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, width, height, 0,
        format, GL_UNSIGNED_BYTE, data2);
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, width, height, 0,
        format, GL_UNSIGNED_BYTE, data3);
    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, width, height, 0,
        format, GL_UNSIGNED_BYTE, data4);
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, width, height, 0,
        format, GL_UNSIGNED_BYTE, data5);
    glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, width, height, 0,
        format, GL_UNSIGNED_BYTE, data6);
    
    glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
    glTextParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    
    // And of course, after you are all done using the textures...
    glDeleteTextures(1, &mHandle);
    

    Now, when doing your shaders, you need the vertex shader to accept and/or pass 3D coordinates (vec3) instead of 2D coordinates (vec2).

    // old GLSL style
    attribute vec3 inTextureCoordinate;
    varying vec3 vTextureCoordinate;
    
    // more recent GLSL
    in vec3 inTextureCoordinate;
    out vec3 vTextureCoordinate;
    

    In this example, your vertex shader would simply assign vTextureCoordinate = inTextureCoordinate. Your fragment shader then needs to accept that texture coordinate and sample the cube map uniform.

    uniform samplerCube cubeMap;
    ...
    gl_FragColor = textureCube(cubeMap, vTextureCoordinate);
    

    Whew! That was a lot. Did I leave anything out?