Luma Arcade's recently released Bladeslinger for iOS (and soon to be on Android) pushes the boundaries of what can be achieved on mobile hardware - soft shadows, depth of field, real-time reflections and stunningly detailed environments.
What started out nearly 2 years ago as a “Sci-Fi-meets-western Infinity Blade clone” evolved dramatically over time into a large world, free roaming, fully engaging and interactive action title with combat based around such titles as Devil May Cry and God of War.
Let’s talk with Luke Lamothe - Technical Director at Luma Arcade about the gritty details behind the Bladeslinger visuals.
Main protagonist William as well as enemies and non-player characters in Bladeslinger are per-pixel lit, have normal maps and exhibit specular effect. First optimization Luma Arcade employed was texture lookup for specular power calculation.
Me: Did you do any optimizations for characters in the distance?
Luke: Yes, we augmented per-pixel character shader with much cheaper version - no bump and specular in this shader is calculated per-vertex. We apply this shader to the lower LOD mesh for all enemies. So when they are far from the camera, they have significantly reduced fillrate cost.
Me: Which format did you pick for normal maps on characters?
Luke: We found that there was negligible visual quality loss in using default compression on normal maps as opposed to 16 or 32 bit ones, and the bandwidth, let alone memory savings far outweighed any hard-to-perceive normal artifacts.
Light Probes were a great help for getting lighting right on Bladeslinger characters.
Luke: We were able to set up and bake light probes every few meters in our scenes and modify our character shaders to take this lighting information into account. This allows our characters to move in and out of both shadow and light in a very realistic manner; something that is of great importance for Bladeslinger with its high use of long shadows on dusty roads and the bright glows of evil glyphs that permeate the streets of Hammer's Peak.
Shadows on Characters
Happy with lighting and shading on characters Luma Arcade wanted to take it a step further and do what nobody had done before in a high end mobile game; that is, have actual shadows cast onto characters.
Luke: At the time, Unity didn't support full on shadow mapping out of the box (be sure to check out Unity 4 for this and more awesome features!), and it is just as well because we probably didn't have the performance wiggle room to add shadow maps. So like all good game developers we decided to get sneaky and do some Static Shadow Mapping of our own. To achieve this, we set up offline bakes of the shadows in our scenes to relatively large textures - basically a lower resolution lightmap of the scene as seen from the light point of view. We then modified our character shader to project this additional texture using matrix constructed from Sun Light. So while this doesn't allow for full dynamic shadows to be cast or self shadowing, it is a relatively inexpensive technique that allows for a quite a major improvement to the visual fidelity of the characters in the scene.
Luke: Between 512x512 and 2048x2048 depending on the scene.
Me: Should be straightforward to apply the same to all dynamic objects in the game, right?
Luke: Yes, it was easy for us to expand the technique to be used on pretty much all of our animating world objects like banners, water wheels, the train that William arrives on, etc.
Me: But that's not all of it...
Luke: Once our artists saw that we had this texture that was used to shade our characters, they decided to get fancy and paint in the odd additional lighting and shading details by hand! This allowed our artists some finer control for areas of the world where maybe the light probes just didn't give them 100% of the result that they would have liked.
Shadows from Characters
Next, grounding characters with shadows and thus tying them into environment was a must-have feature. Again, Luma Arcade wanted to push the envelope and do something that wasn’t being done widely in high quality mobile titles.
Luke: There are many, many ways to go about rendering projected shadows, but we believe that what we ended up with is quite unique in its efficiency and quality, though it isn’t without its own limitations. We start with an optimized mesh for each character that has a custom shader on it. Shader renders that mesh to a single channel (either R, G, B, or A) of a high resolution shadow texture. As you can see, this limits us to having only 4 projected shadows at once, meaning William + up to 3 different enemies. There are ways to expand this system and allow for more shadows, but 4 was actually ideal for what we needed to do in Bladeslinger. Once we have chosen and rendered our characters to this shadow texture in a pre-render pass, this shadow texture and 4 matrices required to project shadow map coordinates for each individual channel are set in global shader values. While rendering the world, certain materials in the scene (generally ground meshes, but sometimes other ones too) are set to use special versions of their shaders which can sample projected shadows.
Me: How large was your shadow texture?
Luke: Usually 512x512, but can vary per scene.
Me: Since all 4 channels where used independently - effectively it is 4 shadow textures each 512x512 resolution.
Me: What was the main benefit of storing shadows in a separate channels instead of in separate textures? Did it help to save memory or to reduce cost of render-target switches or maybe something else?
Luke: The main reason for the single texture with using 4 channels approach was to save render-target switches and to keep the same resolution of our shadows with 1/4 the memory used. It was also a great way of doing a blur to "soften" the shadow edges in a single pass as opposed to having to do 4 blurs - a separate blur for every one of those separate shadow textures.
Me: How do you manage what geometry gets projected shadows and how many of them?
Luke: We make use of Unity’s Shader Keywords to define multiple permutations of these “character shadow shaders” that are toggled on the fly. This way, if we only need to render William’s shadow, shaders are set that only sample that 1 channel, instead of always sampling from all 4 channels. In the end, this technique allows us to have very high resolution character shadows that are projected and blended together seamlessly on any arbitrary geometry that we choose. It gives us the ability to be optimal and choose which surfaces receive shadows based on the light source in a scene, but only setting certain geometry to have the complex shaders necessary to sample and render the shadows.
That’s it for 1st part of this blog. In next part we will talk with Luke about techniques used to make the town of Hammer’s Peak one of the most visually stunning worlds created for a mobile device.
Stay tuned for: