Fractal Brownian Motion and Shaders
10-23-2023
Motivation and Starting point
Its been a bit since I wrote something but I really wanted to get back into shaders and graphics programming. What better place to start (again) than The Book of Shaders ? I was sitting there reading when I found a very interesting chapter on noise and Fractal Brownian Motion
Math Part :(
So what is Fractional Brownian Motion (fBm)? According to some site from Worcester Polytechnic Institute, fBm is a random walk process, consisting of steps that are taken in a random direction with step lengths that hold a characteristic value.
But what does that mean exactly? Without having to write out the dirivatives in latex ill simplify it to these couple of statements:
- fBM of index α (0 < α < 1)
- X(t) is a continuous function of t
- For any t > 0 and h > 0 ,
- X(t + h) - X(T) is normally distributed with a mean of 0 and varience of the h^2α
Here h is the step taken.
Picture taken from the WPI website |
Uses for fBm
Ive mentioned him before but Inigo Quilez has a really interesting article and video detailing how fBm is used to create procedural landscapes in 3-D.
From the code you can see why:
// normalizing our coordinates
vec2 uv = gl_FragCoord.xy / u_resolution;
// centering the origin and making clipspace [1,1]
uv = 2.0*(uv - 0.5);
// adjusting x coords to adjust for stretching
uv.x *= u_resolution.x/u_resolution.y;
//
//
//
float displacement = patternfbm(uv*3.0 );
I have used this technique before when making a music visualizer which you can read about here, but basically we can generate a normal map from the fBm function and use those values (which are normalized) in whatever way we want.
How to construct the fBm
Lets take a look at the code for making this function and see what it looks like:
float random (in vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898,78.233)))*
43758.5453123);
}
float noise (in vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
// Four corners in 2D of a tile
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
#define OCTAVES 5
float fbm (in vec2 st) {
// Initial values
float value = 0.0;
float amplitude = .5;
float frequency = 0.;
//
// Loop of octaves
for (int i = 0; i < OCTAVES; i++) {
value += amplitude * noise(st);
st *= 2.;
amplitude *= .5;
}
return value;
}
The main sections that make this system work are the random
, noise
, and fbm
function. It's important to note that 2 of these functions are somewhat arbitrary.
The noise
and random
functions here can be replaced by any number of other random (sudo) and noise functions that are out there, I could use the Perlin noise function from my last article and plug it in:
fBm with a Perlin noise function |
This is a bit too messy for my taste so lets stick to the original noise function. Youll notice that all we are doing is adding some noise to the wave, and by multiplying the aplitude by 0.5, we are essentially making the details we see smaller or larger depending on the number of octaves we decide to use. I found the best results at 4-5.
Briefly on Color
I have been deeply inspired by the works of artist FELIPE PANTONE, his color palletes and use of gradients is very cool to me and using this website, I was able to create a color pallete that would work with GLSL, mimicking his own palletes:
vec3 pallete(float t){
vec3 a = vec3(0.5,0.5,0.5);
vec3 b = vec3(0.5,0.5,0.5);
vec3 c = vec3(1.0,1.0,1.0);
vec3 d = vec3(0.236,0.416,0.557);
return a + b*cos(6.28318*(c+t+d));
}
Results
Below is a brief animation which to my eye looks really sick. Kinda like multicolored flames from the one episode of Avatar the last Airbender. (iykyk):
fBm with a random noise function from shadertoy |