How can I achieve pixel-perfect images in OpenGL?

Hi,

I'm having some trouble with image blurring, I believe due to anti aliasing in an opengl 3d scene. Basically the app is a picture viewer where the pictures are floating around in a 3d space. When I click on one of the floating images it moves to the foreground but even at zero in the z coordinates, opengl seems to render the image with a slight blur.

Is there an OpenGL expert around who can suggest a method for drawing pixel-perfect textures on top of a 3d scene? I've tried every combination of blend modes and orthographic projections I can think of.

The app works perfectly except for that last step where I need the picture to be pixel-for-pixel as it was when it was loaded.

Thanks!
LVL 1
fluctoAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Jose ParrotGraphics ExpertCommented:
If you need to render pixel by pixel images, then you should use a 2D tool (GDI, GDI+, for instance). The alternative is to use OpenGL as a 2D API and make your window coincident to the projection plane, but you already know the results...
You may want experiment glDrawPixels function and GL_PACK_ALIGNMENT and GL_PACK_UNLIGNMENT variables. Also take a look at
http://www.opengl.org/resources/features/KilgardTechniques/oglpitfall/
Jose Parrot
0
jgordosCommented:
There is no good answer here.

And to further complicate the problem, different video cards will render the exact same scene differently.

The basic issue is that the process of texturing a 2D image on to a 3D surface is a mapping of some sort.. from the 3d polygon to the flat, 2D screen.

This mapping inherently performs transformations, because there is no such thing as a one to one mapping between the 2d pixel space and the 3d image space.

You will need to adopt a hybrid approach...

The images floating in the 3d space are in fact, 3d images.

The one that is presumably being selected and displayed on the screen would be a 2d image overlay on the 3d scene... ie, there is nothing really 3d about it.

Hope this helps,
-john
0
ikeworkCommented:
Hi flucto,

I would object to my fellow experts.

You can render the image pixel-perfectly as long as the following conditions are met:


1) Switch off any antialiasing and other post effects

2) Make sure after the projection to the screen the projected texture is as big as the original texture in screen-pixels

3) Dont use MipMaps nor Bilinear Texture-Filtering (GL_LINEAR), use Nearest Filtering (GL_NEAREST). Then only the nearest texel is used for texture-filtering, which is the original pixel as long as condition 2) is met

    Nearest Filtering:

      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


ike
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

ikeworkCommented:
Here is a small example-app that does the job, it maps the texture to a full-window-quad. The image looks excactly like in any image-viewer.

The example image is attached too.

Hope it helps :)

ike
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <GL/glut.h>
#include <GL/gl.h>

////////////////////////////////////////////////////
//      glut callbacks 
static void         key(unsigned char k, int x, int y);
static void         special(int k, int x, int y);
static void         render();
////////////////////////////////////////////////////
static GLboolean    load_texture_gl( const char *filename, GLuint *pTexture, GLboolean linearFiltering );
static void         initializeGL();

static GLuint       g_texture = 0;
static const int    g_imageWidth = 256;
static const int    g_imageHeight = 256;

static void resize(int width, int height)
{
    glViewport( 0, 0, width, height );

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho( 0.0, 1.0, 0.0, 1.0, -1.0f, 1.0f );

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(g_imageWidth, g_imageHeight);
    glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE );
    glutCreateWindow( argv[0] );

    initializeGL();

    glutDisplayFunc( render );
    glutKeyboardFunc( key );
    glutReshapeFunc( resize );

    glutMainLoop();

    return 0;
}

static void initializeGL()
{
    glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
    glEnable( GL_TEXTURE_2D );
    if(GL_FALSE == load_texture_gl("et.bmp", &g_texture, GL_TRUE)) exit(-1);
}

static void key(unsigned char k, int x, int y)
{
    switch(k)
    {
        // Escape
        case 27:
            exit(0);
            break;
    }
}

static void render()
{
    glClear( GL_COLOR_BUFFER_BIT );

    glLoadIdentity();

    glBindTexture( GL_TEXTURE_2D, g_texture );

    glBegin(GL_QUADS);
        glTexCoord2f(0.0f, 0.0f);
        glVertex3f(0.0f, 0.0f, 0.0f);
        
        glTexCoord2f(0.0f, 1.0f);
        glVertex3f(0.0f, 1.0f, 0.0f);

        glTexCoord2f(1.0f, 1.0f);
        glVertex3f(1.0f, 1.0f, 0.0f);

        glTexCoord2f(1.0f, 0.0f);
        glVertex3f(1.0f, 0.0f, 0.0f);
    glEnd();

    glFlush();
    glutSwapBuffers();
}

typedef struct _tex_data
{
    // size of the image in bytes
    int size;
    int width;
    int height;
    // the texture-data, must be freed by client
    unsigned char *data;

} tex_data;

// quick and dirty bitmap loader...for 24 bit bitmaps with 1 plane only.
// See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/BMP.txt for more info.
static int load_bmp24( tex_data *pTex, FILE *pFile )
{
    int i;                              // standard counter.
    unsigned short int planes;          // number of planes in image (must be 1)
    unsigned short int bpp;             // number of bits per pixel (must be 24)
    unsigned char temp;                 // temporary color storage for bgr-rgb conversion.

    memset( pTex, 0, sizeof( tex_data ) );

    if( fseek( pFile, 0, SEEK_SET ) ) return -1;

    // seek through the bmp header, up to the width/height:
    if( fseek( pFile, 18, SEEK_CUR ) ) return -1;

    // read the width
    if( ( i = fread( &pTex->width, 4, 1, pFile ) ) != 1 ) return -1;

    // read the height
    if( ( i = fread( &pTex->height, 4, 1, pFile ) ) != 1 ) return -1;

    // calculate the size (assuming 24 bits or 3 bytes per pixel).
    pTex->size = pTex->width * pTex->height * 3;

    // read the planes
    if( ( fread( &planes, 2, 1, pFile ) ) != 1 ) return -1;
    if( planes != 1 ) return -1;

    // read the bpp
    if( ( i = fread( &bpp, 2, 1, pFile ) ) != 1 ) return -1;
    if( bpp != 24 ) return -1;

    // seek past the rest of the bitmap header.
    fseek( pFile, 24, SEEK_CUR );

    // read the data.
    pTex->data = (unsigned char*)malloc( sizeof( unsigned char ) * pTex->size );
    if( !pTex->data ) return -1;

    if( ( i = fread( pTex->data, pTex->size, 1, pFile ) ) != 1)
    {
        free( pTex->data );
        return -1;
    }

    for( i = 0; i < pTex->size; i += 3 )
    {
        // reverse all of the colors. (bgr -> rgb)
        temp = pTex->data[ i ];
        pTex->data[ i ] = pTex->data[ i + 2 ];
        pTex->data[ i + 2 ] = temp;
    }

    // we're done.
    return 0;
}

static GLboolean load_texture_gl( const char *filename, GLuint *pTexture, GLboolean linearFiltering )
{
    GLint filtering;
    tex_data tex;
    FILE *pFile = fopen( filename, "rb" );
    if( !pFile )
    {
        fprintf( stderr, "failed to open texture-file %s\n", filename );
        return GL_FALSE;
    }
    if( load_bmp24( &tex, pFile ) )
    {
        fprintf( stderr, "failed to load texture-file %s\n", filename );
        fclose( pFile );
        pFile = NULL;
        return GL_FALSE;
    }

    glGenTextures( 1, pTexture );
    glBindTexture( GL_TEXTURE_2D, *pTexture );
    filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering );
    glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width, tex.height, 0,
                  GL_RGB, GL_UNSIGNED_BYTE, tex.data );

    free( tex.data );
    tex.data = NULL;

    fclose( pFile );
    pFile = NULL;

    return GL_TRUE;
}

Open in new window

et.bmp
0
fluctoAuthor Commented:
Thanks very much, Ike. Give me a day to mess with that. Looks very promising.
0
ikeworkCommented:
No rush, take your time :)
0
fluctoAuthor Commented:
Ike, that works well for images with sizes that are powers of 2, do you have any suggestion for images that have arbitrary sizes? Thanks (a lot) once again.
0
ikeworkCommented:
But thats a general problem with textures, not only for your special case.

Please lets focus on this case first, once its fixed we move on.

>> When I click on one of the floating images it moves to the foreground but
>> even at zero in the z coordinates, opengl seems to render the image with a slight blur.

Did you manage to render the texture *pixel perfectly* like shown in the code above?
0
fluctoAuthor Commented:
Thank you Ike, it works well. I just have to figure out how I will handle non-powers of 2. I'm sure I can think of something.
0
ikeworkCommented:
You have basically 2 options:

1) use an extension for non-power-of-2-textures, check this:

    http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=160026&page=1

2) fill your texture with additional rows and cols (i.e. with black pixels)
0
fluctoAuthor Commented:
Thanks again, Ike. I was already trying both approaches. Unfortunately, the extension isn't available on the hardware platform I'm using so it looks like powers of 2 will be the answer.

Best regards/Mit freundlichen Grüßen
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Game Programming

From novice to tech pro — start learning today.