Instanced Billboard Clouds in Three.js

This post covers rendering simple yet good-looking clouds in Three.js. This is based on Mr doob's cloud demo with some modifications.

This post covers rendering simple yet good-looking clouds in Three.js. This is based on Mr doob’s cloud demo with some modifications.

Overview

To achieve the looks of the clouds in the demo we’re going to distribute several planes facing the camera with cloud texture on top in an area. To make it look more interesting, we can rotate each plane randomly. We can also use different images as well.

Geometry

There are a few approaches to creating cloud planes:

  1. Creating several plane meshes and rendering them separately
  2. Creating several plane meshes and merging them into one geometry
  3. Use instancing

The first approach is not very performant as we need to make a draw call per cloud plane.

The second approach is what is employed in the original demo. It has the advantage of having to issue one draw call for all the clouds:

It starts by creating an empty geometry instance and a plane mesh. In the loop, it positions the clouds randomly and chooses a random scale for each instance. The resulting transformed geometry is then appended to the geometry instance. Finally, it creates a new mesh based on the filled geometry buffer.

One problem with this approach is that you can not animate the clouds individually. For my project, I chose to go for the 3rd approach as it gives some flexibility to change individual planes in your shader while issuing one draw call.

Here is the simplified version to create instanced planes:

To create instanced planes, first, we create a plane geometry and then assign its index and attributes to an instance of InstancedBufferGeometry. To create interesting cloud patterns, I used the simplex function and utilized values from the loops for each axis that is scaled and shifted to have more control over the noise function. We add X, Y, Z, and a random scale value which is calculated the same way as the instancePositionArray. Also, there is a separate array for rotations.

Later on, in our shader material, we access these two arrays as attributes.

Shaders

One thing that I needed for my project was to have the camera positioned freely in the scene, not just in front of the cloud planes as in the original demo. This is easily fixed in the vertex shader:

The original plane’s vertex is rotated by instanceAngle amount around the Z axis and then added to view the space instance position set in the previous step. The shader also calculates the scale by using the W component of the instancePosition.

The pixel shader uses depth and fog range values set in the application to blend fog color with the cloud planes.