Skip to main content
GameDev.net gamedev.net
🔒 Locked

Smaller square by GL_QUADS than by GL_LINE_LOOP?

Started by danath Feb 19, 2009 at 8:44 AM 3 replies 3k views
Original Post
danath
danath
Hi there, I am quite new to OpneGL and I am trying example 9-1 (the checkimage) from 'OpenGL Programming Guide (2nd)' but there is something strange after I modified it a little. I found that the square drawn on the screen by GL_QUADS was smaller than the one by GL_LINE_LOOP, even they were drawn with the same size (64 * 64). The result was the one by GL_QUADS was actually 63 * 63, while the one by GL_LINE_LOOP was actually 64 * 64, and I checked the screen shot with GIMP. I searched a lot on Google, but found little help. Could you please give me some clue about what's the matter here? Thanks in advance. My box is with Mobility Radeon 7500, running Mandriva Linux 2009.0 (OpenGL 1.3, Mesa 7.04). I compiled the program with 'g++ gl.cpp -ogl -lGL -lglut'. The complete code is as below: --------------------------------------------------------------------------------------- #include #include #include #include #include /* Create checkerboard texture */ #define checkImageWidth 64 #define checkImageHeight 64 static GLubyte checkImage[checkImageHeight][checkImageWidth][4]; static GLuint texName; void makeCheckImage(void) { int i, j, c; for (i = 0; i < checkImageHeight; i++) { for (j = 0; j < checkImageWidth; j++) { c = ((((i&0x8)==0)^((j&0x8))==0))*255; checkImage[j][0] = (GLubyte) c; checkImage[j][1] = (GLubyte) c; checkImage[j][2] = (GLubyte) c; checkImage[j][3] = (GLubyte) 255; } } } void init(void) { glClearColor (0.5, 0.5, 0.5, 0.0); glShadeModel(GL_FLAT); glEnable(GL_DEPTH_TEST); makeCheckImage(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glGenTextures(1, &texName); glBindTexture(GL_TEXTURE_2D, texName); 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_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, checkImageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage); } void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glBindTexture(GL_TEXTURE_2D, texName); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i( 10, 10); glTexCoord2i(0, 1); glVertex2i( 10, 73); glTexCoord2i(1, 1); glVertex2i( 73, 73); glTexCoord2i(1, 0); glVertex2i( 73, 10); glEnd(); glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT, GL_LINE); glBegin(GL_LINE_LOOP); glVertex2i( 90, 90); glVertex2i( 90, 153); glVertex2i( 153, 153); glVertex2i( 153, 90); glEnd(); glFlush(); } void reshape(int w, int h) { glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, w, 0, h, 1, -1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void keyboard (unsigned char key, int x, int y) { switch (key) { case 27: exit(0); break; default: break; } } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(250, 250); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }
Brother Bob
Brother Bob
This is the classical pitfall when it comes to rasterization rules in OpenGL.

The quad you draw is actually correct; the coordinates span a 63x63 square (from 10 to 73 is 63 units), and you say you get a 63x63 pixel square. So this result is actually correct and expected.

The line loop is, actually, also one of the correct renderings to be expected. The thing is, the coordinate system you have setup maps integer coordinates to pixel edges, and since you draw the lines exactly on integer coordinates, the line to be rasterized is on pixel edges. Since you can only draw pixels on pixel centers, and the pixels needed to be drawn are located exactly between two pixels, there must be some choise what pixel to chose (this comes down do things similar to using > or >= for comparisons for floating point values--doesn't matter for anyghing except things that happens to be equal, which is usually by accident or limited special cases--and floating point math precision).

To draw filled primitives exactly, the setup you have is correct (except that the quad you draw is 63x63 and the one you want is 64x64), but it is not correct for line and point drawing. In that case you need to offset the coordinates by one-half.
glMatrixMode(GL_PROJECTION);glLoadIdentity();glOrtho(0, w, 0, h, 1, -1);glTranslatef(-0.5, -0.5, 0);

But keep in mind that using this projection matrix for filled primitives is as dangerous as using the other one for lines and points.

The rasterization rules are perfectly defined in OpenGL, and the result you get is conformant with the specification. You need to be aware of these rules to get pixel perfect rendering.
danath
danath
Bob,

Thank you so much for quick response, but I'm afraid I don't understand you very well.

1. the span of the square, I still think the size should be 64? I mean the first pixel is on 10 and the last pixel is on 73, so the distance occupied by the 2 pixels is 73 - 10 + 1 = 64 and the size of the square should be 64 * 64?

2. do you mean the 'pixel choice' is done for the line loop? it also happens to the square? if yes why the results are different?

3. I added the translation to my code, but I did not see much difference as the square was still 63 * 63, but the missing right-up corner of the line loop was back.


Further I have another bad case. I displayed 2 GL_QUADS on the screen, both of actual shapes were 1 pixel smaller than the size I specified and the texture looked to be scaled down: suppose the original parameters are (0, 0) ~ (49, 49), and one was scaled to (0, 0) ~ (48, 48) and the other was scaled to (1, 1) ~ (49, 49), so I even could not fix it by drawing a larger rectangle!

hmmmmmm...
Brother Bob
Brother Bob
Concider this rough diagram. It represents a 4x3 pixel viewport.
+---+---+---+---+|   |   |   |   |+---+---+---+---+|   |   |   |   |+---+---+---+---+|   |   |   |   |+---+---+---+---+

Each square is a pixel, and the whole size represent the window. Now, with your projection matrix setup, you have (0,0) in the lower left corner, and (4,3) in the upper right. Notice how one unit corresponds to exactly one pixel, which is desired.
3 +---+---+---+---+  |   |   |   |   |2 +---+---+---+---+  |   |   |   |   |1 +---+---+---+---+  |   |   |   |   |0 +---+---+---+---+  0   1   2   3   4

Now, let's address some of your questions.
Quote:

1. the span of the square, I still think the size should be 64? I mean the first pixel is on 10 and the last pixel is on 73, so the distance occupied by the 2 pixels is 73 - 10 + 1 = 64 and the size of the square should be 64 * 64?

Draw a quad from (1,1) to (3,2) and see what happens. The quad is, in terms of units, exactly 2x1 units large. In the coordinate system above, it covers exactly a complete 1x2 pixel grid, and nothing more. From the pixel edge at (1,1) to the pixel edge at (3,2). Now, extend that to your (10,10) to (73,73) pixel grid, and you will see it covers a 63x63 pixel grid.

Or look at it from another way, assuming you should actually get a 64x64 grid, what do you have to do to draw a 63x63 grid? A 62x62 grid? Or, most importantly, a 1x1 grid? The 1x1 grid would need to be four identical coordinates.
Quote:

2. do you mean the 'pixel choice' is done for the line loop? it also happens to the square? if yes why the results are different?

Draw a line from (1,1) to (3,1). What pixels does the line go through? The line lies exactly on the edge between two pixel rows. So what pixel rows are actually rasterized? That's the choise that has to be made, because your line is exactly between pixels.

To draw a line correctly, you need to specify pixel centers as start and end points. Pixel centers are located between the integer coordinates, so the bottom left pixel xenter is located at (0.5, 0.5). To draw a line from pixel centers, you need to specify half-integer coordinates, which is done by offseting coordinates.
Quote:

3. I added the translation to my code, but I did not see much difference as the square was still 63 * 63, but the missing right-up corner of the line loop was back.

Perhaps becuase you offsetted the wrong coordinate system. Given the code you provided, the quad shall be 63x63, and a missing pixel on the line (also a common phenomenon) usually happens when the coordinate system is not setup, or coordinates not specified, properly.
Quote:


Further I have another bad case. I displayed 2 GL_QUADS on the screen, both of actual shapes were 1 pixel smaller than the size I specified and the texture looked to be scaled down: suppose the original parameters are (0, 0) ~ (49, 49), and one was scaled to (0, 0) ~ (48, 48) and the other was scaled to (1, 1) ~ (49, 49), so I even could not fix it by drawing a larger rectangle!

Again, this is the typical result of incorrect setup or coordinates.
danath
danath
Bob,

I think the biggest difference I find from your reply is that OpenGL's pixel is an abstract one while the pixel on the screen is a small square, while I used to take them as the same thing.

As to the last bad case I mentioned, I should call gluOrtho2D() like:

gluOrtho2D(0, Width, 0, Height);

instead of:

gluOrtho2D(0, Width - 1, 0, Height - 1);

to avoid the 'scaling down' result.

Thanks again for your patience & reply.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.