Thursday, October 20, 2011

Billboards, an Update

Last night I finally had a breakthrough moment with the billboard thing I discussed in my previous post.  It took some time to get it right, because my bullets (which are modeled after a popular open-source game's design) have alpha gradients.  The trouble with alpha in general is that it doesn't look right if objects are drawn out of depth order. 

To illustrate, imagine two objects, A and B. A is nearer the viewer and a semi-transparent color.  B is farther from the viewer, partially obscured by A (i.e. visually overlapping), and nearly opaque.

If B is drawn first followed by A, you'll get what you expect: a view of A, with the obscured part of B semi-visible through A.  However, if A is drawn first, the portion of B overlapped by A will appear in front of A, even though foreshortening and other depth cues would indicate it is behind.

This would be easier to explain with a diagram, but I'm not somewhere I can easily draw one.  Google around for alpha blend depth sort if you want to learn more.

Anyway, the well-known solution to this is to always draw alpha-blended objects in reverse depth order; that is, draw objects that are farther away from the viewer before drawing objects that are nearer.  This of course requires additional processing to sort the objects prior to passing them to the render pipeline.

After doing a lot of searching and seeking of examples, I finally landed on the solution of creating an unsorted vertex buffer and a sorted index buffer.  This helps to minimize computational overhead in the sorting process, as it needs only to move 4-byte integers around instead of entire vertex declarations (which, in this example, are 64 bytes in size).  Of course, sorting does require knowing the distance from the viewer, which means doing extra work in the form of applying the view and perspective transformations to every vertex to discover their depth.  Fortunately that happens pretty quickly, even in software.

I also decided to reduce the likelihood of garbage collection trouble and made the vertex buffer a static array, and use it in a ring queue form.  The index buffers are destroyed and recreated every frame, but the vertex array stays put in memory for the lifetime of the containing object.  Since the index buffers are usually fairly short (six indices per shot, times however many shots are active on the screen at once which usually isn't many and can be limited by game rules), garbage collection isn't too much of a concern.  Again, it's 6 x 4 (six 4-byte indices), or 24 bytes per shot, instead of 4 x 64 (four 64-byte vertices), or 256 bytes per shot.

SO, after a wild evening of coding, I now have a working blaster.  I may post a video; I'm rather proud of my accomplishment!

No comments:

Post a Comment