p5.js作业A5


针对《代码本色》一书中以运动为主题的编程创造,这里进行一些p5的尝试。

粒子旋转

控制粒子按照动态角度进行旋转,旋转角度和时间的平方呈正比。
在这里插入图片描述
按住下键控制偏转角度变小
在这里插入图片描述

按住上键控制偏转角度变大
在这里插入图片描述
上箭头键控制粒子的旋转偏转角增大,下箭头控制粒子的旋转偏转角度减小。由于粒子的旋转角度按照帧率运动,所以粒子的轨迹会逐渐变成为圆。此时,增大或缩小旋转偏转角度就约等于增大或缩小旋转半径。而角度过大或者过小的粒子角度就会作为“不稳定”的存在脱离原本的旋转轨道。

let objs;
var rotatesize = 0.0074;
function setup() {
    createCanvas(800, 800);
    objs = new Array();
    noStroke();
    colorMode(HSB, 360, 100, 100);
}

function draw() {
    background(10, 0.02);
    translate(width / 2, height / 2);
    objs.push(new myline(createVector(2, 0).rotate(frameCount * frameCount / 1000), color(frameCount % 360, frameCount % 20 + 20, frameCount % 100 + 50)));
    objs.forEach(element => {
        element.cnt++;
        element.position.add(element.vec);
        if (element.cnt < 600) {
            element.vec.rotate(rotatesize);
        }
        fill(element.color);
        ellipse(element.position.x, element.position.y, 2, 2);
    });
    if (frameCount > 1000) {
        objs.shift();
    }
}
function mouseClicked() {
    if (mouseButton == LEFT) {
        rotatesize = rotatesize + 0.001;
    }
    else if (mouseButton == CENTER) {
        rotatesize = rotatesize - 0.001;
    }
}
class myline {
    constructor(vec, col) {
        this.position = createVector(0, 0);
        this.vec = vec;
        this.color = col;
        this.cnt = 0;
    }
}

引力与斥力(接实验A3的粒子吸引)

上一次的粒子只有单一的色彩,本次将粒子的颜色进行优化,使其看起来像一个小光斑。同时,设置了粒子的边界运动,粒子运动出边界时,会将其从另一侧进入。
在这里插入图片描述
在这里插入图片描述
用该系统模拟眼泪下坠过程
降低速度
按下e键降低粒子运动速度,按下q键生成粒子,按下w键清空画布。相比较于A3作业的结果多了一些光晕,这是通过选择blendMode模式来使得整体粒子的观感提升。

var g, friction;
var particles = [];
var n = 0;
var fr;
function setup() {
    createCanvas(600, 600);
    blendMode(ADD);
    imageMode(CENTER);
    frameRate(60);
    background(0);
}
function draw() {
    clear();
    background(0);
    for (var i = 0; i < n; i++) {
        for (var j = 0; j < n; j++) {
            if (i != j) {
                var force = particles[i].attract(particles[j]);
                particles[i].applyForce(force);
            }
        }
    }
    for (var i = 0; i < n; i++) {
        particles[i].update();
        particles[i].wallThrough();
        particles[i].display();
    }
}
function mouseClicked() {
    particles.push(new ParticleA(mouseX, mouseY, 0, 0));
    particles[n].createParticleImage();
    n++;
}
function keyTyped() {
    if (key === 'q') {
        particles.push(new ParticleA(mouseX, mouseY, 0, 0));
        particles[n].createParticleImage();
        n++;
    }
    else if (key === 'w') {
        particles = [];
        n = 0;
    }
    else if (key === 'e') {
        for (var particle of particles) {
            particle.velocity.x = random(-2, 2);
            particle.velocity.y = random(-2, 2);
        }
    }
}
class ParticleA {
    constructor(x, y, vx, vy) {
        this.mass = 10.0;
        this.location = createVector(random(width), random(height));
        this.location = createVector(x, y);
        this.velocity = createVector(0.0, 0.0);
        this.accelaration = createVector(vx, vy);
        this.img;
    }
    update() {
        this.velocity.add(this.accelaration);
        this.velocity.mult(1.0 - friction);
        this.location.add(this.velocity);
        this.accelaration.set(0.0, 0.0);
    }
    display() {
        image(this.img, this.location.x, this.location.y);
    }
    attract(particle) {
        var force = p5.Vector.sub(particle.location, this.location);
        var distance = force.mag();
        distance = constrain(distance, 4.0, 1000.0);
        if (distance > 20.0) {
            force.normalize();
            var strength = (g * this.mass * particle.mass) / pow(distance, 2.0) / 100.0;
            force.mult(strength);
            print(1);
        }
        else {
            force.normalize();
            var strength = (g * this.mass * particle.mass) / pow(distance, 2.0) / 100.0;
            force.mult(strength);
            print(2);
        }
        return force;
    }
    applyForce(force) {
        var f = p5.Vector.div(force, this.mass);
        this.accelaration.add(f);
    }
    wallThrough() {
        if (this.location.x > width) {
            this.location.x = 0;
        }
        if (this.location.x < 0) {
            this.location.x = width;
        }
        if (this.location.y > height) {
            this.location.y = 0;
        }
        if (this.location.y < 0) {
            this.location.y = height;
        }
    }
    createParticleImage() {
        var side = 400;
        var center = 200;
        this.img = createImage(side, side);
        var num = pow(10, 1.8);
        var Cr = random(100, 255);
        var Cg = random(100, 255);
        var Cb = random(100, 255);
        while ((Cr / Cg > 0.8 && Cr / Cg < 1.2) && (Cr / Cb > 0.8 && Cr / Cb < 1.2)) {
            var Cr = random(50, 255);
            var Cg = random(50, 255);
            var Cb = random(50, 255);
        }
        this.img.loadPixels();
        for (var y = 0; y < side; y++) {
            for (var x = 0; x < side; x++) {
                var d = (sq(center - x) + sq(center - y)) / num;
                var col = color(Cr / d, Cg / d, Cb / d);
                this.img.set(x, y, col);
            }
        }
        this.img.updatePixels();
        return this.img;
    }
}

水波模拟

在这里插入图片描述
单点波动
在这里插入图片描述

模拟多点水波浮动

由于需要控制每个点的大小,所以粒子系统需要统筹所有屏幕上点的运动。因此,当波运动到全屏幕时,需要计算width*height个点的运动情况,这样的内存消耗很大可能会造成卡顿。目前没有想到比较好的解决方法。
通过粒子的大小来表示水波运动情况,这是基于水波运动过程可以分解为两个方向上的分量。同时,在三维空间上水的运动轨迹是一个椭圆,所以向心加速度是一个恒定值。基于这一点,我们通过给圆的变化速度设置一个加速度值,这样就可以表现水波在二维平面上的分量。

var scrWidth = 600;
var scrHeight = 600;
var W = 10;
var R = 2;
var grid;
var w_t;
var h_t;

class Particle {
    constructor(x, y) {
        this.pos = new p5.Vector(x, y, 0);
        this.veloZ = 0;
    }
    tick(grid, x, y) {
        var a = new p5.Vector(0, 0, 0);
        if (x > 0) {
            a.add(p5.Vector.sub(grid.p[(x - 1) * h_t + y].pos, this.pos));
        }
        if (x < w_t - 1) {
            a.add(p5.Vector.sub(grid.p[(x + 1) * w_t + y].pos, this.pos));
        }
        if (y > 0) {
            a.add(p5.Vector.sub(grid.p[x * w_t + y - 1].pos, this.pos));
        }
        if (y < h_t - 1) {
            a.add(p5.Vector.sub(grid.p[x * w_t + y + 1].pos, this.pos));
        }
        this.veloZ += a.z * 0.01;
        this.veloZ -= this.pos.z * 0.05;
        this.veloZ -= this.veloZ * 0.01;
        this.pos.z += this.veloZ;
    }
    draw() {
        var r = lerp(1, R * 2, this.pos.z + 1);
        fill('#48B9FE');
        ellipse(this.pos.x, this.pos.y, r, r);
    }
}

class Grid {
    constructor(w, h) {
        this.p = [];
        for (var i = 0; i < h; i++) {
            for (var j = 0; j < w; j++) {
                this.p.push(new Particle(j * W, i * W));
            }
        }
    }
    tick() {
        if (mouseIsPressed) {
            var x = int((mouseX + W / 2) / W);
            var y = int((mouseY + W / 2) / W);
            this.p[x * w_t + y].pos.z -= 1;
        }
        for (var i = 0; i < h_t; i++) {
            for (var j = 0; j < w_t; j++) {
                this.p[j + i * w_t].tick(this, j, i);
            }
        }
    }
    draw() {
        var dx = (scrWidth - (h_t - 1) * W) / 2;
        var dy = (scrHeight - (w_t - 1) * W) / 2;
        translate(dx, dy);
        ellipseMode(RADIUS);
        noStroke();
        fill(255, 50, 0);
        for (var i = 0; i < h_t; i++) {
            for (var j = 0; j < w_t; j++) {
                this.p[j + i * w_t].draw();
            }
        }
    }
}
function setup() {
    createCanvas(600, 600);
    w_t = int(scrWidth / W);
    h_t = int(scrHeight / W);
    grid = new Grid(w_t, h_t);
}

function draw() {
    background(40);
    grid.tick();
    grid.draw();
}

流体模拟

在这里插入图片描述

通过流体方程来模拟流体运动,由于红色的效果比较好,所以使用红色来展示。
具体的思路是先根据鼠标的位置先生成上下左右中五个点的像素值,然后根据前一次和后一次的中心位置来确定流体的流向。位于运动背向的像素值快速消失,运动正向的像素值缓慢消失。为了有一个冲力效果,控制方向直线上的点运动快,方向两侧的店运动。然后通过ADD模式下的覆盖绘制,就可以控制整体红色染料的流动。

var N = 128;
var size;
var u = [];
var v = [];
var u_prev = [];
var v_prev = [];
var dens = [];
var dens_prev = [];
var Im = [];
var source = 10;
var diff = 0.0;
var visc = 0.0;
var dt = 0.01;
var dx = 1.0;
var vc = 5.0;


function setup() {
    createCanvas(500, 500);
    background(0);
    stroke(0, 0, 200);
    blendMode(ADD);
    initSim();
}

function draw() {
    background(0);
    dens_prev = dens.slice();
    u_prev = u.slice();
    v_prev = v.slice();
    add_density();
    add_velocity();
    vel_step();
    dens_step();
    vort_step();
    drawDensity();
}
function initSim() {
    size = (N + 2) * (N + 2);
    for (var i = 0; i < size; i++) {
        u[i] = 0.0;
        v[i] = 0.0;
        dens[i] = 0.0;
        Im[i] = 0.0;
    }
}

function ImageX(i, j) {
    return i + (N + 2) * j;
}

function PixelX(x, y) {
    return (x + width * y) * 4;
}

function normalize(x, y) {
    var lenxgth = sqrt(x * x + y * y) + 1e-5;
    x = x / length;
    y = y / length;
}

function add_density() {
    if (mouseIsPressed) {
        dens[ImageX(int((N / width) * mouseX), int((N / height) * mouseY))] += source;
        dens[ImageX(int((N / width) * mouseX - 1), int((N / height) * mouseY))] += source / 2;
        dens[ImageX(int((N / width) * mouseX + 1), int((N / height) * mouseY))] += source / 2;
        dens[ImageX(int((N / width) * mouseX), int((N / height) * mouseY - 1))] += source / 2;
        dens[ImageX(int((N / width) * mouseX), int((N / height) * mouseY + 1))] += source / 2;
    }
}

function add_velocity() {
    var i;
    if (mouseIsPressed) {
        i = ImageX(int((N / width) * mouseX), int((N / height) * mouseY));
        var xv = (N / width) * (mouseX - pmouseX);
        var yv = (N / height) * (mouseY - pmouseY);
        u[i] += xv * (2 / (abs(xv) + 1)) * 15;
        v[i] += yv * (2 / (abs(yv) + 1)) * 15;
    }
}

function calc_vorticity(Im0, u0, v0) {
    for (var i = 1 ; i <= N ; i++) {
        for (var j = 1 ; j <= N ; j++) {
            Im0[ImageX(i, j)] = (u0[ImageX(i, j + 1)] - u0[ImageX(i, j - 1)]) / dx - (v0[ImageX(i + 1, j)] - v0[ImageX(i - 1, j)]) / dx;
        }
    }
}

function confine_vorticity(Im0, u0, v0) {
    var Imgrad_x, Imgrad_y;
    for (var i = 1 ; i <= N ; i++) {
        for (var j = 1 ; j <= N ; j++) {
            Imgrad_x = abs(Im0[ImageX(i - 1, j)]) - abs(Im0[ImageX(i + 1, j)]);
            Imgrad_y = abs(Im0[ImageX(i, j - 1)]) - abs(Im0[ImageX(i, j + 1)]);
            normalize(Imgrad_x, Imgrad_y);
            u0[ImageX(i, j)] += Imgrad_y * Im0[ImageX(i, j)] * vc * dt;
            v0[ImageX(i, j)] += -Imgrad_x * Im0[ImageX(i, j)] * vc * dt;
        }
    }
}

function diffuse(b, x, x0, diff0) {
    var a = dt * diff0 * N * N;
    for (var k = 0 ; k < 20 ; k++) {
        for (var i = 1 ; i <= N ; i++) {
            for (var j = 1 ; j <= N ; j++) {
                x[ImageX(i, j)] = (x0[ImageX(i, j)] + a * (x[ImageX(i - 1, j)] + x[ImageX(i + 1, j)] + x[ImageX(i, j - 1)] + x[ImageX(i, j + 1)])) / (1 + 4 * a);
            }
        }
        set_bnd(b, x);
    }
}

function advect(b, d, d0, u0, v0) {
    var i0, j0, i1, j1;
    var x, y, s0, t0, s1, t1, dt0;
    dt0 = dt * N;
    for (var i = 1 ; i <= N ; i++) {
        for (var j = 1 ; j <= N ; j++) {
            x = i - dt0 * u0[ImageX(i, j)];
            y = j - dt0 * v0[ImageX(i, j)];
            if (x < 0.5)
                x = 0.5;
            if (x > N + 0.5)
                x = N + 0.5;
            i0 = int(x);
            i1 = i0 + 1;
            if (y < 0.5)
                y = 0.5;
            if (y > N + 0.5)
                y = N + 0.5;
            j0 = int(y);
            j1 = j0 + 1;
            s1 = x - i0;
            s0 = 1 - s1;
            t1 = y - j0;
            t0 = 1 - t1;
            d[ImageX(i, j)] = s0 * (t0 * d0[ImageX(i0, j0)] + t1 * d0[ImageX(i0, j1)]) + s1 * (t0 * d0[ImageX(i1, j0)] + t1 * d0[ImageX(i1, j1)]);
        }
    }
    set_bnd(b, d);
}

function dens_step() {
    diffuse(0, dens_prev, dens, diff);
    advect(0, dens, dens_prev, u, v);
}

function vel_step() {
    diffuse(1, u_prev, u, visc);
    diffuse(2, v_prev, v, visc);
    project(u_prev, v_prev, u, v);
    advect(1, u, u_prev, u_prev, v_prev);
    advect(2, v, v_prev, u_prev, v_prev);
    project(u, v, u_prev, v_prev);
}

function vort_step() {
    calc_vorticity(Im, u, v);
    confine_vorticity(Im, u, v);
}

function project(u0, v0, p, div) {
    var h = 1.0 / N;
    for (var i = 1 ; i <= N ; i++) {
        for (var j = 1 ; j <= N ; j++) {
            div[ImageX(i, j)] = 0.5 * h * (u0[ImageX(i + 1, j)] - u0[ImageX(i - 1, j)] + v0[ImageX(i, j + 1)] - v0[ImageX(i, j - 1)]);
            p[ImageX(i, j)] = 0;
        }
    }
    set_bnd(0, div);
    set_bnd(0, p);
    var residual;
    for (var k = 0 ; k < 100 ; k++) {
        for (i = 1 ; i <= N ; i++) {
            for (j = 1 ; j <= N ; j++) {
                residual = -4 * p[ImageX(i, j)] + p[ImageX(i - 1, j)] + p[ImageX(i + 1, j)] + p[ImageX(i, j - 1)] + p[ImageX(i, j + 1)] - div[ImageX(i, j)];
                p[ImageX(i, j)] += residual * (1.7 / 4);
            }
        }
        set_bnd(0, p);
    }
    for (i = 1 ; i <= N ; i++) {
        for (j = 1 ; j <= N ; j++) {
            u0[ImageX(i, j)] -= 0.5 * (p[ImageX(i + 1, j)] - p[ImageX(i - 1, j)]) / h;
            v0[ImageX(i, j)] -= 0.5 * (p[ImageX(i, j + 1)] - p[ImageX(i, j - 1)]) / h;
        }
    }
    set_bnd(1, u0);
    set_bnd(2, v0);
}

function set_bnd(b, x) {
    for (var i = 1 ; i <= N ; i++) {
        x[ImageX(0, i)] = b == 1 ? -x[ImageX(1, i)] : x[ImageX(1, i)];
        x[ImageX(N + 1, i)] = b == 1 ? -x[ImageX(N, i)] : x[ImageX(N, i)];
        x[ImageX(i, 0)] = b == 2 ? -x[ImageX(i, 1)] : x[ImageX(i, 1)];
        x[ImageX(i, N + 1)] = b == 2 ? -x[ImageX(i, N)] : x[ImageX(i, N)];
    }
    x[ImageX(0, 0)] = 0.5 * (x[ImageX(1, 0)] + x[ImageX(0, 1)]);
    x[ImageX(0, N + 1)] = 0.5 * (x[ImageX(1, N + 1)] + x[ImageX(0, N)]);
    x[ImageX(N + 1, 0)] = 0.5 * (x[ImageX(N, 0)] + x[ImageX(N + 1, 1)]);
    x[ImageX(N + 1, N + 1)] = 0.5 * (x[ImageX(N, N + 1)] + x[ImageX(N + 1, N)]);
}

function drawDensity() {
    var dx, dy, ddx, ddy;
    var df, di;
    loadPixels();
    for (var x = 0; x < width; x++) {
        for (var y = 0; y < height; y++) {
            dx = (N / width) * x;
            ddx = dx - int(dx);
            dy = (N / height) * y;
            ddy = dy - int(dy);
            df = (dens[ImageX(floor(dx), floor(dy))] * (1.0 - ddx) + dens[ImageX(ceil(dx), floor(dy))] * ddx) * (1.0 - ddy) + (dens[ImageX(floor(dx), ceil(dy))] * (1.0 - ddx) + dens[ImageX(ceil(dx), ceil(dy))] * ddx) * ddy;
            di = int(df * 255);
            if (di < 0)
                di = 0;
            if (di > 255)
                di = 255;
            pixels[PixelX(x, y)] = pixels[PixelX(x, y)] * (1 - df) + di * df;
            pixels[PixelX(x, y) + 1] = 0;
            pixels[PixelX(x, y) + 2] = 0;
            pixels[PixelX(x, y) + 3] = 255;
        }
    }
    updatePixels();
}
  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值