суббота, 8 марта 2014 г.

Order independent transparency in flash

Hi. The word of warning in the beginning - my approach is some sort of depth peeling algorithm, but it's not true depth peeling - it's impossible in flash yet. It also not uses different buffers - A, K etc - simply because we don't have it in flash. My Approach uses too many passes to be useful real-time technique. It's just a proof of concepts and was a good train for my brain :)

The algorithm is quite simple - we choose the number of layers we want to draw, draw that layers by order starting from the closest to the viewer and blending results.

Step by step it looks like:
- clear main depth buffer to 0.
- clear main color buffer.
for every layer:
- draw to render target with depth compare LESS. If current depth is greater than depth in main depth buffer, discard pixel. Now we have depth for current layer.
- draw color to render target. If current depth is equal to the depth for current layer - draw color. Now we have color for current layer.
- blend newly created color layer with main color buffer.
- combine layer depth with main depth - write in main buffer greatest depth.

It sounds complicated, but it's very simple. Let's make it step by step.

1. Imagine that we want to draw 3 planes. This is side view, so planes are just lines. The arrow is our looking direction:

2. We draw to depth buffer. Since we have compare mode set to LESS we'll get this depths:

i.e. the closest pixels to camera (red lines).

3. Now we draw a new layer. We draw the same geometry again, but this time we compare depths. If depth is the same as in previous layers or greater we discard pixel. And, again, since we have compare mode set to LESS we'll receive closest pixels to camera. But since we discard pixels that already drawn we get next closest to camera layer (green lines)!
4. We need to combine depths from previous and current layers and this resulting depth in next layer draw. That's simple - we read depths from both depth buffers and write the greatest one. So, next time we'll draw new layer we'll discard already drawn pixels. The combined depth buffer now should look like this (purple lines):
5. Draw a new layer. Same as point 3 (blue line).
That was how we manage depths. And knowing a depth for a layer we simply draw geometry (yeah, again) and write only pixels that belongs to that layer - comparing depths (they should be equal).

Having a color map for a layers we need to blend them.  We need a trick - we can't have separate texture for every layer. I use 3 textures. Imagine that we have already one layer drawn - it will be in some texture "A". Next, we need to add new layer under it (recall - we're drawing layers from front to back) - this new layer we hold in some texture "B". We take this 2 textures "A" (destination) and "B" (source) and draw to some texture "C" with some blending formula. I use standard alpha blending as here at first, but this gives not good-looking effect (it's a simple and fast formula, but not correct).  And I took this formula (from Wikipedia):

So, our texture "C" with blended "A" and "B" now will be used in next pass as source (blended over new layer). And so on.

The demo shows all this in action (notice that packing and unpacking float to rgba channels is not very precise, so I added some epsilon value when compare depths. And this introduce some artifacts if depths of layers nearly equal or depth is near zero):


Source can be found here.

Upd.: don't know why, but this demo doesn't work in pepper plugin. Will try to find out.

PS: I heard that it's possible to achieve same thing using stencil operation. I tried a lot, but I can't make it work. If someone will point me to such algorithm - it would be great ).

4 комментария:

  1. Sorry, didn't understand a question. It's 'order independent' without any sorting. And you'll get the same picture independent of of the drawing order.

  2. Я просто не понял как это draw that layers by order starting from the closest to the viewer = without any sorting.

  3. Just draw all the stuff with depth compare LESS - so you'll get closest pixel out of the box. And need to write this depth to custom depth buffer. But if this pixel have a depth less than depth in my custom depth buffer I simply discard it.