Perlin Noise Subsurface Scattering

📅 April 23, 2026 🏷️ art
worth-a-look subsurface scattering perlin noise particle system emergent behavior light simulation organic motion flow field translucent materials
Generated by GridFlow AI | Tags: subsurface scattering, perlin noise, particle system, emergent behavior, light simulation, organic motion, flow field, translucent materials

💡 AI 提示词

Create a generative art piece that simulates subsurface scattering using Perlin noise-driven particle systems. The particles should exhibit wavelength-dependent color shifts as they move through simulated translucent materials, with additive blending creating ethereal glow effects. Focus on emergent complexity through simple velocity rules influenced by noise fields.

🔧 核心算法要点

  1. Initialize 1200 particles with randomized positions, base hues in the blue-violet range, depth values (0.15-1.0), and unique noise offsets
  2. Use multi-octave Perlin noise sampling to calculate angular flow fields that influence particle velocity vectors
  3. Implement wavelength-to-hue mapping where shorter wavelengths (deeper penetration) shift toward red-orange and longer wavelengths remain blue-violet
  4. Apply depth-dependent brightness and saturation to simulate light scattering intensity within translucent media
  5. Use ADD blend mode to accumulate overlapping particle glows, creating realistic subsurface illumination
  6. Add central gravitational attraction scaled by particle depth to create organic clustering behavior
  7. Render particles with layered ellipses: bright core, main body, and soft outer halo for volumetric appearance

🎨 原始代码

var sketch = function(p) {
    let particles = [];
    let numParticles = 1200;
    let t = 0;
    let noiseScale = 0.0015;
    let timeScale = 0.0008;
    let container;

    p.setup = function() {
        container = document.getElementById('p5-wrapper');
        p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
        p.colorMode(p.HSB, 360, 100, 100, 100);
        p.noStroke();

        for (let i = 0; i < numParticles; i++) {
            particles.push(createParticle());
        }
    };

    function createParticle() {
        return {
            x: p.random(p.width),
            y: p.random(p.height),
            vx: 0,
            vy: 0,
            baseHue: p.random(180, 280),
            hueShift: p.random(-40, 40),
            depth: p.random(0.15, 1.0),
            size: p.random(2, 15),
            noiseOffsetX: p.random(10000),
            noiseOffsetY: p.random(10000),
            noiseOffsetZ: p.random(10000),
            lifetime: p.random(0.5, 1.0)
        };
    }

    p.draw = function() {
        p.blendMode(p.BLEND);
        p.background(240, 30, 8, 100);

        for (let i = 0; i < particles.length; i++) {
            updateParticle(particles[i]);
        }

        p.blendMode(p.ADD);

        for (let i = 0; i < particles.length; i++) {
            renderParticle(particles[i]);
        }

        p.blendMode(p.BLEND);
        t += 0.01;
    };

    function updateParticle(pt) {
        let n1 = p.noise(pt.x * noiseScale + pt.noiseOffsetX, pt.y * noiseScale + pt.noiseOffsetY, t + pt.noiseOffsetZ);
        let n2 = p.noise(pt.x * noiseScale * 2 + pt.noiseOffsetX, pt.y * noiseScale * 2 + pt.noiseOffsetY, t * 0.5 + pt.noiseOffsetZ + 100);

        let angle = n1 * p.TWO_PI * 4;
        let force = (n2 - 0.5) * 1.2;

        pt.vx += p.cos(angle) * force * 0.3;
        pt.vy += p.sin(angle) * force * 0.3;

        let centerX = p.width * 0.5;
        let centerY = p.height * 0.5;
        let dx = centerX - pt.x;
        let dy = centerY - pt.y;
        let distToCenter = p.sqrt(dx * dx + dy * dy);
        let maxDist = p.sqrt(centerX * centerX + centerY * centerY);

        pt.vx += (dx / maxDist) * 0.15 * pt.depth;
        pt.vy += (dy / maxDist) * 0.15 * pt.depth;

        pt.vx *= 0.94;
        pt.vy *= 0.94;

        pt.x += pt.vx;
        pt.y += pt.vy;

        if (pt.x < -50) { pt.x = p.width + 50; }
        if (pt.x > p.width + 50) { pt.x = -50; }
        if (pt.y < -50) { pt.y = p.height + 50; }
        if (pt.y > p.height + 50) { pt.y = -50; }
    }

    function renderParticle(pt) {
        let n = p.noise(pt.x * noiseScale + pt.noiseOffsetX, pt.y * noiseScale + pt.noiseOffsetY, t * 0.3 + pt.noiseOffsetZ);

        let wavelength = 380 + (1 - pt.depth) * 320;
        let scatterHue = p.map(wavelength, 380, 700, 0, 360);
        let finalHue = (pt.baseHue + scatterHue + pt.hueShift * n) % 360;

        let saturation = p.map(pt.depth, 0, 1, 40, 90) * (0.6 + n * 0.4);
        let brightness = p.map(pt.depth, 0, 1, 30, 95) * (0.5 + n * 0.5);
        let alpha = p.map(n, 0, 1, 15, 55) * pt.depth * pt.lifetime;

        let particleSize = pt.size * (0.6 + pt.depth * 0.8) * (0.5 + n * 0.5);

        let coreHue = (finalHue + 20) % 360;
        p.fill(coreHue, saturation * 0.6, brightness * 1.1, alpha * 1.5);
        p.ellipse(pt.x, pt.y, particleSize * 1.8, particleSize * 1.8);

        p.fill(finalHue, saturation, brightness, alpha);
        p.ellipse(pt.x, pt.y, particleSize * 2.5, particleSize * 2.5);

        let haloHue = (finalHue + 30) % 360;
        p.fill(haloHue, saturation * 0.4, brightness * 0.8, alpha * 0.3);
        p.ellipse(pt.x, pt.y, particleSize * 5, particleSize * 5);
    }

    p.windowResized = function() {
        p.resizeCanvas(container.offsetWidth, container.offsetHeight);
    };
};

✨ AI 艺术解读

This piece visualizes the complex physical phenomenon of subsurface scattering, where light penetrates translucent materials and emerges at different locations with altered wavelengths. The Perlin noise-driven particle flow creates organic, living patterns that suggest light moving through wax, skin, or jade. As particles cluster near the center, their overlapping glows intensify, mimicking how subsurface materials appear to emit light from within. The emergent complexity arises from simple velocity rules, yet produces behaviors that feel genuinely organic and alive.

📝 补充说明

  • Subsurface scattering depends on wavelength - this code simulates it by mapping penetration depth to hue shifts from blue toward red-orange
  • Using ADD blend mode with multiple overlapping transparent layers creates the characteristic glow of translucent materials
  • Particle depth values affect both visual properties and physical behavior, creating natural variation in the scattering pattern
  • Multiple noise octaves prevent repetitive patterns and create the organic, flowing quality essential for realistic subsurface effects
  • The central attraction can be modulated over time or replaced with other force fields for different aesthetic results