In the Metroid Prime games Samus has a series of tools to aid her in her adventures. A type of tool she can get are visor upgrades. These visors allow views of the world that are not normals visible, Such as a Scanner Visor, Thermal Visor, and X-Ray Visor. In this post we will be covering and breaking down the effects, how they are achieved, and how to implement them into a modern graphics engine. I was inspired by André Cardoso’s base project he made on his awesome Mix And Jam: Metroid HUD tutorial! This tutorial will not require his base project, but still a great watch to get into the mood.
Notice: This tutorial was written with Unity3D in mind, but the shaders we write are very easy to port.
The X-ray is supposed to imitate the images of an actual X-ray scan. There is a couple things to keep in mind when looking at Metroid’s implementation.
Very high contrast
Surfaces accumulate ‘brightness’
Content Darkens as it gets further away.
The Color Pallet, it’s not quite black and white
With these in mind we can start working on our shader. I started off with a new unlit shader, and there are a few things we need to configure specifically for the transparency. First we need to set the render queue to transparent, and then change the blending mode to be
At a high level additive rendering takes the pixel that is there, and adds the surfaces being rendered onto it, where normal rendering replaces if the surface is closer to the camera then what is already rendered. This is an effect desirable for lights and fire effects, that we are going to take advantage of.
Let’s Make a material with a texture, and put a few in our scene.
The overlapping, and accumulation is exactly what we want. Let’s set clear flags to
Solid Color, the Background to be black on the camera, and change the shader to always output a gray.
This effect is starting to look a bit closer to what we want, but the x-ray visor starts to darken objects based off of the depth of the surface. Inside the shader, we need to calculate the
clip space position for each vertex, and thus fragment. Once calculated we will put the screen position into the
varying v2f so our fragment shader can read it.
Unity has a built in method that calculates screen pose for us. Once we have the clip space, we want to use the z component to scale our color. I am using
smoothstep with some hard coded values to calibrate the values.
Keep in mind when you pass something from the vertex to the fragment shader, it tri-linearly interpolates every varying value, for every rendered pixel. This allows a smooth transition of color, and even uvs to stretch over the surface!
To polish this off, we need to introduce the blue into the midtones. There are a few ways to approach this, you could mathematically define the color curve, but that can be difficult to match, and can take a decent amount of time. I choose to do a Lookup Texture, or LUT. A LUT is a means to use a value to drive where the shader is looking into a texture. After a short time in Photoshop, I made a gradient that I will use for the LUT
Note the texture I actually saved out, and am using is 256x2 pixels I scaled it up to be more visible.
I made, and applied to the camera, a simple screen space/image effect shader that looks into the screen texture, and uses the red channel
col.r of it to look into the U axis of our lut. Keep in mind I could use and color channel since the image is grayscale, it will yield the same results.
That is the basics of how I mimicked the X-Ray Visor from Metroid prime. This effect can be extended if you want, from doing unique textures or meshes inside this view to adding more visual flair to the post effect.
The Thermal visor in Metroid is mimicking infrared camera, which translate black body radiation (heat) to visible light usually in the form of a rainbow gradient. There is an attempt to get similar results, but Metroid took some artistic liberties.
- There is a gradient that ramps from a deep violet up to a hot yellow
- The ‘hottest’ sources emit more heat from surfaces facing the camera
- ‘Cooler’ sources to me be very flatly colored
Similar to the x ray visor, I made another Lookup texture (LUT) to map a grayscale image with. This LUT gives up additional control over the ramping, and simplifies how we will be generating the thermal effect.
There is a technique in rendering called a Fresnel (also known as rim lighting). Fresnel is used when trying to simulate surfaces that gather more light on their edges, like fur or velvet. This is also used in games usually to help show silhouettes like in holograms, ghosts, or even stylized characters. We will be using this as our base technique to help drive the thermal view. The formula for fresnel is quite simple, just the
dot product of the surface normal, and your view vector.
Notice the white outlining on the edges of the surface, that is going to be the main driver of our LUT for objects that are ‘hot’. So now we are going to use the
fresnel to drive the the
That’s starting to look pretty good, however we need to add some controls for adjusting the material, and the amount of ‘heat’ objects emit. Let’s add a Vector
Property(uniform), so we can start packing in metadata about the objects.
therm object, and
smoothstep might look a little intimidating, but what this is letting us do is remap the surface’s Fresnel to the texture coord. By Adjusting
_Heat.xwe can adjust the middle ground, where the 50% gray lines up on the texture, and the
_Heat.y let’s adjust the width, of the texture on the surface. While Adjusting
_Heat.x the LUT slides over the range of the Fresnel, and the
_Heat.y pinches it.
Now that we can adjust how the gradient wraps around objects we need to have a way to make things look cooler, or atleast pull away from the Fresnel, and make objects look a bit flatter. Let’s continue to use the
_Heat Vector we are already passing in. Inside the z, and w components (the 3rd and 4th floats in a Vector), we are going to store a flat color (grayscale) in the
_Heat.z, and an interpolation value, or how much of the fresnel we are going to use compared to the z in
When we turn up
_Heat.w it will blend to the
_Heat.z value. So if we set W near one, and Z near zero we will get a nice cold look. Now that we have this in place we can start customizing scenes with nice hot spots that will pull attention, and be able to darken areas of less importance!
These remapping techniques are really nice because they can be used as a foundation for fun and stylized effects. You can do a cell shading effect with smoother falloffs, non traditional colors, or use a really unique effect with 2D gradients, and even time/animation! With this, and future tutorials I want to bring tools to people who may have been intimidated by shaders or graphics programming in the past, and motivate them to make something we may never have seen before.
Thank you for taking a look at my tutorial if you have any questions or suggestions about it feel free to message me on twitter. Thank you André Cardoso’s for the Mix and Jam video this tutorial was based from, and the entire series.