Loading, please wait
0.00.8
0.03.0
0.01.0
0.03.0
0.0150.0
2.026.0

Tell me more about the...

Colors and Lighting

\(\mathrm{intensity}=-\mathrm{direction}_\mathrm{light}\cdot\mathrm{normal}\)

Incoming light Normal of surface Lighting strength

For something rough like stone or trees, assume the light is scattered equally in all directions. In that case, the brightness is proportional to how "densely" the incoming rays hit the surface.

\(\mathrm{color}_\mathrm{out}=\mathrm{color}_\mathrm{surface}\circ\mathrm{color}_\mathrm{light}\mathrm{intensity}\)

For each RGB channel, multiply the surface and scaled light values together. That's all there is to it!

So then what are the surface and light colors?

The surface is stone when it is higher in elevation and sloped, and vegetation when it is lower and flatter.

Stone (0) Vegetation (1)

The precise equation it follows is \(1.2\cdot\mathrm{normal}_y-0.8\cdot\mathrm{elevation}\), although \(\mathrm{elevation}\) isn't particularly meaningful since it doesn't have well-defined units.

The light and sky colors depend on the height of the sun in the sky.

Light Sky

\(\mathrm{light}=\begin{bmatrix}\max(0.6h+0.4,0.0)\\\max(0.8h+0.2,0.0)\\\max(h,0.0)\end{bmatrix}\)

\(\mathrm{sky}=\begin{bmatrix}\max(0.3h+0.2,0.0)\\\max(0.4h+0.1,0.0)\\\max(0.8h,0.0)+0.1h+0.15\end{bmatrix}\)

where \(h\) is between \(-1.0\) and \(1.0\).

Ground

This uses Improved Perlin Noise to compute the height, as well as basically everything else. The basic idea is that Perlin Noise is coherent, meaning that it continuous like a hill rather than discontinuous like static, and procedural, meaning that the value at each point can be calculated independently.

Traditionally, Perlin Noise is explained as an interpolation of several dot products, which is useful as an implementation guide but not so much for seeing how it works. If you want to learn more about it in that context, you should check out this, this, and this. Instead, this article will provide a more visual description.

To start with, the space is divided into cells. At each lattice point, a random vector is chosen, which affects the four cells it is a part of. In the Improved Perlin Noise, there are only four vectors to choose from, but in the original there are infinitely many. Each vector creates a rotation of the same shape, shown in the animation below. When the shapes from each lattice point are added together, a smooth random-looking pattern is obtained. To see what it looks like, set the Ground Noise Gain to \(0\).

HTML5 not supported!

If you tried that, you can tell it does not look remotely realistic. It's missing all the detail found in real life. To be realistic, the noise needs to be fractal noise. So what is that?

Try listening to a note played on a piano, then a note as a sine wave. The difference is that the piano has harmonics, additional waves with higher frequency and lower amplitude. In music these are integer multiples of the base frequency, but in the terrain generator only power of two multiples are used, since they decrease faster and are therefore easier to compute.

HTML5 not supported!"
0.01.0

\(y=\sum_{n=0}^\infty a^n\sin{(2^n x)}\)

So this is how to create fractals out of a wave, but how is it done with noise? Does noise have frequency? If a bunch of hills are squeezed together it certainly looks different, so that would make sense. As it turns out, there's an easy stand-in for how close the hills are: how small the cells are. And that's exactly how it's done here.

\(y=\sum_{n=0}^\infty ka^n\mathrm{perlin}(2^n x, 2^n z)\)

Sky

Clouds are generally bunched together, but have soft edges. This means that the cloud density should be relatively smooth. Since that restriction eliminates a simple cutoff function, the next thing to try is a power function. It ends up looking reasonably good.

HTML5 not supported!"
0.03.0

\(y=(\frac{1}{2}\sin{x}+\frac{1}{2})^b\)

Putting this together with the noise octaves gives the equation for cloud density.

HTML5 not supported!"
0.01.0
0.03.0

\(y=(\frac{1}{2}(\sum_{n=0}^\infty a^n\sin{(2^n x)})+\frac{1}{2})^b\)

\(y=(\sum_{n=0}^\infty a^n\mathrm{perlin}(2^n x, 2^n z))^b\)

The cloud density causes the color to fade from the sky color at density \(0.0\), to \(90\%\) of the light color at density \(0.5\), to \(40\%\) of the light color at density \(1.0\).

0.0 Cloud Density 1.0

In real life, the stars are always shining, but the sun is so much brighter that our eyes adjust and we can't see them. Computer screens don't adjust to things that are very bright; they cap the color and display white. Because of this, the model compensates internally by reducing the brightness of stars when it's lighter out.

Sky Stars

\(y=\max(\mathrm{perlin}(100x, 100z)^b-\begin{bmatrix}1.0\\1.0\\1.0\end{bmatrix}\cdot\mathrm{color}_\mathrm{sky}-0.3,0.0)\)

The star color uses gain \(0\), and to get the correct brightness, the value is shifted down instead of scaled so the stars make distinct points. This value is added to the sky color, so the stars are hidden by clouds.

Things further away from the viewer are blended in to the sky color.

Stuff I don't see

  • Vertices are more densely packed closer to the viewer.
  • The sky is not flat - further away from the viewer it's actually closer to the ground.
  • The clouds fade away slower than the ground does to make the scene look deeper than it is.
  • Stars are not round, especially if the Star Exponent is low.
  • Because GLSL doesn't support arbitrary array lookup, instead of using a shuffled array as a source of pseudorandomness, this uses the slightly worse sine-chopping method.

Potential Improvements

  • The cloud density function can be improved.
  • More than one color of vegetation might look good.
  • Valleys are just as rough as mountains. Could be fixed by applying an exponent to the elevation, but mountain ranges have multiple peaks and the area shown might not be wide enough for valleys to be expected.
  • Ridged noise can give the mountains a nice jagged look, but doesn't look so nice for hills or forests.