Las coordenadas de la textura cerca de 1 se comportan de manera extraña.

Estoy usando OpenGL (Py) para mostrar 256 imágenes indexadas de colores. Uso un sombreador junto con una textura 1D que contiene la paleta. Aquí está el código del sombreador de fragmentos:

#version 330
uniform sampler2D texture; 
uniform sampler1D palette;

void main() 
{ 
    vec2 uv = gl_TexCoord[0].xy; 
    vec4 color = texture2D(texture, uv); 
    gl_FragColor = texture1D(palette, color.a) ;
}

Para evitar errores de redondeo, todos los filtros MAG y MIN se ajustan a NEAREST.

La forma en que veía las coordenadas de textura para la textura 1D era:

el color 0 se encuentra en el intervalo [0; 1/256 [el color 1 se encuentra en el intervalo [1/256; 2/256 [...el color 255 está en el intervalo [255/256; 1

Me convertí en índices enteros para flotar entre 0 y 1, para asegurarme de lo que estaba sucediendo, utilizando la fórmula x_float = (x_int + .4) / 256, es decir, x_float está dentro de los intervalos mencionados, un poco antes de su centro (para evitar que el resultado se redondee en el lado incorrecto del intervalo).

Pero no funciona. Hice un tablero de control de 256 celdas, con índices de colores de 0 a 255 y una paleta de niveles de gris (de 0x000000 a 0xFFFFFF). El código se da a continuación. Luego hice una instantánea de la pantalla y la edité en Paint.NET para ver si los colores eran correctos, y noté un salto en el color 0xE0: obtengo el doble de color 0xDF, y desde este punto, todo se desplaza en uno: el último color es 0xFE. de 0xFF.

Sospecho que hay algún tipo de error de redondeo, pero no veo cómo ...

Aquí está el código completo:

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

from OpenGL.arrays import vbo
from OpenGL.GL import shaders
from numpy import *

def checkboard(size = 512, cell = 32):
    bitmap = zeros(size * size, 'u8')
    bitmap.shape = (size, size)
    current_color = 0

    for y in range(0, size, cell):
        for x in range(0, size, cell):
            bitmap[y : y + cell, x : x + cell] = current_color
            current_color += 1

    palette = array([[a, a, a, 255] for a in range(256)], 'u8')

    return bitmap, palette

def reshape(w, h):
    glutDisplayFunc(lambda: display(w, h))
    glutPostRedisplay();

glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
glutCreateWindow("Hello World :'D")

### init code

# vbo
quad = (0.0, 0.0, 512.0, 512.0)
tex = (0., 0., 1., 1.)

my_vbo = vbo.VBO(
        array( [
            ( quad[0], quad[1], 0, tex[0], tex[1]),
            ( quad[0], quad[3], 0, tex[0], tex[3]),
            ( quad[2], quad[3], 0, tex[2], tex[3]),
            ( quad[2], quad[1], 0, tex[2], tex[1])
        ],'f,f,f,f,f')
    )

# texture
bitmap, palette = checkboard()
height, width = bitmap.shape
f_image = (array(bitmap, 'f') + .4) / 256.0

# Image to be displayed
image_id = glGenTextures(1)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, image_id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
             GL_ALPHA, GL_FLOAT, f_image)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glActiveTexture(GL_TEXTURE0)

# palette
f_palette = (palette / float32(255))

palette_id = glGenTextures(1)
glEnable(GL_TEXTURE_1D)
glBindTexture(GL_TEXTURE_1D, palette_id)
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0,
             GL_RGBA, GL_FLOAT, f_palette)
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
#glActiveTexture(GL_TEXTURE1)

# shaders
VERTEX_SHADER = shaders.compileShader("""#version 330

layout(location = 0) in vec4 position;
uniform vec2 offset ;

void main()
{
      gl_FrontColor = gl_Color;
      gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;
      gl_Position = vec4((offset.x + position.x - 256) / 256, (256 - offset.y - position.y)/256, 0.0, 1.0);
}""", GL_VERTEX_SHADER)

FRAGMENT_SHADER = shaders.compileShader("""#version 330
    uniform sampler2D texture; 
    uniform sampler1D palette;

    void main() 
    { 
        vec2 uv = gl_TexCoord[0].xy; 
        vec4 color = texture2D(texture, uv); 
        gl_FragColor = texture1D(palette, color.a) ;
    }""", GL_FRAGMENT_SHADER)

shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER)

# uniform variables
offset_uniform_loc = glGetUniformLocation(shader, "offset")
texture_uniform_loc = glGetUniformLocation(shader, 'texture' )
palette_uniform_loc = glGetUniformLocation(shader, 'palette' )


def display(w, h):
    """Render the geometry for the scene."""
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0, w, 0, h, -1, 1)

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity()

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glEnable( GL_TEXTURE_2D )
    glActiveTexture( GL_TEXTURE0 )
    glBindTexture( GL_TEXTURE_2D, image_id)

    glEnable( GL_TEXTURE_1D )
    glActiveTexture( GL_TEXTURE1 )

    shaders.glUseProgram(shader)
    shaders.glUniform1i(texture_uniform_loc, 0)
    shaders.glUniform1i(palette_uniform_loc, 1)
    shaders.glUniform2f(offset_uniform_loc, 0, 0)

    try:
        my_vbo.bind()
        try:
            glEnableClientState(GL_VERTEX_ARRAY)
            glEnableClientState(GL_TEXTURE_COORD_ARRAY)
            glVertexPointer(3, GL_FLOAT, 20, my_vbo)
            glTexCoordPointer(2, GL_FLOAT, 20, my_vbo + 12)

            glBindTexture( GL_TEXTURE_1D, palette_id)
            glDrawArrays(GL_QUADS, 0, 4)
        finally:
            my_vbo.unbind()
            glDisableClientState(GL_TEXTURE_COORD_ARRAY)
            glDisableClientState(GL_VERTEX_ARRAY)
    finally:
        shaders.glUseProgram( 0 )

    glutSwapBuffers()


glutReshapeFunc(reshape)
glutIdleFunc(glutPostRedisplay)
glutMainLoop()