Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 1 of 3 123 LastLast
Results 1 to 10 of 25

Thread: 2d orthographic projection bug???

  1. #1
    Intern Newbie
    Join Date
    Feb 2008
    Location
    Budapest
    Posts
    37

    2d orthographic projection bug???

    Hello everyone I know that claiming a bug is the last thing that a serious programmer should do... but anyway I would like you to have a look at this function that draws a simple square:

    static void draw_screen( void )
    {
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    GLint viewport[] = {0,0,0,0};
    glGetIntegerv(GL_VIEWPORT, viewport);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D( 0, viewport[2]-1, 0, viewport[3]-1 );

    // float eps = 0.001937f;
    float eps = 0.0f;
    glBegin(GL_LINE_LOOP);
    glColor3f(1,1,0);
    glVertex2f(eps, eps);
    glVertex2f(100, eps);
    glVertex2f(100, 100);
    glVertex2f(eps, 100);
    glEnd();

    SDL_GL_SwapBuffers();
    }

    The function above simply draws a 100x100 square with the bottom/left corner set to 0,0 that is the bottom/left of the screen. With my NVIDIA 8600 GT driver the bottom and left edge of the square are not visible unless you add a little "epsilon" as in the example. Instead with the Microsoft GDI driver the square is correctly drawn even without the "epsilon".

    If you cut and past the function in the SDL OpenGL sample from the SDL documentation you can immadiately try it out.

    What do you think? it's a bug or it's a feature?

    Thanks

  2. #2
    Senior Member OpenGL Guru
    Join Date
    Feb 2000
    Location
    Sweden
    Posts
    3,115

    Re: 2d orthographic projection bug???

    If your viewport dimensions are W by H pixels, then the correct setup for the projection matrix is gluOrtho2D(0, W, 0, H). Don't subtract 1 from the width and height.

    It's easy to realize that. If your viewport is W pixels wide, and you want a one to one relationship between coordinates and pixels, then the view volume must be W units wide aswell. Your viewport, however, is W-1 units wide.

  3. #3
    Intern Newbie
    Join Date
    Feb 2008
    Location
    Budapest
    Posts
    37

    Re: 2d orthographic projection bug???

    We are getting a bit off topic but anyway... since this is actually a common misconception that many programmers do, I will explain it here:

    glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);

    "...(left,bottom,-near) and (right,top,-near) are points on the near clipping plane that are mapped to the lower left and upper right corners of the viewport window, respectivey." [OpenGL Programmer's Guide 6th ed.]

    So if I have a 640 x 480 pixel screen my coordinates have to go from 0 to 639 on the x and from 0 to 479 on the y, respectively mapped to the lower left and upper right corner of my viewport, as the specs say (and the program shows). If my coordinates go from 0 to 640 and from 0 to 480 what I get is a projection matrix for a 641x481 screen, which is not what we are looking for.

    A simple test will wipe out any doubt, let's try to draw a box on the borders of our viewport:

    The following is wrong since it leaves a pixel on the top and right borders.
    gluOrtho2D(0, W, 0, H)
    glVertex2f(0, 0);
    glVertex2f(W-1, 0);
    glVertex2f(W-1, W-1);
    glVertex2f(eps, W-1);

    The following is correct:
    gluOrtho2D(0, W-1, 0, H-1)
    glVertex2f(...);
    ...

    Also some times graphics programmers fall in the trap of mixing up distances in pixel spaces and in "real" spaces. For example a BOX whose opposite corners are at (0,0)-(10,10) is 10 units wide in "real" coordinates (for example a box in the real world, or in a 3D scene). But, if that box is in a 2D scene, so we are counting in pixels, that box is 11 pixels wide not 10. Infact the cardinality of the following set { 0,1,2,3,4,5,6,7,8,9,10 } is 11, one for each pixel covered by the 2D box.

    Hope this cleared up the background theory

  4. #4
    Junior Member Newbie
    Join Date
    Sep 2006
    Posts
    21

    Re: 2d orthographic projection bug???

    I think what you're seeing is an artifact of how lines are rasterized. When you specify gluOrtho(0,w,0,h), the exact edges of the screen (not the centers of the border pixels, but the outer edges of those pixels) are at x=0,w and y=0,h. So when you draw a line from (0,0) to (100,0), that line is exactly on the border of the image, and gets rasterized (apparently) to the pixels below and to the left. It's a degenerate case. If you want to exactly color pixels with a line, you need to specify that the line goes through the _centers_ of the pixels, which would be at an offset of 0.5 (for a correctly set up pixel projection).

    On that last point, for a one to one pixel mapping, what you should set up is an ortho of (0,w,0,h), with a viewport of (0,0,w,h). This gives you a situation where (0.5,0.5) is the center of the lower left pixel, and (w-0.5,h-0.5) is the center of the upper-right-most pixel. Drawing a quad from (0,0) to (w,h) will exactly fill the screen (because quads are rasterized based on the _interior_ of the specified geometry), while a line that defines the border of the screen should be from (0.5,0.5) to (w-0.5,h-0.5).

    I've heard that this behavior differs between OpenGL and Direct3D, but I'm not sure about that. No D3D experience.

  5. #5
    Senior Member OpenGL Guru
    Join Date
    Feb 2000
    Location
    Sweden
    Posts
    3,115

    Re: 2d orthographic projection bug???

    The misconception and lack of understanding about the rasterization rules employed by OpenGL is, actually, on you here. The coordinate system in OpenGL, which you specify with gluOrtho2D, is a continuous coordinate system, not a discrete system measured in discrete pixels.

    Take a look at this diagram, illustrating the problem. It's a viewport being 5 pixels wide. Same with height, so just going for a one-dimensional example here.
    Code :
    |--x--|--x--|--x--|--x--|--x--|
    0     1     2     3     4     5
    This is the whole viewport. | represents pixel boundaries, x represents pixel centers, and - representes the continuous axis. Notice how the viewport is 5 pixel wide, the very left edge is coordinate 0, and the very right edge is coordinage 5, not 4 as you're arguing for. Note also that pixel centers are located at integer plus one half offset, and that integer coordinates are located on the boundaries between adjacent pixels.

    Rasterization rules in OpenGL states that a pixel is rasterized (for a filled primitive) if it's center is located inside the primitive being drawn. Now draw a "quad" from 1 to 4, and it will cover the pixel centers at 1.5, 2.5, and 3.5. The quad is therefore 3 pixels wide when rasterized, and it was specified as being 3 units wide (from 1 to 4). Everything is consistent.

  6. #6
    Senior Member OpenGL Guru
    Join Date
    Feb 2000
    Location
    Sweden
    Posts
    3,115

    Re: 2d orthographic projection bug???

    And I just noticed that you're drawing lines, not filled primitives. While the theory for the continuous axis I provided is correct and still applies, the rasterization rule for quads I mentioned is not really relevant to your post. What stephen diverdi said about drawing lines from/to half-integer coordinates are correct. Different rules applies for lines and points that for filled primitives.

  7. #7
    Advanced Member Frequent Contributor
    Join Date
    Apr 2004
    Posts
    999

    Re: 2d orthographic projection bug???

    Here's a link to another thread on this forum discussing this problem.

    Like Stephen already mentioned, this behaviour differs between OpenGL and D3D as you can see here. The second figure shows that D3D has its origin in the center of the corner pixel while OpenGL defines the origin as the corner of the corner pixel.

  8. #8
    Intern Newbie
    Join Date
    Feb 2008
    Location
    Budapest
    Posts
    37

    Re: 2d orthographic projection bug???

    First of all thank you guys for your attention.

    Yes, drawing lines and drawing polygons leads to very different behaviours because polygons have to be drawn in a way that they don't overwrite each other when they are drawn one next to the other.

    With respect to the "misconception and lack of understanding" topic I think that the documentation states it very clearly, you cannot really go that much wrong: review the part about what maps to what. I also invite you to take your time and try the simple tests I mentioned above, and see it working. My experience shows very clearly that what you state in theory don't match reality. The "drawing" above shows the "misconception" I was talking about or if you like it more, that's not the 2D I'm looking for, which is a mapping from glVector2f to the pixel on the screen (m still talking bout lines and points), which means that if you want to draw on the last pixel of a 640 wide screen I would like to use glVector2f/i(639,y) as in any other 2D program and not glVector2f/i(640,y).

    Further note about pixel and centers (also very easy to test) and possible explaination of the originally repoted problem:

    Established that gluOrtho2D(0, W-1, 0, H-1) produces the expected resuts (as you might have tested by now) we can see that the coordinate glVector2f(0,0) is __inside__ the viewing frustum, to be more precise is at the very bottom left border of the bottom left pixel. On the other hand, the coordinate glVector2f(W-1,H-1) is __inside__ the viewing volume as expected, more precisely at the very top/right edge of the top/right pixel.

    This might mean that in my NVIDIA driver (and not with the GDI one and another Radeon I just tested) numerical instability creep in and makes the (0,0) coordinates snap to the previous pixel, thus contraddicting what according to the documentation should happen, that is, coordinate (0,0) __should__ be mapped to pixel (0,0).

    Cheers

  9. #9
    Junior Member Newbie
    Join Date
    Sep 2006
    Posts
    21

    Re: 2d orthographic projection bug???

    If you want to use points and lines and have integer coordinates exactly specify pixel centers, the correct way to do that would be to use an ortho of (-0.5,w-0.5,-0.5,h-0.5). Then (0,0) would exactly map to the center of the lower left pixel, and (w-1,h-1) would exactly map to the center of the upper right pixel.

    The problem with your examples is they are not thorough enough - you are testing degenerate conditions. When you put a point or line on (0,0) with an ortho of (0,w,0,h) or (0,w-1,0,h-1), your geometry falls exactly on the corner of four pixels - which one should be turned on? It's implementation dependent, and subtle rounding issues can influence it.

    One thing is certain - if you set up an ortho of (0,w-1,0,h-1) and draw a quad from (0,0) to (w-1,h-1) it will exactly fill the scene. Similarly, if you set up an ortho of (0,1,0,1) and draw a quad from (0,0) to (1,1) it will also exactly fill the screen. But neither of those will give you exact pixel addressability. Your (0,w-1,0,h-1) may give you the appearance of exact pixel coordinates because of rounding and nearest sampling. Try using anti-aliasing or linear texture filtering and compare exact bit values in the resulting images and you will see errors.

    This is a common problem in GPGPU work and has been hashed out very carefully for this reason. If you have clear documentation that directly contradicts the above, please post a link to it.

  10. #10
    Intern Newbie
    Join Date
    Feb 2008
    Location
    Budapest
    Posts
    37

    Re: 2d orthographic projection bug???

    Thank you NiCo!
    the link on the MSDN is indeed very interesting and explains also some other "artefacts" about 2D and texturing I was getting on another project...

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •