A slow motion video of how the snow melts around the player.
Before I continue, I would like to mention once more that Snowball Earth is Ronimo's cancelled game from 2008, and that the complete prototype can be downloaded here:
Download Snowball Earth prototype (Torrent)
So how did I implement this melting effect? The basic trick is that for every vertex, I store the 'meltness': to what extend the ground at that vertex has already been melted. A value of 0 means snow, 0.5 means water and 1 means grass. The pixel shader then takes this value and uses it to simply choose between three textures, for grass, water and snow.
To store the correct value in each vertex, the code simply updates all vertices for which the value has been changed. This is done every frame. This would not be very efficient if the vertex count were very high, but it works well enough here. Especially since this is only a prototype and performance isn't as much of an issue as it would be in a released game.
A nice property of storing this value at the vertex, is that the value gets interpolated before it gets to the pixel shader. So if two vertices are next to each other, and one has value 1 (grass) and the other 0 (snow), then a pixel in the middle would get value 0.5 (water). This means that if I smoothly increase the value at the snow vertex during the melting, then the edge of the snow in between the two vertices smoothly moves towards the snow vertex, which looks like the snow is melting at the edges.
The water automatically always becomes a thin edge of water in between snow and grass, because vertices usually rather quickly go from 0 (snow) to 1 (grass).
So far the edges between snow and water and between water and grass would still be straight lines, since they are simply based on interpolated vertex values. This looks kind of okay, but it is still too geometrical to be really convincing. I would like to break up the border and add patterns to it, so I have a special greyscale texture that contains puddle-like patters. This texture is used to offset the 'meltness' value: I simply add the texture's value to it. The effect this has, is that wherever the offset-texture contains white, the water will disappear into grass earlier, while wherever the offset-texture contains black, the water will remain longer.
Our artist Ralph jumped on this and created two offset-textures: one for the water and one for the snow. He made the offset-texture for the water so that it contains roundish, puddle-like patterns, while the offset-texture for the snow contains long curves. This works really well in the actual game.
In practice, the grass, water and snow are all a little bit more complex than simply 'a texture'. The snow has noisy specular reflections, the water reflects the sky a bit, etc. So instead of choosing which texture I use, I choose which material to use: snow, water or grass.
I wanted this to run on shader model 2 videocards (pretty ancient these days), so I couldn't use an if/else statement to choose the material, since if/else is not supported in shader model 2. Instead, I used the step(a, b) function, which returns 0 if a is larger and 1 otherwise. With some puzzling, most math that needs if/else-statements can be replaced by step-functions, allowing quite complex things to be done on ancient shader model 2 videocards. This is also how I got the snow/water/grass choice working. Just look at this tiny bit of shader code for how that could be done (note that the final line could be replaced by a lerp(a, b, c) call instead):
float4 grassColour = tex2D(grassTexture, uv); float4 snowColour = tex2D(snowTexture, uv); float choice = step(0.5f, meltness); float4 final = (1 - choice) * grassColour + choice * snowColour;
For a complete look at the shaders described above, you can check them out in the file Data\Assets\Shaders\Snow.cg in the Snowball Earth prototype.
The final element to the snow effect is that the snow has little piles around trees and such, and when it melts, it lowers and the piles disappear. This was done with a simple morph: our artists made two versions of the ground meshes: one low one for the grass, and a slightly higher one for the snow. As the ground melts, the two positions for each vertex are simply interpolated to get the final position. Every snow-pile in the game was made by hand by our art-team, who raised the vertices around trees and next to walls.
Only one of the two meshes for the ground is used to handle physics and collisions. This is the lower grass-mesh. A nice added benefit of that is that all characters walk in the snow and on the grass/sand, giving the snow just that little bit extra.
To add to the atmosphere, there are also two different sets of lightmaps in Snowball Earth, each with different colours. This way I was able to give the snowy world a colder, more blueish lighting than the unfrozen world.
Having two sets of lightmaps also has an added benefit. There are no real-time shadows in Snowball Earth, so normally there would be the problem that objects that appear dynamically (like leafs and smaller plants) cannot cast shadows. Having separate shadow maps for the frozen and melted versions makes it possible to calculate these shadows into the melted world only.
This solution is not completely correct, though. If you look closely, you can sometimes see that if an area near a big plant is already melted while the plant itself is still frozen, it already contains the shadow of that plant, even though the plant itself has not appeared yet. However, this visual error only happens at the transition from frozen to unfrozen and is hardly visible (unless you look for it), so I never really considered that a problem.
That's it for Snowball Earth for the moment! Let me know if there are any further topics about Snowball Earth you would want to read more about! In the coming weeks I'll be getting back to posting about Awesomenauts, Cello Fortress, and hopefully also about some exciting graphics experiments I have been doing!