All lights used are directional spotlights, so their shadows can each be stored in a single shadow map. Both the light sources and the shadow casters can include dynamic objects, which means that shadow maps usually can't be reused across frames. I didn't attempt to detect and optimize for the case where everything was static (streetlights with no moving cars in their light volumes). No, every shadow map is recreated each frame by rendering all objects within their view frustums. Only the texture slots are shared across frames.
In the case of cars, I used a lower detail model where the materials that had little effect on the shadow were disabled. In most cases, I only enabled the car body and underside. These materials represent maybe 20-25% of the total triangles. This means that there were some light leaks through holes in various places where objects/triangles were missing such as wheels, headlights, etc. This made such a huge difference in frame rate (14 FPS => 48 FPS for 64 lights) that I decided it was worth the loss in quality. For most of the car models, the visual difference wasn't really that noticeable.
I'm using 512x512 texel shadow maps for dynamic objects, which is a compromise between performance and quality. For reference, the global sun and moon shadows use multiple 4096x4096 shadow maps - about one per city. Shadow maps use a 9-tap PCF (Percentage Closer Filter) with a Poisson distribution of sample points. This makes the shadow edges softer and hides aliasing.
Now that I have the 3DWorld source code up on GitHub, I can actually point to the source files:
- C++ code for city generation, road networks, and cars can be found here.
- C++ code for building generation and rendering can be found here.
- GLSL Shader code for dynamic light sources with shadow maps can be found here. Note that this is just part of a shader.
|The lamp post casts a shadow on the cars and building behind it.|
|Dynamic lights + dynamic shadows with car headlights. The area under and in front of the cars is dark due to shadowing.|
|The orange car is caught in the police car's headlights and projects a shadow on the building behind it.|
|The traffic light casts a shadow on the blue car to the left.|
|Soft shadows from cars parked under a streetlight.|
|Shadows cast by various parked cars in a parking lot with streetlights.|
|Streetlight shadows produced by the lamp post and the building.|
I also added car shadows for the sun and moon, but only for static parked cars. These were relatively easy and inexpensive to add, once I had the framework in place. If you look closely you can see some of the new car models I'm using. The number of unique models has increased from 6 to10. I also tuned some of the level of detail parameters to improve frame rates.
Here is a screenshot of the current state of parking lots. In addition to shadows, I randomized car parking positions some more.
|Soft sun shadows from parked cars. Moving cars still use the cheap method of a dark textured quad under each car.|
That's it for this post. I still need to figure out how to add better sun and moon shadows for dynamic cars. Currently, sun and moon shadow maps are generated once and cached, and only updated when the sun/moon moves. There are many high resolution shadow maps involved, and objects are drawn to them in full detail. I'm not sure how to do this efficiently for moving cars. I think it may require a new system.
I'm probably done with city lighting and shadows for now. There are still quite a few other tasks that I can work on. For example, I need to add more objects to fill in the empty spaces between buildings. I also need to add windows to the smaller brick and concrete block buildings somehow. I've removed them from the city scenes because they appear too plain and somewhat out of place. Ideally, I want to find a way to combine different window textures/types with the various brick and block exterior building material textures.