Hello there! My name is Florian Gelzenleuchter and I’m currently studying game design at HTW Berlin, Germany. I just turned 22 years old and am on my way to become a 3D Modeler and Technical Artist. I also enjoy doing 2D concept art in my free time.
Since the day my brother put the original Gameboy in my hands, I have always loved videogames, but I’ve never thought I could have the opportunity of entering the games industry myself. I explored different fields like photography and graphic design but always knew that working on videogames would be a dream come true. Back in 2016, I stumbled across Blender for a school project by accident and thought: “Wow, maybe I can do that too!” and never put 3D aside ever since. With the beginning of my studies in late 2017, I also started digging into Unity and Unreal, asset optimization, shader programming and much more.
Gathering the Reference
Almost everything I’m working on in my free time right now is related to a project, called “The Bells’ Arietta”. It’s a personal project of mine, in which I try to recreate the look of Zelda: Breath of the Wild all by myself in Unity. The Legend of Zelda is one of my favorite franchises and by analyzing the art style and the game thoroughly, I want to discover and learn a lot about game development. Here’s a teaser from last year:
Did you get fooled? Since then, I learned a tremendous amount of new things like writing shaders or working with Substance Painter, just to name a few. During this time I overhauled my workflow a few times, each time becoming more efficient and organized.
While gathering references, I try to look at real life as well as in-game implementations of the effect I’m going for, if there are any. This can be helpful not only as inspiration, but also provide useful information on what’s possible and what might turn out to be a problem later. When getting asked, what creates the unique feeling of being inside a shrine in Breath of the Wild, I would break it down into three key aspects. The cold, polished marble-like stone, light shafts breaking through the air and the crystal clear water. With this in mind, I started creating the water shader.
The Legend of Zelda: Breath of the Wilde
The water in Breath of the Wild keeps the camera of the player always above the surface since Link can’t dive underwater. Having to render water from inside the waterbody generally requires a different setup of shaders and additional underwater effects. Also having the camera restricted to the surface simplifies camera movement.
Restricting myself to the same is an extreme advantage since I can purely focus on the look of the water from above the surface. This makes space for shader tricks like using the depth buffer for underwater fog.
Creating the Caustics
One of the effects, that wasn’t included in the newest Zelda entry, are caustics. These mesmerizing patterns appear when the light gets refracted by a curved surface and projected onto everything behind it. Common examples for this would be a glass of water or the surface of the swimming pool getting hit by sunlight.
Example of caustics in the ocean
The first problem to solve was the animation of the caustics. Like many other energy effects, caustics have a very organic appearance. My go-to solution for noises such as this is prototyping with a simple blend setup in Photoshop. In this example, you can see the base texture to the left. I also added a small offset to each channel of the texture to account for light refraction. I then copied the layer, scaled it up a bit and changed the blend mode to darken. When moving the copy now, the blending magic will appear. In the last panel, I just blended the textures additively onto a sand texture. In the final shader, both layers move individually.
Now I can project the result from the light direction on my object. And by additionally masking by the NdotL and the shadow map of the object, the caustics themselves are pretty much finished.
Now all these steps rely on a per-object basis. Normally there would be a height value that cuts off the effect at the same level as the water surface the object is in. This would probably work in most cases if the assets are modular and small. But as soon as we had a connected terrain with different heights, we couldn’t define a general cutoff anymore. Using the depth buffer would be the answer, but we can’t access the depth of a transparent object before even rendering it. Also having to calculate the caustics on every object and shader doesn’t seem performant.
The approach I use to solve this problem exploits the nature of transparent objects in game engines. Those don’t get rendered to the depth or g-buffer, which allows me to access the normals and the depth of all the objects below the water. The depth, in particular, is used to calculate the world positions, a technique normally used for post-process effects. With this, everything can be done in one shader and the caustics automatically affect everything that is written into the depth buffer.
The water shader, in general, is made out of several parts. It utilizes the good old technique of scrolling two normal maps at different speeds and sizes across each other. That way, I can easily alter the look of the surface in general. The same goes for the caustics texture and another noise texture, which gets used for additional detail like foam. The water also gets faster, when it reaches a specific angle thanks to planar mapping. This allows me to put the shader on anything and the water flows downwards as it should in reality.
Here’s a list of features that make up this shader:
- normal mapped surface detail
- flowing faster at steep angles
- caustics projected from sunlight direction onto everything below the surface
- depth-based refraction (so less distortion in shallow water)
- foam and shadows on the surface
- underwater fog, blur, and chromatic aberration
- displacement areas definable via vertex color
Challenges when working in Unity
Striking a good balance between aesthetics and interaction is key in making believable water shaders. Realistic or not, water should look and feel alive. That’s why even a single colored ocean like the one in Zelda: Windwaker looks believable. Vertex displacement or scrolling two normal maps across each other are also a good starting point. Add in some refraction and you’ve got a basic water shader for further refinement.
The other key part is interaction. Characters leave waves when swimming, foam splashes in the air on collision or the sand gets wet as waves hit the shore. Integrate the water into your world like it fuses everything together instead of appearing as something separate. Of course, this is highly dependent on the needs of the individual game itself.
A good way to check if your water shader is any good is to ask people if they would want to go for a swim in your virtual pixel fluid. If so, then that’s a good sign!
Advice on dealing with shaders
Know what you want to achieve with your shaders and create them with the game’s mechanics in mind. They are a great tool to bring the gameplay and the world of your game together.
Another advice I want to give, especially to fellow shader beginners, is to build up a library of tools to work with. This could be applied to many fields, but regarding shaders, in particular, it’s your way of making something awesome. I’m always trying out new features that cross my mind, even though I might never use them in any project. But spending some time in something that may seem not as useful at the moment might become the next puzzle piece in a bigger project.
And despite relying on new tech, don’t forget about older or simpler techniques. In game development, everything goes and using basic techniques in combination to create complex illusions is incredible fun! Always running after the newest trends and neglecting everything that came before is just a waste of potentially great tools. Use both sides to their full potential and you’ll have a great time!
Despite some factors to look out for, I believe that water is a beautiful subject for shader programming. There are so many great examples out there and I still get surprised by new ideas and art styles all the time. Pretty water never gets old, but it sure makes everybody miss the beach.
I also want to thank 80.lv for reaching out to me. It’s a dream come true getting featured here and I hope a few of you could get something useful out of this article. Thank you for reading and see you around!