Wednesday, February 3, 2016

Rain and Snow Progress

The past few weeks of 3DWorld development have mostly been spent improving rain and snow effects. I have added several new features, including dynamic wetness/drying effects with puddles, realtime snow accumulation and coverage, ice, and snow footprints. I'll briefly cover each of these below.


I continued implementing features from this blog post, and the next item up was puddles. I wanted to have puddles generated procedurally rather than having to place/paint them all by hand. 3DWorld is all about procedural generation and automating as much of the level design as possible. It's too much work to add every puddle by hand to every horizontal surface of the scene, for all of my test scenes. I don't own any 3D modeling tools and I only use free image editing tools such as Gimp.

Normally, puddles are formed in low areas of the ground, but most of the man-made (non-natural) surfaces in 3DWorld such as concrete floors are modeled as flat planes. The normal maps are too regular and finely detailed to use for puddle depth, so some other tiny invisible adjustment to surface height was required. It seemed like this was another place where I could apply Perlin noise, and as expected, it worked pretty well.

The wet/dry/rain cycle works like this:
  1. The ground is dry
  2. Rain starts, making mostly horizontal surfaces that are open to the sky uniformly wet over time
  3. Rain stops, and the ground dries non-uniformly, leaving puddles
  4. After some time has passed, the ground becomes dry again (same as 1.)
There are three states of rain surfaces: wet, dry, and full of puddles. These are implemented with shader flags that control how the surface is rendered, affecting diffuse albedo, specular intensity, specular gloss, and reflectivity (Fresnel term). Here is a screenshot of some procedural puddles from state 3 generated on the ground after the rain has stopped.

Procedural puddles on the ground as the concrete is drying out after a heavy rainstorm. Wet surfaces are reflective.

And here is a YouTube video showing the transition from dry ground, to wet reflective ground, to puddles, and back to dry ground. Oh, and yes, I finally added procedurally generated palm trees.

Realtime Snow Accumulation

My previous post was on volumetric/3D snow accumulation. That system produced good results, but required dropping a very large number (1 billion) of snowflakes from the sky and recording where each of them landed. This works well for a static scene where a one-time preprocessing step is acceptable. But I wanted another solution for faster snow accumulation that could be used with 3DWorld's realtime, dynamic weather system.

I decided to add support for a thin dusting of snow that "paints" upward oriented surfaces a white color. This isn't true volumetric snow with depth, but it still produces a nice snowy scene effect. The most difficult problem was determining which parts of each surface receive snow. Outdoor areas only please - we don't want snow accumulating in the lobby, offices, or basement!

My first attempt was to cast rays through scene vertices and tag a surface as "outdoors" if any rays reach the sky, and "indoors" otherwise. This worked somewhat, and was good enough to use for the rain wet mask, but wasn't quite right for snow. It left areas under objects such as benches snowy because there was a single large polygon covering a large surface area. It looked correct to have wet areas under cover during rain because rainwater will flow across flat surfaces over time, but snow doesn't do this.

The next thing I tried was closer to how shadow mapping works. From what I understand, this is how other games mask snowy and wet areas. The scene is drawn in a "shadow" pass where the objects are projected down, or in the direction of the falling snow if there is wind. This is normally done on the GPU, but that would have produced aliasing and other artifacts. Since shadow mapping tends to sample a pixel once, this means that a small/thin object such as a flagpole or lamppost that happened to fall in a bad spot near the pixel center would leave a hole in the snow mask. Have you ever seen a flagpole that blocks the snow from accumulating around it's base? It looks very odd and unnatural.

Instead of rendering the scene to a snow shadow map, I compute the coverage map on the CPU. A group of rays are traced through a pixel in a 3x3 grid pattern, and all ray intersections with the scene are determined. The furthest/deepest distance is used as the snow depth for that grid position (pixel). This way, a thin object such as a flagpole may block some of the rays, but won't block all 9 of them. It took a bit of further experimenting, but in the end I was able to remove all of the incorrect holes in the snow mask. I also tried adding random noise to the samples, which helped remove some of the artifacts from the snow mask, but also added noise to the smooth edges of snow around building walls and overhangs. So in the end I used a simple fixed grid.

The snow coverage mask is sent to the GPU and used in the fragment shader for per-pixel snow masking. The (x,y) position of the surface to be drawn is used to look up the depth (z) value in the snow mask, which tells the GPU how deep the snow penetrates the scene in z at that position. If the surface has a z-value below this point, it's underneath some other surface that blocks the snow. If its z-value is less than or equal to the snow map depth, it's covered with snow. I addition, there is a soft transition from no snow to full snow based on the difference in depth/z between the snow mask and the surface. Finally, I used the slope of the surface (technically the z-component of the normal vector) to blend snow density from 1.0 for horizontal surfaces to 0.0 for nearly vertical surfaces.

The advantage of this approach is that I was able to produce a 256x256 pixel 2D snow coverage mask using only 256*256*3*3 = ~600K samples. Compare this to 1 billion samples required for smooth snow in the 3D volumetric case. Using spatial coherency of the rays to trace the 3x3 rays as a group, the 3DWorld ray tracing system can achieve around 35M rays per second on 4 CPU cores, which translates into a mere 17ms of runtime. That's quite a bit faster than the ~20 min. required for volumetric snow accumulation! This algorithm allows the snow coverage mask to be recomputed in realtime as the scene is modified and the weather changes. As a bonus, no precomputed snow coverage files need to be stored on disk and loaded.

Below is a screenshot showing the results of realtime dynamic show accumulation. Note that only areas of the scene that have an unobstructed view of the sky accumulate snow (except for the grass, which doesn't use the snow mask).

Realtime procedural snow accumulation in the office building scene.

Here is a close-up view of the snow on objects of various shapes. Note that there is no snow under the trees and benches.

Close-up view of snow accumulating on various objects based on a generated sky coverage mask.

I also recorded a video showing how snow accumulates during a heavy snowfall.


I decided to implement the mixing of rain and snow to produce ice. The ice effect is somewhat different depending on which comes first. Rain on top of snow melts the snow, but snow on top of rain forms a layer of reflective ice on horizontal surfaces. Angled surfaces accumulate less water and eventually turn to pure snow. It looks like this:

Rain mixed with snow produces a layer of reflective ice on horizontal surfaces. Angled surfaces are more snowy.

Note that the water in the fountain has been frozen and covered with a layer of snow.

Snow Footprints

The day after I published the previous blog post, someone asked me if players left footprints in the snow. What a great idea! It wasn't even that hard to add, though I went back and tweaked the parameters several times. I had to define the player stride, foot width, foot spacing, foot height/crush depth, etc. to get realistically shaped and placed footprints. The numbers don't quite work out for my spherical "smiley face" character model, but it's close enough. Both the user/player and AI controlled players leave footprints in deep snow. Here is a screenshot showing footprints in the snow after I walked around for a while. The gaps in the footsteps where the places where I jumped (which doesn't leave footprints). If you look closely, you can see that the footprints are actually changes applied to the snow mesh rather than decals because that footprint on the very edge on the top of the wall leaves a small gap in the shadow on the snow below.

Footprints created using mesh deformation when the player and AI characters walk in the snow.

I made a Fraps video of a walk in the snow, complete with footstep sounds. The footsteps are a bit too fast, so they sound more like I'm running. Note that snow and concrete have different footstep sounds. I'll probably add a larger variety of footsteps and other sounds in the future.

That's all for now. I wonder what other interesting weather effects I can add?

No comments:

Post a Comment