Texturkoordinaten in der Nähe von 1 verhalten sich merkwürdig
Ich verwende (Py) OpenGL, um mit 256 Farben indizierte Bilder anzuzeigen. Ich verwende einen Shader zusammen mit einer 1D-Textur, die die Palette enthält. Hier ist der Fragment-Shader-Code:
#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) ;
}
Um Rundungsfehler zu vermeiden, sind alle MAG- und MIN-Filter auf NEAREST eingestellt.
Die Art und Weise, wie ich die Texturkoordinaten für die 1D-Textur sah, war:
Farbe 0 liegt im Intervall [0; 1/256 [Farbe 1 liegt im Intervall [1/256; 2/256 [...Farbe 255 liegt im Intervall [255/256; 1 [Ich habe mir ganzzahlige Indizes so konvertiert, dass sie zwischen 0 und 1 liegen, um sicherzustellen, dass das Geschehen mit der Formel x_float = (x_int + .4) / 256, dh x_float befindet sich in den oben genannten Intervallen, kurz vor seiner Mitte (to) Vermeiden Sie, dass das Ergebnis auf der falschen Seite des Intervalls gerundet wird.
Aber es geht nicht. Ich habe ein Checkboard aus 256 Zellen mit den Farbindizes 0 bis 255 und einer Palette von Graustufen (von 0x000000 bis 0xFFFFFF) erstellt. Der Code ist unten angegeben. Ich habe dann einen Screenshot erstellt und ihn in Paint.NET bearbeitet, um zu prüfen, ob die Farben stimmen. Dabei habe ich einen Farbsprung bei 0xE0 festgestellt: Ich erhalte zweimal die Farbe 0xDF, und von dieser wird alles um eins verschoben: Die letzte Farbe ist stattdessen 0xFE von 0xFF.
Ich vermute einen Rundungsfehler, verstehe aber nicht, wie ...
Hier ist der vollständige Code:
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()