I'm trying to teach myself the ways for 3D programming with OpenGL, however I am struggling with some things, especially projection matrices.
I defined some vertices for a cube and successfully handed them to my graphics processor. The cube goes from xyz -0.5 to xyz 0.5 respectively, which gets rendered fine.
To move it into my world coordinate system, I am using this model matrix:
auto model = glm::mat4(
glm::vec4(1, 0, 0, 0),
glm::vec4(0, 1, 0, 0),
glm::vec4(0, 0, 1, 0),
glm::vec4(0, 0, 0, 1)
);
model = glm::translate(model, glm::vec3(0.f, 0.f, 495.f));
model = glm::scale(model, glm::vec3(100.f, 100.f, 100.f));
This successfully moves my cube to (-50, -50, 445) -> (50, 50, 545) so its now centered in the 200x200x1000 world coordinates I defined for myself.
My camera / view matrix is
auto view = glm::lookAt(
glm::vec3(0.f, 0.f, 5.f),
glm::vec3(0.f, 0.f, 0.f),
glm::vec3(0.f, 1.f, 0.f)
);
which moves the cube slightly closer, changing the z coordinate to 440 and 540 respectively. I don't understand why this is happening but I guess it has something to do with glm expecting a right hand coordinate system while I am working with a left handed one? While this is not why I am posting this question, I would be happy if someone would clear it up for me.
Now to my actual problem: I am trying to make use of glm::perspective. I call it like this:
auto perspective = glm::perspective(glm::radians(55.f), 1.f, 0.f, 1000.f);
If I'm not mistaken, at a z value of 440 I can expect the clipping area to go from roughly -229 to 229, so I would expect that bottom right cube vertex at (-50,-50) is visible. I calculated this by drawing the frustum in 2D, when I noticed that I should be able to calculate the height of any distance to the camera using tan(alpha / 2) * distToCamera = maxVisibleCoordinate (working with a 1:1 aspect ratio). Is this a correct assumption? Here is my terrible drawing, maybe you can tell that I have a wrong understanding of something with it:
In the final step I am trying to get all this together in my vertex shader using
gl_Position = projection * view * model * vec4(pos.x, pos.y, pos.z, 1.0);
which yields a perfectly reasonable result for the x and y value but the z value is always -1 which is, as far as I know, just right for not being displayed.
For my front-bottom-left vertex of the cube (-0.5, -0.5, -0.5) the result is (-96.04, -96.04, -440, -440), normalized to (-0.218, -0.218, -1).
For my back-top-right vertex of the cube (0.5, 0.5, 0.5) the result is (96.04, 96.04, -550, -550), normalized to (0.218, 0.218, -1).
What am I getting wrong, that my z value is lost and just set to -1 instead? When playing around with the camera position, the best I can get is getting it to 1, which also results in an empty window and is definitely not what I would expect.
A projection matrix is like this:
In the picture, f is for zfar and n is for znear.
As you can see, if you put znear = 0, the term at the 4th column become zero, which is incorrect. Also, -(f+n)/(f-n) = -1, which is incorrect too.
So, the conclusion is, znear cannot be zero. It is usually a small value, for example, 0.1
Since Amadeus already answered the question correctly, I'm going to just use this space to add some clarifying information about why it's correct.
We can refer back to the diagram you provided to explain what the problem is: You have two planes, the near plane and the far plane, representing the range at which you may view objects. What the Perspective Matrix does is it takes everything in between those two planes, within the Frustrum that you've defined (mathematically a cone, but our monitors are rectangular, so...) and maps them onto the flat Near-plane to create the final image. In a sense, you can think of the Near Plane as representing the monitor.
So given this context, if you were to set the Near Plane's distance to 0, meaning it was identical to the camera, what would happen? Well, in a cone it would set the plane to a single point, and in a frustrum, it's the same. You cannot view objects drawn onto a single point. You need a surface with actual surface area to draw onto.
That is why it is inappropriate to set the near value to 0. It would turn the drawing surface into a single point, and you cannot mathematically render any objects on a single point. Hence why the essential mathematical formulas backing the matrix will break down and result in bad outcomes if you try to do so anyways.
Related
I have fov angle = 60, width = 640 and height = 480 of window, near = 0.01 and far = 100 planes and I get projection matrix using glm::perspective()
glm::perspective(glm::radians(fov),
width / height,
zNear,
zFar);
It works well.
Then I want to change projection type to orthogonal, but I don't knhow how to compute input parameters of glm::ortho() properly.
I've tried many ways, but problem is after switching to orthographic projection size of model object become another.
Let I have a cube with center in (0.5, 0.5, 0.5) and length size 1, and camera with mEye in (0.5, 0.5, 3), mTarget in (0.5, 0.5, 0.5) and mUp (0, 1, 0). View matrix is glm::lookAt(mEye, mTarget, mUp)
With perspective projection it works well. With glm::ortho(-width, width, -height, height, zNear, zFar) my cube became a small pixel in the center of window.
Also I've tried implement this variant How to switch between Perspective and Orthographic cameras keeping size of desired object
but result is (almost) same as before.
So, first question is how to compute ortho parameters for saving original view size of object/position of camera?
Also, zooming with
auto distance = glm::length(mTarget - mEye)
mEye = mTarget - glm::normalize(mTarget - mEye) * distance;
have no effect with ortho. Thus second question is how to implement zooming in case of ortho projection?
P.s.
I assume I understand ortho correctly. Proportions of model doesn't depends on depth, but nevertheless I still can decide where camera is for setting size of model properly and using zoom. Also I assume it is simple and trivial task, for example, when developing a 3D-viewer/editor/etc. Correct me if it is not.
how to compute ortho parameters for saving original view size of object/position of camera?
At orthographic projection the 3 dimensional scene is parallel projection to the 2 dimensional viewport.
This means that the objects, which are projected on the viewport always have the same size, independent of their depth (distance to the camera).
The perspective projection describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport.
This means an object which is projected on the viewport becomes smaller, by its depth.
If you switch form perspective to orthographic projection only the objects in 1 plane, which is planar (parallel) to the viepwort, and keeps its depth. Note, a plane is 2 dimensional and has no "depth". This cause that a 3 dimensional object never can "look" the same, when the projection is switched. But a 2 dimensional billboard can keep it's size.
The ration of depth an size at perspective projection is linear and can be calculated. It depends on the field of view angle only:
float ratio_size_per_depth = atan(glm::radians(fov / 2.0f) * 2.0f;
If you want to set up an orthographic projection, which keeps the size for a certain distance (depth) then you have to define the depth first:
e.g. Distance to the target point:
auto distance = glm::length(mTarget - mEye);
the projection can be set up like this:
float aspect = width / height
float size_y = ratio_size_per_depth * distance;
float size_x = ratio_size_per_depth * distance * aspect;
glm::mat4 orthProject = glm::ortho(-size_x, size_x, -size_y, size_y, 0.0f, 2.0f*distance);
how to implement zooming in case of ortho projection?
Scale the XY components of the orthographic projection:
glm::mat4 orthProject = glm::ortho(-size_x, size_x, -size_y, size_y, 0.0f, 2.0f*distance);
float orthScale = 2.0f;
orthProject = glm::scale(orthProject, glm::vec3(orthScale, orthScale, 1.0f));
Set a value for orthScale which is > 1.0 for zoom in and a value which is < 1.0 for zoom out.
I am a beginner in openGL. I am currently working on a program which take in inputs the width and the length of a board. Given those inputs i want to dynamically position my camera so that i can have a view on the whole board. Let' s say that my window size is 1024x768.
Are there any mathematical formula to compute the different parameters of the opengl function glookat to make it possible ?
the view i want to have on the board should look like this.
It doesn't matter if a board too big will make things look tiny. What matters the most here is to position the camera in a way that the view on the whole board is made possible
So far i am hopelessly randomly changing the parameters of my glookat function till i ran into something decent for a X size width and and Y size Height.
my gluperpective function :
gluPerspective(70 ,1024 / 768,1,1000)
my glooatfunction for a 40 * 40 board
gluLookAt(20, 20, 60, 20, -4, -20, 0, 1, 0);
how i draw my board (plane):
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt(20, 20, 60, 20, -4, -20, 0, 1, 0);
glBindTexture(GL_TEXTURE_2D, texture_sol);
glBegin(GL_QUADS);
glTexCoord2i(0, 0); glVertex3i(width, 0, height);
glTexCoord2i(10, 0); glVertex3i(0, 0, height)
glTexCoord2i(10, 10); glVertex3i(0, 0, 0);
glTexCoord2i(0, 10); glVertex3i(width, 0, 0);
glEnd();
the output looks as follow :
gluLookAt takes 2 points and a vector; the eye and centre positions and the up vector. There's no issue with the last parameter. The first two are relevant to your question.
I see that your board in the world space is extending on the positive X and Y axes with some arbitrary width and height values. Lets take width = height = 1.0 for instance. So the board spans from (0, 0), (1, 0), (1, 1), (0, 1); the Y value is ignored here since the board lies on the Y = 0 plane and have the same value for all vertices; these are just (X, Z) values.
Now coming to gluLookAt, eye is where the camera is in world space and centre is the point where you want the camera to be looking at (in world space)
Say you want the camera to look at centre of the board I presume, so
eye = (width / 2.0f, 0, height/2.0f);
Now you've to position the camera at its vantage point. Say somewhere above the board but towards the positive Z direction since there's where the user is (assuming your world space is right handed and positive Z direction is towards the viewer), so
centre = (width / 2.0f, 5.0f, 1.0f);
Since the farthest point on Z is 0, I just added one more to be slightly father than that. Y is how much above you want to see the board from, I just chose 5.0 as an example. These are just arbitrary values I can come up with, you'll still have to experiment with these values. But I hope you got the essence of how gluLookAt works.
Though this is written as an XNA tutorial, the basic technique and math behind it should carry over to OpenGL and your project:
Positioning the Camera to View All Scene Objects
Also see
OpenGL FAQ
8.070 How can I automatically calculate a view that displays my entire model? (I know the bounding sphere and up vector.)
Edit in response to the comment question
A bounding sphere is simply a sphere that completely encloses your model. It can be described as:
A bounding sphere, S, of a point set P with n points is described by
a center point, c, and a radius, r.
So,
P = the vertices of your model (the board in this case)
c = origin of your model
r = distance from origin of the vertex, in P, farthest from the origin
So the Bounding Sphere for your board would be composed of the origin location (c) and the distance from one corner to the origin (r) assuming the board is a square and all points are equidistant.
For more complicated models, you may employ pre-created solutions [1] or implement your own calculations [2] [3]
How do I determine what transforms I need to make a square fill an entire window in modern OpenGL. Say for example I have an 800 x 600 window and the coordinates with the vertices of two triangles extending from -1 and 1. Without any type of transformation, these coordinated would fill an 800 x 600 window because OpenGL's coordinates extend from -1 to 1. What if I want to use a standard MVP transformation, though? How do I determine what needs to be done in order to fill an entire window. Consider this code:
glm::mat4 projectionMatrix = glm::perspective(60.0f, 4.f/3.f, 0.1f, 100.0f); // gluPerspective equivalent for those who may not know about glm
glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5.0f));;
glm::mat4 modelMatrix = glm::mat4(1.0f);
with the same coordinates. I would now get a square somewhere in the middle of the window. Assuming I do not change the projection matrix, what changes would need to be made to the View and Model matrices? I understand the matrix math, but not how it relates to window coordinates themselves.
Could anyone help me understand this?
For 2D elements (like a HUD) I generally set an ortographic matrix with left = 0, right = width, bottom = 0, top = height where width and height are the size of the window. znear = -1 and zfar = 1.
Then your rectangle vertices would be at 0, 0, width and height.
Is that what you want? I'm not sure why you want to use a perspective matrix for a rectangle that fills the screen.
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.
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