home / skills / bbeierle12 / skill-mcp-claude / shader-noise

shader-noise skill

/skills/shader-noise

This skill helps you generate and optimize GLSL procedural noise setups for realistic textures, terrain, clouds, and fluids with versatile noise types.

npx playbooks add skill bbeierle12/skill-mcp-claude --skill shader-noise

Review the files below or copy the command above to add this skill to your agents.

Files (4)
SKILL.md
10.1 KB
---
name: shader-noise
description: Procedural noise functions in GLSL—Perlin, simplex, Worley/cellular, value noise, FBM (Fractal Brownian Motion), turbulence, and domain warping. Use when creating organic textures, terrain, clouds, water, fire, or any natural-looking procedural patterns.
---

# Shader Noise

Procedural noise creates natural-looking randomness. Unlike `random()`, noise is coherent—nearby inputs produce nearby outputs.

## Quick Start

```glsl
// Simple usage
float n = snoise(uv * 5.0);              // Simplex 2D
float n = snoise(vec3(uv, uTime));       // Animated 3D
float n = fbm(uv, 4);                    // Layered detail

// Common range adjustments
float n01 = n * 0.5 + 0.5;               // [-1,1] → [0,1]
float sharp = step(0.0, n);              // Binary threshold
float smooth = smoothstep(-0.2, 0.2, n); // Soft threshold
```

## Noise Types Comparison

| Type | Speed | Quality | Use Case |
|------|-------|---------|----------|
| Value | Fastest | Blocky | Quick prototypes |
| Perlin | Fast | Good | General purpose |
| Simplex | Fast | Best | Modern default |
| Worley | Slower | Cellular | Cells, cracks, scales |

## Value Noise

Interpolated random values at grid points. Simple but blocky.

```glsl
float random(vec2 st) {
  return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

float valueNoise(vec2 st) {
  vec2 i = floor(st);
  vec2 f = fract(st);
  
  // Smoothstep interpolation
  vec2 u = f * f * (3.0 - 2.0 * f);
  
  // Four corners
  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));
  
  // Bilinear interpolation
  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
```

## Simplex Noise (Recommended)

Best quality-to-performance ratio. Use this as default.

### 2D Simplex

```glsl
vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }

float snoise(vec2 v) {
  const vec4 C = vec4(0.211324865405187, 0.366025403784439,
                      -0.577350269189626, 0.024390243902439);
  vec2 i  = floor(v + dot(v, C.yy));
  vec2 x0 = v - i + dot(i, C.xx);
  vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
  vec4 x12 = x0.xyxy + C.xxzz;
  x12.xy -= i1;
  i = mod(i, 289.0);
  vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
  vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
  m = m*m;
  m = m*m;
  vec3 x = 2.0 * fract(p * C.www) - 1.0;
  vec3 h = abs(x) - 0.5;
  vec3 ox = floor(x + 0.5);
  vec3 a0 = x - ox;
  m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
  vec3 g;
  g.x = a0.x * x0.x + h.x * x0.y;
  g.yz = a0.yz * x12.xz + h.yz * x12.yw;
  return 130.0 * dot(m, g);
}
```

### 3D Simplex

```glsl
vec4 permute(vec4 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }

float snoise(vec3 v) {
  const vec2 C = vec2(1.0/6.0, 1.0/3.0);
  const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
  
  vec3 i  = floor(v + dot(v, C.yyy));
  vec3 x0 = v - i + dot(i, C.xxx);
  
  vec3 g = step(x0.yzx, x0.xyz);
  vec3 l = 1.0 - g;
  vec3 i1 = min(g.xyz, l.zxy);
  vec3 i2 = max(g.xyz, l.zxy);
  
  vec3 x1 = x0 - i1 + C.xxx;
  vec3 x2 = x0 - i2 + C.yyy;
  vec3 x3 = x0 - D.yyy;
  
  i = mod(i, 289.0);
  vec4 p = permute(permute(permute(
    i.z + vec4(0.0, i1.z, i2.z, 1.0))
    + i.y + vec4(0.0, i1.y, i2.y, 1.0))
    + i.x + vec4(0.0, i1.x, i2.x, 1.0));
    
  float n_ = 0.142857142857;
  vec3 ns = n_ * D.wyz - D.xzx;
  
  vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
  
  vec4 x_ = floor(j * ns.z);
  vec4 y_ = floor(j - 7.0 * x_);
  
  vec4 x = x_ *ns.x + ns.yyyy;
  vec4 y = y_ *ns.x + ns.yyyy;
  vec4 h = 1.0 - abs(x) - abs(y);
  
  vec4 b0 = vec4(x.xy, y.xy);
  vec4 b1 = vec4(x.zw, y.zw);
  
  vec4 s0 = floor(b0)*2.0 + 1.0;
  vec4 s1 = floor(b1)*2.0 + 1.0;
  vec4 sh = -step(h, vec4(0.0));
  
  vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
  vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
  
  vec3 p0 = vec3(a0.xy, h.x);
  vec3 p1 = vec3(a0.zw, h.y);
  vec3 p2 = vec3(a1.xy, h.z);
  vec3 p3 = vec3(a1.zw, h.w);
  
  vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
  p0 *= norm.x;
  p1 *= norm.y;
  p2 *= norm.z;
  p3 *= norm.w;
  
  vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
  m = m * m;
  return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
}
```

## Worley (Cellular) Noise

Creates cell-like patterns. Great for scales, cracks, caustics.

```glsl
vec2 random2(vec2 st) {
  st = vec2(dot(st, vec2(127.1, 311.7)), dot(st, vec2(269.5, 183.3)));
  return fract(sin(st) * 43758.5453123);
}

float worley(vec2 st) {
  vec2 i_st = floor(st);
  vec2 f_st = fract(st);
  
  float minDist = 1.0;
  
  // Check 3x3 neighborhood
  for (int y = -1; y <= 1; y++) {
    for (int x = -1; x <= 1; x++) {
      vec2 neighbor = vec2(float(x), float(y));
      vec2 point = random2(i_st + neighbor);
      
      // Animate points
      // point = 0.5 + 0.5 * sin(uTime + 6.2831 * point);
      
      vec2 diff = neighbor + point - f_st;
      float dist = length(diff);
      minDist = min(minDist, dist);
    }
  }
  
  return minDist;
}

// F2 - F1 variant (cracks/veins)
vec2 worley2(vec2 st) {
  vec2 i_st = floor(st);
  vec2 f_st = fract(st);
  
  float f1 = 1.0;  // Closest
  float f2 = 1.0;  // Second closest
  
  for (int y = -1; y <= 1; y++) {
    for (int x = -1; x <= 1; x++) {
      vec2 neighbor = vec2(float(x), float(y));
      vec2 point = random2(i_st + neighbor);
      vec2 diff = neighbor + point - f_st;
      float dist = length(diff);
      
      if (dist < f1) {
        f2 = f1;
        f1 = dist;
      } else if (dist < f2) {
        f2 = dist;
      }
    }
  }
  
  return vec2(f1, f2);
}
```

## FBM (Fractal Brownian Motion)

Layer multiple noise octaves for natural detail at all scales.

```glsl
float fbm(vec2 st, int octaves) {
  float value = 0.0;
  float amplitude = 0.5;
  float frequency = 1.0;
  
  for (int i = 0; i < octaves; i++) {
    value += amplitude * snoise(st * frequency);
    frequency *= 2.0;      // Lacunarity
    amplitude *= 0.5;      // Gain/Persistence
  }
  
  return value;
}

// Configurable FBM
float fbm(vec2 st, int octaves, float lacunarity, float gain) {
  float value = 0.0;
  float amplitude = 0.5;
  float frequency = 1.0;
  
  for (int i = 0; i < octaves; i++) {
    value += amplitude * snoise(st * frequency);
    frequency *= lacunarity;
    amplitude *= gain;
  }
  
  return value;
}
```

### FBM Variants

```glsl
// Ridged FBM (mountains, lightning)
float ridgedFbm(vec2 st, int octaves) {
  float value = 0.0;
  float amplitude = 0.5;
  float frequency = 1.0;
  
  for (int i = 0; i < octaves; i++) {
    float n = snoise(st * frequency);
    n = 1.0 - abs(n);  // Ridge
    n = n * n;          // Sharpen
    value += amplitude * n;
    frequency *= 2.0;
    amplitude *= 0.5;
  }
  
  return value;
}

// Turbulence (absolute value, always positive)
float turbulence(vec2 st, int octaves) {
  float value = 0.0;
  float amplitude = 0.5;
  float frequency = 1.0;
  
  for (int i = 0; i < octaves; i++) {
    value += amplitude * abs(snoise(st * frequency));
    frequency *= 2.0;
    amplitude *= 0.5;
  }
  
  return value;
}
```

## Domain Warping

Distort the input coordinates with noise for organic shapes.

```glsl
// Simple domain warp
float warpedNoise(vec2 st) {
  vec2 q = vec2(
    snoise(st),
    snoise(st + vec2(5.2, 1.3))
  );
  
  return snoise(st + q * 2.0);
}

// Double domain warp (more complex)
float doubleWarp(vec2 st) {
  vec2 q = vec2(
    fbm(st, 4),
    fbm(st + vec2(5.2, 1.3), 4)
  );
  
  vec2 r = vec2(
    fbm(st + q * 4.0 + vec2(1.7, 9.2), 4),
    fbm(st + q * 4.0 + vec2(8.3, 2.8), 4)
  );
  
  return fbm(st + r * 4.0, 4);
}

// Animated warp
float animatedWarp(vec2 st, float time) {
  vec2 q = vec2(
    fbm(st + vec2(0.0, 0.0), 4),
    fbm(st + vec2(5.2, 1.3), 4)
  );
  
  vec2 r = vec2(
    fbm(st + q * 4.0 + vec2(1.7, 9.2) + 0.15 * time, 4),
    fbm(st + q * 4.0 + vec2(8.3, 2.8) + 0.126 * time, 4)
  );
  
  return fbm(st + r * 4.0, 4);
}
```

## Common Use Cases

### Terrain Height

```glsl
float terrainHeight(vec2 pos) {
  float height = 0.0;
  
  // Base terrain
  height += fbm(pos * 0.01, 6) * 100.0;
  
  // Mountains (ridged)
  height += ridgedFbm(pos * 0.005, 4) * 200.0;
  
  // Detail
  height += snoise(pos * 0.1) * 5.0;
  
  return height;
}
```

### Clouds

```glsl
float clouds(vec2 uv, float time) {
  vec2 motion = vec2(time * 0.1, 0.0);
  
  float density = fbm(uv * 3.0 + motion, 5);
  density = smoothstep(0.0, 0.5, density);
  
  return density;
}
```

### Fire/Flames

```glsl
float fire(vec2 uv, float time) {
  // Upward motion
  uv.y -= time * 2.0;
  
  // Turbulent distortion
  float turb = turbulence(uv * 4.0, 4);
  
  // Fade out at top
  float fade = 1.0 - uv.y;
  
  return turb * fade;
}
```

### Water Caustics

```glsl
float caustics(vec2 uv, float time) {
  vec2 w = worley2(uv * 8.0 + time * 0.5);
  return pow(1.0 - w.x, 3.0);
}
```

### Marble/Stone

```glsl
float marble(vec2 uv) {
  float n = fbm(uv * 2.0, 4);
  float veins = sin(uv.x * 10.0 + n * 10.0);
  return veins * 0.5 + 0.5;
}
```

## Performance Tips

| Technique | Impact |
|-----------|--------|
| Fewer octaves in FBM | Major speedup |
| 2D vs 3D noise | 2D ~2x faster |
| Bake to texture | Massive speedup for static |
| Lower frequency = fewer samples | Faster |

## File Structure

```
shader-noise/
├── SKILL.md
├── references/
│   ├── noise-comparison.md    # Visual comparison of types
│   └── optimization.md        # Performance techniques
└── scripts/
    ├── noise/
    │   ├── simplex2d.glsl     # Copy-paste simplex 2D
    │   ├── simplex3d.glsl     # Copy-paste simplex 3D
    │   ├── worley.glsl        # Copy-paste Worley
    │   └── fbm.glsl           # FBM variants
    └── examples/
        ├── terrain.glsl       # Terrain generation
        ├── clouds.glsl        # Cloud shader
        └── fire.glsl          # Fire effect
```

## Reference

- `references/noise-comparison.md` — Visual comparison of noise types
- `references/optimization.md` — Performance optimization techniques

Overview

This skill provides a compact GLSL noise toolkit for procedural shaders, including Perlin/value, simplex (2D/3D), Worley (cellular), FBM, turbulence, ridged FBM, and domain warping patterns. It targets organic textures like terrain, clouds, water, fire, marble, and caustics, with ready-to-use functions and usage snippets for quick integration. The implementations balance quality and performance and include tips for animation and optimization.

How this skill works

The collection exposes coherent noise functions that map spatial coordinates (vec2/vec3) to smoothly varying values. Simplex serves as the recommended default for quality vs. speed, Worley produces cell-like features, and value noise is a fast, blockier option. FBM layers octaves of base noise (with configurable lacunarity and gain) to create multi-scale detail; domain warping distorts coordinates with noise to produce complex organic forms.

When to use it

  • Generating terrain height maps and layered geological features
  • Synthesizing volumetric cloud density or animated sky patterns
  • Creating fire, smoke, and turbulent fluid details
  • Producing water caustics, scales, or cracked/cellular surfaces
  • Adding veins, marble, or stone patterns to materials
  • Distorting UVs or geometry with domain-warped organic detail

Best practices

  • Prefer 2D noise for screen-space effects; use 3D for volumetric or time-animated fields
  • Use simplex for general use, value noise for cheap prototypes, and Worley for cellular patterns
  • Reduce FBM octaves or bake static noise into textures for major performance gains
  • Animate by sampling noise in an extra time dimension (vec3) or offsetting inputs
  • Combine FBM, ridged FBM, and domain warps to vary scales and silhouette detail

Example use cases

  • Terrain: base FBM for broad shape, ridged FBM for mountain ranges, small simplex for fine detail
  • Clouds: moving FBM field with smoothstep to control density and edge softness
  • Fire: upward-shifted UVs with turbulence to create flickering flames
  • Water caustics: worley F2-F1 variant to emphasize veins and light patterns
  • Marble: low-frequency FBM modulating a sine-based vein function for natural streaks

FAQ

Which noise should I pick as default?

Use 2D simplex as the default for a good balance of speed and visual quality.

How do I make noise animate smoothly?

Animate by sampling a 3D noise with time as the third coordinate or by adding small time offsets to the inputs used in domain warps or FBM layers.