Fake ambient occlusion for tile maps

I was wondering how could I improve the looks of a simple tile map to make it look less blocky. I thought about developing an simple ambient occlusion shader, but at the same time I wanted to make it fast enough for mobile development. I wanted to make a method that doesn’t even require the use of OpenGL at all if I don’t want to.

The solution is pre-computed lightmaps which can be just composited over any texture to give it a smoother look even without any additional shading.

Fake ambient occlusion rendered 2D over wood texture

Fake ambient occlusion rendered 2D over wood texture

To get the desired effect, I had to generate a lightmap for every possible tile layout. Around every floor tile, there are 8 adjacent tiles which might contribute to our fake ambient occlusion. That gives us 256 separate tile configurations, but luckily, many of these are identical by applying simple transformations.

  • By adding 90, 180 and 270 degree rotation, it cuts the number right down to 70.
  • By adding mirroring and rotating the image by 0, 90, 180 and 270 degrees, it’s down to 51.
  • By assuming that a wall to the top and to the left completely seals the top-left corner, regardless whether there is a wall tile to the top-left, it’s down to a nicely manageable 33.

Each layout configuration has a layout ID, which is the 8 adjacent tiles each represented by a bit, giving the 8-bit value between 0 and 255. To keep the rotation of layouts in the lightmap generator simple, the first bit represents the top-left corner, the other bits allocated in clockwise direction. Rotation is always clockwise, mirroring is done horizontally by swapping the following bits: 1-3, 4-8, 5-7.

Layout bits allocation

Layout bits allocation

After evaluating which lightmaps need to be generated, the process is quite simple. Create an image with dimensions three times that of the desired texture, fill the whole area with white and solid fill the wall tiles with the desired shade of gray. Then simply apply gaussian blur over the entire image and extract the middle tile as the finished lightmap texture.

Ambient occlusion lightmap generation

Ambient occlusion lightmap generation

At the end of the process, we have all our unique lightmaps ready to use:

Generated lightmaps

Generated lightmaps

Here’s the rendering of the lightmaps in a simple 2D scene:

Fake ambient occlusion rendered over white background

Fake ambient occlusion rendered over white background

For applying it over a texture, you just have to bake the lightmaps into a single texture, and composite them over it, for example by simply multiplying the colours in a pixel shader, the result is the first image in the article.

For more information and the generator code, check out the FakeTileAO project page.

Share Button
Posted in Blog Tagged with: , , ,
2 comments on “Fake ambient occlusion for tile maps
  1. bostich83 says:

    Hi DarthJDG,

    nice post!
    Can you give an example what exactly do you mean by

    “Each layout configuration has a layout ID”

    Looking at your example graphics, i would evalute the bitmask from the center tile, aka a wall, by evaluating it’s neighbours (wall yes or no). But i dont’t get the correct numbers which your lightmap generator is spitting out…

    I think i missed something obvious :S

    • DarthJDG says:

      Hi, sorry for the late reply, I was pretty busy lately. I understand the confusion, this was supposed to be part 1 of 2 articles, but I never got around writing part 2 on how to actually use the generated images.

      The center tile is never a wall, it is the piece of floor you’re finding the right shading image for. You generate the layout ID for it by evaluating its neighbours, which is a bitmask starting in the top-right corner. The bit is 1 for walls, 0 for floors. Let’s say there are two walls to the top and the left, then the layout ID is 2 + 128 = 130.

      The numbers it’s spitting out is sort of a lookup table. To save on resources, only a handful of images are stored, you will need to apply transformations to them based on the lookup table. For the above layout ID 130, it says “same as 14 ROTATE_270”, which means take image #14 and rotate it 270 degrees clockwise to get the correct shading texture.

      Other possible transformations are MIRROR_X, which you need to mirror horizontally, then rotate X degrees. Where it says GENERATED, you can use the image directly as it was created in the output folder. If it says “same as X IDENTICAL”, you can use image number X directly.

      In the usage implementation, you wouldn’t manipulate the images directly, just apply those transformations to the texture coordinates.

      I hope this somewhat clarifies how to make use of these images, let me know if you have any more questions. Good luck!

Leave a Reply