I'm using (Py)OpenGL to display 256 colors indexed images. I use a shader together with a 1D texture containing the palette. Here's the 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) ;
}
To avoid rounding errors, all MAG and MIN filters are set to NEAREST.
The way I was seeing the texture coordinates for the 1D texture was :
- color 0 lies in interval [0 ; 1/256[
- color 1 lies in interval [1/256 ; 2/256[
...
- color 255 lies in interval [255/256 ; 1[
I converted myself integer indexes to float between 0 and 1, to make sure what was happening, using the formula x_float = (x_int + .4)/256, that is x_float is inside the before mentioned intervals, a little before its center (to avoid the result being rounded at the wrong side of the interval).
But it doesn't work. I made a checkboard of 256 celles, with colors indexes 0 to 255 and a palette of levels of grey (from 0x000000 to 0xFFFFFF). The code is given below. I then made a screen snapshot and edited it in Paint.NET to look if colors were right, and noticed a jump at color 0xE0 : I get twice color 0xDF, and from this one, everything is shifted by one : last color is 0xFE instead of 0xFF.
I suspect some kind of rounding error, but don't see how...
Here's the complete 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()
See Question&Answers more detail:
os