How to mitigate "flickering" of thin elements in a image/texture?

Viewed 400

The title might sound a bit confusing, but I don't want to make it too long, so please bear with me and read on...

I'm writing a very simple OpenGL demo that loads an image as texture, then scrolls the image horizontally across the screen infinitely. Here's how I load the texture and render a single frame:

void load_texture(char* filename)
{
    glEnable(GL_TEXTURE_2D);
    gl_texture = SOIL_load_OGL_texture(filename, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y);
    glBindTexture(GL_TEXTURE_2D, gl_texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);
}

void render_single_frame()
{
    glBindTexture(GL_TEXTURE_2D, gl_texture);
    {
        glBegin(GL_QUADS);
        {
            float delta = fmod(1.0f / FRAME_RATE / pan_seconds * current_frame, 1.0f);
            glTexCoord2f(0.0f + delta, 0.0f); glVertex2f(-1.0f, -1.0f);
            glTexCoord2f(1.0f + delta, 0.0f); glVertex2f(+1.0f, -1.0f);
            glTexCoord2f(1.0f + delta, 1.0f); glVertex2f(+1.0f, +1.0f);
            glTexCoord2f(0.0f + delta, 1.0f); glVertex2f(-1.0f, +1.0f);
        }
        glEnd();
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    glutSwapBuffers();
    glFinish();
    current_frame++;
}

...where FRAME_RATE is the target frame rate and pan_seconds is the seconds it should take to scroll across an entire image.

Overall the demo runs quite well, but to nitpick I crafted a test image containing very thin elements - some thin texts and a 1-pixel-wide vertical line (on the very right) on a black background.

Test image

When this image is fed into the demo, and when pan_seconds is high (i.e. image scrolls slowly), the problem emerges - all these elements would noticeably flicker (fluctuate in brightness), most obviously present for the vertical line.

My analysis is that the high pan_seconds results in fractional deltas, i.e. subpixel movements, and OpenGL would have to filter the texture to determine the color of a pixel. Take the vertical line as an example, the interpolation of Bilinear filtering (GL_LINEAR) will for most of the time produce a 2-pixel-wide gray line, which the human eye perceives as 1-pixel-wide, but at the same time will appear less bright compared to the original white line.

This doesn't help with solving the problem though... Changing filter mode to GL_NEAREST will break the smooth scrolling effect; changing to GL_LINEAR_MIPMAP_LINEAR does eliminate the flicker, but at the cost of significantly blurring the image, which is not an acceptable outcome.

Is there a way to reduce this flickering without losing much sharpness, or do I have to trust my own theoretical analysis and call it impossible?

1 Answers

You could work with coordinates equal to pixels, e.g.

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-w/2.f, +w/2.f, -h/2.f, h/2.f, ZNEARCLIP * -1.f, ZFARCLIP * -1.f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, w, h);

If you now draw a line when x and y are whole numbers, the line should end up 1 px wide and not spanned over 2 pixels with interpolation.

Related