I'm working with 2D sprites - and I want to do 3D style depth testing with them. When writing a pixel shader for them, I get access to the semantic DEPTH0. Would writing to this value help? It seems it doesn't. Maybe it's done before the pixel shader step? Or is depth testing only done when drawing 3D things (I'm using SpriteBatch)? Any links/articles/topics to read/search for would be appreciated.
Depth testing, according to the description of modern 3D pipelines, is done after the pixel shader, which is why Direct3D provides the
However, it's kind of a waste of precious GPU processing power to shade a pixel and then throw it out because it's behind an object that we already knew was there, particularly if the pixel shader doesn't change the Z value. This is the idea behind the Early-Z optimization, where if you're rendering a pixel whose pixel shader doesn't change the depth, the hardware may never actually run the pixel shader (or, more likely, if a full 2x2 quad of pixels is occluded, then none of them will be run through the pixel shader). This is why you want to render a fully-opaque scene from front to back.
This leads into another almost universally-implemented optimization called hierarchical Z-culling, or Hi-Z. The idea here is that the hardware can keep lower-resolution conservative depth buffers around and throw out huge chunks of pixels at once if it knows that all those pixels are occluded by pixels currently in the depth buffer (by knowing the "rearmost" depth over large regions of the depth buffer). This process is not unlike mipmapping, but it keeps the maximum (or most permissive) depth value instead of the average one.
Neither of those optimizations are available if you change your output depth in the pixel shader, so for 3D scenes doing so is discouraged. However, both Direct3D and OpenGL have a "conservative depth output" feature that is intended to let you change the depth and still let the GPU do its optimization (this is
All this is intended to say that you should be careful when changing a pixel's depth. In 2D most of the culling-related implications probably won't affect you very much, so you're probably in the clear. But you should be aware that when pipeline descriptions say that the depth test happens after the pixel shader, they're only telling part of the truth.
Depth testing happens in what is known as the merging stage after the pixel shader (also check this link). There's however an optimization technique called the "Early-Z Test" which is available in some graphic cards, and it consists of moving the check before the pixel shader so that it can be skipped completely for occluded fragments.
As for whether you can modify the depth value of a fragment in the pixel shader, yes you can and this source confirms it. although I'm not sure how to use it correctly. Quoting:
As for your confusion regarding 3D, you should know that the
Finally, I think what you're trying to achieve is usually done in another way, by using deferred rendering, where you'll also need an heightmap (and a normal map if you intend to do lighting) for each for your sprites. Check this amazing video example of the technique. I used to see an article with an implementation of the technique but I can't seem to find it at the moment. The video does state at one point "Writing the heightmap to the depth buffer allows for objects to intersect in 3D" which might be a useful hint.
In the traditional render pipeline, depth testing occurs between the stencil and blending steps of fragment processing (source). Usually it's something that can only be taken control of via state variables (at least in OpenGL), and I must admit that I'm not as familiar with the pipeline of XNA.
I guess what I'm trying to get at is the depth-buffer corresponding to the active frame-buffer is not typically something that is part of the programmable graphics pipeline. Several techniques will usually fill the depth-buffer in one pass and access it as a texture in a second pass.
To sum up, depending on how you draw your primitives, it may already be done for you.