Value and Gradient Noise
Smooth randomness for natural textures
Hash functions give us random values, but they jump abruptly from one grid cell to the next. Nature does not work this way. Clouds do not have hard edges. Terrain does not step up in uniform blocks. To create organic, natural-looking patterns, we need noise that varies smoothly and continuously.
Value Noise: The Foundation
Value noise is the simplest continuous noise algorithm. The idea is straightforward: assign random values to grid points, then interpolate smoothly between them.
float valueNoise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}Let us trace through what happens:
floor(p)gives us the integer grid cell containing our pointfract(p)gives us the position within that cell (0 to 1)- We sample hash values at all four corners of the cell: a, b, c, d
- Bilinear interpolation blends these four values based on our position within the cell
Value Noise with Linear Interpolation
Value noise with linear interpolation. Notice the blocky appearance where grid lines are visible.
The result is continuous noise, but something is wrong. Look closely and you will see the grid structure. The transitions between cells are smooth, but you can still perceive where the grid lines are. The noise looks artificial, blocky.
The Importance of Smooth Interpolation
The problem is linear interpolation. When we use mix(a, b, f.x), the blend is a straight line from a to b. Where this line meets the next cell's blend, there is a discontinuity in the derivative. The value is continuous, but the rate of change is not.
Our eyes are remarkably sensitive to these derivative discontinuities. They create visual artifacts that look like a grid overlay.
The solution is smoother interpolation. Instead of using f directly, we transform it with a smoothing function:
Smoothstep (Hermite curve):
vec2 u = f * f * (3.0 - 2.0 * f);This S-curve has zero derivative at the endpoints, meaning the transition into and out of each grid cell is smooth.
Quintic:
vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);This curve has zero first and second derivatives at the endpoints. The improvement is subtle but meaningful: even the rate of change of the rate of change is continuous.
Compare Interpolation Methods
Smoother interpolation functions eliminate the blocky grid appearance. Quintic interpolation has zero first and second derivatives at grid points.
With quintic interpolation, the grid structure essentially vanishes. The noise appears as a continuous, organic field of values.
Gradient Noise Concept
Value noise interpolates between random values. Gradient noise, invented by Ken Perlin, interpolates between random gradients (directions).
At each grid point, instead of storing a random brightness, we store a random direction. The noise value at any point is computed by taking the dot product of vectors pointing from the grid corners to our point, with the random gradients at those corners.
The mathematics is more involved, but the visual result is noise with even better quality: fewer directional artifacts and a more natural appearance. The famous Perlin noise and Simplex noise are gradient noise variants.
For most shader work, value noise with quintic interpolation is sufficient and easier to understand. When you need the absolute best quality, gradient noise is there.
Fractal Brownian Motion (fBm)
Single-scale noise is useful, but real natural phenomena have detail at multiple scales. A mountain range has broad valleys and peaks, medium ridges and slopes, and fine rocky texture. Clouds have large formations and small wisps.
Fractal Brownian Motion achieves this by layering multiple "octaves" of noise at different frequencies and amplitudes:
float fbm(vec2 p, int octaves) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for (int i = 0; i < octaves; i++) {
value += amplitude * valueNoise(p * frequency);
frequency *= 2.0;
amplitude *= 0.5;
}
return value;
}Each iteration doubles the frequency (making details smaller) and halves the amplitude (making them subtler). The result is noise with structure at every scale, from broad shapes to fine detail.
View Individual Octaves
Each octave adds finer detail. Low octaves provide the broad shape; high octaves add texture and variation.
Controlling fBm Parameters
The fBm algorithm has several parameters that control its character:
Octaves determines how many layers of detail to add. More octaves means finer detail, but also more computation. For most uses, 4-6 octaves is sufficient.
Lacunarity is the frequency multiplier between octaves. The standard value is 2.0, meaning each octave has twice the frequency of the previous. Higher values create more contrast between scales.
Persistence is the amplitude multiplier between octaves. The standard value is 0.5. Higher persistence means fine details remain prominent; lower persistence lets broad shapes dominate.
Interactive: fBm Parameter Explorer
Fractal Brownian Motion layers multiple octaves of noise at increasing frequencies and decreasing amplitudes.
These parameters give you artistic control over the character of your noise. Experiment with them to find the right feel for your effect.
Practical Applications
Clouds
Clouds are perhaps the most iconic use of fBm noise. The noise value becomes cloud density, and we apply a threshold with soft edges:
float n = fbm(uv * 3.0 + time * 0.1);
float cloud = smoothstep(0.35, 0.65, n);
vec3 color = mix(skyColor, cloudColor, cloud);Adding time to the input coordinates creates drifting motion.
Animated Clouds
Animated clouds using fBm noise. The threshold determines cloud coverage, while the smooth transition creates soft edges.
Terrain
For terrain heightmaps, noise values map directly to elevation. Different height ranges become different biomes:
Terrain with Biomes
Terrain heightmap using fBm. Noise values are mapped to biomes: water, sand, grass, rock, and snow.
This simple technique produces surprisingly convincing landscapes. Real terrain generation systems use variations like domain warping (feeding noise into itself) and erosion simulation, but fBm is the foundation.
Other Uses
The patterns you can create with noise are endless:
- Fire and smoke with time-varying noise and color gradients
- Water caustics using noise to distort light patterns
- Organic textures like marble, wood grain, or rust
- Displacement for geometric distortion
- Randomized animation timing and motion paths
Noise is one of the most versatile tools in shader programming. Once you understand how to generate and layer it, organic effects become approachable.
Performance Considerations
Each octave of noise requires multiple hash function calls and interpolations. On mobile devices or with complex shaders, the cost adds up.
Strategies to improve performance:
- Use fewer octaves (often 3-4 is visually sufficient)
- Compute noise at lower resolution and upscale
- Precompute noise into a texture for repeated access
- Use simpler hash functions where quality permits
The right balance depends on your target platform and visual requirements.
Looking Ahead
Noise functions are building blocks for procedural textures. In upcoming chapters, we will use them to create wood, marble, and other materials. Combined with domain warping and color mapping, noise becomes a complete toolkit for organic visual effects.
Key Takeaways
- Value noise interpolates random values at grid points for smooth, continuous variation
- Linear interpolation creates visible grid artifacts; quintic interpolation eliminates them
- Gradient noise interpolates directions instead of values for even higher quality
- Fractal Brownian Motion (fBm) layers multiple octaves for multi-scale detail
- Octaves add detail; lacunarity controls frequency ratio; persistence controls amplitude ratio
- fBm is the foundation for clouds, terrain, and countless organic effects