2
\$\begingroup\$

Quite a few of the 2D games that I have seen use per-tile lighting, where each tile has a set brightness (continuous scale of 0 to 1, for example).

One way of calculating the brightness of any one tile is to loop with all light sources and add a brightness value to the tile based on distance from the light source. However, with 100^2 tiles and 100 lights, that's a million iterations.

What's a CPU efficient method of calculating the light on each tile with multiple light sources?

Light sources that have objects in the way of tiles should also have a lesser impact but it doesn't matter if this doesn't resemble shadows.

The light sources change every few seconds.

\$\endgroup\$
  • \$\begingroup\$ If you can do something equivalent to deferred shading, then instead of always calculating lighting for every object in the scene you do it for only the texels that reach the screen. Further you can optimize this technique with view frustum culling. What library are you using for rendering? \$\endgroup\$ – Yattabyte Nov 29 '15 at 16:34
  • \$\begingroup\$ @Yattabyte, just WebGL using javascript. I'll read up on deferred shading though. \$\endgroup\$ – Lolums Nov 29 '15 at 16:40
4
\$\begingroup\$

Use a lightmap.

A lightmap is an array in memory which is as large as your map and represents the light level of each tile.

When you create/remove a light source, add/subtract its light intensity from the surrounding lightmap values. As long as no lightsources move, keep the lightmap as it is. No matter how many light sources you have, the overhead per render frame will be constant.

Moving lightsources can be represented by removing the source from the old position and recreating it at the new position.

When a lightsource is added or removed, only update the light values of the tiles within its light range. So when you create a lightsource with a 10x10 tile range on a 100x100 map, you only need to update the 100 tiles which can actually be affected by it.

Note that your lightmap must still store values which exceed the maximum light level. Otherwise you get inconsistent results when multiple lightsources affect a tile to above maximum intensity and then some of them are removed. Clamp the values when you read them, not when you write them.

\$\endgroup\$
  • \$\begingroup\$ Nice method! The fragment shader can handle clamping. Since that runs directly on the GPU, it should have virtually no performance cost, compared to the javascript code. \$\endgroup\$ – Lolums Nov 29 '15 at 17:38
  • 2
    \$\begingroup\$ @Lolums When you are using shaders, you might consider to calculate the lightmap per tile vertex, not per tile, and interpolate the light level linearly. That way your lighting won't look as blocky. \$\endgroup\$ – Philipp Nov 29 '15 at 17:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.