Generating an edge map after loading the tilemap of all static tiles.

Each light instance culls its own version of the edgemap that only exists inside its own bounding box.

The intersections used to create the visibility map is created by rayasting to the corners its own culled edgemap.

Now we use the intersections to build the visiblity shape by sorting them clockwise and creating a custom shape that becomes the actual visiblity map.

Each light has its own sprite (light sprite)


We support max 16 lights at same time, so in the Light Renderer we have 16 render targets for each light to draw its visiblity shape.

We now loop through all visible lights and draw the light visiblity shape onto that render target. (L121)


Loop all lights again, but now draw the above render targets onto a new render target with a specific shader to mask off the visiblity shape result with the light’s sprite and combine them on a single texture. (L136)

Next step is to blur the results with a gaussian blur and the follow image shows the result.

Last step is to draw a black sprite onto a new render target that acts as the ambience then using another custom shader, draw the blurred lights onto the render target and the result is finished.


The first step to optimization was to simply cull the lights that are not visible on camera using their bounding sphere.

Next very big improvement came from using an isDirty flag and only updating world lights if their position was changed. Meaning all lights in world being static are only calculated once.

We can do even better to simply parallelizing the light intersection updates and gain a massive improvement on multi core CPU’s.