I've coded a GLSL shader for doing a bilinear filtering on a texture
but I get odd effects depending on the size the quad I used to display
result. I picked shader code from http://www.gamerendering.com/2008/10/05/bilinear-interpolation/
and I thought I made not mistakes, but obviously, something's wrong.
I post code below (it depends on glut and glew). On at least two
machines (one NVIDIA, one ATI), when you resize the window you should
see some odd lines from time to time.
Nicolas
// MacOs :
// g++ glut-glsl.cc -Wall -O3 -I/opt/local/include -framework OpenGL -
framework GLUT -lGLEW -L/opt/local/lib -o glut-glsl
//
// Linux :
// g++ glut-glsl.cc -Wall -O3 -lGL -lglut -lGLEW -o glut-glsl
#include <GL/glew.h>
#if defined(__APPLE__)
#include <Glut/glut.h>
#else
#include <GL/glut.h>
#endif
#include <cstdio>
#include <cstdlib>
int height=599, width=599;
GLuint shader, texture;
void
log(GLuint obj)
{
int infologLength = 0;
char infoLog[1024];
if (glIsShader(obj))
glGetShaderInfoLog(obj, 1024, &infologLength, infoLog);
else
glGetProgramInfoLog(obj, 1024, &infologLength, infoLog);
if (infologLength > 0)
printf("%s\n", infoLog);
}
GLuint
make_shader(void)
{
GLuint vs,fs, sp;
const char *vsSource[1] = {" \
void main(void) { \
gl_TexCoord[0] = gl_MultiTexCoord0; \
gl_Position = ftransform(); \
}"};
const char *fsSource[1] = {"
\
uniform sampler2D image;
\
uniform vec2 pixel;
\
void main()
{ \
vec2 c = gl_TexCoord[0].xy;
\
vec4 tl = texture2D(image, c);
\
vec4 tr = texture2D(image, c+vec2(1,0)*pixel*1.00);
\
vec4 bl = texture2D(image, c+vec2(0,1)*pixel*1.00);
\
vec4 br = texture2D(image, c+vec2(1,1)*pixel*1.00);
\
vec2 f = fract(c/pixel);
\
float a = mix(mix(tl, tr, f.x), mix(bl, br, f.x), f.y).a;
\
gl_FragColor = vec4 (a,a,a,1.0);
\
}"};
vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, vsSource, NULL);
glCompileShader(vs);
log(vs);
fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, fsSource, NULL);
glCompileShader(fs);
log(fs);
sp = glCreateProgram();
glAttachShader(sp, vs);
glAttachShader(sp, fs);
glLinkProgram(sp);
log(sp);
return sp;
}
GLuint
make_texture (int w, int h)
{
float data[w*h];
for (int i=0; i<w*h; i++)
data[i] = i/float(w*h);
GLuint texture;
glGenTextures(1, &texture);
glEnable(GL_TEXTURE_2D);
glBindTexture (GL_TEXTURE_2D, texture);
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glPixelStorei (GL_PACK_ALIGNMENT, 1);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D (GL_TEXTURE_2D, 0, GL_ALPHA16, w, h, 0, GL_ALPHA,
GL_FLOAT, data);
return texture;
}
void
display (void)
{
glClearColor (1,1,1,1);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shader);
glBegin(GL_QUADS);
glTexCoord2f(0,1); glVertex2f(0, 0);
glTexCoord2f(0,0); glVertex2f(0, height);
glTexCoord2f(1,0); glVertex2f(width,height);
glTexCoord2f(1,1); glVertex2f(width,0);
glEnd();
glUseProgram(0);
glutSwapBuffers();
}
void reshape (int w, int h) {
width = w;
height = h;
glViewport (0,0,width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, 0, height, -1, 1);
glMatrixMode(GL_MODELVIEW);
glutPostRedisplay();
}
void on_key_press (unsigned char key, int x, int y) {
if (key == 27)
exit(0);
}
int main (int argc, char **argv) {
glutInit (&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutCreateWindow ("glut-glsl");
glutReshapeFunc (reshape);
glutDisplayFunc (display);
glutKeyboardFunc (on_key_press);
glutReshapeWindow (width,height);
glewInit();
int w = 4, h = 4;
shader = make_shader();
texture = make_texture(w,h);
glUseProgram(shader);
GLint imageLoc = glGetUniformLocation(shader, "image");
GLint pixelLoc = glGetUniformLocation(shader, "pixel");
glUniform1f(imageLoc, 0);
glUniform2f(pixelLoc, 1.0/w, 1.0/h);
glUseProgram(0);
glColor4f(1, 1, 1, 1);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glBindTexture (GL_TEXTURE_2D, texture);
glutMainLoop();
return 0;
}
Nicolas
.. on hardware you testing on. I wrote bicubic shared few years ago
and it worked fine on NV hardware. On ATI I got similar "random" line
streaks you describe, the problem was that I forgot to substitute the
fractional coordinate from the texcoord; oops!
How can that be a problem? I did have to scale the computed texture
coordinates back to normalized range after I did the fractional part
extraction, some really extreme value like 0.9999999 * scale did come
out 1.00000000001 * scale, and BANG, we're hitting the wrong texel.
Fix was easy enough:
texcoord -= fracpart;
Now the texcoord's all were integer multiplies, scaling was no
problem. Note: on D3D and OGL the sampling rules are different, of
course.. now, if we sample from precisely center of the sampling
hotspot we can have a HUGE error (half texels) and still be alright
with nearest neighbour sampling.
This means the alternate fix would be to offset the texture coordinate
and not subtract the fracpart, but for getting the correct fracpart
you don't want to offset the input texcoords. Either way works just
fine.. I wish I could see recording of my face when I tried the code
on different vendors GPU. =) =)
Nicolas