Perlin Nebula Drift

📅 April 23, 2026 🏷️ art
worth-a-look perlin-noise cloud-rendering 3d-effect particle-system flow-field volumetric emergent-art
Generated by GridFlow AI | Tags: perlin-noise, cloud-rendering, 3d-effect, particle-system, flow-field, volumetric, emergent-art

💡 AI 提示词

Create a 3D volumetric cloud rendering using 3D Perlin noise as a driving force for particle movement and field-based interactions, with emergent complexity through flow field dynamics and density-based visual effects

🔧 核心算法要点

  1. 3D Perlin noise sampling at multiple octaves creates a dynamic flow field with angle and strength vectors
  2. Particles navigate through the noise field, deriving their movement vectors from interpolated field values
  3. Time-based noise evolution (z-offset) creates continuously morphing cloud structures
  4. Depth-based rendering with particle size, alpha, and speed scaling creates 3D perception
  5. Multi-octave noise combination generates turbulence and organic curl motion patterns
  6. Life cycle system with edge-based respawning maintains particle density while preventing stagnation
  7. Density-based glow rendering creates volumetric cloud illusion through overlapping particle trails

🎨 原始代码

var sketch = function(p) {
    let particles = [];
    let numParticles = 1200;
    let noiseScale = 0.003;
    let timeOffset = 0;
    let flowField = [];
    let cols, rows;
    let resolution = 20;

    p.setup = function() {
        let container = document.getElementById('p5-wrapper');
        p.createCanvas(container.offsetWidth, container.offsetHeight).parent(container);
        p.colorMode(p.HSB, 360, 100, 100, 100);
        p.background(220, 30, 8);
        
        cols = p.floor(p.width / resolution) + 1;
        rows = p.floor(p.height / resolution) + 1;
        
        for (let i = 0; i < numParticles; i++) {
            particles.push(createParticle());
        }
    };

    function createParticle() {
        return {
            x: p.random(p.width),
            y: p.random(p.height),
            prevX: 0,
            prevY: 0,
            vx: 0,
            vy: 0,
            ax: 0,
            ay: 0,
            life: p.random(200, 500),
            maxLife: 0,
            size: p.random(1.5, 4),
            hue: p.random(180, 260),
            speed: p.random(0.3, 1.2),
            depth: p.random(0.3, 1.0)
        };
    }

    p.draw = function() {
        p.background(220, 30, 8, 8);
        timeOffset += 0.004;
        
        updateFlowField();
        
        for (let particle of particles) {
            updateParticle(particle);
            renderParticle(particle);
        }
        
        drawFlowFieldHints();
    };

    function updateFlowField() {
        let yOff = 0;
        for (let y = 0; y < rows; y++) {
            let xOff = 0;
            for (let x = 0; x < cols; x++) {
                let index = x + y * cols;
                
                let n1 = p.noise(xOff, yOff, timeOffset);
                let n2 = p.noise(xOff * 1.5, yOff * 1.5, timeOffset * 0.7);
                let angle = n1 * p.TWO_PI * 2.5 + n2 * p.TWO_PI;
                
                let strength = p.map(p.noise(xOff * 0.5, yOff * 0.5, timeOffset * 1.3), 0, 1, 0.5, 1.5);
                
                flowField[index] = {
                    angle: angle,
                    strength: strength,
                    noiseVal: n1
                };
                
                xOff += noiseScale * resolution;
            }
            yOff += noiseScale * resolution;
        }
    }

    function updateParticle(particle) {
        particle.prevX = particle.x;
        particle.prevY = particle.y;
        
        let col = p.floor(particle.x / resolution);
        let row = p.floor(particle.y / resolution);
        col = p.constrain(col, 0, cols - 1);
        row = p.constrain(row, 0, rows - 1);
        let index = col + row * cols;
        
        let field = flowField[index];
        if (field) {
            let curlStrength = 0.3;
            let perpendicularAngle = field.angle + p.HALF_PI;
            
            let turbulence = p.noise(particle.x * 0.01, particle.y * 0.01, timeOffset * 2);
            let turbulenceAngle = turbulence * p.TWO_PI * 0.5;
            
            particle.ax = p.cos(field.angle + turbulenceAngle) * field.strength * particle.speed;
            particle.ay = p.sin(field.angle + turbulenceAngle) * field.strength * particle.speed;
            
            let n = p.noise(particle.x * 0.008, particle.y * 0.008, timeOffset * 0.5);
            particle.hue = p.map(n, 0, 1, 200, 260);
        }
        
        particle.vx = particle.vx * 0.92 + particle.ax * 0.08;
        particle.vy = particle.vy * 0.92 + particle.ay * 0.08;
        
        particle.x += particle.vx * particle.depth;
        particle.y += particle.vy * particle.depth;
        
        particle.life--;
        
        if (particle.life <= 0 || particle.x < -50 || particle.x > p.width + 50 || particle.y < -50 || particle.y > p.height + 50) {
            resetParticle(particle);
        }
    }

    function resetParticle(particle) {
        let edge = p.floor(p.random(4));
        if (edge === 0) {
            particle.x = p.random(p.width);
            particle.y = -10;
        } else if (edge === 1) {
            particle.x = p.random(p.width);
            particle.y = p.height + 10;
        } else if (edge === 2) {
            particle.x = -10;
            particle.y = p.random(p.height);
        } else {
            particle.x = p.width + 10;
            particle.y = p.random(p.height);
        }
        particle.vx = 0;
        particle.vy = 0;
        particle.life = p.random(150, 400);
        particle.maxLife = particle.life;
        particle.depth = p.random(0.2, 1.0);
    }

    function renderParticle(particle) {
        let lifeRatio = particle.life / particle.maxLife;
        let alpha = p.map(lifeRatio, 0, 1, 10, 60) * particle.depth;
        let displaySize = particle.size * particle.depth * p.map(lifeRatio, 0, 1, 0.3, 1);
        
        let speed = p.sqrt(particle.vx * particle.vx + particle.vy * particle.vy);
        let brightness = p.map(speed, 0, 2, 70, 95);
        
        p.stroke(particle.hue, 60, brightness, alpha);
        p.strokeWeight(displaySize);
        p.line(particle.prevX, particle.prevY, particle.x, particle.y);
        
        if (speed > 0.5) {
            p.noStroke();
            p.fill(particle.hue, 40, 100, alpha * 0.3);
            p.ellipse(particle.x, particle.y, displaySize * 2, displaySize * 2);
        }
    }

    function drawFlowFieldHints() {
        p.noStroke();
        for (let i = 0; i < flowField.length; i += 3) {
            let x = (i % cols) * resolution;
            let y = p.floor(i / cols) * resolution;
            let field = flowField[i];
            
            if (field && field.noiseVal > 0.5) {
                let alpha = p.map(field.noiseVal, 0.5, 1, 0, 15);
                p.fill(220, 20, 100, alpha);
                p.ellipse(x, y, resolution * 0.8, resolution * 0.8);
            }
        }
    }

    p.windowResized = function() {
        let container = document.getElementById('p5-wrapper');
        p.resizeCanvas(container.offsetWidth, container.offsetHeight);
        cols = p.floor(p.width / resolution) + 1;
        rows = p.floor(p.height / resolution) + 1;
        flowField = [];
    };
};

✨ AI 艺术解读

This piece visualizes the hidden mathematics of turbulence through an emergent particle system driven by 3D Perlin noise. The particles act as traces of an invisible force field, revealing the elegant complexity that arises from simple noise-based rules. The drifting nebula effect represents how order emerges from chaos - each particle follows deterministic paths, yet their collective behavior creates organic, cloud-like formations that feel alive and unpredictable. The depth layering and parallax create a window into a three-dimensional noise space that the viewer can explore through the movement of thousands of individual traces.

📝 补充说明

  • Use multiple noise octaves (FBM-style) to create more organic and turbulent flow patterns
  • Interpolate flow field values for smooth particle movement between grid cells
  • Implement particle life cycles with edge respawning to maintain visual density over time
  • Map noise values to HSB color space for smooth hue transitions in the cloud formations
  • Adjust the ratio of background fade to create either wispy ethereal trails or dense volumetric clouds