In most main-series Metal Gear Solid games, you can obtain a special item called the Stealth Camo by being bad at mashing the circle button, holding up enemies and getting them to shake their booty, or shooting adorable toy frogs. Standard hero stuff. The stealth camo makes Snake or Raiden totally invisible to enemies, and in this tutorial, weāre going to recreate its look in Shader Graph.
Check out this tutorial over on YouTube too!
Setup
To our eyes, Snake appears like a translucent ghost, although the specific appearance changes slightly from game to game. Usually, itās vaguely green, transparent, and it refracts light from occluded objects a little, as you can see from the straight lines that turn wobbly when seen through Snake. At least, thatās how it appears in MGS2, which Iāll primarily use as my reference.
That gives us a couple of clues about how we might want to make our own version of the shader. Evidently, itāll need to be transparent. To do the light refraction, weāll sample the frame buffer - well, not quite, weāll get the screen contents through the camera texture, which is an image of the game screen captured partway through the rendering loop, which can then be accessed later on during rendering.
To use the camera texture, that means step 1 will be heading over to your URP Asset
and ticking the Opaque Texture option near the top in the Inspector. In a default URP project there are three of these assets for different quality settings inside Assets/Settings, so make sure you enable the texture on all three. Now we can sample the camera texture inside our shaders. Hereās the effect weāll get by the end of this tutorial:
Creating the Shader
Letās create a new Unlit shader via Create -> Shader Graph -> URP -> Unlit Shader Graph, and Iāll name it StealthCamo
. You can make it Lit if you want, but Iāll keep it simple.
When we open the graph, weāll be met with the output stack and the Graph Inspector. Over in the Graph Settings tab of the latter, I will change the Surface Type to Transparent. This is important because Unity captures the camera texture between rendering all the opaques and all the transparents, so this graph will not work if we use Opaque rendering here.
Next, Iām gonna add some graph properties. Iāll actually add all three of them up front. The first will be a Color
named Base Color
- this lets us choose which flavor of jelly to turn Snake into. The alpha channel of this color will control the ratio of the final pixel color between the Base Color
RGB values and the camera texture values. Basically, without implementing any distortion, this shader would act like a bog-standard transparency shader with extra steps.
Next up are two Float
properties called Noise Size
and Noise Strength
. If we just wanted this camo shader to look transparent, we would only need a color property. But what Iām going to do is wobble the camera texture around a bit to simulate light refracting through Snake, which Iāll do using noise. These two properties simply control the size of the noise clouds and how strongly they disrupt the original texture - I used default values of 20 for the size and 0.5 for the strength.
Letās now construct the graph - itās relatively small! Iāll work from the outputs backwards. First, make sure the Alpha output is set to a constant 1. Counterintuitively, we donāt want Unity to handle any of the transparency by itself, because weāre manually going to control the appearance of every pixel of the output.
For the Base Color
output, Iām going to use a Lerp
node to pick between a Scene Color
node in the A slot - this is the node that gets the camera texture - and the Base Color
property in the B slot. As I mentioned, the Base Color
alpha channel controls the ratio between these two colors, so use a Split
node to get only the alpha (A) component of Base Color
and connect it to the T slot on the Lerp
node.
Already if we save and observe the shader in the Scene View, by changing the Base Color
alpha we can slide between seeing the color and seeing the wall behind Snake. Now letās add some wobble.
Weāre going to do that by changing the UVs used for the Scene Color
node. Start with a Simple Noise
node and use the Noise Size
property for its Size parameter. This outputs noise clouds with values between 0 and 1. The next step is a small tweak, but basically, I want the noise to move the camera texture pixels in all directions rather than just towards the bottom-left (with a [0, 1] range, thatās what will happen currently), so Iāll remap that [0, 1] range to a [-1, 1] range instead using a Remap
node.
Then, Iāll Multiply
by Noise Strength
to get an offset value. But what will it offset? By default, the Scene Color
node takes a set of screen-space UVs which are identical to the Screen Position
node, so all we need to do is add our offset to Screen Position
, and input it to the Scene Color
node. And thatās the graph complete!
Back in the Scene View, we can bump the Noise Strength
to anything above 0 and see some distortion of the wall behind Snake. Here, the Base Color
alpha is at about 30% so we get a good look at the wall while still retaining the green jelly baby look. Now fake Snake can slip past the enemy without being seen and complete his goal of meeting up with his jelly brethren.
Subscribe to my Patreon for perks including early access, your name in the credits of my videos, and bonus access to several premium shader packs!
Acknowledgements
Special thanks to my Patreon backers
for Mar 2024!
Leonard Mizu Verisutha Jack Dixon Morrie Mr.FoxQC Pascal pixel_Wing Alexis Lessard claudio croci Jun Lukas Schneider Muhammad Azman Olly J Paul Froggatt Will Poillion Zachary Alstadt ęŗ å