Crafting The Perfect Ambiance: Lighting Our Hero Lounge
As a Technical Artist specializing in graphics and rendering, I'm most excited about pushing our game's (Mighty Action Heroes) graphical quality and visual fidelity while ensuring it performs well on low-end hardware.
Graphics refer to the technical aspects of the game’s visuals, e.g., resolution, frame rate, level of detail, texture resolution, etc. It focuses on how well the game looks on the target hardware.
Visuals refer to the visual design and the game's art direction. It focuses on how realistic or stylized the art style is and how it contributes to the overall game narrative.
Lighting for both WebGL and Mobile presents a unique set of challenges requiring a holistic approach to problem-solving. In this article, I'll share my process for building polished lighting and ambiance for the Hero Lounge.
Hero Lounge refers to the environment in the main menu where the player can access all game features.
We must first define the outcomes before setting out to accomplish a task. For the Hero Lounge, we have two primary outcomes we want to achieve:
- The lighting has to look great at all camera angles in the scene with UI present.
- The lighting technique has to be performant on embedded systems (mobile devices).
Challenges And Limitations
With the outcomes defined, we can consider the possible challenges and limitations we will face:
- Our main menu exists in a scene where the camera shifts to the respective area of the scene according to the selected menu. Therefore those areas must have great lighting that supports the User Interface (UI).
- There are three areas of the scene where the camera can shift to. Each of them must be able to stand independently when the camera moves to them but not draw attention away when the camera can other parts of the scene.
- Every additional real-time light that affects the same object means that the object has to be rendered again for each light (additional rendering pass). We want to keep the number of passes low to minimize GPU load.
- Objects affected by real-time shadow-casting lights must be kept to a minimum, as they will be rendered several times during the shadow-casting stage of the graphics pipeline.
- If we consider using lightmap baking, the number and size of lightmaps have to be kept small to minimize memory usage and download size.
- The final picture quality has to look good on both big (WebGL) and small (mobile) screens.
With these points in mind, let's examine how I approached lighting.
Designing The Scene’s Lighting
The Concept Team provided a concept for the Hero Lounge, with hints on how the overall scene should be lit. It's military-themed, with colorful splashes of light.
Each area (of the overall scene) has a corresponding menu interaction; when chosen, the camera moves to the corresponding area and uses that as a background for the UI. For example, selecting the Hero Manager brings you to the locker area, where the Hero Manager UI is shown.
Therefore, each area must be able to stand independently when the camera moves to them but not draw attention away when the camera can see other parts of the scene. In other words, areas must visually work cohesively while being isolated in composition.
A fundamental principle of lighting is that there should be a separation between the foreground, midground, and background elements to convey the scene's depth.
Normally, Lighting Artists would rely heavily on value separation to separate the elements of the picture; however, I could not solely rely upon that. There may be parts of the image where values are similar, but objects are far from each other. Therefore, I had to consider the other fundamentals of art (together with value control) to convey the scene’s depth:
I worked with the Art Director and Environment Team to quickly establish the composition's focus for the couch area. The view from the couch is particularly important, as the player would be greeted by it when they play Mighty Action Heroes. It has to look good upon the first viewing.
We moved props around to frame the composition, pulled the camera in, and decreased the field of view to tighten the shot. This made it such that the character is the main focus, and we want you to feel intimate with them.
After figuring out the composition, I improved the separation between elements with the remaining fundamentals.
- Silhouettes: Silhouettes are made to pop with a rim light.
- Color: The couch area is lit primarily with cooler tones, while the background is lit with warmer tones.
- Values: The composition's focus is lit slightly brighter than the surrounding objects; vice-versa, unimportant objects are darkened. Fog is also utilized to lower the contrast of background elements.
The same process is repeated for the remaining areas. Each iteration is checked on both a large and small screen. This ensures we don't over-tune/under-tune the lights.
Optimizing For Embedded Systems
Mobile devices are battery-powered, meaning their processors are designed to be energy efficient rather than performant.
This means we must use solutions that are not computationally expensive for lighting. Fortunately, a solution for that exists — lightmap baking.
Lightmap baking refers to calculating the effect of lights, like global illumination and shadows, and storing this information in a separate texture called a lightmap. This texture is applied to objects in place of lights.
Objects with lightmaps must not move, or the lighting will look wrong. We call these objects static objects. The opposite would be dynamic objects, which can move, and lighting is calculated in every frame.
Since the environment in our Hero Lounge does not move (static), we can bake lighting for them, which frees up the GPU to calculate lights for our dynamic objects (characters).
Lightmaps are textures; they have a file size associated with them. The number and size of lightmaps have to be kept small to minimize memory usage and download size.
We can't have large lightmap textures due to the reason above. We must use the space available efficiently. That means we have to prioritize objects of higher visual importance with a higher resolution (occupy more space) on the lightmap, while other less important objects can have a lower resolution (occupy less space).
For example, the wooden table in front of the camera. If we use the object's original size on the light maps, there isn't enough resolution to represent the lights. So the lighting is "blended" over what little pixels there are, rather than having a defined edge. This is extremely noticeable because it is close to the camera.
Likewise, objects further away from the camera can occupy a smaller area. It's not as important for them to have accurate lighting. This balancing act takes some time to solve; even toward the end of the task, I would spend an hour or so optimizing the space used.
Our dynamic objects (characters) are lit with real-time lights. To minimize the performance impact of real-time shadow and lighting calculation, I set two guidelines:
- Keep the number of lights affecting a character and surrounding objects to a minimum.
- Fake lighting where possible.
We use one real-time light for the character on the couch. However, this one light cannot convey the richness of the scene's lighting (and colors) on the character. The character looks like it's overlaid on top of everything.
I created a shader for characters to give the appearance of additional lights.
Shaders are programs that run on the GPU, usually controlling the visual appearance of objects or surfaces, i.e., they can simulate different materials, or create lighting effects.
In the shader, I added a blue rim light from the top and a desaturated red rim light from the left side of the screen. The added fake lights make our characters feel more grounded in the scene.
Lastly, we must consider how shadows are calculated for the character and surrounding objects.
Objects (static and dynamic) affected by real-time shadow-casting lights are likely to be rendered several times during the shadow-casting stage of the graphics pipeline. Our characters cast real-time shadows, and environment objects receive them.
It is computationally expensive to send all objects affected by the real-time lights to the shadow-casting stage, as some objects won't receive shadows from the character. Fortunately, we can filter objects that will receive shadows from the characters and use them in the shadow-casting stage.
Optimizing lightmaps, minimizing objects affected by real-time lights, and adding fake lighting in shaders allow us to build lighting with good visual fidelity.
Pushing The Visual Fidelity
Until now, we've only been tweaking the scene's lighting. A lot more can be done for the materiality of objects. This is the last step in achieving great visual fidelity.
Since we are building for WebGL and Mobile, we use mobile-friendly shaders: Simple Lit, and Baked Lit. These shaders are more performant as they implement a simple shading model, skipping the expensive calculations in complex shading models.
In Unity, when an object has a lightmap applied, the shaders (Simple Lit & Baked Lit) used no longer calculates highlights. Those highlights are important, as they tell us how rough or smooth an object’s surface is. It tells us the object's material and gives information about the three-dimensional form without looking at the object from another angle.
I created another shader, adding highlights back in using a MatCap texture. MatCaps are commonly used in most 3D rendering tools, where models are shaded by referencing a texture that stores how a sphere should be shaded.
For the MatCap textures used in the shader, only highlights are stored. Artists can create more MatCap textures by painting one in Photoshop or using Blender (shown in the image below).
This shader is applied to environment objects which would benefit the most from a boost in materiality. MatCap calculations are computationally heavy on our target devices. We can’t afford to use the MatCap shader on everything in the scene.
Remember: Objects must look good in a composition, not in isolation only. If adding a MatCap on an object does not significantly improve the visual fidelity, the object should not have a MatCap.
This is a high-level overview of how I approached creating the ambiance and lighting in the Hero Lounge. There’s much more to baking the lightmaps and developing shaders for performance that I didn’t get to share in this article.
It takes a lot of iterations, with help from other teams, to figure out the trade-off, what to keep, and let go of for the highest return on (visual) investment while working within performance constraints. We push it as far as we can. We don’t settle for less than what’s possible.
I enjoyed the process and hope you have learned something about the work that goes into Mighty Action Heroes.
Screenshots Of The Hero Lounge
The following images are screenshots taken of the Hero Lounge without the UI.
Crafting The Perfect Ambiance: Lighting Our Hero Lounge was originally published in Mighty Bear Games on Medium, where people are continuing the conversation by highlighting and responding to this story.