Monday, August 27, 2018

Lucky Bioms: Lava

The lava biom is another procedural-ish background that goes on forever.  The main components are the tessellated displaced ground plane that makes up the rocky lava surface, and a second duplicate plane that uses a geometry shader for the lava bubbles.  On top of that there are some smoke particles that spawn in a large circle around the camera and some swirling ember particles.



The mesh is made up of triangulated quads that are 10x10 units square.

Terrain Mesh

These meshes and particles follow the camera on the X and Z snapping to every 10 units.  This prevents any vertex swimming that can occur when a displacement map moves along tessellated geometry.

Following Camera

The surface of the lava I started with a pretty simple terrain made in world machine.

World Machine Terrain

Simple Terrain Layout

This outputs the normal, height and sediment channels that I combines into a single texture and made tile using Materialize.  This texture was going to be viewed at a very low resolution so I added a bit of blur to the whole thing, just enough so that there's no single pixel deails.  The normals are also used for the flow direction of the lava streams and the sediment was used as a mask for the streams.

Terrain

The other textures used are a tiling rock that was made by taking some of the existing rock assets and arranging them in a tiling pattern and then rendering out the normals.  I generated an edge map, height map, and ambient occlusion map in Materialize and combined them all into a single texture.  The edge and ao are combined in the blue channel and used as an overlay for the rock color in the shader.

The tessellation is edge length based with an adjustable capping value.  This means that the mesh topology will change as the camera gets nearer and further from it, this can cause vertex swimming as the tessellating vertexes slide over the displacement map.  An easy way to hide this is to use the mip maps of the displacement textures.  This shader uses mip0 at 50 meters and lerps to mip8 at 400 meters.  There are better, mathier, ways to determine the best mip level to use but this was quick and easy and, along with some extra fog on the ground, hid most of the problems.

Rocks

The flowing lava and lava pool texture have normals in their red and green channels and value overlay in the blue, same as the rock; but have a glow mask in the alpha channel.  These 2 textures don't contribute to the displacement.  A moving displacement map would cause vertex swimming and not sampling these textures makes the vertex shader a bit cheaper.

Flowing Lava

Lava Pool

The height from the terrain is added to the height from the rocks masked by the inverse of the sediment map.  The flowing lava is masked by the sediment map, the flow direction is generated from the blurry terrain normal map, and it's intensity is scaled by the sediment map.  This makes the flow faster in the middle of a stream and slower at the edges.


Lava Flow

The lava pool is masked by a height cutoff and can be adjusted to make larger or smaller pools.


Lava Pool Height Adjust

The lava bubbles are made using a geometry shader to turn the triangles into particles.  I rendered out some flip book animations of fluid sims to create the splashes.  The alpha is changed to a distance field so the splashes will have a smoother cutoff when they get alpha clipped.


Bubble Particle 25%

Bubble Particle 100%

The displacement code needs to be copied in the bubble shader so that the bubbles will know what height they should be.  The also get masked out based on if there is lava where they spawned.  And finally, since they have access to the flow map, they get a little nudge in that direction so it looks like they are following the lava streams.


Lava Bubble Flow

An interesting, and very much undocumented at the time of this implementation, trick you can do with opaque shaders is adjust the depth that they draw.  This can help a lot when you have opaque particles that are clipping through opaque geometry.  By adjusting the depth of the particle ( screenDepth + (1.0 - alpha) * adjustAmount ) you can ensure that there will never be a hard line where the flat particle geo intersects with the world.  In Super Lucky's Tale custom surface shaders can adjust their depth by defining _DEPTH_ADJUST and passing the adjustment through the DepthAdjust parameter that is part of the custom lighting surface struct.
 
// depth adjust fragment struct
struct fragOut
{
 fixed4 color : SV_Target;
 float depth : SV_DEPTH;
};

// fragment shader
#ifdef _DEPTH_ADJUST
fragOut frag_surf (v2f_surf IN) {
#else
fixed4 frag_surf (v2f_surf IN) : SV_Target {
#endif

 #ifdef _DEPTH_ADJUST
  fragOut output;
  output.color = c; // color from the surface function
  output.depth = ( ( 1.0 / ( IN.screenPos.z + o.DepthAdjust ) ) - _ZBufferParams.w ) / _ZBufferParams.z;
  return output;
 #else
  return c;
 #endif
}
The smoke particles turned out to be far more work than they should have been.  At the time Super Lucky's Tale was going to be a vr game, and one really annoying thing that happens with billboard particles in vr games is that when you tilt your head to the side, the particles tilt with you.  I tried using vertical aligned particles but those were horizontally aligned with the camera view direction and would whip around and cut through the world unnaturally when you turned your head.  So I wrote this big awful custom particle shader just for this smoke that was vertically aligned and would point at the camera position.  These features are now standard in the particle editor so hopefully no one will ever have to deal with that.


Smoke Particle
Smoke Particle Distortion


As for the smoke pixel shader, it uses some fake lighting from the normal map and a distortion texture that gives is a swirly dissolve like the smoke from Breath of the Wild.  Drop some embers on there and it's done!

Flying Around


That's about it!  Phoenix FD was used for the fluid sims, World Machine for the terrain, and lots of little things were done with Materialize, which you can download for free here.

1 comment: