Jump to content

  • Log In with Google      Sign In   
  • Create Account

- - - - -

Building An Isometric Game Using Horde3D (Part 2)

Posted by , 01 August 2011 · 3,563 views

Here is an environment graphic sprite from Goblinson Crusoe, with alpha channel beside it:

Attached Image

See those pixels? The ones where the alpha value is a gray value between 0 and 1? The official 3D graphics-programming term for pixels like those is "assholes." Those pixels are assholes. You see, that sprite was rendered from Blender with anti-aliasing enabled. This means that the exported alpha channel is blended at the edges. This is cool for an isometric game, because it allows "free" anti-aliasing of rendered objects, as well as smooth blending of objects that are supposed to "fade" into the background. (Refer to the bottom of that mountain, where the blend helps it to superimpose upon the ground without a harsh line edge.) When the sprites are layered together and drawn, the alpha-blending of the sprites will cause them to be smoothly composited together without harsh edges or "jaggies" caused by aliasing.

A "traditional" 2D isometric is constrained to draw back-to-front anyway, due to the layering requirements, so this anti-aliasing incurs no additional cost. The rendering algorithm of an isometric is designed for a single special case. However, this is not the case with a generalized 3D engine. When 3D objects are drawn as solids (ie, no alpha blending) it is not necessary to sort them based on depth. They can be drawn in batches, willy-nilly, and the Z-buffer will cull obstructed fragments. However, in order to implement partially transparent objects (ie, anti-aliased rendered sprites) the transparent geometry needs to be sorted back-to-front relative to the view. With a 2D isometric engine, this sorting is performed as a consequence of the rendering algorithm which iterates the scene back to front. In a general 3D engine, though, it is necessary to apply an actual sort algorithm to visible objects, and this extra step of sorting means an additional performance hit when rendering. It also means that optimization using batches of primitives becomes a little more hairy, since batches can be spatially interleaved with one another. (Imagine vegetation batches, where each batch represents one type of vegetation. The various types are all mixed up, making back-to-front sorting of the batches impossible. So the only type of alpha blending that is practical for this type of batched entity is a 1 or 0 alpha "cutout" that does not actually blend any pixels. Blending based on alpha with partial transparency will result in occasional "halos" around entities that just looks bad.)

That is why those pixels are assholes. They make the end product look good, but in so doing they force a full-scene sort that, in the case where there are lots and lots of objects on screen, may impose a rather stiff performance penalty upon rendering with a general 3D engine. This, for me, is one of the strongest arguments in favor of rolling your own, rather than using a general scene manager. Isometric cameras are extremely predictable (ie, no perspective and no arbitrary rotation means that you can always calculate the exact visible area and use a known method for iterating it back-to-front). If you can roll your own scene manager to permit the specific type of drawing required by the isometric camera, you can save yourself a great deal of processing time.

However, this particular article isn't about that. I haven't delved into Horde3D deeply enough to unravel the rendering system yet, so I couldn't say at this moment what would be required of me to roll my own scene manager using Horde3D as a basis. Instead, I want to see exactly how well the general scene manager will perform in this case.

Now, there are several ways you can do environment graphics for an isometric game that uses a 3D engine underlayer. You can use the same pre-rendered sprites that you would use in a 2D game (ie, the above mountain sprite from GC) and map them to either billboards or low-detail poly meshes; or you can use fully modelled and textured 3D geometry, instanced in the scene in a manner identical to a non-isometric scene. Reasons for using pre-rendered sprites might include re-using an existing set of assets; smooth, anti-aliased blending of the edges as described above; higher visual detail without the associated rendering cost; and so forth. Reasons for using modelled geometry include the ability to easily switch to a camera that is arbitrarily rotatable and enabling the use of perspective, performance reasons as detailed above (ie, avoiding a sort and using batches), and so forth.

If you go the fully modelled route, then the above discussion/rant on partially-transparent sprites, and most of the discussion comprising the remainder of this post, are not relevant. If you do go the pre-rendered route, you can render your sprites without anti-aliasing and, again, the above discussion is not relevant. However, in these cases, you will potentially have to deal with the jaggies. They can be mitigated, of course, using FSAA, but again a performance penalty is incurred, and older hardware might not support FSAA. Further, I have frequently made use of heavy AA using, for example, a Gaussian kernel, as a stylistic choice, producing a very soft, almost "fuzzy" type of scene; something that is trivial to do with the traditional layered approach (in fact, incurring no extra overhead at all), but less easy to do as a post-process of a 3D render. However, the exact route you choose must be chosen on the basis of your individual requirements.

Another consideration in the choice is characters. Since the whole point of me switching GC to a 3D underlayer is to allow 3D characters, without the pain in the arse task of writing my own 3D animation code, then I need to consider the fact that while the background scenery might be softly-blended and nicely anti-aliased, the characters will not be. This could be a problem. Maybe.

For this part of the experiment, I'm choosing to map pre-rendered, anti-aliased sprites onto geometry that roughly approximates the shape of the object meant to be represented. This gives each entity a rough approximation of their depth; something that is useful when special effects and particle systems interact with the level geometry. In these cases, the flat nature of a world built out of billboards is betrayed by these interactions, which can look bad. Sure, interaction with a low-res mesh world isn't a whole lot better; nevertheless, it is still an improvement.

Anyway, before I can worry about any of that, I need to get the camera built and setup the isometric view...

Attached Thumbnails

  • Attached Image
  • Attached Image
  • Attached Image
  • Attached Image
  • Attached Image
  • Attached Image
  • Attached Image
  • Attached Image

Great post :) Everyone hates assholes!

I've decided to sort blended objects in my project and only have blended objects when its really needed (windows, fires etc.). Though my sorting algorithm is rather crapy. I'll look forward to future posts.

The sorting problem has been... uh.... sorted. I'll detail it further in the next post, but basically it has to do with the way I did the isometric camera. Turns out, the sort in the h3d engine uses the fabs() of the distance, so nodes that fell behind the camera eye point weren't sorted correctly. The fix is to offset the camera upward on Y, then correct the position on X and Z in SetPosition accordingly.

Hi O-san! How's Medieval Story coming along?
Tiny steps - a week at a time, difficult to keep it going when you got a full-time job. Other ways its going okay :)