Współrzędne tekstury w pobliżu 1 zachowują się dziwnie

Używam (Py) OpenGL do wyświetlania 256 kolorów obrazów indeksowanych. Używam shadera wraz z teksturą 1D zawierającą paletę. Oto kod cieniowania Fragment:

#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) ;
}

Aby uniknąć błędów zaokrąglania, wszystkie filtry MAG i MIN są ustawione na NAJBLIŻSZE.

Sposób, w jaki widziałem współrzędne tekstury dla tekstury 1D, był następujący:

kolor 0 leży w przedziale [0; 1/256 [kolor 1 leży w przedziale [1/256; 2/256 [...kolor 255 leży w przedziale [255/256; 1 [

Przekształciłem swoje indeksy całkowite, aby płynąć między 0 a 1, aby upewnić się, co się dzieje, używając formuły x_float = (x_int + .4) / 256, czyli x_float jest wewnątrz wcześniej wspomnianych interwałów, trochę przed jej środkiem (do unikaj zaokrąglania wyniku po niewłaściwej stronie przedziału).

Ale to nie działa. Zrobiłem checkboard z 256 komórek, z kolorami indeksów od 0 do 255 i paletą poziomów szarości (od 0x000000 do 0xFFFFFF). Kod jest podany poniżej. Następnie zrobiłem zrzut ekranu i edytowałem go w Paint.NET, aby sprawdzić, czy kolory są poprawne, i zauważyłem skok w kolorze 0xE0: Dostaję dwukrotny kolor 0xDF, a od tego wszystko jest przesunięte o jeden: ostatni kolor to zamiast 0xFE 0xFF.

Podejrzewam jakiś błąd zaokrąglania, ale nie wiem, jak ...

Oto pełny kod:

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()

questionAnswers(1)

yourAnswerToTheQuestion