Development Diaries, Volume 6
Posted by Alex Jordan on
I got a lot work done last night. My goal was to replace the inefficient, ugly water shader from the last Development Diary with something superior. First, a primer:
This is an overhead shot of Melee Island, the starting island in the original Secret of Monkey Island. The graphics are from the remake, and you can see the pretty pretty water effects surrounding the island.
I captured this to video so I could stare at it and deduce its workings through... magic? Something like that. "Careful analysis" (read: staring) shows that, since this is a 2D game, what we're looking at is a 2D refraction effect used to distort another 2D image, namely, the background. In this case, the background consists of Melee Island and a rendering of what the surrounding land would look like if it didn't have water over it. Beneath the water effect, you can see white sand slowly transition to dark blue and eventually black as the water gets deeper and deeper. That is what is getting distorted by the water effect.
And so that's what I wanted to recreate. A problem, though: I'm not working on a 2D game. I'm working on a 3D game. I don't have any art for what lies beneath the world model I made. It's a model of the world, and nothing else. In fact, where each country and continental border ends, there is complete transparency. I set it up that way to demonstrate whatever pretty water effect I chose in the background. That kind of hurts me in my attempt to use the Secret of Monkey Island water effect, huh? There's nothing for the water effect to refract, it's all just blank!
Okay, then. Getting to where I want would require some finesse.
First thing's first. My game is a 3D game, right? Time to try 3D-only refraction effects! I cranked out a quick 3D model of a flat plane and then quickly added some soft, undulating waves to it. The model didn't have a texture... my game would only use the surface distortions of the model to calculate refraction. Think of it as the immediate aftermath of disturbing a body of water and then freezing the resultant waves in place. That's what I created.
But what was my water effect going to look like? Sure, I had a new model with waves frozen on its surface, but it didn't have any 2D art on it. I needed to use refraction to generate the art, to show what light refracting through water would show beneath the water. And since my world model was transparent where the oceans would be, I'd need to plug some art directly into my effect that could be used to generate something pretty.
Enter: cubemaps! A cubemap is exactly what it sounds like: six images stitched together to form a cube. The images are seamless, so if you were in the middle of the cube, wherever you'd look, you'd see seamless surroundings. In this case, I used a cubemap of a mountain panorama, under a bright blue sky and above some dark blue water. Think of it like hovering midair over a South Pacific atoll.
I told my new water effect to use this cubemap and to refract using the angle of the undulating surface of my new model. And this is what I got:
Watch the video and notice that I dynamically change the refraction index ingame to add more distortion.
First, a problem: why is my distortion showing mountains? Mountains should be off to the side, considering I'm looking straight down (at the world). Looks like I screwed up in how I laid out my models in 3D space. I put them on the near-to-far dimension, not the up-and-down dimension. Whoops. Easy fix.
However, another problem: that reflection is pretty low-quality. It runs efficiently, sure! I solved that little problem. But the cubemap isn't of a high enough resolution to make it look good. Doesn't matter, this effect was just a stepping stone anyway. I didn't actually want to refract data from any old cubemap that I could feed my effect but otherwise wouldn't draw in game. I just wanted to make sure the effect worked.
See that ugly stand-in water I added in the background? The mottled blue "I made this in 30 seconds in Photoshop" image? That's what I want to refract. That's right! Monkey Island used a 2D water surface to reflect 2D background data. I'm going to use a 3D water surface to reflect 2D background data!
I recommend watching this one in High Definition in YouTube proper rather than this embedded format, as the 2D water image I'm using to refract isn't high-contrast enough to show up really well.
And, well, yeah, the 2D image I'm using to feed the refraction effect isn't particuarly good-looking, so the results aren't exactly good-looking either. (And I can't change the refraction index ingame... yet.) But it works. The theory is sound, which means now I get to (a) generate better 2D art to feed the effect, and (b) tweak the effect to optimize it.
In transitioning from the previous Development Diary's water, I decided that I wanted a more efficient shader effect, and a prettier one. I've already made it more efficient. Now to make it prettier.