Understanding the OpenGL projection matrix - opengl

I've been writing a program to display 3d models using OpenGL and until now I've used orthographic projection, but I want to switch to a perspective projection so that as the camera goes toward the model it appears to get larger. I understand that I have to multiply three matrices (model, view, and projection) together to correctly apply all of my transformations. As you can see in the following code, I have attempted to do that, and was able to correctly create the model and view matrices. I know these work properly because when I multiply the model and view projections together I can rotate and translate the object, as well as change the position and angle of the camera. My problem is that when I multiply that product by the projection matrix I can no longer see the object on the screen.
The default value for the camera struct here is {0,0,-.5} but I manipulate that value with the keyboard to move the camera around.
I am using GLFW+glad, and linmath.h for the matrix math.
//The model matrix controls where the object is positioned. The
//identity matrix means no transformations.
mat4x4_identity(m);
//Apply model transformations here.
//The view matrix controls camera position and angle.
vec3 eye={camera.x,camera.y,camera.z};
vec3 center={camera.x,camera.y,camera.z+1};
vec3 up={0,1,0};
mat4x4_look_at(v,eye,center,up);
//The projection matrix flattens the world to 2d to be rendered on a
//screen.
mat4x4_perspective(p, 1.57, width/(float)height, 1,10); //FOV of 90°
//mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f);
//Apply the transformations. mvp=p*v*m.
mat4x4_mul(mvp, p, v);
mat4x4_mul(mvp, mvp, m);

When the perspective projection matrix is set up, then the distance to the near plan and far plane are set. In your case this is 1 for the near plane and 10 for the far plane:
mat4x4_perspective(p, 1.57, width/(float)height, 1,10);
The model is clipped by the near plane. The model has to be in clip space.
The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).
All the geometry which is not in the volume of the frustum is clipped.
This means the distance of the model to the camera has to be greater than the distance to the near plane (1) and less than the distance to the far plane (10).
Since you can "see" the model when you don't use any projection matrix, the actual distance to the model is in range [-1, 1] (normalize device space). Note if you don't use a projection matrix, then the projection matrix is the identity matrix. This behaves like an orthographic projection, with a near plane distance of -1 and a far plane distance of 1.
Change the position of the camera to solve the issue:
e.g.
vec3 eye = {camera.x, camera.y, camera.z - 5}; // <--- 5 is in range [1, 10]
vec3 center = {camera.x, camera.y, camera.z};
vec3 up = {0, 1, 0};
mat4x4_look_at(v, eye, center, up);

Related

Coordinates of frustum corners

A method for extracting the 6 plane equations of a viewing frustum given by a MVP matrix is given by this.
It there a similar method known for getting the world space coordinates of its 8 corner points, directly out of the 4x4 MVP matrix and without using trigonometry?
Solved it by multiplying the projected coordinates of the frustum corners e.g. (1, -1, -1) with the inverse MVP matrix.
Edit:
If an OpenGL-type projection matrix P is used, the coordinates c of the frustum's corners in model space are (±1, ±1, ±1) (inside the frustum each coordinate goes from -1 to +1).
The corresponding coordinates in world space can be calculated as (P * V * M)^(-1) * c. (in homogeneous coordinates)

GLSL compute world coordinate from eye depth and screen position

I'm trying to recover WORLD position of a point knowing it's depth in EYE space, computed as follow (in a vertex shader) :
float depth = - uModelView * vec4( inPos , 1.0 ) ;
where inPos is a point in world space (Obviously, I don't want to recover this particular point, but a point where depth is expressed in that format).
And it's normalized screen position (between 0 and 1), computed as follow (in a fragment shader ) :
vec2 screen_pos = ( vec2( gl_FragCoord.xy ) - vec2( 0.5 ) ) / uScreenSize.xy ;
I can access to the following info :
uScreenSize : as it's name suggest, it's screen width and height
uCameraPos : camera position in WORLD space
and standard matrices :
uModelView : model view camera matrix
uModelViewProj : model view projection matrix
uProjMatrix : projection matrix
How can I compute position (X,Y,Z) of a point in WORLD space ? (not in EYE space)
I can't have access to other (I can't use near, far, left, right, ...) because projection matrix is not restricted to perspective or orthogonal.
Thanks in advance.
I get your question right, you have x and y as window space (and already converted to normalized device space [-1,1]), but z in eye space, and want to recosntruct the world space position.
I can't have access to other (I can't use near, far, left, right, ...)
because projection matrix is not restricted to perspective or
orthogonal.
Well, actually, there is not much besides an orthogonal or projective mapping which can be achieved by matrix multiplication in homogenous space. However, the projection matrix is sufficient, as long as it is invertible (In theory, a projection matrix could transform all points to a plane, line or a single point. In that case, some information is lost and it will never be able to reconstruct the original data. But that would be a very untypical case).
So what you can get from the projection matrix and your 2D position is actually a ray in eye space. And you can intersect this with the z=depth plane to get the point back.
So what you have to do is calculate the two points
vec4 p = inverse(uProjMatrix) * vec4 (ndc_x, ndc_y, -1, 1);
vec4 q = inverse(uProjMatrix) * vec4 (ndc_x, ndc_y, 1, 1);
which will mark two points on the ray in eye space. Do not forget to divide p and q by the respective w component to get the 3D coordinates. Now, you simply need to intersect this with your z=depth plane and get the eye space x and y. Finally, you can use the inverse of the uModelView matrix to project that point back to object space.
However, you said that you want world space. But that is impossible. You would need the view matrix to do that, but you have not listed that as a given. All you have is the compisition of the model and view matrix, and you need to know at least one of these to reconstruct the world space position. The cameraPosition is not enoguh. You also need the orientation.

openGL ray pick

The general Ray picking process should be as follows(experiment result proved to be right):
transform screen point to normalized device space direction vector:
float x = (2.0f * mouse_x) / width - 1.0f;
float y = 1.0f - (2.0f * mouse_y) / height;
float z = 1.0f;
vec3 ray_nds = vec3 (x, y, z);
transform direction vector to Homogeneous Clip Coordinates
vec4 ray_clip = vec4 (ray_nds.xy, -1.0, 1.0);
transform direction vector to eye space direction vector
vec4 ray_eye = inverse (projection_matrix) * ray_clip;
transform direction vector to world space, get a pick ray with world space camera position and the direction vector
My problem is, in normalized device space, why the z component of the direction vector is 1.0?
I mean, in OpenGL normalized device space, xyz component should all be in the range of -1~1, so the camera should be in the center of the plane z=-1. So the direction vector should be: view target position - camera position, and the z component should be 1-(-1)=2.0f. (in DirectX normalized device space, xy component is in the range of -1~1, z component is in the range of 0~1, the camera position should be in the center of the plane z=0, say, (0,0,0), and the z component of the direction vector should be 1-0=1)
ray_nds.z is completely irrelevant, because you don't use it anyway. That's because you don't know the pixel's depth.
ray_clip is not a direction, but a position on the near clipping plane (z=-1) after projection. If you undo this projection (with the inverse projection matrix) you end up with the same point in camera space. In camera space, the camera is centered at (0, 0, 0). The direction vector of the ray can be calculated with ray_eye - (0, 0, 0), which is essentially ray_eye. So if we ignore the w-component, we can use the position as a direction. This does only work in camera space! Both clip space and world space are most likely to have the projection center somewhere else.
Don't mix up the camera position in the different spaces. In camera space, it is at the origin. In clip space it can be assumed to be at (0, 0, -infinity). The point (x, y, ...) is just an arbitrary point that is covered by the according pixel. And you need any of them to define the ray.
the camera is NOT located at z=-1 (or 0) it is even behind that.
The near clip plane is located at z=-1. this makes all the complexity of this kind of maths, because all equations involves if traced would make curves that don't pass through 0. because of that we always carry over lots of zn and zm.
check that out, equation 4.2 : http://www.arcsynthesis.org/gltut/Positioning/Tut04%20Perspective%20Projection.html.
even scarier but more complete: http://www.songho.ca/opengl/gl_projectionmatrix.html
more links:
http://unspecified.wordpress.com/2012/06/21/calculating-the-gluperspective-matrix-and-other-opengl-matrix-maths/
http://schabby.de/projection-matrix/

How to set orthographic near/far clipping planes to display all vertices

I am rendering scenes using a glm::ortho projection. I want the rendering to include every vertex I draw without adding unnecessary depth (i.e. with minimal depth buffer resolution impact).
I've seen this post for a similar question on perspective projections, but I am looking for both near and far clipping plane values in an orthographic projection.
I can calculate the z values of each vertex using the viewMatrix to transform the vertices into screen coordinates. In pseudo code:
float near;
float far;
for (each glm::vec4 vertex)
{
glm::vec4 trans = viewMatrix * vertex;
// invert for z-buffer
trans.z = -trans.z;
if (trans.z < near)
near = trans.z;
if (trans.z > far)
far = trans.z;
}
This way, near and far represent the z-values of the nearest and farthest vertices in screen coordinates, respectively.
But when I use these values as zNear and zFar in the glm::ortho matrix, much of the rendering is still clipped. What am I missing?

C++/OpenGL convert world coords to screen(2D) coords

I am making a game in OpenGL where I have a few objects within the world space. I want to make a function where I can take in an object's location (3D) and transform it to the screen's location (2D) and return it.
I know the the 3D location of the object, projection matrix and view matrix in the following varibles:
Matrix projectionMatrix;
Matrix viewMatrix;
Vector3 point3D;
To do this transform, you must first take your model-space positions and transform them to clip-space. This is done with matrix multiplies. I will use GLSL-style code to make it obvious what I'm doing:
vec4 clipSpacePos = projectionMatrix * (viewMatrix * vec4(point3D, 1.0));
Notice how I convert your 3D vector into a 4D vector before the multiplication. This is necessary because the matrices are 4x4, and you cannot multiply a 4x4 matrix with a 3D vector. You need a fourth component.
The next step is to transform this position from clip-space to normalized device coordinate space (NDC space). NDC space is on the range [-1, 1] in all three axes. This is done by dividing the first three coordinates by the fourth:
vec3 ndcSpacePos = clipSpacePos.xyz / clipSpacePos.w;
Obviously, if clipSpacePos.w is zero, you have a problem, so you should check that beforehand. If it is zero, then that means that the object is in the plane of projection; it's view-space depth is zero. And such vertices are automatically clipped by OpenGL.
The next step is to transform from this [-1, 1] space to window-relative coordinates. This requires the use of the values you passed to glViewport. The first two parameters are the offset from the bottom-left of the window (vec2 viewOffset), and the second two parameters are the width/height of the viewport area (vec2 viewSize). Given these, the window-space position is:
vec2 windowSpacePos = ((ndcSpacePos.xy + 1.0) / 2.0) * viewSize + viewOffset;
And that's as far as you go. Remember: OpenGL's window-space is relative to the bottom-left of the window, not the top-left.