我想在网络浏览器中使用每帧/顶点操作最少的方法进行简单的布料模拟,我认为这是显式的欧拉。这是一个使用香草 JS 的非常基本的初稿,布料是 4D,但这不应该对算法产生影响:
class Phase {
constructor(r, bf, ns) {
this.r = r;
this.bf = bf;
this.ns = ns;
this.s = math.random([4], 0, 2 * math.pi);
this.o = math.multiply(this.r,math.sin(this.s));
}
tck() {
this.s = math.mod(
math.add(this.s, this.bf, math.random([4], 0, this.ns)),
2 * math.pi
);
this.o = math.multiply(this.r,math.sin(this.s));
}
}
class Cloth {
constructor(n, d, e) {
this.n = n;
this.d = d;
this.e = e;
this.dt = [];
for (let i = 0; i < this.n; i++) {
this.dt[i] = [];
for (let j = 0; j < this.n; j++) {
this.dt[i][j] = {
p: [i / n, j / n, 0, 0],
v: [0, 0, 0, 0]
};
}
}
}
tck() {
for (let i = 0; i < this.n; i++) {
for (let j = 0; j < this.n; j++) {
if ((i == 0 || i == this.n - 1) && (j == 0 || j == this.n - 1)) {
continue;
}
let nbh = [];
if (0 < i) {
nbh.push(this.dt[i - 1][j]);
}
if (0 < j) {
nbh.push(this.dt[i][j - 1]);
}
if (j < this.n - 1) {
nbh.push(this.dt[i][j + 1]);
}
if (i < this.n - 1) {
nbh.push(this.dt[i + 1][j]);
}
this.dt[i][j].v = math.multiply(this.d, this.dt[i][j].v);
for (let nb of nbh) {
this.dt[i][j].v = math.add(
this.dt[i][j].v,
math.multiply(
this.e,
math.distance(nb.p, this.dt[i][j].p) - 1 / this.n,
math.subtract(nb.p, this.dt[i][j].p)
)
);
}
}
}
for (let i = 0; i < this.n; i++) {
for (let j = 0; j < this.n; j++) {
if ((i == 0 || i == this.n - 1) && (j == 0 || j == this.n - 1)) {
continue;
}
this.dt[i][j].p = math.add(this.dt[i][j].p, this.dt[i][j].v);
}
}
}
drw(ctx, bs, ds) {
ctx.beginPath();
for (let i = 0; i < this.n; i++) {
for (let j = 0; j < this.n; j++) {
if (i < this.n - 1) {
ctx.moveTo(
bs + ds * this.dt[i][j].p[0],
bs + ds * this.dt[i][j].p[1]
);
ctx.lineTo(
bs + ds * this.dt[i + 1][j].p[0],
bs + ds * this.dt[i + 1][j].p[1]
);
ctx.stroke();
}
if (j < this.n - 1) {
ctx.beginPath();
ctx.moveTo(
bs + ds * this.dt[i][j].p[0],
bs + ds * this.dt[i][j].p[1]
);
ctx.lineTo(
bs + ds * this.dt[i][j + 1].p[0],
bs + ds * this.dt[i][j + 1].p[1]
);
ctx.stroke();
}
}
}
}
}
cloth = new Cloth(20, 0.99, 5);
phase = [...Array(4)].map(() => new Phase(0.02, 0.2, 0.001));
cntxt = document.createElement("canvas").getContext("2d");
cntxt.canvas.width = 500;
cntxt.canvas.height = 500;
document.body.append(cntxt.canvas);
(function f() {
cloth.dt[0][0].p = phase[0].o;
cloth.dt[0][cloth.n - 1].p = math.add([0, 1, 0, 0], phase[1].o);
cloth.dt[cloth.n - 1][0].p = math.add([1, 0, 0, 0], phase[2].o);
cloth.dt[cloth.n - 1][cloth.n - 1].p = math.add([1, 1, 0, 0], phase[3].o);
cntxt.clearRect(0, 0, 500, 500);
cloth.drw(cntxt, 50, 400);
cloth.tck();
for (ph of phase) {
ph.tck();
}
window.requestAnimationFrame(f);
})()
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/9.3.0/math.js"></script>
两个明显的加速方法是 WASM 和 WebGL(例如通过 GPU.js)。我从未尝试过 WASM,并且在 WebGL 方面有一些经验,但不适用于通用计算。所以在我开始研究其中之一之前,最终哪一个能让我获得最快的结果?