Wednesday, July 29, 2009

Infinite Depth Buffer

I've been planning out how I'm going to rewrite my planet algorithm once DirectX 11 is out. I've decided to focus on problems I have now that will still be a problem in DX11. One such problem that I've always been having in the past is the depth buffer.

My planet is Earth-sized so in order to keep it visible as you fly away from it, I pushed the far clipping plane way out. Obviously this destroyed the precision of my depth buffer and I had big problems with Z-fighting (far off mountains were being drawn in front of closer ones).

Rant: Why the heck are most GPUs these days still stuck with a 24-bit depth buffer? The Xbox 360 and my GeForce 9800M GT both only support up to a 24-bit depth buffer. DX11 level GPUs will have 64-bit floating point (double) support in shaders, so why not a 64-bit depth buffer?

In the videos and screenshots I have posted in the past, I did two different things to try and fix my problem with the depth buffer. First, I had a "sliding" far clipping plane that would have a minimum value, but as you flew away from the planet, it would extend out in order to continually show the planet. My second solution was to just disable the depth buffer. Both of these solutions only worked because I was drawing only 1 planet and there were no other objects being rendered. Obviously I want to keep my depth buffer around, keep the high precision for any near objects, but continue to draw far off planets (not have them clipped by the far clipping plane).

In order to fully understand my problem, I read about how exactly the depth buffer works and how a position is transfomed and then clipped. I found this article very informative about the inner workings of the GPU in terms of the depth buffer. I did not change my depth buffer to be linear like he does though. The article helped me to understand the relationship between the Z component and W component of the transformed vertex position.

Between the vertex shader and the pixel shader, the Z component is divided by W in order to "normalize" the depth (range 0-1). If the Z value is greater than 1 then it is clipped. So, I needed to make it so that the normalized Z value never exceeded 1. This was a very simple thing to fix, once I understood it. In my vertex shader I check the Z value to see if it is greater than the far clipping plane value (which I pass into the shader). If it is greater, then I simply set the W component equal to the Z component. This means that the Z / W calculation thus becomes Z / Z = 1. Now I can have good depth buffer precision for things close to the camera, but I will continue to draw things even if they are an infinite (theoretically) distance away!

Obviously this solution isn't perfect and there are some "gotchas". If I am drawing a planet and a moon, and the moon is behind the planet, and I am flying away from the planet, AND the moon is being drawn after the planet in the C# code, then the moon will suddenly pop in front of the planet once the planet exceeds the far clipping plane. That means I'll have to have a manager of large objects in the "local system" to make sure they are drawn in back to front order. That should be really easy to implement.

Hopefully this all makes sense and it helps someone else struggling with the same problem.

Until next time...

Update: I would now strongly encourage people to use a Logarithmic Depth Buffer to solve all of your depth buffer precision issues. You can read about it here:

Wednesday, July 1, 2009

Perlin Noise in JavaScript

I apologize for not having any update for the month of June, but I was gone on vacation for a majority of it.

I saw tons of places on the road trip. Illinois, Indiana, Michigan, Ontario, New York, Vermont, New Hampshire, Maine, Massachusetts, New Jersey, Delaware, Maryland, Virginia, Pennsylvania, West Virginia, and Ohio (in that order)! I had never been to the New England area of the country, so it was great being able to see it all.

In terms of development, I haven't really done any since I wrote that Perlin Noise article. I've been tossing around the idea in my head to go try out SlimDX again. I know, I know, I just can't make up my mind on things, right? It would just be very convenient to be writing C# again, and I found the great PhysX.Net wrapper that should allow me to continue to use PhysX with my C# development. Plus SlimDX already is supporting DirectX 11, which is awesome! (Tessellation, here I come!)

By the way, I was curious about JavaScript so I developed a simple little webpage that implements Perlin Noise in JavaScript.

You can find it here:

I used a Canvas element to display the result, so it won't work in Internet Explorer.

Be sure to check out the source code since I implemented several different types of summation as well (fBm, turbulence, and ridged multifractal). Enjoy!