Is the w component of gl_Position required to be greater than zero? Because when I set it to a negative number nothing is drawn but positive numbers are fine.
gl_Position = vec4(vPos,0,-1);
Face culling is not enabled btw.
Has the w component of gl_Position required to be greater than zero?
No, but the result of gl_Position.xyz / gl_Position.w has to be in the range (-1,-1,-1) to (1,1,1), the normalized device space. This means each component (x, y and z) of the result, has to be >= -1.0 and <= 1.0.
But, if the w component is negative, nothing is draw anyway. Because gl_Position defines the clip space. The condition for a homogeneous coordinate to be in clip space is
-w <= x, y, z <= w.
If w = -1 this would mean:
1 <= x, y, z <= -1.
and that can never be fulfilled.
(see Why does GL divide gl_Position by W for you rather than letting you do it yourself?)
Explanation:
The coordinates which are set to gl_Position are Homogeneous coordinates. If the w component of a Homogeneous coordinate is 1, it is equal to the Cartesian coordinate built of the components xyz.
Homogeneous coordinates are used for the representation of the perspective projection.
In a rendering, each mesh of the scene usually is transformed by the model matrix, the view matrix and the projection matrix.
The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. The projection matrix transforms from view space to the clip space, and the coordinates in the clip space are transformed to the normalized device coordinates (NDC) in the range (-1, -1, -1) to (1, 1, 1) by dividing with the w component of the clip coordinates. Every geometry which is out of the NDC is clipped.
At Orthographic Projection the coordinates in the eye space are linearly mapped to normalized device coordinates. (TCommonly the w component is 1.0)
At Perspective Projection the projection matrix describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport. The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).
Related
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);
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)
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.
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/
I read lots of information about getting depth with fragment shader.
such as
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=234519
but I still don't know whether or not the gl_FragCoord.z is linear.
GLSL specification said its range is [0,1] in screen sapce without mentioning it's linear or not.
I think linearity it is vital since I will use the rendered model to match depth map from Kinect.
Then if it is not linear, how to linearlize it in the world space?
but I still don't know whether or not the gl_FragCoord.z is linear.
Whether gl_FragCoord.z is linear or not depends on, the projection matrix.
While for Orthographic Projection gl_FragCoord.z is linear, for Perspective Projection it is not linear.
In general, the depth (gl_FragCoord.z and gl_FragDepth) is calculated as follows (see GLSL gl_FragCoord.z Calculation and Setting gl_FragDepth):
float ndc_depth = clip_space_pos.z / clip_space_pos.w;
float depth = (((farZ-nearZ) * ndc_depth) + nearZ + farZ) / 2.0;
The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. It transforms from eye space to the clip space, and the coordinates in the clip space are transformed to the normalized device coordinates (NDC) by dividing with the w component of the clip coordinates
Orthographic Projection
At Orthographic Projection the coordinates in the eye space are linearly mapped to normalized device coordinates.
Orthographic Projection Matrix:
r = right, l = left, b = bottom, t = top, n = near, f = far
2/(r-l) 0 0 0
0 2/(t-b) 0 0
0 0 -2/(f-n) 0
-(r+l)/(r-l) -(t+b)/(t-b) -(f+n)/(f-n) 1
At Orthographic Projection, the Z component is calculated by the linear function:
z_ndc = z_eye * -2/(f-n) - (f+n)/(f-n)
Perspective Projection
At Perspective Projection the projection matrix describes the mapping from 3D points in the world as they are seen from of a pinhole camera, to 2D points of the viewport. The eye space coordinates in the camera frustum (a truncated pyramid) are mapped to a cube (the normalized device coordinates).
Perspective Projection Matrix:
r = right, l = left, b = bottom, t = top, n = near, f = far
2*n/(r-l) 0 0 0
0 2*n/(t-b) 0 0
(r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1
0 0 -2*f*n/(f-n) 0
At Perspective Projection, the Z component is calculated by the rational function:
z_ndc = ( -z_eye * (f+n)/(f-n) - 2*f*n/(f-n) ) / -z_eye
Depth buffer
Since the normalized device coordinates are in range (-1,-1,-1) to (1,1,1) the Z-coordinate has to be mapped to the depth buffer range [0,1]:
depth = (z_ndc + 1) / 2
Then if it is not linear, how to linearize it in the world space?
To convert form the depth of the depth buffer to the original Z-coordinate, the projection (Orthographic or Perspective), and the near plane and far plane has to be known.
Orthographic Projection
n = near, f = far
z_eye = depth * (f-n) + n;
Perspective Projection
n = near, f = far
z_ndc = 2.0 * depth - 1.0;
z_eye = 2.0 * n * f / (f + n - z_ndc * (f - n));
If the perspective projection matrix is known this can be done as follows:
A = prj_mat[2][2]
B = prj_mat[3][2]
z_eye = B / (A + z_ndc)
See also the answer to
How to recover view space position given view space depth value and ndc xy
Assuming a usual perspective projection matrix, once the perspective division (by gl_Position.w) step is performed the depth loses its linearity, so gl_FragCoord.z is not linear. For a more detailed explanation read #Dreamer's answer.
To revert back to linear you should perform 2 steps:
1) Transform the variable gl_FragCoord.z to normalized devices coordinates in the range [-1, 1]
z = gl_FragCoord.z * 2.0 - 1.0
2) Apply the inverse of the projection matrix (IP). (You can use arbitrary values for x and y), and normalize for the last component.
unprojected = IP * vec4(0, 0, z, 1.0)
unprojected /= unprojected.w
you will obtain a point in view space (or camera space, you name it) that has a linear z between znear and zfar.
Whether gl_FragCoord.z is linear or not depends on your transformation matrix. gl_FragCoord.z is determined by computing gl_Position.z / gl_Position.w for all vertices of your triangle and then interpolating the result over all fragments of that triangle.
So gl_FragCoord.z is linear when your transformation matrix assigns a constant value to gl_Position.w (which usually happens with ortho projection matrices) and is non-linear when gl_Position.w depends on the x, y, or z coordinate of your input vector (which happens with perspective projection matrices).
Its up to you to decide if you want linear Z or not, everythings relies on your projection matrix. You may read this:
http://www.songho.ca/opengl/gl_projectionmatrix.html
Which explains very well how projection matrices works. It may be better to have non-linear Z in order to have better precision in the foreground and less in the backgrounds, depth artifacts are less visible when far away...