GM Shaders Mini Tuts

Share this post

GM Shaders Mini: Noise 2

mini.gmshaders.com

Discover more from GM Shaders Mini Tuts

A weekly series of mini shader tutorials for game developers and beyond!
Over 1,000 subscribers
Continue reading
Sign in

GM Shaders Mini: Noise 2

Xor
Nov 11, 2022
1
Share this post

GM Shaders Mini: Noise 2

mini.gmshaders.com
Share

Hi folks,

Last week we went over 3 common noise algorithms. Today we will continue where we left off and show 3 more! Worley, Voronoi, and fractal noise.

Tutorial Difficulty: Intermediate


Worley, Voronoi and fractal noise respectively

Worley Noise

Worley noise functions a bit differently from the other noise algorithms because there is no interpolation involved. The concept is simple, scatter a bunch of points randomly and use the distance to the closest point to determine the output brightness. 0.0 at the point center and 1.0 for cell unit a way.

Let's break this down into simple steps:

Step 1: Round To Cells

Like before, round down the input coordinates to cells:

vec2 cell = floor(p);

And we'll also store our distance value in a float:

float dist = 9.0;

I'm using 9 because it's our maximum distance, that we should never exceed (even in higher dimensions).

Step 2: Sample Point

Compute the difference from p to a random point in the cell:

vec2 worley_dif = hash2(cell) + cell - p;

hash2 gives us a random x and y value between 0 and 1. Then add cell so the point position ranges from cell to cell+1.0 and subtract p to find the relative difference.

We can then calculate the distance by using the length of this difference:

dist = length(worley_dif);

Doing so gives us the result on the left:

Worley cells without and with neighbors

So we now have our random points and we just want it to smoothly blend with the neighboring cells as shown above on the right. You might be able to guess the last step.

Step 3: Sample Neighbors

Loop through the 3x3 cell area instead

for(int x = -1; x<=1; x++)

for(int y = -1; y<=1; y++)

And we'll add this offset before sampling each cell:

vec2 sample_cell = cell + vec2(x,y);

So we'll use our sample_cell when computing worley_dif instead:

vec2 worley_dif = hash2(sample_cell) + sample_cell - p;

And we'll just use minimum distance across all neighbor cells:

dist = min(dist, length(worley_dif));

This gives us the desired cellular pattern we were looking for!

Again, I'll link a ShaderToy example at the end.

Voronoi Noise

Voronoi noise is a simple variation of Worley noise. Instead of using the distance to nearest point, we'll pick a random value for every point and use the closest points value for the output.

Step 1: Voronoi Cells

So use your Worley function as a base, but before the loops, add another vec2:

vec2 voronoi_cell = cell;

This will be used store the cell coordinates of the nearest point.

Step 2: Find Nearest Cell

Inside the loop, we need more than just the minimum distance to neighbor cells. We'll put our Worley distance value into a new float:

float new_dist = length(worley_dif);

And when our new distance value is smaller than our old one:

if (dist > new_dist)

Replace our old distance with the new, smaller value:

dist = new_dist;

This does the same thing as min, but we'll also store our nearest sample cell:

voronoi_cell = sample_cell;

Step 3: Final Hash

And finally, once the loop is complete, we can use our hash function on the coordinates of the nearest Voronoi cell:

return hash1(voronoi_cell);

This gives you neat honeycomb-like pattern:

Results from the Voronoi noise

Fractal Noise

Now let's take a look at fractal noise! Fractal noise is when you layer multiple noise "octaves" at different scales and intensities to produce a more natural look. This can be used for clouds, smoke, fire or even terrain generation!

The neat part is that it can be applied to any of the other noise algorithms:

Value, Perlin, Worley and Voronoi noise (left to right, top to bottom)

Let's break it down!

Step 1: Initialize

The first step is to initialize the variables we need:

float noise_sum = 0.0; //Noise total across octaves

float weight_sum = 0.0; //Weight total across octaves

float weight = 1.0; //Octave weight

int oct = 6; //Number of octaves

float per = 0.5; //Octave persistence (intensity)

Step 2: Octaves

A simple loop through octaves will do:

for(int i = 0; i < oct; i++)

For each octave, we want to add a weighted layer of noise.

noise_sum += value_noise(p) * weight;

And add the current weight to a total for averaging.

weight_sum += weight;

Next, we'll multiply our octave weight by the persistence value.

weight *= per;

This means with a persistence value of 0.5, each octave has half the intensity of the octave before it. Play around with different values to see how it looks.

And finally, quite importantly we rotate and scale each octave:

p *= mat2(1.6, 1.2, -1.2, 1.6);

This matrix scales coordinates by 2 and rotates by about 143 degrees. The rotation helps break up any patterns that might arise from noise octaves overlapping each. p *= 2.0 also works, but doesn't look as good.

Step 3: Weighted Average

And finally, just return the weighted average:

return noise_sum / weight_sum;

In case you didn't know, a weighted average is an average where some values have more of an effect than others. In this case, If you have a persistence value of 0.5, the first octave contributes to half of the average, the second octave: one-quarter, the third: one-eighth, and so on.

It's a great trick to learn and use in any shader where you need to blend multiple values together with different weights!

Conclusion

That covers it for tonight. Let me know if there are any other noise algorithms you'd like me to go over.

You can take a look at the code I used in my ShaderToy example here!

And you're bored, check out this 3D game I made in 2 weeks using just GameMaker (no 3D modeling/animation software, just shader code!)

Thanks for reading! Enjoy your night!

1
Share this post

GM Shaders Mini: Noise 2

mini.gmshaders.com
Share
Comments
Top
New
Community

No posts

Ready for more?

© 2023 Xor
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing