Lighting and Line of Sight Testing

Hey Folks! Hope everyone had a good weekend. Pretty smooth sailing here, though the air is getting a bit chilly.

Today was a bit shorter than usual, due to a parent teacher conference this morning. So once I got back to the office, I decided to tackle something a bit different: lighting/line-of-sight rendering.

As some of you may recall, I've been through a few iterations of the lighting and line of sight code. And so far, none have really been up to the task of rendering thousands of models with dozens of lights. Unity's default lighting starts to degrade after a few shadow-casting lights, and even non-shadow lights start to eat into performance quickly.

I also tried adding some raycasting line of sight code to hide tiles not visible to the player, thinking that might allow me to disable rendering on certain far away lights/meshes. This had a slight benefit under most circumstances, but really nailed the framerate whenever it required a recalculation involving a large space (like when opening the door to a big station or reactor room).

So for the time being, I put all that on hold and turned shadow-casting off, just using default Unity lights.

Today, I decided to try a different approach. This is a technique used in a few other places to simulate line of sight and shadow casting in a 2D space. The basic idea is to start at some point (a light or AI's eyes), and sweep around in a circle, and figure out which meshes block sight. The corners of these meshes are stored in a list, and when the sweep is done, the game generates a mesh that covers only the visible areas of the scene. (The link above has some interactive demos to better illustrate.)

Today's screenshot shows one possible output from this technique: blue areas are visible from the yellow dot's position, and black areas are not. Dark red boxes are the meshes that cast the black "shadows." And depending on how you use it, this mesh can do a variety of things.

One use is for line of sight calculations. For a given object, I could detect whether any of it is inside the blue area, and if so, we know it can be seen from the yellow dot's position. E.g. for use in detecting another AI.

Another use is to project where lights can reach vs. not. The blue area could be rendered out to a texture, and that texture could be sampled or combined with the scene render to selectively dim or light areas. E.g. wherever it is blue, calculate lighting on an object, and render black everywhere else.

The real test, however, is performance. So far, in these trivial test scenes, it's fine. And that's no surprise. But what happens in more complex scenes, with multiple lights?

For that, we'll have to see. But there could be some useful tricks in employing this technique. For example, I might be able to check player line of sight first, then decide which lights I can totally ignore in the scene based on range and whether their areas intersect the visible (blue) places. Perhaps I could even use lower resolution render textures to get "good enough" light and shadow regions, instead of a full res lighting overlay?

I might even be able to skip Unity's lighting entirely, and use some sort of texture-based light zones combined with this shadow mesh, so we apply lighting textures/colors to regions that are blue, and don't bother with the black.

And if it works, maybe we can start seeing better performance in the game with large/complex ships!

Tags: Ostranauts