A helping hand for bedroom coders throughout the land.
Battle of Trafalgar for Windows Phone 7–Part 4, Fake shadows, but real name labels.

Shadows are hard, and on under WP7 they are really hard. However, the do *really* improve the sense that your models are truly part of the scene because they blend the otherwise artificially bright boundary between each element so that everything looks coherent. Without custom shader support, I could only think of a 3 ways of achieving this effect on the phone (in increasing order of complexity):

  • Simple rendered 2D images under each object
  • Rendered 2D images, but created dynamically from the 3D model according to its orientation
  • Full-on CPU Stencil Shadows

The first two options are only really viable if the ‘ground plane’ is at least approximately flat, and the second option is only necessary if the object’s orientation to that ground plane is going to change during the game. I’ve not yet had to implement Option 3, but a new project I am working on is looking like it might need it – so we may look at that in another post soon… Luckily for me in this project I have ships that are always floating in the same orientation, on an essentially flat sea – phew, option 1 it is then!

Rendering a 2D image in 3D space can be done pretty well using the Spritebatch & BasicEffect and I will cover this technique in Part 5 of this series - Billboards and Particle Effects. However in the case of the shadows and labels for this project, I made use of a conventional textured quad primitive DrawableGameCoomponent. I created a blurred and alpha blended png of the ship outline, and I also took the opportunity to add in a simple wake at the stern of each ship in the same image. The resulting texture rendered over the sea looks like this:

ShadowOverSeaimage

To render all the shadows I created a single DrawableGameComponent called ShadowRenderer that re-draws the same textured panel with the position and orientation of each ship. To ensure that it always draws ‘through’ the sea (even if the sea is undulating slightly) I set:

m_depth_stencil.DepthBufferEnable = false;

so that it will always be drawn. Obviously I also ensure that the shadows and labels are drawn after the sea, but before the ships. This could be achieved by setting the DrawOrder of each element appropriately, but I generally find it easier to leave this value at default, then add the components to the game collection in the order that I need them to render instead.

Ship Name Labels

The name of each ship are known at the beginning of the game and never change so although I build the name textures at runtime, I chose to create and cache a texture array once and then simply render them each draw call. This trades-off memory usage against rendering performance, and may or may not be appropriate for other games.

In Trafalgar, this rendering element is controlled by the NameLabelRenderer DrawableGameComponent. It keeps and array of RenderTarget2D’s and uses a single TexturedPanelPrimitive which is moved and re-textured for each ship – in a very similar way to the shadows. The interesting bit of this process is, therefore, creating the initial texture set. This is done during LoadContent using the following method:

m_name_label_textures = new RenderTarget2D[m_ship_manager.UnitList.Count];
for (int i = 0; i < m_ship_manager.UnitList.Count; i++)
{
    m_name_label_textures[i] = new RenderTarget2D(Game.GraphicsDevice, 128, 128, true, SurfaceFormat.Color, DepthFormat.None);
    GraphicsDevice.SetRenderTarget(m_name_label_textures[i]);
    GraphicsDevice.Clear(Color.Transparent);
    m_spritebatch.Begin();
    m_spritebatch.DrawString(m_font, m_ship_manager.UnitList[i].Name, Vector2.Zero, Color.White);
    m_spritebatch.End();
    GraphicsDevice.SetRenderTarget(null);
}

Essentially, for each ship in the ship manager we create a new rendertarget2D, set it to the graphics device, render the text name using the spritebatch and then resolve it (by setting the device render target to null). Simples.

So this post details probably the simplest of all the effects I used, but I think it also delivers the best bang for buck in terms of visual improvement to the scene, for the least amount of effort – if only they were all like that… Winking smile


Posted Sun, Apr 10 2011 10:12 AM by Edward Powell