Understanding the window coordinates' interpretation in OpenGL - opengl

I was trying to understand OpenGL a bit more deep and I got stuck with below issue.
This segment describes my understanding, and the outputs are as assumed.
glViewport(0, 0 ,800, 480);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-400.0, 400.0, -240.0, 240.0, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -1);
glRotatef(0, 0, 0, 1);
glBegin(GL_QUADS);
glVertex3f(-128, -128, 0.0f);
glVertex3f(128, -128, 0.0f);
glVertex3f(128, 128, 0.0f);
glVertex3f(-128, 128, 0.0f);
glEnd();
The window coordinates (Wx, Wy, Wz) for the above snippet are
(272.00000286102295, 111.99999332427979, 5.9604644775390625e-008)
(527.99999713897705, 111.99999332427979, 5.9604644775390625e-008)
(527.99999713897705, 368.00000667572021, 5.9604644775390625e-008)
(272.00000286102295, 368.00000667572021, 5.9604644775390625e-008)
I did a glReadPixels() and dumped to a bmp file. In the image I get a quad as expected with the (Wx, Wy) mentioned above ( since incase of images, the origin is at the top left, while verifying the bmp image I took care of subtracting the the window height i.e 480). This output was as per my understanding - (Wx, Wy) will be used as a 2D coordinate and Wz will be used for depth purpose.
Now comes the issue. I tried the below code snippet.
glViewport(0, 0 ,800, 480);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-400.0, 400.0, -240.0, 240.0, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(100, 0, -1);
glRotatef(30, 0, 1, 0);
glBegin(GL_QUADS);
glVertex3f(-128, -128, 0.0f);
glVertex3f(128, -128, 0.0f);
glVertex3f(128, 128, 0.0f);
glVertex3f(-128, 128, 0.0f);
glEnd()
The window coordinates for the above snippet are
(400.17224205479812, 242.03174613770986, 1.0261343689191909)
(403.24386530741430, 238.03076912806583, 0.99456100555566640)
(403.24386530741430, 241.96923087193414, 0.99456100555566640)
(400.17224205479812, 237.96825386229017, 1.0261343689191909)
When I dumped output to a bmp file, I expected to have a very small parallelogram(approx like a 4 x 4 square transformed to a parallelogram) based on the above (Wx, Wy). But this was not the case. The image had a different set of coordinates as below
(403, 238)
(499, 113)
(499, 366)
(403, 241)
I have mentioned the coordinates in CW direction as seen on the image.
I got lost here. Can anyone please help in understanding what and why it is happening in the 2nd case??
How come I got a point (499, 113) on the screen when it was no where in the calculated window coordinates?
I used gluProject() to the window coordinates.
Note : I'm using OpenGL 2.0. I'm just trying to understand the concepts here, so please don't suggest to use versions > OpenGL 3.0.
edit
This is an update for the answer posted by derhass
The homogenous coordinates after the projection matrix for the 2nd case is as follows
(-0.027128123630699719, -0.53333336114883423, -66.292930483818054, -63.000000000000000)
(0.52712811245482882, -0.53333336114883423, 64.292930722236633, 65.00000000000000)
(0.52712811245482882, 0.53333336114883423, 64.292930722236633, 65.000000000000000)
(-0.027128123630699719, 0.53333336114883423, -66.292930483818054, 63.000000000000000)
So here for the vertices where z > -1, the vertices will get clipped at the near plane. When this is the case, shouldn't GL use the projected point at z = -1 plane?

The thing you are missing here is clipping.
After this
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-400.0, 400.0, -240.0, 240.0, 1.0, 100.0);
you basically have a camera at origin, looking along the -z direction, and the near plane at z=-1, the far plane at z=-100. Now you draw a 128x128 square rotated at 30 degrees aliong the y (up) axis, and shifted by -1 along z (and 100 along x, but that is not the crucial point here). Since You rotated the square around its center point, the z value for two of the points will be way before the near plane, while the other two should fall into the frustum. (And you can also see that as those two points match your expectations).
Now directly projecting all 4 points to window space is not what GL does. It transforms the points to clip space, intersects the primitives with all 6 sides of the viewing frustum and finally projects the clipped primitives into window space for rasterization.
The projection you did is actually only meaningful for points which lie inside the frustum. Two of your points lie behind the camrea, and projecting points behind the camera will create an mirrored image of these points in front of the camera.

Related

How can I define starting position of glutWireCube?

I am rather new to programming so I may not use the correct terminology. I am trying to create a dog out of only glutwirecubes, however I cannot figure out how to define a starting position for the back legs, nor can I figure out how to close the space between my 'shoulder' and 'elbows'. I have each body part assigned to rotate by key press. I also realize that my use of glPushMatrix and glPopMatrix may not be correct as I do not fully understand how the matrix stack is saved/loaded.
glPushMatrix();
glTranslatef(-1, 0, 0);
glRotatef((GLfloat)body,1, 0, 0);//sets rotations about x,y,z axis
glTranslatef(1, 0, 0);
glPushMatrix();
glScalef(2.0, 0.4, 0.5);//sets dimensions of cube
glutWireCube(2.0);//sets scale of wire cube
glPopMatrix();
glPopMatrix();
glPushMatrix();
glTranslatef(-1, 0, 0);
glRotatef((GLfloat)shoulder, 0, 0, 1);//sets rotations about x,y,z axis
glTranslatef(1, 0, 0);
glPushMatrix();
glScalef(1.5, 0.4, 0.5);//sets dimensions of cube
glutWireCube(.75);//sets scale of wire cube
glPopMatrix();
glTranslatef(1,0,0);
glRotatef((GLfloat)elbow,0,0,1);//sets rotations about x,y,z axis
glTranslatef(1,0,0);
glPushMatrix();
glScalef(1.5,0.4,0.5);//sets dimensions of cube
glutWireCube(.75);//sets scale of wire cube
glPopMatrix();
glPopMatrix();
glutSwapBuffers();
The picture is the position I am aiming for, however my cubes always start oriented horizontally.
with glTranslate right before, as for its friends !
For instance you might translate so that the center is now at the corner, so that your rotations rotate around this new handle.

OpenGL change background without changing perspective of previously drawn pictures

So I draw an 'I' and use gluLookAt(0.f,0.f,3.f,0.f,0.f,0.f,0.f,1.f,0.f), and the I is moderate size. Then I add a drawScene() function which draw the background with gradient color, and then the 'I' becomes super big. I guess it is because I change matrix mode to GL_PROJECTION and GL_MODELVIEW in drawScene(), and those change the perspective maybe? I guess glPushMatrix() and glPopMatrix() are needed to reserve matrix status, but I have hard time finding where to put them. So how can I make the 'I' look normal size? Here are my drawI() and drawScene():
void drawI(int format)
{
glBegin(format);
glColor3f(0, 0, 1);
glVertex2f(point[3][0], point[3][1]);
glVertex2f(point[2][0], point[2][1]);
glVertex2f(point[1][0], point[1][1]);
glVertex2f(point[12][0], point[12][1]);
glVertex2f(point[10][0], point[10][1]);
glEnd();
glBegin(format);
glVertex2f(point[10][0], point[10][1]);
glVertex2f(point[11][0], point[11][1]);
glVertex2f(point[12][0], point[12][1]);
glEnd();
glBegin(format);
glVertex2f(point[9][0], point[9][1]);
glVertex2f(point[10][0], point[10][1]);
glVertex2f(point[3][0], point[3][1]);
glVertex2f(point[4][0], point[4][1]);
glVertex2f(point[6][0], point[6][1]);
glColor3f(1, 0.5, 0);
glVertex2f(point[7][0], point[7][1]);
glVertex2f(point[8][0], point[8][1]);
glEnd();
glBegin(format);
glColor3f(0, 0, 1);
glVertex2f(point[5][0], point[5][1]);
glVertex2f(point[6][0], point[6][1]);
glVertex2f(point[4][0], point[4][1]);
glEnd();
}
void drawScene()
{
glBegin(GL_QUADS);
//red color
glColor3f(1.0,0.0,0.0);
glVertex2f(-1.0,-1.0);
glVertex2f(1.0,-1.0);
//blue color
glColor3f(0.0,0.0,1.0);
glVertex2f(1.0, 1.0);
glVertex2f(-1.0, 1.0);
glEnd();
}
Thanks a lot!
So I take glMatrixMode() and glLoadIdentity() out of drawScene() and drawI() and put them in display(). I changed drawScene() and drawI() above, and here is my display()
void display()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70.f,1.f,0.001f,30.f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
drawScene();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.f,0.f,3.f,0.f,0.f,0.f,0.f,1.f,0.f);
drawI(GL_TRIANGLE_FAN);
glutSwapBuffers();
}
The normal way to do this (in a 3D mode) is in your code, before you call drawI or drawScene would be:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fov, aspect, near, far); // fov is camera angle in degrees, aspect is width/height of your viewing area, near and far are your near and far clipping planes.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0,0.0,3.0,0.0,0.0,0.0,0.0,1.0,0.0)
In your 2D rendering, you probably don't need the call to gluPerspective, but these calls should be done in your code before you call drawI or drawScene. Do this and delete the glMatrixMode() and glLoadIdentity() calls from drawI and drawScene.
Edit:
If your "I" is still too big, there are a number of things you could do, but you should probably be operating in 3D (giving a Z coordinate also).
You could scale the object:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0,0.0,3.0,0.0,0.0,0.0,0.0,1.0,0.0)
glScalef(0.5, 0.5, 0.5);
You could move the camera further back (you'll need to include the gluPerspective() call as well):
gluLookAt(0.0,0.0,50.0,0.0,0.0,0.0,0.0,1.0,0.0);
Perhaps the easiest way to control the rendered image size in a 3D mode is to both move the camera (eye) back a way and then control the image size by changing the camera aperture angle (fov in the gluPerspective() call). A wider fov will shrink the rendered image; a smaller fov will enlarge it.
I don't know what the values for your coordinates are in drawI since they're variables, but a camera position of 3.0, an fov of 70.0 and an aspect of 1 should give you left, right, top and bottom clipping planes of about +/- 2.1 at Z = 0.
If you kept everything else the same and moved the camera to 50.0, the clipping planes would be at about +/- 35.0, so your "I" would occupy a much smaller portion of the viewing area.
If you then left the camera position at 50.0, but changed the fov to 40.0, the clipping planes would be at about +/- 18.2. Your "I" would fill a larger area than it did at cameraZ = 50.0, fov = 70.0, but a smaller area than cameraZ = 3.0, fov = 70.0.
You can play with camera position and fov to get the image size you want, or you could just scale the image. I like to keep camera position constant and change the fov. If I provide a function that changes the fov based on user input (maybe a mouse scroll), it's a good way to provide a zoom in/out effect.
BTW, in your original code, if you called:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0,0.0,3.0,0.0,0.0,0.0,0.0,1.0,0.0)
Then later in DrawI or drawScene call:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
You've trashed the matrix loaded by your earlier call to gluLookAt().

Drawing a scene in xz plane opengl

I am working on an opengl program. The viewing parameters are :
eye 0 -4 6
viewup 0 1 0
lookat 0 0 0
I want to draw a background rectangle (with texture) such that I will be able to see it from the current eye location. Right now, the eye is looking from the -ve Y direction. I want to be able to draw a rectangle that covers the entire screen. I am not understanding what coordinates to give to the rectangle and how to get the texture mapping.
Currently I have this in my method:
What would be the code for the same. I have this in function:
glPushMatrix();
glLoadIdentity();
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(-1.0, -1.0);
glVertex2f(-1.0, 1.0);
glVertex2f(1.0, 1.0);
glVertex2f(1.0, -1.0);
glEnd();
glPopMatrix();
The easiest option for drawing a background image that is independent of the camera is to draw it in normalized device coordinates (NDC) and do not perform any transformations/projections on it.
To cover the whole screen, you have to draw a quad going from p = [-1, -1] to [1,1]. The texture coordinates can then be found by tex = (p + 1)/2
Normalized device coordinates are the coordinates one would normally get after applying projection and the perspective divide. They span a cube from [-1,-1,-1] to [1,1,1] where the near plane is mapped to z = -1 (at least in OpenGL, in DirectX the near plane is mapped to z=0). In your special case, the depth should not matter, as long as you draw the background plane as the first element in each frame and disable the depth-test.

Setting the coordinate system for drawing in OpenGL

I just started reading initial chapters of Blue book and got to understand that the projection matrix can be used to modify the mapping of our desired coordinate system to real screen coordinates. It can be used to reset the coordinate system and change it from -1 to 1 on left, right, top and bottom by the following (as an example)
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); //With 1's in the diagonal of the identity matrix, the coordinate system is rest from -1 to 1 (and the drawing should happen then inside those coordinates which be mapped later to the screen)
Another example: (Width: 1024, Height: 768, Aspect Ratio: 1.33) and to change the coordinate system, do:
glOrtho (-100.0 * aspectRatio, 100.0 * aspectRatio, -100.0, 100.0, 100.0, 1000.0);
I expected the coordinate system for OpenGL to change to -133 on left, 133 on right, -100 on bottom and 100 on top. Using these coordinates, I understand that the drawing will be done inside these coordinate and anything outside these coordinates will be clipped.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-100 * aspectRatio, 100 * aspectRatio, -100, 100, 100, 1000);
glMatrixMode(GL_MODELVIEW);
glRectf(-50.0, 50.0, 200, 100);
However, the above command doesn't give me any output on the screen. What am I missing here?
I see two problems here:
The rect should not by show at all, since glRectf() draws at depth z=0, but you set up your orthorgraphic projection to cover the z range [100,1000], so the object lies before the near plane and should be clipped away.
You do not specifiy waht MODELVIEW matrix you use. In the comments, you mention that the object does show up, but not in the place where you expect it. This also violates my first point, but could be explained if the ModelView matrix is not identity.
So I suggest to first use a different projection matrix with like glOrtho(..., -1.0f, 1.0f); so that z=0 is actually covered, and second insert a glLoadIdentity() call after the glMatrixMode(GL_MODELVIEW) in the above code.
Another approach would be to keep the glOrtho() as it is and to specify a translation matrix wich moves the rect somewhere between z=100 and z=1000.

Is gluLookAt together with glFrustum distorting the rendering?

I am wondering if gluLookAt together with glFrustum is distorting the rendered picture.
This is how a scene is rendered:
And here's the code that rendered it.
InitCamera is called once and should, as I understand it now, set up a matrix so as if I looked from a position 2 units above and 3 units in front of the origin towards the origin. Also glFrustum is used in order to create a perspective`.
void InitCamera() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt (
0, 2 , 3,
0, 0 , 0,
0, 1 , - 0
);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum (- 1, 1,
- 1, 1,
1,1000.0);
glMatrixMode(GL_MODELVIEW);
}
Then TheScene is what actually draws the picture:
void TheScene() {
glClear(
GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT
);
glMatrixMode(GL_MODELVIEW);
// Draw red circle around origin and radius 2 units:
glColor3d(1,0,0);
glBegin(GL_LINE_LOOP);
for (double i = 0; i<=2 * M_PI; i+=M_PI / 20.0) {
glVertex3d(std::sin(i) * 2.0, 0, std::cos(i) * 2.0);
}
glEnd();
// draw green sphere at origin:
glColor3d(0,1,0);
glutSolidSphere(0.2,128, 128);
// draw pink sphere a bit away
glPushMatrix();
glColor3d(1,0,1);
glTranslated(8, 3, -10);
glutSolidSphere(0.8, 128, 128);
glPopMatrix();
SwapBuffers(hDC_opengl);
}
The red ball should be drawn in the origin and at the center of the red circle around it. But looking at it just feels wierd, and gives me the imprssion that the green ball is not in the center at all.
Also, the pink ball should, imho, be drawn as a perfect circle, not as an ellipse.
So, am I wrong, and the picture is drawn correctly, or am I setting up something wrong?
Your expectations are simply wrong
The perspective projection of a 3d circle (if the circle is fully visible) is an ellipse, however the projection of the center of the circle is NOT in general the center of the ellipse.
The outline of the perspective projection of a sphere is in general a conic section i.e. can be a circle, an ellipse, a parabola or an hyperbola depending on the position of viewpoint, projection plane and sphere in 3D. The reason is that the outline of the sphere can be imagined as a cone starting from the viewpoint and touching the sphere being intersected with the projection plane.
Of course if you're looking at a circle with a perfectly perpendicular camera the center of the circle will be projected to the center of the circle projection. In the same manner if your camera is pointing exactly to a sphere the sphere outline will be a circle, but those are special cases, not the general case.
These differences between the centers are more evident with strong perspective (wide angle) cameras. With a parallel projection instead this apparent distortion is absent (i.e. the projection of the center of a circle is exactly the center of the projection of the circle).
To see the green sphere in the centre of the screen with a perfect circle around it you need to change the camera location like so:
gluLookAt (
0, 3, 0,
0, 0, 0,
0, 0, 1
);
Not sure what's causing the distortion of the purple sphere though.
The perspective is correct, it just looks distorted because that's how things fell together here.
try this for gluLookAt, and play around a bit more.:
gluLookAt (
0, 2 , 10,
0, 0 , 0,
0, 1 , 0
);
The way I tried it out was with a setup that allows me to adjust the position and view direction with the mouse, so you get real time motion. Your scene looks fine when I move around. If you want I can get you the complete code so you can do that too, but it's a bit more than I want to shove into an answer here.