Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am trying to draw spheres using a billboard, so I have a normal map with a z component that I am sampling in my fragment shader. I just need to add this z component to the depth of my fragment to get proper 3D spheres...

How do you determine the current fragment's z-depth, add my offset and then set gl_FragDepth?

I've found lots of confusing contradictory hints and partially working bits of code all over the 'net, but no definitive clear answer.

UPDATE

final result (no shadowing) http://www.youtube.com/watch?v=88idbFlvQV0

share|improve this question
add comment

2 Answers 2

up vote 6 down vote accepted

I just need to add this z component to the depth of my fragment to get proper 3D spheres...

First of all, this will not work. Unless your spheres are very small (relative to the camera), simply adding to the depth will not create a proper impostor. If the sphere is towards the edge of the screen, then you will not get a proper visualization effect. A sphere becomes oblong towards the edge of the screen, and the "up" direction for a particular point on the sphere isn't the same everywhere.

You have to use ray tracing to render a proper sphere impostor for spheres of a decent size. And if your spheres are always too small to notice, then you won't notice the lack of depth bias either.

Also, I'm not sure what good biasing the depth will do. It will certainly make interpenetrations more realistic, but that's about it. The bump-mapped lighting will do far more for your visuals than bumping the depth of the fragment.

However, if you insist on doing this without raytracing (a version with raytracing is available here), the first step is to take the current Z value and transform it back into clip-space. This will help linearize it. This has been asked and answered. That gets us back to clip space. But you need to take that clip-space position and go back to camera space. So you need the inverse projection matrix. Given that, you do this:

 vec4 cameraSpacePosition = invProjMat * vec4(0, 0, clipDepth, gl_FragCoord.z);

Next, you bias the camera space Z position with a camera space offset. It's important that the offset be in camera space; otherwise, it doesn't work.

Once you have that, you just perform the transformation back to window space:

cameraSpacePosition.z += bias;
vec4 clipPos = projMat * vec4(cameraSpacePosition, 1.0);
float ndcDepth = clipPos.z / clipPos.w;
gl_FragDepth = ((gl_DepthRange.diff * ndcDepth) +
    gl_DepthRange.near + gl_DepthRange.far) / 2.0;
share|improve this answer
    
Thank you. You are of course spot on on your criticism, but it I have a large number of small balls to draw. The problem I've found however is performance; with flat impostors, integrated intel cards can push 10K or more at 60fps, which was stunning and why I took that root. Computing the z in the fragment shader with all that matrix math etc only knocked it down to 50 fps. But setting the gl_FragDepth - even to a fixed value rather than computing a real value - knocked the fps down to ~5! I speculate that the compiler puts an early z cull only if your fragment shader doesn't compute z :( –  Will Aug 31 '11 at 12:24
    
@Will: Of course writing to the depth turns off early z-tests; how else could it possibly work? It doesn't know what your depth value is going to be, so any tests it might do are wrong. Honestly, if the balls you're drawing aren't big enough for perspective to be an issue (and thus requiring raytracing), then depth isn't going to be much of an issue for them as well. –  Nicol Bolas Aug 31 '11 at 18:14
    
I was rather impressed that there was an early z cull on the cards at all. Unfortunately my billboards fight each other for z as you make small rotations of the camera as there are lots of intersections. I feel so close to a good solution and yet so far. I say 'small' meaning less than 64x64. I can't afford to turn the scene into a mesh unfortunately. –  Will Aug 31 '11 at 18:43
add comment

According to the GLSL specification (both 1.10 and 4.20) gl_FragCoord holds the window relative coordinates of the fragment, from which z is the depth that gets written into the depth buffer (if not changing gl_FragDepth). So you can just write

gl_FragDepth = gl_FragCoord.z + depthFromTexture;
share|improve this answer
    
The z depth is not linear, its log. The spec doesn't describe how to decode and encode values. –  Will Aug 30 '11 at 12:21
1  
@Will Oh yes, forgot about the non-linearity, but the decoding only depends on you projection matrix. Actually gl_FragCoord.w contains the negative reciprocal of the eye-space depth. Maybe you can use this and then retransform it into the non-linear [0,1] range? –  Christian Rau Aug 30 '11 at 12:33
    
don't you need to know the near and far too? I mean, I've been struggling to work this out myself so I was rather holding out for a definitive answer :/ –  Will Aug 30 '11 at 14:59
    
@Will Yes, as the eye space depth is in, well, eye space. So you need the near and far values for transforming it back into post projective space, or maybe you can use the values of the projection matrix directly, as they are encoded in there. Maybe this is a bit of help (assuming you compute your shader projection matrix similarly). –  Christian Rau Aug 30 '11 at 15:27
1  
@Will: The specification very much does describe how to decode and encode them. –  Nicol Bolas Aug 30 '11 at 18:02
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.