#atom

Gradient noise algorithm that creates natural-looking procedural patterns and textures

Core Idea: Perlin noise is a procedural gradient noise function that generates smoothly varying pseudo-random patterns used in computer graphics for creating natural-looking textures, terrain, animations, and other organic phenomena.

Key Elements

Core Characteristics

Mathematical Foundations

Variations and Extensions

Implementation Techniques

Classic 2D Perlin Noise

// Simplified 2D Perlin Noise implementation
function perlin2D(x, y) {
  // Determine grid cell coordinates
  const X = Math.floor(x) & 255;
  const Y = Math.floor(y) & 255;
  
  // Get relative position within grid cell
  x -= Math.floor(x);
  y -= Math.floor(y);
  
  // Compute fade curves
  const u = fade(x);
  const v = fade(y);
  
  // Hash coordinates of the 4 square corners
  const A = permutation[X] + Y;
  const B = permutation[X + 1] + Y;
  const AA = permutation[A];
  const BA = permutation[B];
  const AB = permutation[A + 1];
  const BB = permutation[B + 1];
  
  // Add blended results from 4 corners of square
  return lerp(
    lerp(
      grad(permutation[AA], x, y),
      grad(permutation[BA], x - 1, y),
      u
    ),
    lerp(
      grad(permutation[AB], x, y - 1),
      grad(permutation[BB], x - 1, y - 1),
      u
    ),
    v
  );
}

// Fade function (6t^5 - 15t^4 + 10t^3)
function fade(t) {
  return t * t * t * (t * (t * 6 - 15) + 10);
}

// Linear interpolation
function lerp(a, b, t) {
  return a + t * (b - a);
}

// Gradient function
function grad(hash, x, y) {
  const h = hash & 15;
  const gradX = 1 + (h & 7); // Gradient x component
  const gradY = gradX & 1 ? 1 : -1; // Gradient y component
  
  // Dot product of gradient vector and distance vector
  return gradX * x + gradY * y;
}

Fractal Brownian Motion (fBm)

// Fractal Brownian Motion using Perlin noise
function fbm(x, y, octaves = 6, lacunarity = 2.0, persistence = 0.5) {
  let total = 0;
  let frequency = 1.0;
  let amplitude = 1.0;
  let maxValue = 0;  // Used for normalizing result to 0.0 - 1.0
  
  for (let i = 0; i < octaves; i++) {
    total += perlin2D(x * frequency, y * frequency) * amplitude;
    
    maxValue += amplitude;
    amplitude *= persistence;
    frequency *= lacunarity;
  }
  
  return total / maxValue;
}

Domain Warping

// Domain warping using Perlin noise to distort coordinates
function warpedNoise(x, y, strength = 10.0) {
  // Use noise to offset the input coordinates
  const warpX = perlin2D(x * 0.5, y * 0.5) * strength;
  const warpY = perlin2D(x * 0.5 + 100, y * 0.5 + 100) * strength;
  
  // Sample noise at the warped position
  return perlin2D(x + warpX, y + warpY);
}

Application Techniques

Terrain Generation

// Generate heightmap for terrain using multi-octave noise
function generateTerrain(width, height, scale = 0.01) {
  const heightmap = new Float32Array(width * height);
  
  for (let z = 0; z < height; z++) {
    for (let x = 0; x < width; x++) {
      // Base terrain shape using FBM
      let elevation = fbm(x * scale, z * scale, 6, 2.0, 0.5);
      
      // Apply non-linear transformations for more natural terrain
      elevation = Math.pow(elevation, 2.2); // Sharpen mountains
      
      // Store in heightmap
      heightmap[z * width + x] = elevation;
    }
  }
  
  return heightmap;
}

Texture Generation

// Generate cloud texture using domain-warped noise
function generateCloudTexture(width, height) {
  const texture = new Uint8Array(width * height * 4); // RGBA
  
  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      // Sample warped noise
      const cloudDensity = warpedNoise(x * 0.003, y * 0.003, 20.0);
      
      // Apply threshold and shaping
      let value = Math.max(0, cloudDensity - 0.4) * 2.5;
      value = Math.min(1, value);
      
      // Convert to RGBA
      const i = (y * width + x) * 4;
      texture[i] = 255;      // R
      texture[i + 1] = 255;  // G
      texture[i + 2] = 255;  // B
      texture[i + 3] = Math.floor(value * 255); // A (opacity)
    }
  }
  
  return texture;
}

Animation with Noise

// Animate objects using Perlin noise for natural motion
function animateWithNoise(objects, time) {
  objects.forEach((obj, index) => {
    // Each object has a different offset in noise space
    const offset = index * 100;
    
    // Sample 3D noise using time as the third dimension
    const xNoise = perlin2D(offset, time * 0.1) * 2 - 1;
    const yNoise = perlin2D(offset + 50, time * 0.1) * 2 - 1;
    const zNoise = perlin2D(offset + 100, time * 0.1) * 2 - 1;
    
    // Apply noise to object position
    obj.position.x += xNoise * 0.05;
    obj.position.y += yNoise * 0.05;
    obj.position.z += zNoise * 0.05;
  });
}

Implementation in Graphics Libraries

Three.js Integration

// Creating Perlin noise-based terrain in Three.js
function createNoiseTerrain(scene, size = 100, resolution = 100) {
  // Create a plane geometry
  const geometry = new THREE.PlaneGeometry(
    size, size,
    resolution - 1, resolution - 1
  );
  
  // Generate height data
  const scale = 0.02;
  const vertices = geometry.attributes.position.array;
  
  for (let i = 0; i < vertices.length; i += 3) {
    const x = vertices[i];
    const z = vertices[i + 2];
    
    // Apply noise-based height
    const height = fbm(x * scale, z * scale, 6, 2.0, 0.5) * 10;
    vertices[i + 1] = height;
  }
  
  // Update normals for proper lighting
  geometry.computeVertexNormals();
  
  // Create mesh with the geometry
  const material = new THREE.MeshStandardMaterial({
    color: 0x3D9970,
    flatShading: false,
  });
  
  const terrain = new THREE.Mesh(geometry, material);
  terrain.rotation.x = -Math.PI / 2; // Rotate to horizontal
  scene.add(terrain);
  
  return terrain;
}

Shader Implementation

// GLSL implementation of 2D Perlin noise (fragment shader)
// Simplified pseudo-code representation

uniform float time;
uniform vec2 resolution;

// Hash function
vec2 hash(vec2 p) {
    p = vec2(dot(p, vec2(127.1, 311.7)),
             dot(p, vec2(269.5, 183.3)));
    return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
}

// 2D Perlin Noise
float perlin(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    
    // Cubic Hermite curve
    vec2 u = f * f * (3.0 - 2.0 * f);
    
    // Sample gradients at corner points
    float n00 = dot(hash(i), f);
    float n01 = dot(hash(i + vec2(0, 1)), f - vec2(0, 1));
    float n10 = dot(hash(i + vec2(1, 0)), f - vec2(1, 0));
    float n11 = dot(hash(i + vec2(1, 1)), f - vec2(1, 1));
    
    // Bilinear interpolation with smoothing
    float nx0 = mix(n00, n10, u.x);
    float nx1 = mix(n01, n11, u.x);
    float nxy = mix(nx0, nx1, u.y);
    
    return nxy * 0.5 + 0.5; // Transform to 0.0-1.0 range
}

void main() {
    vec2 uv = gl_FragCoord.xy / resolution.xy;
    
    // Scale UV coordinates
    vec2 pos = uv * 8.0;
    
    // Add time for animation
    pos.x += time * 0.5;
    
    // Generate noise
    float n = perlin(pos);
    
    // Output noise as grayscale
    gl_FragColor = vec4(vec3(n), 1.0);
}

Practical Applications

In Game Development

In Data Visualization

In Creative Coding

Connections

References

  1. "Improving Noise" by Ken Perlin (SIGGRAPH 2002)
  2. "Texturing and Modeling: A Procedural Approach" by David S. Ebert et al.
  3. Original 1985 Perlin Noise paper by Ken Perlin

#perlin-noise #procedural-generation #computer-graphics #game-development #algorithms


Connections:


Sources: