Search code examples
pythonopengltexture-mappinggldrawarrays

How to use glDrawArrays with a Texture Array without shaders


Im trying to optimise my code by combining my textures into array textures (I realised I can't use texture atlases because most textures are repeated (over ground etc.)). I am working in PyGame and PyOpenGL, and I have never used shaders before. Is it possible to bind a single array texture with glBindTexture(GL_TEXTURE_2D_ARRAY, texname) and use 3d texture coordinates or something to access the different layers? Is it even possible without shaders?

At the moment I have a call to glDrawArrays for every texture with this function:

def DRAW(im, array):
    glBindTexture(GL_TEXTURE_2D,im)
    glTexCoordPointer(2, GL_FLOAT, 32, array)
    glNormalPointer(GL_FLOAT, 32, array[2:])
    glVertexPointer(3, GL_FLOAT, 32, array[5:])
    glDrawArrays(GL_QUADS,0,len(array)/8)

Solution

  • As the spec quote in the answer by @derhass shows, array textures are not supported without shaders.

    However, you can use 3D textures to do pretty much the same thing. You store each texture in a layer of the 3D texture. Then you use the first 2 texture coordinates as usual, to determine the position within the texture image. The 3rd texture coordinate is used to select the layer within the 3D texture.

    To load an image into the 3D texture, you use glTexSubImage3D(). For example, say your textures have size 512x512, and you want to load texture index 20:

    glTexSubImage3D(GL_TEXTURE_3D, 0,
        0, 0, 20, 512, 512, 1,
        GL_RGBA, GL_UNSIGNED_BYTE, data);
    

    The only slightly tricky part is calculating the 3rd texture coordinate properly, since texture coordinates are normalized floats. Particularly if you use linear sampling, you have to hit the center of a layer exactly, otherwise you'll get a mix of images.

    If you have n layers in your 3D texture, the texture coordinate for layer k is:

    (k + 0.5f) / n
    

    For example, with n = 4, the resulting texture coordinates for the 4 layers will be:

    0.125f
    0.375f
    0.625f
    0.875f
    

    Note how the values are spaced by 0.25f, as you would expect, and placed symmetrically within the interval [0.0f, 1.0f]

    One downside of using 3D textures is that they have smaller size restrictions. With this approach, make sure that you don't exceed MAX_3D_TEXTURE_SIZE.

    I would encourage you to learn using shaders. It may look slightly intimidating at first because it's certainly more work for very simple use cases. But you'll find that once you get the hang of it, they actually make things easier when your functionality becomes more complex. It's often much more straightforward to write GLSL code to do exactly what you want, instead of trying to figure out how to properly set a few dozen state values to get the desired results. And of course shaders allow you to implement functionality that is simply impossible in the fixed pipeline.