Recreating the Water Effect from Super Mario Sunshine for Nintendo GameCube
Note: In the original article there is a demo on WebGL2, which in translation is replaced by video and GIF.
One of my hobbies is writing model viewers and graphical tools for games. This is a good combination of topics that interest me - graphics, rendering, reverse engineering of complex systems and nostalgia for old video games.
I recently expanded the capabilities of my WebGL-based game model viewing application.by adding support for some games with the Nintendo GameCube, including The Legend of Zelda: The Wind Waker and Super Mario Sunshine. In the GameCube, if you do not know, an advanced, almost programmable video processor is installed, but with fixed functionality. Developers could not write shaders, and instead programmed sets of texture combinators in a way similar to those used in glTexEnv pipelines , but maximized. For those who are used to modern programmable video processors, it may seem insane to think that using this technique you can implement complex effects. Nevertheless, in 2002 Super Mario Sunshine was released with excellent looking water at that time. Here's how it looks in action:
This effect is loaded into the browser directly from the game source files for Delfino Plaza and placed on a plane. Let's take a closer look at how it is implemented.
Texturing the plane
Believe it or not, but this effect actually begins with this:
This effect can be considered as a rather complex variation of a very ancient technique: “texture scrolling”. It is a little more complicated than I showed here, but the basics remain the same. Our plane begins its life as this scrolling wave texture that gives us an interesting noise to work with. Then it is combined with a second layer of the same texture, but this time scrolling in only one dimension.
This gives us an interesting moire pattern, which is the basis for the fact that the water seems to bubble and move naturally. You may even notice a “ghostly” overlay when two textures meet. This artifact is noticeable on the source material, but is more like a deliberately added ray of sun or light passing through water. Hiding artifacts like this with a well-thought-out material design is an important part of the game graphics technique.
Obviously, the texture is not black. The colors are not black and white, they mix with the background, which gives us a more transparent look.
And something starts to work out for us. At this stage, the second texture layer has already been added twice, like the first one, which makes it especially bright, as if with the bloom effect. This feature will be useful later when we detect glare on the water.
If we return to the source material, then it looks much more “dynamic”. If you move the camera, zoom in and out, it seems that the texture is changing. Nearby, it is clearly visible, and at a distance it gradually disappears. In a traditional conveyor with fixed functionality, effects of this type are impossible. The material can not find out the distance to the camera! However, to realize this effect, Nintendo performs a tricky trick in a more traditional way. Let's talk about what I call a “mip trick”.
Building a square mip from a round hole
Mip texturing is a traditional technique for optimizing graphics. When video processors impose textures, they strive to ensure that the finished image is as smooth as possible, and at the same time as fast as possible. The texture sampled here is actually only 64 × 64 pixels (yes, that's true!), And the browser windows are usually much larger. If you bring the camera closer, especially in the last demo, you can see the pixels and how they mix with each other, appear and dissolve. But we must not forget that video processors must perform these calculations for each pixel in the finishedImages. If you look from above, then in this case the texture increases, but if you look at an angle, when the plane becomes more compressed at a distance, the texture on the screen becomes smaller than 64 × 64 pixels.
When this happens, they say that the texture is “minimized” and the video processor needs to read a lot more texture pixels to make the finished image smooth. This is costly in terms of computing - video processor tends to read as much as possible lesspixels. For this reason, “mip textures” were invented - pre-computed smaller versions of each image. When minimizing texture, the video processor can use these versions instead of the full texture. So, we have versions of the texture 32 × 32 pixels and 16 × 16 pixels in size, and the video processor can choose the one that it needs, or even mix the two versions to get the best picture. Mip textures are an excellent example of a trade-off between time and space, as well as an example of content optimizations during the build process.
However, as I said, this happens when the texture is minimized. This happens when it gets smaller on the screen, and this usually happens when the texture is farther. See what I'm getting at? This is how you can get the distance from the camera!
What if, instead of using smaller versions of the same texture, we use different textures? Nintendo also came to the same decision. This is what I call the “mip trick”. The wave texture that I showed above is not the whole story. In practice, there is a complete wave texture with all levels of mip textures shown.
At the largest level of mip texture (when the texture is closest to the camera) we have no pixels. In essence, this removes the effect of water in a small radius around the chamber, which makes the water clean. Firstly, this avoids the feeling of monotony of the water material, and secondly, it helps in the gameplay by showing the player underwater objects located next to him. Smart decision! The second level of mip-texture is exactly the texture that was used in the demo until the current moment, and it has “medium strength”.
The third level of the mip texture is the brightest, it corresponds to this “strip” of bright glow in the middle. I think this strip is a smart way to simulate reflections of a medium. At this distance from the camera, you can imagine that it is a reflection of the texture of the sky, tilted to the water at an angle of 20 degrees, for example, clouds. At the Sirena Beach level, this strip has a yellow tint, giving the level a beautiful yellow glow that matches the evening sunset.
Let's try loading all these mip textures into our demo.
We are already much closer to the desired effect!
A brief digression: since the algorithm that selects the mip texture used for the texture is hardcoded in the video processor, this means that it is not necessarily portable. GameCube renders in 640 × 548 resolution, and mip textures are created for this size. The developers of the Dolphin emulator also noticed this - since Dolphin can render at higher resolutions than that supported by the GameCube console, this trick can break if you do not take it into consideration. Fortunately, modern graphics APIs allow you to apply offset when choosing mip textures. Having received the screen resolution and knowledge of the original resolution of 640 × 548 of the GameCube console, we can calculate this offset and use it when sampling.
Having solved this problem, we can make the final touch. I repeat - believe it or not, but there is only one step left to turn the last demo into a finished product. A simple function (called an alpha test) checks how "bright" the final pixel is, and if it is within certain limits, the pixel is completely discarded. In our case, all pixels in the range from 0.13 to 0.92 are completely discarded.
This gives the outer strips of the effect a unique look of “glitter of cling film”. In the middle, water mainly consists of brighter pixels, that is, a high threshold allows only the brightest pixels to be visible. It gives us such an empty streak and these amazing light!
In the modern world of programmable shaders, PBR pipelines and huge graphics budgets, these tricks are more and more reminiscent of forgotten knowledge. In my biased view, GameCube era games have the most beautiful graphics for the time. Even though I mentioned only the GameCube, the Wii essentially uses the same equipment, so the same tricks can be found in games in the Mario Galaxy series, Super Smash Bros. Brawl and even in The Legend of Zelda: Skyward Sword. It's very interesting that with the video processor technology invented in 2001, Nintendo went all the way until 2012, when Wii U was released.
Good artistic direction, huge volumes of creative design and detailed knowledge of “iron” made it possible to create stunning effects even in the face of such severe restrictions. For fun, try exploring the effects of glass panels at the Delfino Hotel level or the enhancements used in the Super Mario Galaxy .
The code used in each of these demos is open source and posted on GitHub . Obviously, all the work on creating the original graphics was done by incredibly talented artists from Nintendo. In addition, I convey special thanks to all my friends who work on the Dolphin emulator team .