View profile

GM Shaders Mini: Noise 2

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, 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
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
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)
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!
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!)
Making a 3D Split-Screen Game in 2 Weeks
Making a 3D Split-Screen Game in 2 Weeks
Thanks for reading! Enjoy your night!
Did you enjoy this issue? Yes No
XorDev @xordev

Weekly mini-tutorials for game development and beyond

In order to unsubscribe, click here.
If you were forwarded this newsletter and you like it, you can subscribe here.
Created with Revue by Twitter.