Related
I am not able to understand the correct way of transforming primitive coordinate values to the screen coordinates.
If I use the following code (where w and h are width and height of my window 640 X 480)
glViewport(0,0,w,h);
// set up the projection matrix
glMatrixMode(GL_PROJECTION);
// clear any previous transform and set to the identity matrix
glLoadIdentity();
// just use an orthographic projection
glOrtho(0,w,h,0,1,-1);
and my primitives are
glBegin(GL_TRIANGLES);
glColor3f(1,0,0);
glVertex3f(-10,-10,0);
glColor3f(0,1,0);
glVertex3f(10,-10,0);
glColor3f(0,0,1);
glVertex3f(0,10,0);
glEnd();
The triangle becomes too big to fit the window. Most of the tutorials have the primitives in the range[-1,1] and their ortho projection between [-1,1], so the triangle comes correctly at the centre.
So, if the coordinates are generated by a 3rd party software (or lies above the range [-1,1], how would I transform them correctly so that the coordinates fit the screen?
I'm trying to zoom out from a polygon with glTranslatef. However, whatever numbers I put in Z (trying to zoom out) inside glTranslatef function, it remains a black window. Here is code:
glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
glClear (GL_COLOR_BUFFER_BIT);
glPushMatrix ();
glTranslatef(0, 0, 0.9f); //Here I'm translating
glBegin (GL_POLYGON);
glColor3f(100, 100, 0); glVertex2f(-1.0f, -1.0f);
glColor3f(100, 0, 100); glVertex2f(-1.0f, 1.0f);
glColor3f(25, 25, 25); glVertex2f(1.0f, 1.0f);
glColor3f(100, 50, 90); glVertex2f(1.0f, -1.0f);
glEnd ();
glPopMatrix ();
SwapBuffers (hDC);
Sleep (1);
I tried with following numbers in Z:
0.9 (works)
-0.9 (works)
1.1 (works not)
-1.1 (works not)
Do I need some other code for this or I'm doing it wrong?
If you haven't specified a projection matrix then the standard one will be an orthographic (non-perspective) projection with left-right top-bottom and near-far all being -1,1.
So translating outside that will make the vertices not draw at all.
The reason this does nothing is because you have no transformation matrices setup.
Right now you are drawing in a coordinate space known as Normalized Device Coordinates, which has the viewing volume encompass the range [-1.0, 1.0] in all directions. Any point existing outside that range is clipped.
Vertices specified with glVertex2f (...) are implicitly placed at z=0.0 and translating more than 1.0 unit along the Z-axis will push your vertices outside the viewing volume. This is why -1.1 and 1.1 fail, while 0.9 and -0.9 work fine.
Even if you translate to a position within the viewing volume, without a perspective projection, translating something along the Z-axis is not going to change its size. The only thing that will happen is that eventually the object will be translated far enough that it is clipped and suddenly disappears (which you already experienced with values > 1.0 or < -1.0).
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.
I want to be able to get the coordinates of an object (e.g. triangle) after it's been translated and rotated, the reason i want to do this is so that later i can do collision detection and calculate the distance between objects using the coordinates. I think I might have to use gluProject but not sure. Also what are the differences between the different coordinate spaces e.g. world, object etc.
I've got some code below it's a circle in the middle of a square, how would i detect when the circle touches one of the edges, i can move it round using the up,down,left, right keys it just changes the x or y coordinates, but i just want to be able to do some basic collision detection and I don't know how to do it.
glPushMatrix();
glColor3f(0.0f, 1.0f, 0.0f);
glTranslatef(0.0f, 0.0f, -5.0f);
glScalef(0.5f, 0.5f, 0.0f);
glBegin(GL_POLYGON);
glVertex3f(-5.0f, -5.0f, 0.0f);
glVertex3f(5.0f, -5.0f, 0.0f);
glVertex3f(5.0f, 5.0f, 0.0f);
glVertex3f(-5.0f, 5.0f, 0.0f);
glEnd();
glPopMatrix();
glPushMatrix();
glColor3f(1.0f, 0.0f, 0.0f);
glTranslatef(x, y, -20.0f);
glBegin(GL_POINTS);
glVertex3f(-5, -5, 10.0f);
glEnd();
GLUquadricObj *qobj = gluNewQuadric();
gluQuadricDrawStyle(qobj, GLU_FILL);
gluSphere(qobj, 1.0f, 20, 20);
gluDeleteQuadric(qobj);
glPopMatrix();
Also what are the differences between the different coordinate spaces e.g. world, object etc.
This is mostly a matter of convention, but:
Model space (= local space) is the coordinate space of a specific model, relative to its "center". If you have a file with a model, the coordinates are centered around some point of it (e.g. it's geometrical center, its base, anything actually).
Scene space (= world space) is the coordinate space relative to an arbitrary point of your scene
Eye space (= view space) is the space where the camera is at point (0,0,0), x faces right, y faces up and z faces out of the screen (-z = deeper)
Clip space is where (-1,-1,*) is the bottom left corner of the viewport, (1,1,*) is the top right corner of the viewport, and the Z coordinate in (-1,1) indicates just the depth (again smaller Z = deeper). (Fragments
Screen space (= window coordinates) is the same as above, except that the coordinates are rescaled from -1..1 to pixel-based values matching the range of the current viewport and depth range.
You transform coordinates from model space to scene space by multiplying (in OpenGL conventions usually left-multiplying) by a model matrix (which contains the information on where the model is on the scene). If you have a scene hierarchy, there can be many "stacked" model matrices for an object (placement of the sword relative to an arm, arm relative to a knight, knight relative to the scene).
Then you transform the coordinates to eye space by multiplying by a view matrix (usually connected to a "camera" object).
After that, using a projection matrix you transform those coords to the screen space, so that OpenGL would map these coords to actual screen pixels (depending on the viewport setting).
Some facts:
Model and view matrices usually contain translation, rotation and/or scaling, while projection matrix usually contains a perspective transformation, which makes the objects further from the screen appear smaller.
Old OpenGL (2.x and earlier) required you to put the matrices on two "matrix stacks":
GL_MODELVIEW stack which should contain View*Model (or View*Model1*Model2...*ModelN),
GL_PROJECTION stack which sould contain only the Projection matrix.
These could just as well be single matrices, not stacks, but the stack (along with glPushMatrix and glPopMatrix) was introduced to let the programmer "save and load" them easily. Only the "topmost" matrix from each stack is used in calculations.
The projection matrix is usually created with gluPerspective or equivalent. The view matrix can be made with gluLookAt (or similarly to model matrices), and the model matrices can be easily assembled using glTranslate, glRotate and glScale.
(note: OpenGL 3.1+ removed these features, allowing you to use any matrices and any conventions you prefer)
Knowing that:
I want to be able to get the coordinates of an object (e.g. triangle) after it's been translated and rotated, the reason i want to do this is so that later i can do collision detection and calculate the distance between objects using the coordinates
A reasonable way to calculate all your physics is to do them in scene space.
Hence if you have a model (e.g. a triangle mesh), to obtain the position of any its vertex in scene space, you need to left-multiply it by only the model's model matrix (or in case of the hierarchy, by all its model matrices).
About gluProject, in case you wondered- it is a convenience method which allows you to multiply a set of coordinates by the current PROJECTION*MODELVIEW and performs viewport transformation to see where it would end up in screen space, and gluUnProject does the reverse.
Ref: http://www.opengl.org/resources/faq/technical/transformations.htm
In addition to Kos' answer, keep in mind that OpenGL is not a scene management library. It is just a drawing API that draws things onto the screen and then forgets about them. Likewise it doesn't have any understanding of what an "object" is, it only knows triangles and even these it can't remember after they have been drawn. Never wondered why you have to render the whole scene anew each frame?
So to know an object's absolute position in the scene, keep track of the transformations yourself and, well, compute its position from these.
mx, my are simply mause cursor coordinates
import numpy as np
i didnt know about glunproject and recalculate it (open version of glunproject)
def CalculateRealCoordinates(mx, my):
Inverseofmodelviewmatrix = np.linalg.inv(glGetDoublev(GL_MODELVIEW_MATRIX))
Inverseofprojectionmatrix = np.linalg.inv(glGetDoublev(GL_PROJECTION_MATRIX))
WindowCoordinates_x = mx
WindowCoordinates_y = my
# glViewport(x, y, w, h)
glViewports = glGetIntegerv(GL_VIEWPORT)
NormalizedDeviceCoordinates_x = (WindowCoordinates_x - (
glViewports[0] + (glViewports[2] / 2))) * (2 / glViewports[2])
NormalizedDeviceCoordinates_y = (WindowCoordinates_y - (
glViewports[1] + (glViewports[3] / 2))) * (2 / glViewports[3])
w = 1
ClipCoordinates_x = NormalizedDeviceCoordinates_x * w
ClipCoordinates_y = NormalizedDeviceCoordinates_y * w
ClipCoordinatesMatrix = [[ClipCoordinates_x],
[-ClipCoordinates_y],
[0],
[0]]
ClipCoordinatesMatrix = np.array(ClipCoordinatesMatrix)
EyeCoordinatesMatrix = np.matmul(Inverseofprojectionmatrix, ClipCoordinatesMatrix)
RealCoordinatesMatrix = np.matmul(Inverseofmodelviewmatrix, EyeCoordinatesMatrix)
RealCoordinates_x = RealCoordinatesMatrix[0, 0]
RealCoordinates_y = RealCoordinatesMatrix[1, 0]
return RealCoordinates_x, RealCoordinates_y
builtin gluUnProject version:
def CalculateRealCoordinates(mx, my):
WindowCoordinates_x = mx
WindowCoordinates_y = my
WindowCoordinates_z = 0
RealCoordinates = gluUnProject(WindowCoordinates_x, WindowCoordinates_y, WindowCoordinates_z, glGetDoublev(GL_MODELVIEW_MATRIX), glGetDoublev(GL_PROJECTION_MATRIX), glGetIntegerv(GL_VIEWPORT))
RealCoordinates_x = RealCoordinates[0]
RealCoordinates_y = RealCoordinates[1]
return RealCoordinates_x, RealCoordinates_y
and if you want to reverse only MODELVIEW_MATRIX
# your projection matrix must be like this -->
# [[1. 0. 0. 0.]
# [0. 1. 0. 0.]
# [0. 0. 1. 0.]
# [0. 0. 0. 1.]]
def CalculateRealCoordinates(mx, my):
Inverseofmodelviewmatrix = np.linalg.inv(glGetDoublev(GL_MODELVIEW_MATRIX))
WindowCoordinates_x = mx
WindowCoordinates_y = my
glViewports = glGetIntegerv(GL_VIEWPORT)
NormalizedDeviceCoordinates_x = (WindowCoordinates_x - (glViewports[0] + (glViewports[2] / 2))) * (
2 / glViewports[2])
NormalizedDeviceCoordinates_y = (WindowCoordinates_y - (glViewports[1] + (glViewports[3] / 2))) * (
2 / glViewports[3])
NormalizedDeviceMatrix = [[NormalizedDeviceCoordinates_x],
[NormalizedDeviceCoordinates_y],
[0],
[0]]
NormalizedDeviceMatrix = np.array(NormalizedDeviceMatrix)
RealCoordinates = np.matmul(Inverseofmodelviewmatrix, NormalizedDeviceMatrix)
print("RealCoordinates:", RealCoordinates)
RealCoordinates_x = RealCoordinates[0, 0]
RealCoordinates_y = RealCoordinates[1, 0]
return RealCoordinates_x, -RealCoordinates_y
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.