Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

3D position to transformed depth value.

89 views
Skip to first unread message

VelociChicken

unread,
Oct 19, 2012, 11:32:14 AM10/19/12
to
Hello, this is driving me crazy, mainly because it's close but not quite
there.
I'm trying to set the fragment depth when I have a 3D position and a
camera position,basically what I get from ray-tracing.

Here's my perspective transform:-

void TerrainManager::Perspective(GLfloat fovy, GLfloat aspect, GLfloat
zNear, GLfloat zFar)
{
GLfloat xmin, xmax, ymin, ymax;

float an = tanf(fovy * PI/ 360.0f);
ymax = zNear * an;
ymin = -ymax;
xmax = ymax * aspect;
xmin = ymin * aspect;
float width = xmax - xmin;
float height = ymax - ymin;
float depth = zFar - zNear;
float q = -(zFar + zNear) / depth;
float qn = -(2.0f * zFar * zNear) / depth;
float w = 2.0f * zNear / width;
float h = 2.0f * zNear / height;

memset((void*)&projectionMatrix, 0, sizeof(mat4));
projectionMatrix[0].x = w;
projectionMatrix[1].y = h;
projectionMatrix[2].z = q;
projectionMatrix[2].w = -1.0;
projectionMatrix[3].z = qn;

// LOOK ===> projAB contains the vec2 that goes to the shader to do
the transform...

projAB.x = (zFar+zNear)/depth;
projAB.y = -(2.0f*zFar*zNear)/depth;

}

To get the real distance from the camera in the shader I use:-

distance = length(position3D - cameraPos);
gl_FragDepth = (projAB.y / distance) + projAB.x;

The value seems slightly off, and I can't work out if it's the near
plane not being included properlyor the aspect ratio or whatever, but
it's SO close it's really annoying. I could use the projection matrix
but this seems a quicker method.

I've used information from here, but I guess it's wrong some how?:-
http://www.iquilezles.org/www/articles/raypolys/raypolys.htm

I noticed that my projAB.y is negative compared to that web example, but
thatway it shows OK. That's why I've included my perspective transform
in case there's a problem with that directly.

Can any one help?

Thanks,
Dave.






VelociChicken

unread,
Oct 19, 2012, 11:43:44 AM10/19/12
to
It's funny how I can spend days on something, then as soon as I ask the
question I've find the solution by myself!! :)
It seems my near and far subtraction were swapped and Inigo's site has a
mistake, possibly (wonderful though it is).
This gives me the correct depth:-

projAB.x = zFar/(zFar - zNear);
projAB.y = (zFar*zNear)/(zNear - zFar );

Thanks,
Dave.



VelociChicken

unread,
Oct 19, 2012, 11:47:52 AM10/19/12
to
Scrap that, it's very very close now but slightly off at the edges of
the screen, so close it's hardly visible at first.
Please help!!





Nobody

unread,
Oct 19, 2012, 12:36:29 PM10/19/12
to
On Fri, 19 Oct 2012 16:47:52 +0100, VelociChicken wrote:

> Scrap that, it's very very close now but slightly off at the edges of
> the screen, so close it's hardly visible at first.
> Please help!!

Are you sure you're not just running into differences at the level of
rounding error between the CPU and GPU?

E.g. the CPU using double (or even extended) precision internally then
converting to single precision at the end, versus the GPU using single
precision throughout.

VelociChicken

unread,
Oct 19, 2012, 1:04:39 PM10/19/12
to
How can I possibly fix that?Use an internal shader value?

The effect I'm getting is the values are getting too large near the
edges of the screen.
What I thought was a fix did work for distance to the camera changes in
depth error.

Thanks,
Dave.

bob smith

unread,
Oct 19, 2012, 5:32:07 PM10/19/12
to
Are you at least using a 32 bit depth buffer as opposed to 16 bit?

VelociChicken

unread,
Oct 20, 2012, 7:19:57 AM10/20/12
to
I'm using the following:-

glGenTextures(1, &depthTex);
glBindTexture(GL_TEXTURE_2D, depthTex);
glTexParameterf(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width,
height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);




bob smith

unread,
Oct 22, 2012, 11:16:25 AM10/22/12
to
Doesn't that GL_UNSIGNED_BYTE mean your depth values are just 8 bits?

That's not very much.

I would make sure OpenGL is using 32 bits as well as your texture.

Nobody

unread,
Oct 22, 2012, 4:49:58 PM10/22/12
to
On Mon, 22 Oct 2012 08:16:25 -0700, bob smith wrote:

>> glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width,
>> height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
>
> Doesn't that GL_UNSIGNED_BYTE mean your depth values are just 8 bits?

No. The internal format is GL_DEPTH_COMPONENT24, which is 24 bits. The
type parameter (which is GL_UNSIGNED_BYTE) refers to the data used to
initialise the texture, and is ignored in this case as the data pointer is
NULL.

VelociChicken

unread,
Oct 23, 2012, 11:21:21 AM10/23/12
to
I've tried it with all variants with the same erroneous result.
Is that perspective transform done correctly? The FOV is half the view
screen, so 60 for 120 degrees.
I'm sure I've seen others without the 2*'s in there and some with
positive instead of negative numbers.
My guess is that the projection values I'm getting are off because the
projection matrix is set up different from others?
This matrix still confuses me, I have to admit.

Any thoughts?

Thanks,
Dave.

Nobody

unread,
Oct 23, 2012, 1:20:59 PM10/23/12
to
On Tue, 23 Oct 2012 16:21:21 +0100, VelociChicken wrote:

> I've tried it with all variants with the same erroneous result. Is that
> perspective transform done correctly? The FOV is half the view screen, so
> 60 for 120 degrees.
> I'm sure I've seen others without the 2*'s in there and some with positive
> instead of negative numbers. My guess is that the projection values I'm
> getting are off because the projection matrix is set up different from
> others? This matrix still confuses me, I have to admit.

It shouldn't matter. You should be using the same matrix for everything,
so the results should be consistent even if they're wrong.

The matrix generated by gluPerspective() can be found at:

http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml

If you need OpenGL calculations to be consistent with client-side
calculations, generate the matrices yourself then upload them with
glLoadMatrix(), glUniformMatrix(), etc, rather than using the legacy
OpenGL functions (glRotate() etc).

But if you need a high degree of consistency, you may be better off
performing all of the calculations using shaders.

VelociChicken

unread,
Oct 30, 2012, 3:31:52 PM10/30/12
to
I still can't get this to work, thanks for everybody's pointers though.
Using the value for distance fog parameters produces less fog at the
corners of the screen, it's almost as if it's linear across the screen
rather than curving with the landscape perspective changes at the edge.
I think I'm expecting it to give me the actual distance from the view
point, when it's actually not doing that at all?
Dave

bob smith

unread,
Oct 30, 2012, 6:25:54 PM10/30/12
to
I bet it's using the distance from the near plane.

Nobody

unread,
Oct 30, 2012, 6:50:51 PM10/30/12
to
On Tue, 30 Oct 2012 19:31:52 +0000, VelociChicken wrote:

> I still can't get this to work, thanks for everybody's pointers though.
> Using the value for distance fog parameters produces less fog at the
> corners of the screen, it's almost as if it's linear across the screen
> rather than curving with the landscape perspective changes at the edge. I
> think I'm expecting it to give me the actual distance from the view point,
> when it's actually not doing that at all? Dave

Depth corresponds to the distance "in front of" the viewpoint, not the
distance "from" the viewpoint.

Also, it's not linear, it's reciprocal; specifically: a+(b/z), where a and
b are such that the near and far planes map to -1 and +1 respectively.

VelociChicken

unread,
Oct 30, 2012, 7:56:30 PM10/30/12
to
So I can't get the real distance to a point without 'unprojecting' it?

Nobody

unread,
Oct 31, 2012, 6:23:49 PM10/31/12
to
On Tue, 30 Oct 2012 23:56:30 +0000, VelociChicken wrote:

>> Also, it's not linear, it's reciprocal; specifically: a+(b/z), where a and
>> b are such that the near and far planes map to -1 and +1 respectively.
>
> So I can't get the real distance to a point without 'unprojecting' it?

It's easier to just keep an unprojected copy, i.e. as well as:

gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

(or similar), add:

position = gl_ModelViewMatrix * gl_Vertex;

In theory, it's possible to reconstruct the latter from gl_FragCoord,
gl_ProjectionMatrix and the viewport (which isn't available via a built-in
shader variable). In practice, it's typically simpler to add a separate
variable (vertex shader output, fragment shader input).

If you do want to reconstruct it from gl_FragCoord, gl_FragCoord.w is the
reciprocal of the interpolated value of gl_Position.w, which will normally
just be the negative of the unprojected z coordinate (the standard
perspective matrix has [0 0 -1 0] in the bottom row).

So, for the standard gluPerspective() matrix:

nx = 2 * (gl_FragCoord.x - viewport_x) / viewport_width - 1;
ny = 2 * (gl_FragCoord.y - viewport_y) / viewport_height - 1;
nz = 1.0/gl_FragCoord.w;
x = nz * nx * aspect / f; // f = 1/tan(fovy/2)
y = nz * ny / f;
z = -nz;
distance = length(vec3(x,y,z));

Note that zNear and zFar aren't relevant, as we're using gl_FragCoord.w
rather than gl_FragCoord.z.

VelociChicken

unread,
Nov 1, 2012, 1:28:20 PM11/1/12
to
Thanks for the full answer, I didn't realise I could just use the '.w'
that's really great.
I presume I could use '* f' as f is '1.0/tan' so it all falls out really
cheaply.
Thanks again 'Nobody' from 'nowhere'!
Dave


Nobody

unread,
Nov 1, 2012, 5:56:32 PM11/1/12
to
On Thu, 01 Nov 2012 17:28:20 +0000, VelociChicken wrote:

> Thanks for the full answer, I didn't realise I could just use the '.w'
> that's really great.
> I presume I could use '* f' as f is '1.0/tan' so it all falls out really
> cheaply.

You could merge the 1/f with nz.

The relationship between f and the view angle doesn't really matter at
this point. f is just gl_ProjectionMatrix[1][1], while aspect is
gl_ProjectionMatrix[1][1] / gl_ProjectionMatrix[0][0].

But personally, I'd rather add another varying for the unprojected
position than "unproject" gl_FragCoord.

VelociChicken

unread,
Nov 2, 2012, 2:49:09 PM11/2/12
to
It will save some data transfer, because I'm using deferred rendering
and the XYZ coords are stored in a texture, which seems a waste when I
can grab them from the screen and depth values.
Dave.

Nobody

unread,
Nov 2, 2012, 4:45:21 PM11/2/12
to
On Fri, 02 Nov 2012 18:49:09 +0000, VelociChicken wrote:

>> But personally, I'd rather add another varying for the unprojected
>> position than "unproject" gl_FragCoord.
>
> It will save some data transfer, because I'm using deferred rendering
> and the XYZ coords are stored in a texture, which seems a waste when I
> can grab them from the screen and depth values.

Deferred rendering shouldn't make any difference. You don't need to store
the X/Y coordinates, regardless of whether they're unprojected,
normalised, screen-space, etc. For the Z, you can store (and use) either
reciprocal or linear values. Except ...

If you calculate gl_FragDepth yourself rather than letting the GPU use
gl_FragCoord.z implicitly, it may disable any early-depth-test
optimisation, which could be an issue if you have a lot of overdraw. I
don't know how smart current implementations are (i.e. whether they can
still do early-depth-test with a shader which sets gl_FragDepth).

VelociChicken

unread,
Nov 2, 2012, 5:52:28 PM11/2/12
to
What I meant was I all I need now is that one depth value as opposed to
three values, thanks to you.
I have to have the XYZ coords in my colouring stage, and the less I
store the better. So just using the depth coefficient then calculating
XYZ in the deferred stage is faster or leaves room for other data types.
For the routine that sets the FragDepth, it's probably OK because I'm
ray-tracing into a finite box which is in known space - it's difficult
to explain! ;)
0 new messages