Billboard Trees — In Tiny Tokyo, having lots of background trees was really important to make it feel like you’re in a real city surrounded by forest and countryside. Having tons of 3D trees would’ve meant having many hundreds of thousands of verts in the scene just for background foliage. On low-end platforms, switching those trees to be screenshots of the 3D trees on transparent cards with alpha cutout ended up giving a shorter render time. Unsatisfied with the look of static cards, I wrote a shader to billboard these cards so they would always face the camera and give the illusion of being fully 3D. I could only get this effect working on certain platforms due to differences in shader compilers on various platforms. Here’s the breakdown:

Place 3D modelled out version of the trees in a lit scene against a flat color background. Screenshot then delete the background in photoshop.

Use Unity’s terrain tool to place 3D trees as desired around the scene. Use a script to replace the terrain decorations with prefabs. This is important for swapping in tree cards and because terrain decorations use an ugly shader.

Bake lighting with the 3D trees to get realistic shadows despite our goal of using cards to save polys.

Update the tree prefabs to the low poly tree cards and apply billboard shader that forces verts to face the camera.

Shader Features

CGInclude Files — A handful of Object-Oriented C++ classes in college drilled into me a healthy disdain for duplicate code. As I was working with shaders more in the middle third of development, having the same functionality in multiple files and having to remember to update them everywhere was super frustrating, so I started moving stuff to cginc files. These are just files you can add functions and variable declarations to and then include in any shader file. This was great and I highly recommend building this into your workflow, but there are some drawbacks to be aware of (at least as of Unity 2017.3.1p3):

Saving code changes in cginc files doesn’t automatically propagate changes to dependent shader files. That means you’ll have to force those to re-compile yourself which is annoying and prone to mistakes.

This also means changing any cginc file that has a lot of dependents means all those files need to be recompiled, which can be a long process depending on how many dependents and which features you’re using.

The path you specify to include a cginc file can either be relative to the shader’s location or relative to the asset folder, both of which will break if you move either the cginc or dependent shaders.

Multi-Compile Branches — Another way to avoid having duplicate code between shaders is to combine shaders using the multi-compile pragma to enable or disable certain features in your shader. I used this a lot, sometimes with up to 5 multi-compile branches, so I didn’t have to have many separate files for the Player shader. Internally this works by compiling different versions of the shader for each of the options in all your multi-compiles and then swapping to the correct one based on which keywords you specify on a material. This means for every additional multi-compile option, the number of times your shader has to be compiled into a new version is multiplied, which will really slow down your iteration time after a point.

Baked Lightmaps

After the Kickstarter ended successfully, I was emboldened to try and up the quality bar of the game and spent some time improving my environment art chops. In that process I discovered baking lightmaps made Sausage Sports Club look much higher quality and run much better, especially on low-end platforms. Here are the settings I used to balance visual quality, memory usage, and light bake times: