2.26.2012

ASCII raytracer


Animated version


Several years ago I started to work on a tile-based ASCII game, similar to roguelikes in appearance, with a certain twist: It had multiple layers of level data and a raytracing algorithm to calculate light and shadows, all fitted into a grid with carefully designed ASCII characters. There were multiple light sources, light maps, line-of-sight style visibility limitation, and transparent map tiles to form a complex and dynamic looking light system. However, I wasn't the best programmer at this time, nor did I take backups, commenting or maintainability serious enough to guarantee that I'll ever be able to expand and run this code later. The only thing that's left is a bunch of non-runnable code fragments and a sequence of screenshots that still persists on my harddrive to remember how well it looked.

I had a lot of ambitious goals: adding a physics engine, light/day cycles, different light types, making a complete game with it and so on... Well, the framerates became more and more horrible the more I added and gave it up after a while. I knew I couldn't make the renderer faster without multithreading, I knew I was too ambitious with video game development and too proud to drop this project. I started to get better at programming in general - with a focus on C/C++. It took me about three years to feel able to tackle this once again. With ITK and IGE as a base, I'm now reimplementing the renderer to get atleast the same visual quality it had before and improve performance where I can. The plan's to make a complete game engine out of it: graphics, physics, permanently modifyable map data, streaming, GUI and scripting. Everything you know from "next gen" engines, just for ASCII graphics. I've already taken a long way to reach this point, so I'm fierce and firm enough to continue were I left the last time.

How the renderer works:
There are three phases each visibly transparent or opaque tile will undergo: a) clip if not visible to camera, b) clip if not visible to player tile and c) clip if illumination is not high enough. All steps will be able to mark the tile as invisible and carry a filter color mixed into the final tile color when drawing to the display. Step c) is composed of adding illumination per light and lightmap affecting this tile. The alpha value of each tile is especially important for stopping light rays from going through walls though they won't be seen in the final image. It'll be subtracted from a light ray's light level (starting by 1.0) and stop the rendering if it becomes 0.0. Lightmaps will be applied like a normal light source but don't need to be traced since their light level is fixed. It is to note that shadows of tiles that weren't present while rendering the lightmap will be updated for each tile when it changes it's position. Lightmaps were limited to a single light angle, therefore only dynamic lights could deliver more rich shadows up-close. There was a mechanic to blend several lightmaps for smooth transition but were never included in any screenshot.

What's planned in the new implementation
  • First of all, applying the complete algorithm from before
  • Add a properly functioning physics and collision engine
  • Use ITK to stream map content
  • Try to create primitive bump mapping using 8bit palettes
  • Entities that are larger than just one tile (and animating them)
  • add eye/brightness adaption for HDR effects
  • No comments: