Thursday, August 20, 2015

Volumetric Clouds

Awhile back I wrote a system to create and render a volumetric nebula for universe mode. Later I realized that a modified version of that effect also made a good debris/dust cloud for ship explosions. Then I used a more colorful version for teleporter graphics in ground gameplay mode. This week I decided to try this approach for rendering volumetric puffy clouds in infinite tiled terrain mode, and it works! Well, almost, they don't quite look like "normal" clouds from a distance but they look great up close. Plus, the player can fly through them.

Before I show you some pretty screenshots, I need to get some technical details out of the way. I'll try not to make it too bad for the nontechnical readers.

The volume cloud system I'm using isn't actually something I thought up myself. I saw it explained in a video that I found on YouTube through a Google search a few years ago. Unfortunately, I can't seem to find that video again, so I can't link it here. The video described how to create a nebula out of a number of randomly oriented 2D slices containing partially transparent plasma textures created in Photoshop. Since I needed one unique nebula per galaxy, in a game with an infinite number of galaxies, I had to do something a bit different. Instead of manually creating an infinite number of textures in Photoshop (which I don't own), 3DWorld generates the color and alpha (transparency) values directly on the GPU using random 3D noise textures to create ridged Perlin noise. The location of the nebula in space is the seed used to determine the starting and step direction/distance into the noise texture so that each nebula is unique.

Each particle cloud (nebula, etc.) consists of 13 2D quads (squares) that all intersect at their centers. Each quad can be viewed from either direction, so it's actually more like 26 2D billboards. The quad planes are oriented in uniformly distributed directions by taking all {x,y,z} values in {-1,0,1} and folding out the symmetric ones. This gives the following 13 directions before normalization: (1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1), (0,1,1), (-1,1,0), (-1,0,1), (0,-1,1), (1,1,1), (1,-1,-1), (-1,1,-1), (-1,-1,1).

The corners of the quads are attenuated to an alpha of zero (fully transparent) so that they appear as circles with a smooth falloff from opaque to transparent at their outer edges. In addition, quads that are nearly parallel to the view direction are faded to transparent to avoid ugly artifacts from looking at them edge-on. Then the color and alpha channels are modulated by the noise functions to give an interesting, cloud-like effect. The fragment shader evaluates the noise function at a position corresponding to the coordinate of each pixel on the quad in 3D world space. Therefore, the cloud looks correct and has proper parallax when the viewer/camera moves around and through it. That is, as long as the view doesn't pass directly through the center where the quads all intersect, which is a singularity where every quad is viewed edge-on and rendered invisible, causing the cloud to disappear. I guess that's what it's like to be in the eye of the storm.

Here are some of the fun things I can render using this system.

Nebulae
Yes, it can draw a nebula, what a surprise! This system was initially used for drawing nebulae in universe mode. I've shown this image in a previous blog post, and here it is again.

Closeup view of a procedurally generated nebula in universe mode. This was in a previous blog post.
A nebula is drawn using ridged noise (just Perlin noise with some extra math). Okay, if you're curious, here is the math that converts a Perlin noise value 'v' into ridged noise:
v = 2.0*v - 1.0; // map [0,1] range to [-1,1]
v = 1.0 - abs(v); // ridged noise
v = v*v; // square it

Yes, exciting, isn't it? The nebula shader uses two random noise color channels where the colors themselves are also randomly selected (I think it was pink and orange in the screenshot) + one alpha channel. The noise is mostly high frequency and the bounding shape is spherical.

Explosions
Who creates a space game without ship explosions? What is left after a big ship's explosion, anyway? A cloud of glowing dust and particles! This can be drawn with the same technique. Just use lower frequency noise to make it more irregular, make it even more ridged and wispy, use different colors for the interior vs. exterior of the volume, and you get this:

Volumetric debris and dust cloud from blue/white and red/orange ship explosions in universe mode.

Teleporters
That's enough particle clouds in universe mode. It looks good, but I don't want to overuse the effect. Where else can it be used? How about using it for the new teleporters in 3rd person shooter ("ground") mode? Since a teleporter doesn't exist in the real world I can make it look however I want. I decided to make it very bright so that it stands out, adds color to the dull grays of my office building scene, and looks like something that is definitely not natural. The colors and noise are animated as well so that it's clear to the player this thing isn't just a static decoration. It draws attention - this glowing object does something. Teleporters use 4 high frequency ridged Perlin noise channels in the shader: red, blue, orange, and alpha.

Closeup of a teleporter gameplay entity. The dynamic, animated, colored cloud casts light on the ground.
Here is a short video of the teleporter in action, where you can see the animated colored volume and how it actually moves the player to a different location in the scene. Note that teleporters don't just work with the player: they work with enemies, items, projectiles, particles, and any other type of dynamic game object. It's fun to throw grenades into the teleporter trying to hit someone you can't see on the other side. Just make sure you count to 5 before walking into it yourself. Sorry, I didn't record a video of this (yet).



Clouds
The obvious use for a volumetric particle cloud rendering system it for rendering ... clouds. There are a ton of competing ways to draw clouds in games. Sometimes clouds are meant to be viewed from below, for example when the player can walk or drive on the ground. Sometimes clouds are meant to be viewed from above or inside, for example in a flight simulator or space game. In 3DWorld, there's really no constraint on where the player can go. It's a game engine, not a game, so it could be used for a ground-based first person shooter or a flight simulator. These clouds need to look correct, with a good frame rate, when viewed from any location. Well, except from their exact centers (again).

Clouds are drawn in a base color of white but are modulated to match the lighting conditions so that they're red-orange during sunrise/sunset and dark gray at night. Their brightness decreases toward their center to simulate self-shadowing inside the cloud volume. I haven't yet tried to add light scattering to these clouds. In addition, they become opaque near the center where the noise value has less of an effect. They use regular Perlin noise rather than ridged noise for a more puffy and natural appearance, and have a mixture of high and low frequencies. The noise function offset varies slowly so that clouds change shape over time. Here are some clouds viewed from the ground below in infinite tiled terrain mode.

View of volumetric procedural cloud puffs from below. These clouds slowly change over time.
Tiled terrain mode actually draws three cloud layers (listed in the order in which I added them):
  • 2D procedural noise cloud plane. The terrain and grass shaders ray cast into this layer for soft cloud shadows. I also have slow God-rays that ray march through the cloud density function. Density depends on weather conditions and atmosphere. Slowly animated/scrolling.
  • Static textured upper cloud layer. Provides a more interesting background than pure blue.
  • Puffy volumetric clouds. The new mode that's the subject of this post. Doesn't cast shadows yet - unclear how the terrain and grass shader can ray cast into these. Animated, but more slowly.
The sky looks a bit cluttered with all three cloud layers enabled now. Also, it's odd that the first layer casts shadows, but the second layer (which is denser and more apparent) doesn't cast shadows. Maybe I'll remove the first cloud plane layer later. Or maybe the set of enabled layers should be determined by atmosphere and weather conditions. For example, use a large number of puffy gray volume clouds when it's rainy, but only sparse/high 2D cloud cover when it's sunny.

This wireframe view of clouds from above shows how they are composed of multiple 2D quad billboards in various orientations arranged around a common center point, similar to flower petals.

Clouds drawn in wire frame mode. The structure of the 13 intersecting planes can be seen.
 
Here is a short video of the player flying through the cloudy sky over some islands. Since the clouds are rendered in 3D, they appear as real volumes when flown through. Most games seem to use 2D cloud billboards or skybox background images that are only meant to be viewed from a distance.



So far I've come up with four different uses of this particle cloud rendering technology. I wonder what else I can use it for? Rocket explosions? Plasma balls? Smoke effects? Insect swarms? Actually this might work well for smoke, and could be a good replacement for the existing billboard cloud system I use for smoke in 3DWorld. I guess we'll just have to wait and see.

6 comments:

  1. Couldn't you offset the planes slightly along their normals, so that instead of intersecting in the center, they surround a central void space? That would eliminate the "eye of the storm" problem.

    Yeah, it might be better to use these "cloud" objects for volumetrics.
    How about foliage for deciduous trees? They are kind of round. Then you would have both a "round tree" and a "triangular tree".

    ReplyDelete
    Replies
    1. That's a good suggestion, thanks. I'll try offsetting the planes to see how that looks. I don't find the center intersection points to be too much of a problem, unless the player can fly through the clouds, which normally isn't allowed.

      The problem with using this approach for trees is that it doesn't work well when other objects clip through the planes: other trees, the trunk/branches, terrain, plants, etc. I think you would have to sort everything by depth and draw it all front-to-back so that alpha blending works, and that would get expensive. I avoid this with clouds by ensuring they don't overlap in space.

      I already have round trees, but they don't look very good. I also have palm trees, probably in later posts.

      Delete
    2. I could use alpha testing rather than alpha blending for trees to avoid the depth sorting problem. But then the texture needs to be very high resolution so that the leaves and small branches resolve to smooth edges rather than jagged ones (since there is no anti-aliasing). That would take a lot of memory if I want to have many unique trees.

      Delete
    3. I added a slight offset to the cloud planes so that they don't all intersect at one point. It looks better in wire frame. With normal volume rendering, it's hard to say. Maybe it's a bit better? It's hard to even find that intersection point in a real cloud based on the way the angle and distance blending work to try and hide the edges of the planes.

      Delete
  2. I know Shamus has used a similar system for bushes and grass in a few of his game experiments. Using these "clouds" for that might work.
    Any sparse collection could work really. How about distant cities and space stations? You could render each large collection of objects along each plane, and use them as LOD substitutes when they are a long ways away, or moving by very quickly.
    The same technique could work for voxel object LODs, so you could have tons of voxel objects visible, but only load the near ones as actual voxels, and display the distant ones as baked renders on a "cloud". Not sure it would look great, but it's probably better than down-rezing (which is what I was originally planning for Uplift) or fading out (which is what happens in Space Engineers, and is really annoying).

    ReplyDelete
  3. Billboard clouds are really only needed for nearby objects where the perspective needs to change as the player moves around them. Distant objects can usually be represented by a single planar billboard that rotates to face the camera, or blends between multiple sides/facets as the player moves. Considering how visible object counts for something like trees increase with the square of distance, this seems to be the way to go.

    3DWorld uses many different types of billboards for distant objects. Each unique tree is rendered to a set of textures and drawn as billboards at some distance from the player. I create albedo, normal, and depth maps for each unique generated tree by rendering them to textures in multiple passes. Then I can draw them as single quads each with a custom shader that accesses all of the textures to do lighting. It works so well that the only way I can tell if I'm looking at a 3D tree or a billboard tree is by walking around it to see if the perspective is off. Well, the shadows can be a bit off as well because it uses the position on the billboard for shadow map lookup.

    I haven't tried any of this for voxels. That's an interesting idea. I find that once I've put in the time to generate the voxel data, and the memory to store it, I can just spam the entire thing to the GPU and it draws fine. With view frustum culling on chunks, of course. But if it needs to be an infinite voxel terrain, rendering sections to billboards could be the way to go.

    I tried down-sizing and alpha fading with grass, trees, and plants. Shifting the zval so that grass grows up from the ground definitely works best. I also found that a "dissolve" effect works best for trees, especially when they're packed together into a dense forest. Each pixel is toggled on/off based on a 2D noise texture, where they transition to all off as the tree disappears. This is a good way to hide LOD transitions. Since distant trees have very noisy colors and lighting, that tends to hide the dissolve pattern very well. If the noise is applied in world space then it won't change as the camera moves. There's also no need for expensive and complex depth sorting that would be needed for alpha blending.

    ReplyDelete