Big Scenes

For this project, I was given a small engine as starting code. I improved it and added in Frustum culling, level-of-detail assigned based on pixel height, and simpler shadow calculations.

Here are some videos showing my work:

Third person view
Overhead view

Below is some analysis of each feature and the performance boost it gave.

Before any optimizations : 3.9 fps

Frustum Culling : 4.2 fps

Just frustum culling

Just setting up frustum culling gave an unsubstantial boost to performance. All of these performance screenshots are taken from the same spot. Obviously, frustum culling has the potential for huge performance gains if the majority of the scene is not within your sight. In my simulation, almost everything is in front of you, so frustum culling doesn’t provide much from this spot. Do a 180 though, and everything is culled, providing a huge boost. 

A possible issue with frustum culling is shadows. Frustum culling needs to be in sync with shadows or you might cull something that maybe the player can no longer see, but they should still see the shadow. For instance, a tall building behind you. I wasn’t sure how to tackle this problem and thus didn’t.

Level of detail assignment based on pixel height : 9.0 fps

Level of detail only

I assigned my level of detail based on screen pixel height. A common strategy is to do it based off of distance from the camera, but I didn’t want to deal with varying sizes of objects. Instead, I calculated screen coordinates of the max and min y value of a models bounds, found the difference between them, and divided that by the screenHeight. This provides a more general and portable way to determine level of detail as it is relative to window size rather than specific to your game. It’s a quick and valid way to determine if a user can see something well enough and is actually the way that Unity’s default LOD system determines automatic LOD assignment. All in all, this brought us down to around 3.5 million triangles from 220 million while providing most likely the exact same experience for a player.

This gave the biggest straight up boost in performance out of any of my ideas. I used for LOD’s:

  • LOD0 : 500,000 triangles
  • LOD1 : 200,000 triangles, 40% of the basic model
  • LOD2 : 25,000 triangles, 5% of the basic model
  • LOD3 : 1,000 triangles, 0.2% of the basic model

These were all pre-generated in Meshlab and are shown below.

The only model that looks drastically different to me is LOD3. I am incredibly impressed with LOD2, looking as great as it does at only 5% of the triangles. LOD3 does look very different and but it’s role is for things that a player can only see silhouettes of, things far away. Still a step up from the boat npcs in Spiderman.

Shadows calculated based on LOD3 mesh : 6.6 fps

Shadows only

This one feels the most hacky. If anything I should have done the shadows based off of LOD2, as LOD3 doesn’t have the most accurate silhouette compared to the original model. However, it brought a big performance boost so I am sure it is part of a common method in game development. Shadows don’t need the same detail as a model; they are just silhouettes. 

This idea cut down shadow triangles from 220 million to just 441 thousand, a bigger triangle savings than Level-of-detail, however they are less useful triangles most likely. Meaning, their colors probably require less computations than triangles used for say your character in a 3rd person game, as games will assume shadows are blackish while doing lots of light calculations on other triangles.

Kind of surprising, but these ideas all combined are much, much better than just a sum of their parts as shown in the below image : 220 fps

Individually

  • Frustum culling provided a 1.07x boost,
  • Level-of-detail provided a 2.3x boost, and
  • Shadows using the LOD3 mesh for calculations provided a 1.69x boost

For a total of a 4.2x boost, or roughly 16fps instead of 3.9

Collectively, however, they boosted the scene to 220fps, or a 56.4x boost in framerate. Which was very interesting to me as they the different optimizations seem to be much greater than a sum of their parts.

Source Code: Here