0

Given the below snippet:

import os
import textwrap

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image

from OpenGL.GL.ARB.multitexture import *
from OpenGL.extensions import alternate


def get_opengl_info():
    return textwrap.dedent("""\
        Vendor: {0}
        Renderer: {1}
        OpenGL Version: {2}
        Shader Version: {3}
        {4:*^80}
        Num Extensions: {5}
        {6}
    """).format(
        glGetString(GL_VENDOR).decode("utf-8"),
        glGetString(GL_RENDERER).decode("utf-8"),
        glGetString(GL_VERSION).decode("utf-8"),
        glGetString(GL_SHADING_LANGUAGE_VERSION).decode("utf-8"),
        "OPENGL EXTENSIONS",
        glGetIntegerv(GL_NUM_EXTENSIONS),
        "\n".join(glGetString(GL_EXTENSIONS).decode("utf-8").split())
    )


def create_gl_texture(use_active_texture, channel, width, height, pbits):
    id_texture = glGenTextures(1)
    if use_active_texture:
        glActiveTexture(GL_TEXTURE0 + channel)
    glBindTexture(GL_TEXTURE_2D, id_texture)
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, pbits)
    glTexParameter(
        GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameter(
        GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
    glGenerateMipmap(GL_TEXTURE_2D)

    return id_texture


def load_texture(use_active_texture, filename, i):
    image = Image.open(filename)
    ix = image.size[0]
    iy = image.size[1]
    pbits = image.convert("RGBA").tobytes("raw", "RGBA")

    id_texture = create_gl_texture(use_active_texture, i, ix, iy, pbits)
    print("Loaded", id_texture)
    return id_texture


X_AXIS = 0.0
Y_AXIS = 0.0
Z_AXIS = 0.0
DIRECTION = 1
id_textures = []


def init_gl(Width, Height):
    global glMultiTexCoord2f, glActiveTexture
    print(get_opengl_info())

    print("Choosing between: ", glMultiTexCoord2f.__name__,
          glMultiTexCoord2fARB.__name__)
    print("Choosing between: ", glActiveTexture.__name__,
          glActiveTextureARB.__name__)

    glMultiTexCoord2f = alternate(
        glMultiTexCoord2f,
        glMultiTexCoord2fARB
    )
    glActiveTexture = alternate(
        glActiveTexture,
        glActiveTextureARB,
    )

    print("Selected: ", glMultiTexCoord2f.__name__)
    print("Selected: ", glActiveTexture.__name__)

    if not glMultiTexCoord2f:
        print('Multitexture not supported!')
        sys.exit(1)

    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClearDepth(1.0)
    glDepthFunc(GL_LESS)
    glEnable(GL_DEPTH_TEST)
    glShadeModel(GL_SMOOTH)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45.0, float(Width) / float(Height), 0.1, 100.0)
    glMatrixMode(GL_MODELVIEW)
    glEnable(GL_TEXTURE_2D)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)


def keyPressed(*args):
    if args[0] == "\033":
        sys.exit()

# Method0: Using glBindTexture + glTexCoord2f per face
def draw_method_0():
    global X_AXIS, Y_AXIS, Z_AXIS
    global DIRECTION
    global id_textures
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glLoadIdentity()
    glTranslatef(0.0, 0.0, -6.0)

    glRotatef(X_AXIS, 1.0, 0.0, 0.0)
    glRotatef(Y_AXIS, 0.0, 1.0, 0.0)
    glRotatef(Z_AXIS, 0.0, 0.0, 1.0)

    glBindTexture(GL_TEXTURE_2D, id_textures[0])
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(1.0,  1.0,  1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glEnd()

    glBindTexture(GL_TEXTURE_2D, id_textures[1])
    glBegin(GL_QUADS)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(1.0, -1.0, -1.0)
    glEnd()

    glBindTexture(GL_TEXTURE_2D, id_textures[2])
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(1.0,  1.0,  1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glEnd()

    glBindTexture(GL_TEXTURE_2D, id_textures[3])
    glBegin(GL_QUADS)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(1.0, -1.0, -1.0)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glEnd()

    glBindTexture(GL_TEXTURE_2D, id_textures[4])
    glBegin(GL_QUADS)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(1.0,  1.0,  1.0)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glEnd()

    glBindTexture(GL_TEXTURE_2D, id_textures[5])
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glEnd()

    X_AXIS = X_AXIS - 0.030
    Z_AXIS = Z_AXIS - 0.030

    glutSwapBuffers()


# Method1: Using glActiveTexture+glBindTexture+glMultiTexCoord2f per face
def draw_method_1():
    global X_AXIS, Y_AXIS, Z_AXIS
    global DIRECTION
    global id_textures
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glLoadIdentity()
    glTranslatef(0.0, 0.0, -6.0)

    glRotatef(X_AXIS, 1.0, 0.0, 0.0)
    glRotatef(Y_AXIS, 0.0, 1.0, 0.0)
    glRotatef(Z_AXIS, 0.0, 0.0, 1.0)

    glActiveTexture(GL_TEXTURE0 + 0)
    glBindTexture(GL_TEXTURE_2D, id_textures[0])
    glBegin(GL_QUADS)
    glMultiTexCoord2f(GL_TEXTURE0 + 0, 0.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 0, 1.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 0, 1.0, 1.0)
    glVertex3f(1.0,  1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 0, 0.0, 1.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 1)
    glBindTexture(GL_TEXTURE_2D, id_textures[1])
    glBegin(GL_QUADS)
    glMultiTexCoord2f(GL_TEXTURE0 + 1, 1.0, 0.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 1, 1.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 1, 0.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 1, 0.0, 0.0)
    glVertex3f(1.0, -1.0, -1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 2)
    glBindTexture(GL_TEXTURE_2D, id_textures[2])
    glBegin(GL_QUADS)
    glMultiTexCoord2f(GL_TEXTURE0 + 2, 0.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 2, 0.0, 0.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 2, 1.0, 0.0)
    glVertex3f(1.0,  1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 2, 1.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 3)
    glBindTexture(GL_TEXTURE_2D, id_textures[3])
    glBegin(GL_QUADS)
    glMultiTexCoord2f(GL_TEXTURE0 + 3, 1.0, 1.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 3, 0.0, 1.0)
    glVertex3f(1.0, -1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 3, 0.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 3, 1.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 4)
    glBindTexture(GL_TEXTURE_2D, id_textures[4])
    glBegin(GL_QUADS)
    glMultiTexCoord2f(GL_TEXTURE0 + 4, 1.0, 0.0)
    glVertex3f(1.0, -1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 4, 1.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 4, 0.0, 1.0)
    glVertex3f(1.0,  1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 4, 0.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 5)
    glBindTexture(GL_TEXTURE_2D, id_textures[5])
    glBegin(GL_QUADS)
    glMultiTexCoord2f(GL_TEXTURE0 + 5, 0.0, 0.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 5, 1.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 5, 1.0, 1.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glMultiTexCoord2f(GL_TEXTURE0 + 5, 0.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glEnd()

    X_AXIS = X_AXIS - 0.030
    Z_AXIS = Z_AXIS - 0.030

    glutSwapBuffers()

# Method2: Using glActiveTexture+glBindTexture+glTexCoord2f per face
def draw_method_2():
    global X_AXIS, Y_AXIS, Z_AXIS
    global DIRECTION
    global id_textures
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glLoadIdentity()
    glTranslatef(0.0, 0.0, -6.0)

    glRotatef(X_AXIS, 1.0, 0.0, 0.0)
    glRotatef(Y_AXIS, 0.0, 1.0, 0.0)
    glRotatef(Z_AXIS, 0.0, 0.0, 1.0)

    glActiveTexture(GL_TEXTURE0 + 0)
    glBindTexture(GL_TEXTURE_2D, id_textures[0])
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(1.0,  1.0,  1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 1)
    glBindTexture(GL_TEXTURE_2D, id_textures[1])
    glBegin(GL_QUADS)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(1.0, -1.0, -1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 2)
    glBindTexture(GL_TEXTURE_2D, id_textures[2])
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(1.0,  1.0,  1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 3)
    glBindTexture(GL_TEXTURE_2D, id_textures[3])
    glBegin(GL_QUADS)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(1.0, -1.0, -1.0)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 4)
    glBindTexture(GL_TEXTURE_2D, id_textures[4])
    glBegin(GL_QUADS)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(1.0,  1.0, -1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(1.0,  1.0,  1.0)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(1.0, -1.0,  1.0)
    glEnd()

    glActiveTexture(GL_TEXTURE0 + 5)
    glBindTexture(GL_TEXTURE_2D, id_textures[5])
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 0.0)
    glVertex3f(-1.0, -1.0, -1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex3f(-1.0, -1.0,  1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex3f(-1.0,  1.0,  1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex3f(-1.0,  1.0, -1.0)
    glEnd()

    X_AXIS = X_AXIS - 0.030
    Z_AXIS = Z_AXIS - 0.030

    glutSwapBuffers()

def load_textures(use_active_texture):
    global id_textures

    id_textures.append(load_texture(use_active_texture, "tex00.jpg", 0))
    id_textures.append(load_texture(use_active_texture, "tex01.jpg", 1))
    id_textures.append(load_texture(use_active_texture, "tex02.jpg", 2))
    id_textures.append(load_texture(use_active_texture, "tex03.jpg", 3))
    id_textures.append(load_texture(use_active_texture, "tex04.jpg", 4))
    id_textures.append(load_texture(use_active_texture, "tex05.jpg", 5))


def main():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
    glutInitWindowSize(640, 480)
    glutInitWindowPosition(200, 200)

    glutCreateWindow(b'OpenGL Python Textured Cube')

    init_gl(640, 480)

    current_method = 2
    draw_methods = {
        0: {"f": draw_method_0, "use_active_texture": False},
        1: {"f": draw_method_1, "use_active_texture": True},
        2: {"f": draw_method_2, "use_active_texture": True}
    }
    draw = draw_methods[current_method]["f"]
    load_textures(draw_methods[current_method]["use_active_texture"])
    glutDisplayFunc(draw)
    glutIdleFunc(draw)
    glutKeyboardFunc(keyPressed)

    glutMainLoop()

if __name__ == "__main__":
    main()

If i set current_method=0 I'll get the output I expect, all cube faces will use a different texture (as intended):

enter image description here

If i set current_method=1 only one face will be textured properly, that's a wrong output:

enter image description here

If i set current_method=2 all faces will be textured with the same texture[0], which is also a wrong output:

enter image description here

I'd like to understand why methods 1 & 2 are not giving the same output as method 0. I know when using shaders using glActiveTexture properly becomes trivial but I'd like to understand what's wrong when using these methods on the old fixed pipeline.

BPL
  • 9,632
  • 9
  • 59
  • 117

2 Answers2

2

I think the core issue here is a misunderstanding of how multitexturing works.

Multitexturing applies multiple textures at the same time to the same primitives. You want to apply different textures, one texture at a time, to different primitives. It should be clear from this that multitexturing does not do what you want, so there is no point in using it.

How multitexturing works

With multitexturing, multiple texture units combine their results using blending blending functions.

texture unit 0 --> +---------+
                   |  blend  | --\
texture unit 1 --> +---------+    \--> +---------+
                                       |  blend  | --> etc.
texture unit 2 ----------------------> +---------+

The blending function for each stage is set by glTexEnv and can be something like GL_MODULATE, GL_DECAL, or GL_ADD. This is how lightmaps were combined with textures back in the day: one texture would have the diffuse texture, and one texture would have the lightmap texture.

Again, this is completely different from the effect which you want to achieve, so multitexturing has no point in your application.

How glActiveTexture works

glActiveTexture doesn't change which texture unit is drawn to the screen. By default, only texture unit #0 will draw to the screen.

glActiveTexture just allows you to bind textures to other texture units. However, since texture unit #1 isn't being used, it doesn't matter what texture is bound to unit #1 or what the coordinates are. So you should always be using glActiveTexture(GL_TEXTURE0), since you always want to change texture unit #0.

Solutions

So, you have working code that doesn't use multitexturing. Great! You're done. You don't get bonus points for using more OpenGL features.

Alternatively, you could draw the entire cube in a single draw call if you use a 2D array texture. You simply load your existing textures as planes in a 2D array texture and use 3D texture coordinates instead of 2D texture coordinates. Again, no multitexturing required.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • Thanks for the advice but your answer is not explaining really my question, which is the "why of the output when use_active_texture=False" :) . I'm trying to understand why this code is not giving me the expected output for some other reasons. The original code wasn't using glMultiTexCoord at all but it was using glActiveTexture... I'm mixing fixed with programable pipeline code so I'd like to understand how it's working behind the curtains. Anyway, let me edit the question without using glMultiTexCoord to point out my original question – BPL Apr 10 '17 at 15:36
  • I've updated the question, hopefully now is more clear... I'm interested in the "why is not working..." more than the "you shouldn't use this or that..." ;) – BPL Apr 10 '17 at 15:48
  • For instance, after having read [this](https://www.opengl.org/discussion_boards/showthread.php/174926-when-to-use-glActiveTexture?p=1223312&viewfull=1#post1223312) I still don't understand the output of methods 1&2, that's what I'm trying to figure out here. – BPL Apr 10 '17 at 16:11
  • 1
    @BPL: You say that you're "mixing fixed with programmable pipeline code" but I don't see any evidence for that. It looks like entirely fixed function code to me, no custom shaders. – Dietrich Epp Apr 10 '17 at 16:28
  • 1
    @BPL: I am trying to explain why this isn't working. The whole point of multitexturing is to combine multiple textures. You're not combining multiple textures, you're using one texture at a time. – Dietrich Epp Apr 10 '17 at 16:30
  • Thanks to elaborate further on your answer but I'm afraid I still don't understand how all of this is working behind the curtains, it'd be awesome to take a look to opengl sources or some doc talking about it. I mean, it's not about getting bonus points or not, it's about knowing the low-level details of the API. I mean, right now I can code complex applications using all sort of texture mapping techniques but I've decided I want to learn exactly how all of this work behind the curtains. I'll continue googling to see if i find some document explaining all of this in detail – BPL Apr 10 '17 at 20:48
  • @BPL: There's not really such a thing as "OpenGL sources", since OpenGL is just an API specification. I was hoping that the diagram would help, but on my end it's a bit frustrating to just hear that you don't understand without any explanation of what it is you don't understand. If you have a specific question, I can answer it, but explaining all of how OpenGL works is far beyond what I can give you. – Dietrich Epp Apr 10 '17 at 21:13
  • Alright, let me try to be more specific then. Let's forget for a moment about method 1 and focusing about the differences between method0 & method 2, why is it [glActiveTexture](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glActiveTexture.xhtml) avoiding to get the same output than method0? I mean, how does it know glTexCoord where to sample from? The texture ids created by glGenTextures will be 1..6 respectively in all 3 methods, i thought this id was good enough to know where to sample from. In any case, I'll continue reading carefully the specs to see if got lucky – BPL Apr 10 '17 at 21:24
  • @BPL: This bears repeating: `glActiveTexture` function does NOT change which texture unit is drawn to screen. It just changes which texture units are affected by `glBindTexture()`. When you call `glActiveTexture(GL_TEXTURE0 + 1)`, the pipeline is still rendering from texture unit #0, regardless of the fact that unit #1 is "active". When you call `glBindTexture` afterwards, you've just changed the texture that unit #1 is using---but it doesn't matter, because you're using still using unit #0 to draw to the screen. You haven't changed that. – Dietrich Epp Apr 10 '17 at 21:28
  • @BPL: This is what people are talking about when they complain about the "OpenGL state machine". The fact is, in a better world, `glBindTexture` would take an extra parameter so you can specify which unit you want to bind the texture to. The way it currently works, `glActiveTexture` basically sets a global variable which is then passed to `glBindTexture` as a hidden input. Awful, right? – Dietrich Epp Apr 10 '17 at 21:32
  • @BPL: This is also why I'd not really recommend trying to get too familiar with the details of how the OpenGL API works. It's full of little hacks and warts like these. If you can understand on a more conceptual level how the graphics pipeline works, you'll get a much larger payoff. – Dietrich Epp Apr 10 '17 at 21:33
  • Ok, let me assume than on method2 the pipeline will render from texture unit #0 regardless glActiveTexture being called. But in method0, why is it working correctly? I mean, if we consider both codes the only difference is the fact of using additionally glActiveTexture, it seems like somehow is messing up. Said otherwise, in method0 the six calls to glBindTexture will be using texture units from #0 to #6, but in method2 will only be using texture unit #0, even if i'm using the texture ids 0..6? Anyway, I understand your point, sometimes considering the API as a black box is less headache. – BPL Apr 10 '17 at 22:06
  • In method0, you always use texture unit #0, which is correct, since you don't need multitexturing. Every call to `glBindTexture` will bind a texture to texture unit #0. In method2, you will bind the textures to textures #0-#5. Note that "textures" and "texture units" are different. You can create as many textures as you want with `glGenTextures` until you run out of memory. You can't create new texture units, since they're actually pieces of hardware on your graphics card. – Dietrich Epp Apr 10 '17 at 22:36
  • This last explanation of yours was quite revealing! I think I'm starting to understand a little bit better what's going on here. In fact, If I used glActiveTexture(GL_TEXTURE0) in all cube's faces in method2 the output would become exactly the same to method0. One last question then, related to `create_gl_texture`, with glGenTexture you're creating a texture name and with glTexImage you're uploading the buffer to the GPU memory... but you're using the texture unit #0 as you've already pointed out (`glBindTexture`), what is the texture name used for behind the curtains? – BPL Apr 10 '17 at 23:00
  • I mean, you got a fixed ammount of texture units as you've explained and you can create as many textures as available VRAM, so... i guess the texture names created by glGenTextures are used as indices to reference an array of buffers in the VRAM... something like this? – BPL Apr 10 '17 at 23:02
  • Somewhere behind the scenes is an array with information for each texture--the format, a pointer to the memory it uses, and the texture dimensions. The texture ID is just an index into that array. This array is almost certainly not in VRAM. The reason for this is because the driver will need to read from this metadata array to do some processing before it sends commands to the graphics card, and reading from VRAM is messy. The part that's probably in VRAM is the actual texture data. So you pass OpenGL a texture index, and OpenGL gives the graphics card a pointer into VRAM. – Dietrich Epp Apr 10 '17 at 23:16
  • Alright, +1 & validation, thanks for the hints, I think the subject is much more clear now – BPL Apr 11 '17 at 01:40
1

If i set current_method=1 only one face will be textured properly, that's a wrong output.

No, this is not a wrong output. This is the perfectly correct output. You never set up your fixed function pipeline state to do any multitexturing (see @Dietrich Epp's answer for the details). What your render pipeline is configured to is just:

  • take the color value of the per-fragment interpolated vertex color (which you never set, iirc the default is white)
  • sample the texture which is currently bound to texture unit 0 with the per-fragment interpolated texture coords for texture coordinate set 0
  • blend the results together using the texture's alpha channel (GL_DECAL mode)
  • use that as the final fragment color.

So it is clear that you will never see anything from textures bound to the texture units > 0. So what do you see?

When current_method==0, you see what you expect.

If current_method==1, you see a single sample taken from the first texture - which is bound on unit 0 all the time. Since you do not set any tex coords for unit 0, the GL will just repeat the most recently set value, which is (0,1) for the last vertex of your first face. This will of course be used for all vertices of the new faces, so the interpolation will yield a constant value, resulting in a constant texture sampling result, resulting in a constant color.

Now with current_method=2 you still have the first texture bound at texture 0 for all faces. But since you now supply proper per-vertex texcoords for texture unit 0 again, sampling it will actually sample the texture at different regions again, so you'll get a sane mapping of that texture to the primitives.

derhass
  • 43,833
  • 2
  • 57
  • 78