Look at the cross section of a tree trunk.
You'll notice a series of rings spreading outward from the center.
These rings aren't perfectly smooth.
They bend.
They twist.
They become thinner in some places and thicker in others.
Those imperfections are exactly what make wood look natural.
The good news is that we already know every technique needed to recreate them.
Wood grain is surprisingly simple.
It consists of three main ingredients.
Circular rings.
Small variations.
Natural distortion.
By combining circles with procedural noise, we can recreate the appearance of real wood without using any image textures.
We'll begin by centering the coordinates.
vec2 uv = vUv - 0.5;
Now the center of the screen becomes the center of the tree.
Next, calculate the distance from the center.
float d = length(uv);
Pixels close to the center have smaller values.
Pixels farther away have larger values.
This creates circular measurements.
Feed the distance into a sine wave.
float rings =
sin(
d * 40.0
);
The result is a series of concentric circles.
Already, it begins to resemble tree rings.
Perfect circles rarely exist in nature.
Let's distort them.
float n = fbm(uv * 5.0);
rings =
sin(
d * 40.0 +
n * 6.0
);
The noise bends each ring slightly.
The result feels much more organic.
Increase the multiplier.
d * 60.0
The rings become tightly packed.
Decrease it.
d * 20.0
The rings spread farther apart.
Different trees have different growth patterns.
This single number controls that appearance.
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 vUv;
void main(){
vec2 uv = vUv - 0.5;
float d = length(uv);
float n = fbm(uv * 5.0);
float wood =
sin(
d * 40.0 +
n * 6.0
);
wood = wood * 0.5 + 0.5;
gl_FragColor = vec4(vec3(wood),1.0);
}
Even in grayscale, the pattern already resembles the inside of a tree trunk.
Color transforms the pattern into convincing timber.
vec3 color = mix(
vec3(0.35,0.20,0.08),
vec3(0.75,0.55,0.30),
wood
);
Light brown and dark brown create a classic oak appearance.
Different colors create entirely different types of wood.
vec3(0.18,0.10,0.05)
vec3(0.42,0.26,0.12)
The result resembles polished walnut.
vec3(0.90,0.78,0.55)
vec3(0.65,0.45,0.22)
Now the texture looks much lighter.
Although wood itself doesn't move, animation helps while experimenting.
uv.x +=
uTime * 0.1;
Watching the rings flow makes it easier to understand how the mathematics works.
Real wood often has long flowing grain.
Instead of using only circular rings, distort the coordinates first.
uv.x +=
fbm(uv * 3.0) * 0.2;
The rings stretch and bend naturally.
This small change adds a surprising amount of realism.
Wood textures appear almost everywhere.
Because the texture is mathematical, it scales to any resolution without losing detail.
Create darker wood.
Create lighter wood.
Increase the number of rings.
Decrease the distortion.
Animate the coordinates.
Add domain warping before drawing the rings.
Observe how each change affects the appearance.
Can you create these materials?
Each one begins with the same procedural rings.
Today we created another procedural material from scratch.
By combining circular distance, sine waves, and FBM, we generated convincing wood grain entirely with mathematics.
This demonstrates how the same building blocks can create completely different materials simply by changing how they're combined.