среда, 6 февраля 2013 г.

Isometric sort. New approach.

Hi. Today I want to share some idea about isometric sorting. This is not stage3D article. Example was compiled against 10.1 flash player.

So, what's wrong with existing solutions? Imagine two walls intersects. No matter what you do, one of the walls will be always above.

Of course this problem can be solved easily by not letting them intersect, or prepare correct asset. But I found a way to fix it in runtime. Take a look at demo.

Use mouse to select an object, WASD or arrows for moving in xy plane, SHIFT + W/S/Up/Down for moving along z axis.

Algorithm is quite simple and uses PixelBender filters. Here's a brief description:
The heart of algorithm are depth maps for every object. In that map 0 means the farthest point and 1 - closest. Then I create something like a depth buffer and compare pixels from that map with buffer. So I can hide pixels that obscured by another.

What are this maps and how they look? Here a map for red fence:

Very strange... But why? In my first approach I decoded depth in single channel of bitmapdata. So, in RGBA only R was used. This is 1 byte, 8 bits, integers 0 to 255 (recall that all depths are in 0..1 range). And this is the reason of some sort of inaccuracy. Imagine for example object with physical depth of 1000 pixels. But we can decode only values from 0 to 255. So, for every 4 pixels approximately I have the same depth value. And this leads to artifacts (z-fightning). To beat it we need to store depth in more than 1 channel. I choose 3 channels - RGB. I'm avoiding to use alpha channel because flash uses premultipled RGB by alpha, but PixelBender not. Quick search in goggle for words "baking float in 24 bits" gave me good solution, so I generate a couple of maps.

Here're steps:
1. Take bitmapdata with black fill, that means 0 depth.
2. With blend filter (blendShader) creates depth buffer - for every object I compare depth in map with current depth in buffer. If objects depth is greater than this value is written to buffer. If lesser, that means that current pixel is behind, so depth value stays as it was.
3. For every object I set filter which again compares depth in map with final depth in buffer. If values are approximatelly equals that means that pixel is visible. If not, I set pixels alpha to 0.

Several steps are needed because of PixelBender limitations - we can output only to one source.

This approach have an advantage - it's not neccessary to manage display object depths - blend filter will do it work and we'll get correct looking picture.

Sad, but there're a lot of disadvantages:
1. It's veeeeeery slow.
2. Need for depth maps creating. I wrote an utility that builds maps by several points. But for complex objects it will be hard.
3. Only integer values for x, y, z can be used or artifacts will appear.
4. There's a problem with transparent objects.

As you can see there're more minuses than pluses. For me it was a challenging task - I met a couple of new algorithms, improve my PixelBender skills. But I think method can't be used for any production. Just as nice demo ).

P.S.: article available in russian (with source code) - link

Комментариев нет:

Отправить комментарий