可视化之粒子风场

绘制风场的关键点在Perlin 噪声。我们依然采用p5.js来绘制风场。 Perlin 噪声,Perlin 噪声是一种基于梯度的连续随机函数,可以用来生成连续的、自然的随机数。在这个代码中,我们使用 Perlin 噪声来计算粒子的运动角度,以此来实现粒子的运动轨迹。

在 Particle 类中,我们使用了 Perlin 噪声来计算粒子的角度,并根据角度计算粒子的方向向量和速度向量。然后,我们将速度向量乘以大小系数,并使用 add() 函数将速度向量加到位置向量上,从而更新粒子的位置。

在 Particle 类中,我们使用 checkEdges() 方法来检测粒子是否碰到边界,如果是则重新生成粒子。这样可以避免粒子飞出画布,同时也可以增加粒子的数量和密度。

在 setup() 函数中,我们创建了三个粒子数组,分别用来存储不同颜色的粒子对象。这样可以方便地遍历和绘制不同颜色的粒子。

在 draw() 函数中,我们使用 rect() 函数来绘制背景。由于我们希望背景具有一定的透明度,所以使用了 fill() 函数来设置背景颜色的透明度。

js 复制代码
// 定义粒子类
class Particle {
    constructor(loc_, dir_, speed_) {
        this.loc = loc_; // 粒子位置向量
        this.dir = dir_; // 粒子方向向量
        this.speed = speed_; // 粒子速度
        this.d = 1; // 粒子大小系数
    }

    // 粒子运动,包括更新位置和速度
    move() {
        // 使用 Perlin 噪声计算粒子的角度
        this.angle = noise(this.loc.x / noiseScale, this.loc.y / noiseScale, frameCount / noiseScale) * TWO_PI * noiseStrength;
        // 根据角度计算粒子的方向向量
        this.dir.x = cos(this.angle) + sin(this.angle) - sin(this.angle);
        this.dir.y = sin(this.angle) - cos(this.angle) * sin(this.angle);
        // 根据方向向量计算粒子的速度向量
        this.vel = this.dir.copy();
        this.vel.mult(this.speed * this.d);
        // 更新粒子的位置向量
        this.loc.add(this.vel);
    }

    // 检测粒子是否碰到边界,如果是则重新生成粒子
    checkEdges() {
        if (this.loc.x < 0 || this.loc.x > width || this.loc.y < 0 || this.loc.y > height) {
            this.loc.x = random(width * 1.2);
            this.loc.y = random(height);
        }
    };

    // 更新粒子的大小和位置
    update(r) {
        ellipse(this.loc.x, this.loc.y, r);
    };

    // 粒子的总运动,包括运动、碰撞检测和更新
    run() {
        this.move();
        this.checkEdges();
        this.update();
    }
}

// 粒子数量
let num = 1024;

// 粒子数组
var particles_a = [];
var particles_b = [];
var particles_c = [];

// 粒子消失的速度
var fade = 600;

// 粒子半径
var radius = 3; 

// 噪声的规模
let noiseScale = 400;

// 噪声的强度
let noiseStrength = 1.4;

function setup() {
    createCanvas(windowWidth, windowHeight);
    noStroke();
    // 创建粒子数组
    for (let i = 0; i < num; i++) {
        // 随机生成粒子的位置向量和方向向量
        let loc_a = createVector(random(width * 1.2), random(height), 2);
        let angle_a = random(TWO_PI);
        let dir_a = createVector(cos(angle_a), sin(angle_a));
        let loc_b = createVector(random(width * 1.2), random(height), 2);
        let angle_b = random(TWO_PI);
        let dir_b = createVector(cos(angle_b), sin(angle_b));
        let loc_c = createVector(random(width * 1.2), random(height), 2);
        let angle_c = random(TWO_PI);
        let dir_c = createVector(cos(angle_c), sin(angle_c));
        // 创建粒子对象并添加到数组中
        particles_a[i] = new Particle(loc_a, dir_a, 0.5);
        particles_b[i] = new Particle(loc_b, dir_b, 0.5);
        particles_c[i] = new Particle(loc_c, dir_c, 0.75);

    }
}

function draw() {
    // 绘制背景
    fill(0, 2); 
    noStroke();
    rect(0, 0, width, height);

    // 遍历所有的粒子
    for (let i = 0; i < num; i++) {
        // 绘制蓝色粒子
        fill(255, fade); 
        particles_a[i].move();
        particles_a[i].update(radius);
        particles_a[i].checkEdges();

        // 绘制青色粒子
        fill(0, 255, 255, fade); 
        particles_b[i].move();
        particles_b[i].update(radius);
        particles_b[i].checkEdges();

        // 绘制浅蓝色粒子
        fill(102, 153, 255, fade); 
        particles_c[i].move();
        particles_c[i].update(radius);
        particles_c[i].checkEdges();
    }
}

在 Particle 类中,我们定义了四个方法:

  1. constructor() 方法:在创建粒子对象时,初始化粒子的位置向量、方向向量、速度和大小系数。

  2. move() 方法:根据 Perlin 噪声计算粒子的角度,并根据角度计算粒子的方向向量和速度向量,最后根据速度向量更新粒子的位置向量。

  3. checkEdges() 方法:检测粒子是否碰到边界,如果是则重新生成粒子。

  4. update() 方法:更新粒子的大小和位置。

在 setup() 函数中,我们完成了以下工作:

  1. 创建画布。

  2. 创建粒子数组。

  3. 随机生成粒子的位置向量和方向向量,并创建粒子对象并添加到数组中。

在 draw() 函数中,我们完成了以下工作:

  1. 绘制背景。

  2. 遍历所有的粒子,在每个粒子的位置绘制相应颜色的粒子。

  3. 调用粒子对象的方法,包括运动、碰撞检测和更新。

相关推荐
卸任2 小时前
Vibe coding工具使用心得
前端·ai编程
brien93 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance3 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M3 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写3 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李3 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易3 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马3 小时前
前沿视界
前端
kyriewen4 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易4 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员