When using a perspective matrix in a vertex shader am I supposed to write code to divide by w or is it done automatically in a later stage?
The reason for my question is that I have seen lots of vertex shaders using:
gl_Position = matrix * pos;
which makes sense if there is a later stage that divides the vector with its w component.
However I never got it to work until I used the following in my vertex shader:
gl_Position = matrix * pos;
gl_Position = gl_Position / gl_Position.w;
Is the second example the correct one or could some other setting be missing?
Put it another way: which of the steps shown in the OpenGL Vertex transformation(first image) do I have to write in the vertex shader?
I know for sure that ModelView and Projection matrix belongs there(or a merge of the two). The viewport transform is not a part of the vertex shader, but what about the divide by w?
My setup is some simple triangles having coordinates within [-1 1] for x/y/z.
The Perspective matrix is supposed to project coordinates from z=-1 to -10 onto z=-1, x=[-1,1], y=[-1,1].
-1.0 0.0 0.0 0.0
0.0 -1.0 0.0 0.0
0.0 0.0 -1.2 -2.2
0.0 0.0 1.0 0.0
It was generated by:
x = 2.0f * zNear / (xMax - xMin);
y = 2.0f * zNear / (yMax - yMin);
a = -(xMax + xMin) / (xMax - xMin);
b = -(yMax + yMin) / (yMax - yMin);
c = (zFar + zNear) / (zNear - zFar);
d = -(2.0f * zFar * zNear) / (zNear - zFar);
To make the matrix P:
x, 0, a, 0
0, y, b, 0
0, 0, c, d
0, 0, 1, 0;
Finally I generate the final matrix by matrix = P * T where T is a translation (0,0,-2)
I have tried to do the math on the CPU and it appears to work generating expected results, however there I also do the divide by w manually.
Update: Solved but need understanding
I negated all components in the matrix (multiply by -1) and now it works.
The example above also had an issue with projecting both positive and negative z-coordinates onto the projection plane which also got solved by this change.
Any references or explanation why it got solved by this change is welcome.
You should not do the perspective divide yourself in the vertex shader, it will be done automatically later in the pipeline.
If that's not working, can you show some code or describe the problem more? I'm surprised that it's making a difference for you.
Related
I have been trying to get variance shadow mapping to work in my webgl application, but I seem to be having an issue that I could use some help with. In short, my shadows seem to vary over a much smaller distance than the examples I have seen out there. I.e. the shadow range is from 0 to 500 units, but the shadow is black 5 units away and almost non-existent 10 units away. The examples I am following are based on these two links:
VSM from Florian Boesch
VSM from Fabian Sanglard
In both of those examples, the authors are using spot light perspective projection to map the variance values to a floating point texture. In my engine, I have so far tried to use the same logic except I am using a directional light and orthographic projection. I tried both techniques and the result seems to always be the same for me. I'm not sure if its the because of me using an orthographic matrix to do projection - I suspect it might be. Here is a picture of the problem:
Notice how the box is only a few units away from the circle but the shadow is much darker even though the camera shadow is 0.1 to 500 units.
In the light shadow pass my code looks like this:
// viewMatrix is a uniform of the inverse world matrix of the camera
// vWorldPosition is the varying vec4 of the vertex position x world matrix
vec3 lightPos = (viewMatrix * vWorldPosition).xyz;
depth = clamp(length(lightPos) / 40.0, 0.0, 1.0);
float moment1 = depth;
float moment2 = depth * depth;
// Adjusting moments (this is sort of bias per pixel) using partial derivative
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += pow(depth, 2.0) + 0.25 * (dx * dx + dy * dy) ;
gl_FragColor = vec4(moment1, moment2, 0.0, 1.0);
Then in my shadow pass:
// lightViewMatrix is the light camera's inverse world matrix
// vertWorldPosition is the attribute position x world matrix
vec3 lightViewPos = lightViewMatrix * vertWorldPosition;
float lightDepth2 = clamp(length(lightViewPos) / 40.0, 0.0, 1.0);
float illuminated = vsm( shadowMap[i], shadowCoord.xy, lightDepth2, shadowBias[i] );
shadowColor = shadowColor * illuminated
Firstly, should I be doing anything differently with Orthographic projection (Its probably not this, but I don't know what it might be as it happens using both techniques above :( )? If not, what might I be able to do to get a more even spread of the shadow?
Many thanks
I have the following fragment and vertex shader, in which I repeat a texture:
//Fragment
vec2 texcoordC = gl_TexCoord[0].xy;
texcoordC *= 10.0;
texcoordC.x = mod(texcoordC.x, 1.0);
texcoordC.y = mod(texcoordC.y, 1.0);
texcoordC.x = clamp(texcoordC.x, 0.0, 0.9);
texcoordC.y = clamp(texcoordC.y, 0.0, 0.9);
vec4 texColor = texture2D(sampler, texcoordC);
gl_FragColor = texColor;
//Vertex
gl_TexCoord[0] = gl_MultiTexCoord0;
colorC = gl_Color.r;
gl_Position = ftransform();
ADDED: After this process, I fetch the texture coordinates and use a texture pack:
vec4 textureGet(vec2 texcoord) {
// Tile is 1.0/16.0 part of texture, on x and y
float tileSp = 1.0 / 16.0;
vec4 color = texture2D(sampler, texcoord);
// Get tile x and y by red color stored
float texTX = mod(color.r, tileSp);
float texTY = color.r - texTX;
texTX /= tileSp;
// Testing tile
texTX = 1.0 - tileSp;
texTY = 1.0 - tileSp;
vec2 savedC = color.yz;
// This if else statement can be ignored. I use time to move the texture. Seams show without this as well.
if (color.r > 0.1) {
savedC.x = mod(savedC.x + sin(time / 200.0 * (color.r * 3.0)), 1.0);
savedC.y = mod(savedC.y + cos(time / 200.0 * (color.r * 3.0)), 1.0);
} else {
savedC.x = mod(savedC.x + time * (color.r * 3.0) / 1000.0, 1.0);
savedC.y = mod(savedC.y + time * (color.r * 3.0) / 1000.0, 1.0);
}
vec2 texcoordC = vec2(texTX + savedC.x * tileSp, texTY + savedC.y * tileSp);
vec4 res = texture2D(texturePack, texcoordC);
return res;
}
I have some troubles with showing seams (of 1 pixel it seems) however. If I leave out texcoord *= 10.0 no seams are shown (or barely), if I leave it in they appear. I clamp the coordinates (even tried lower than 1.0 and bigger than 0.0) to no avail. I strongly have the feeling it is a rounding error somewhere, but I have no idea where. ADDED: Something to note is that in the actual case I convert the texcoordC x and y to 8 bit floats. I think the cause lies here; I added another shader describing this above.
The case I show is a little more complicated in reality, so there is no use for me to do this outside the shader(!). I added the previous question which explains a little about the case.
EDIT: As you can see the natural texture span is divided by 10, and the texture is repeated (10 times). The seams appear at the border of every repeating texture. I also added a screenshot. The seams are the very thin lines (~1pixel). The picture is a cut out from a screenshot, not scaled. The repeated texture is 16x16, with 256 subpixels total.
EDIT: This is a followup question of: this question, although all necessary info should be included here.
Last picture has no time added.
Looking at the render of the UV coordinates, they are being filtered, which will cause the same issue as in your previous question, but on a smaller scale. What is happening is that by sampling the UV coordinate texture at a point between two discontinuous values (i.e. two adjacent points where the texture coordinates wrapped), you get an interpolated value which isn't in the right part of the texture. Thus the boundary between texture tiles is a mess of pixels from all over that tile.
You need to get the mapping 1:1 between screen pixels and the captured UV values. Using nearest sampling might get you some of the way there, but it should be possible to do without using that, if you have the right texture and pixel coordinates in the first place.
Secondly, you may find you get bleeding effects due to the way you are doing the texture atlas lookup, as you don't account for the way texels are sampled. This will be amplified if you use any mipmapping. Ideally you need a border, and possibly some massaging of the coordinates to account for half-texel offsets. However I don't think that's the main issue you're seeing here.
I am writing a deferred shader, and am trying to pack my gbuffer more tightly. However, I cant seem to compute the view position given the view space depth correctly
// depth -> (gl_ModelViewMatrix * vec4(pos.xyz, 1)).z; where pos is the model space position
// fov -> field of view in radians (0.62831855, 0.47123888)
// p -> ndc position, x, y [-1, 1]
vec3 getPosition(float depth, vec2 fov, vec2 p)
{
vec3 pos;
pos.x = -depth * tan( HALF_PI - fov.x/2.0 ) * (p.x);
pos.y = -depth * tan( HALF_PI - fov.y/2.0 ) * (p.y);
pos.z = depth;
return pos;
}
The computed position is wrong. I know this because I am still storing the correct position in the gbuffer and testing using that.
3 Solutions to recover view space position in perspective projection
The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. It transforms from view (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. The NDC are in range (-1,-1,-1) to (1,1,1).
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
it follows:
aspect = w / h
tanFov = tan( fov_y * 0.5 );
prjMat[0][0] = 2*n/(r-l) = 1.0 / (tanFov * aspect)
prjMat[1][1] = 2*n/(t-b) = 1.0 / tanFov
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
The depth (gl_FragCoord.z and gl_FragDepth) is calculated as follows:
z_ndc = clip_space_pos.z / clip_space_pos.w;
depth = (((farZ-nearZ) * z_ndc) + nearZ + farZ) / 2.0;
1. Field of view and aspect ratio
Since the projection matrix is defined by the field of view and the aspect ratio it is possible to recover the viewport position with the field of view and the aspect ratio. Provided that it is a symmetrical perspective projection and the normalized device coordinates, the depth and the near and far plane are known.
Recover the Z distance in view space:
z_ndc = 2.0 * depth - 1.0;
z_eye = 2.0 * n * f / (f + n - z_ndc * (f - n));
Recover the view space position by the XY normalized device coordinates:
ndc_x, ndc_y = xy normalized device coordinates in range from (-1, -1) to (1, 1):
viewPos.x = z_eye * ndc_x * aspect * tanFov;
viewPos.y = z_eye * ndc_y * tanFov;
viewPos.z = -z_eye;
2. Projection matrix
The projection parameters, defined by the field of view and the aspect ratio, are stored in the projection matrix. Therefore the viewport position can be recovered by the values from the projection matrix, from a symmetrical perspective projection.
Note the relation between projection matrix, field of view and aspect ratio:
prjMat[0][0] = 2*n/(r-l) = 1.0 / (tanFov * aspect);
prjMat[1][1] = 2*n/(t-b) = 1.0 / tanFov;
prjMat[2][2] = -(f+n)/(f-n)
prjMat[3][2] = -2*f*n/(f-n)
Recover the Z distance in view space:
A = prj_mat[2][2];
B = prj_mat[3][2];
z_ndc = 2.0 * depth - 1.0;
z_eye = B / (A + z_ndc);
Recover the view space position by the XY normalized device coordinates:
viewPos.x = z_eye * ndc_x / prjMat[0][0];
viewPos.y = z_eye * ndc_y / prjMat[1][1];
viewPos.z = -z_eye;
3. Inverse projection matrix
Of course the viewport position can be recovered by the inverse projection matrix.
mat4 inversePrjMat = inverse( prjMat );
vec4 viewPosH = inversePrjMat * vec3( ndc_x, ndc_y, 2.0 * depth - 1.0, 1.0 )
vec3 viewPos = viewPos.xyz / viewPos.w;
See also the answers to the following question:
How to render depth linearly in modern OpenGL with gl_FragCoord.z in fragment shader?
I managed to make it work in the end, As its a different method from above I will detail it so anyone who sees this will have a solution.
Pass 1: Store the depth value in view space to the gbuffer
To re-create the (x, y, z) position in the second pass:
Pass the horizontal and vertical field of view in radians into the shader.
Pass the near plane distance (near) to the shader. (distance from camera position to near plane)
Imagine a ray from the camera to the fragment position. This ray intersects the near plane at a certain position P. We have this position in the ndc space and want to compute this position in view space.
Now, we have all the values we need in view space. We can use the law of similar triangles to find the actual fragment position P'
P = P_ndc * near * tan(fov/2.0f) // computation is the same for x, y
// Note that by law of similar triangles, P'.x / depth = P/near
P'.xy = P/near * -depth; // -depth because in opengl the camera is staring down the -z axis
P'.z = depth;
I wrote a deferred shader, and used this code to recalculate screen space positioning:
vec3 getFragmentPosition()
{
vec4 sPos = vec4(gl_TexCoord[0].x, gl_TexCoord[0].y, texture2D(depthTex, gl_TexCoord[0].xy).x, 1.0);
sPos.z = 2.0 * sPos.z - 1.0;
sPos = invPersp * sPos;
return sPos.xyz / sPos.w;
}
where depthTex is the texture holding depth info, and invPersp is a pre-calculated inverse perspective matrix. You take the screen's fragment position, and multiply it by the inverse perspective matrix to get model-view coordinates. Then you divide by w to get homogenous coordinates. The multiplication by two and subtraction by one is to scale the depth from [0, 1] (as it is stored in the texture) to [-1, 1].
Also, depending on what kind of MRTs you are using, the recalculated result won't be exactly equal to the stored info, since you lose the float precision.
Sampling from a depth buffer in a shader returns values between 0 and 1, as expected.
Given the near- and far- clip planes of the camera, how do I calculate the true z value at this point, i.e. the distance from the camera?
From http://web.archive.org/web/20130416194336/http://olivers.posterous.com/linear-depth-in-glsl-for-real
// == Post-process frag shader ===========================================
uniform sampler2D depthBuffTex;
uniform float zNear;
uniform float zFar;
varying vec2 vTexCoord;
void main(void)
{
float z_b = texture2D(depthBuffTex, vTexCoord).x;
float z_n = 2.0 * z_b - 1.0;
float z_e = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear));
}
[edit] So here's the explanation (with 2 mistakes, see Christian's comment below) :
An OpenGL perspective matrix looks like this :
When you multiply this matrix by an homogeneous point [x,y,z,1], it gives you: [don't care, don't care, Az+B, -z] (with A and B the 2 big components in the matrix).
OpenGl next does the perspective division: it divides this vector by its w component. This operation is not done in shaders (except special cases like shadowmapping) but in hardware; you can't control it. w = -z, so the Z value becomes -A/z -B.
We are now in Normalized Device Coordinates. The Z value is between 0 and 1. For some stupid reason, OpenGL requires that it should be moved to the [-1,1] range (just like x and y). A scaling and offset is applied.
This final value is then stored in the buffer.
The above code does the exact opposite :
z_b is the raw value stored in the buffer
z_n linearly transforms z_b from [-1,1] to [0,1]
z_e is the same formula as z_n=-A/z_e -B, but solved for z_e instead. It's equivalent to z_e = -A / (z_n+B). A and B should be computed on the CPU and sent as uniforms, btw.
The opposite function is :
varying float depth; // Linear depth, in world units
void main(void)
{
float A = gl_ProjectionMatrix[2].z;
float B = gl_ProjectionMatrix[3].z;
gl_FragDepth = 0.5*(-A*depth + B) / depth + 0.5;
}
I know this is an old, old question, but I've found myself back here more than once on various occasions, so I thought I'd share my code that does the forward and reverse conversions.
This is based on #Calvin1602's answer. These work in GLSL or plain old C code.
uniform float zNear = 0.1;
uniform float zFar = 500.0;
// depthSample from depthTexture.r, for instance
float linearDepth(float depthSample)
{
depthSample = 2.0 * depthSample - 1.0;
float zLinear = 2.0 * zNear * zFar / (zFar + zNear - depthSample * (zFar - zNear));
return zLinear;
}
// result suitable for assigning to gl_FragDepth
float depthSample(float linearDepth)
{
float nonLinearDepth = (zFar + zNear - 2.0 * zNear * zFar / linearDepth) / (zFar - zNear);
nonLinearDepth = (nonLinearDepth + 1.0) / 2.0;
return nonLinearDepth;
}
I ended up here trying to solve a similar problem when Nicol Bolas's comment on this page made me realize what I was doing wrong. If you want the distance to the camera and not the distance to the camera plane, you can compute it as follows (in GLSL):
float GetDistanceFromCamera(float depth,
vec2 screen_pixel,
vec2 resolution) {
float fov = ...
float near = ...
float far = ...
float distance_to_plane = near / (far - depth * (far - near)) * far;
vec2 center = resolution / 2.0f - 0.5;
float focal_length = (resolution.y / 2.0f) / tan(fov / 2.0f);
float diagonal = length(vec3(screen_pixel.x - center.x,
screen_pixel.y - center.y,
focal_length));
return distance_to_plane * (diagonal / focal_length);
}
(source) Thanks to github user cassfalg:
https://github.com/carla-simulator/carla/issues/2287
How do I compute an eye space coordinate from window space (pixel in the frame buffer) coordinates + pixel depth value in GLSL please (gluUnproject in GLSL so to speak)?
Looks to be duplicate of GLSL convert gl_FragCoord.z into eye-space z.
Edit (complete answer):
// input: x_coord, y_coord, samplerDepth
vec2 xy = vec2(x_coord,y_coord); //in [0,1] range
vec4 v_screen = vec4(xy, texture(samplerDepth,xy), 1.0 );
vec4 v_homo = inverse(gl_ProjectionMatrix) * 2.0*(v_screen-vec4(0.5));
vec3 v_eye = v_homo.xyz / v_homo.w; //transfer from homogeneous coordinates
Assuming you've stuck with a fixed pipeline-style model, view and projection, you can just implement exactly the formula given in the gluUnProject man page.
There's no matrix inversion built into GLSL, so ideally you'd so that on the CPU. So you need to supply a uniform of the inverse of your composed modelViewProjection matrix. gl_FragCoord is in window coordinates, so you also need to supply the view dimensions.
So, you'd probably end up with something like (coding extemporaneously):
vec4 unProjectedPosition = invertedModelViewProjection * vec4(
2.0 * (gl_FragCoord.x - view[0]) / view[2] - 1.0,
2.0 * (gl_FragCoord.y - view[1]) / view[3] - 1.0,
2.0 * gl_FragCoord.z - 1.0,
1.0);
If you've implemented your own analogue of the old matrix stack then you're probably fine inverting a matrix. Otherwise, it's possibly a more daunting topic than you had anticipated and you might be better off using MESA's open source implementation (see invert_matrix, the third function in that file), just because it's well tested if nothing else.
Well, a guy on opengl.org has pointed out that the clip space coordinates the projection produces are divided by clipPos.w to compute the normalized device coordinates. When reversing the steps from fragment over ndc to clip space coordinates, you need to reconstruct that w (which happens to be -z from the corresponding view space (camera) coordinate), and multiply the ndc coordinate with that value to compute the proper clip space coordinate (which you can turn into a view space coordinate by multiplying it with the inverse projection matrix).
The following code assumes that you are processing the frame buffer in a post process. When processing it while rendering geometry, you can use gl_FragCoord.z instead of texture2D (sceneDepth, ndcPos.xy).r.
Here is the code:
uniform sampler2D sceneDepth;
uniform mat4 projectionInverse;
uniform vec2 clipPlanes; // zNear, zFar
uniform vec2 windowSize; // window width, height
#define ZNEAR clipPlanes.x
#define ZFAR clipPlanes.y
#define A (ZNEAR + ZFAR)
#define B (ZNEAR - ZFAR)
#define C (2.0 * ZNEAR * ZFAR)
#define D (ndcPos.z * B)
#define ZEYE -(C / (A + D))
void main()
{
vec3 ndcPos;
ndcPos.xy = gl_FragCoord.xy / windowSize;
ndcPos.z = texture2D (sceneDepth, ndcPos.xy).r; // or gl_FragCoord.z
ndcPos -= 0.5;
ndcPos *= 2.0;
vec4 clipPos;
clipPos.w = -ZEYE;
clipPos.xyz = ndcPos * clipPos.w;
vec4 eyePos = projectionInverse * clipPos;
}
Basically this is a GLSL version of gluUnproject.
I just realized that it's unnecessary to do these computations in the fragment shader. You can save a couple operations by doing this on the CPU and multiplying it with the MVP inverse (assuming glDepthRange(0, 1), feel free to edit):
glm::vec4 vp(left, right, width, height);
glm::mat4 viewportMat = glm::translate(
vec3(-2.0 * vp.x / vp.z - 1.0, -2.0 * vp.y / vp.w - 1.0, -1.0))
* glm::scale(glm::vec3(2.0 / vp.z, 2.0 / vp.w, 2.0));
glm::mat4 mvpInv = inverse(mvp);
glm::mat4 vmvpInv = mvpInv * viewportMat;
shader->uniform("vmvpInv", vmvpInv);
In the shader:
vec4 eyePos = vmvpInv * vec4(gl_FragCoord.xyz, 1);
vec3 pos = eyePos.xyz / eyePos.w;
I think all available answers are touching the problem from an aspect, and khronos.org has a Wiki page with a few different cases listed and explained with shader code, so it's worth posting here.
Compute eye space from window space.