js实现的模拟弹性网格布拉扯仿真动画可拉伸可切割

题目比较长,先提供一个图片看看效果

 也可以自己查看预览图 在线预览icon-default.png?t=M5H6http://mubanmao.top/content/preview?id=686f9976-3c54-45b1-9e51-1a81389d5fbb

主要的js代码

let accuracy = 5,
    clothX = 60,
    clothY = 25,
    spacing = 8,
    tearDist = spacing * 4,
    friction = 0.99,
    bounce = 0.5,
    gravity = 400,
    modePull = true;


const canvas = document.getElementById("canvas"),
      ctx = canvas.getContext("2d");

ctx.strokeStyle = "#555";

const w = canvas.width = Math.min(700, window.innerWidth),
      h = canvas.height = 400;

const mouse = {
  cut: spacing,
  influence: spacing * 2,
  down: false,
  //button: 1,
  x: 0,
  y: 0,
  px: 0,
  py: 0,
};

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.px = x;
    this.py = y;
    this.vx = 0;
    this.vy = 0;
    this.pinX = null;
    this.pinY = null;

    this.constraints = [];
  }

  update(delta) {
    if (this.pinX && this.pinY) { return this; }

    if (mouse.down) {
      let dx = this.x - mouse.x;
      let dy = this.y - mouse.y;
      let dist = Math.sqrt(dx * dx + dy * dy);

      if (modePull && dist < mouse.influence) {
        this.px = this.x - (mouse.x - mouse.px);
        this.py = this.y - (mouse.y - mouse.py);
      } else if (dist < mouse.cut) {
        this.constraints = [];
      }
    }

    this.addForce(0, gravity);

    let nx = this.x + (this.x - this.px) * friction + this.vx * delta;
    let ny = this.y + (this.y - this.py) * friction + this.vy * delta;

    this.px = this.x;
    this.py = this.y;

    this.x = nx;
    this.y = ny;

    this.vy = this.vx = 0;

    if (this.x >= w) {
      this.px = w + (w - this.px) * bounce;
      this.x = w;
    } else if (this.x <= 0) {
      this.px *= -1 * bounce;
      this.x = 0;
    }

    if (this.y >= h) {
      this.py = h + (h - this.py) * bounce;
      this.y = h;
    } else if (this.y <= 0) {
      this.py *= -1 * bounce;
      this.y = 0;
    }

    return this;
  }

  draw() {
    let i = this.constraints.length;
    while (i--) this.constraints[i].draw();
  }

  resolve() {
    if (this.pinX && this.pinY) {
      this.x = this.pinX;
      this.y = this.pinY;
      return;
    }

    this.constraints.forEach(constraint => constraint.resolve());
  }

  attach(point) {
    this.constraints.push(new Constraint(this, point));
  }

  free(constraint) {
    this.constraints.splice(this.constraints.indexOf(constraint), 1);
  }

  addForce(x, y) {
    this.vx += x;
    this.vy += y;
  }

  pin(pinx, piny) {
    this.pinX = pinx;
    this.pinY = piny;
  }
}

class Constraint {
  constructor(p1, p2) {
    this.p1 = p1;
    this.p2 = p2;
    this.length = spacing;
  }

  resolve() {
    let dx = this.p1.x - this.p2.x;
    let dy = this.p1.y - this.p2.y;
    let dist = Math.sqrt(dx * dx + dy * dy);

    if (dist < this.length) return;

    let diff = (this.length - dist) / dist;

    if (dist > tearDist) this.p1.free(this);

    let mul = diff * 0.5 * (1 - this.length / dist);

    let px = dx * mul;
    let py = dy * mul;

    !this.p1.pinX && (this.p1.x += px);
    !this.p1.pinY && (this.p1.y += py);
    !this.p2.pinX && (this.p2.x -= px);
    !this.p2.pinY && (this.p2.y -= py);

    return this;
  }

  draw() {
    ctx.moveTo(this.p1.x, this.p1.y);
    ctx.lineTo(this.p2.x, this.p2.y);
  }
}

class Cloth {
  constructor(free) {
    this.points = [];

    let startX = w / 2 - clothX * spacing / 2;

    for (let y = 0; y <= clothY; y++) {
      for (let x = 0; x <= clothX; x++) {
        let point = new Point(startX + x * spacing, 20 + y * spacing);
        !free && y === 0 && point.pin(point.x, point.y);
        x !== 0 && point.attach(this.points[this.points.length - 1]);
        y !== 0 && point.attach(this.points[x + (y - 1) * (clothX + 1)]);

        this.points.push(point);
      }
    }
  }

  update(delta) {
    let i = accuracy;

    while (i--) {
      this.points.forEach(point => {
        point.resolve();
      });
    }

    ctx.beginPath();
    this.points.forEach(point => {
      point.update(delta * delta).draw();
    });
    ctx.stroke();
  }
}

dragTracker({
  container: canvas,
  callbackDragStart: (_, pos) => {
    mouse.down = true;
    mouse.px = mouse.x = pos[0];
    mouse.py = mouse.y = pos[1];
  },
  callback: (_, pos) => {
    mouse.px = mouse.x;
    mouse.py = mouse.y;
    mouse.x = pos[0];
    mouse.y = pos[1];
  },
  callbackDragEnd: () => {
    mouse.down = false;
  },
});


let cloth = new Cloth();

//Animation loop
(function update(time) {
  ctx.clearRect(0, 0, w, h);
  cloth.update(0.016);

  requestAnimationFrame(update);
})();


let _g;
function zeroG() {
  if(gravity) {
    _g = gravity;
    gravity = 0;
  }
  else {
    gravity = _g;
  }
}
function setMode(pull) {
  modePull = pull;
}
function reset() {
  cloth = new Cloth(!gravity);
}





// window.requestAnimFrame =
//   window.requestAnimationFrame ||
//   window.webkitRequestAnimationFrame ||
//   window.mozRequestAnimationFrame ||
//   window.oRequestAnimationFrame ||
//   window.msRequestAnimationFrame ||
//   function (callback) {
//     window.setTimeout(callback, 1e3 / 60)
//   }

// let accuracy = 5
// let gravity = 400
// let clothY = 24
// let clothX = 50
// let spacing = 8
// let tearDist = 60
// let friction = 0.99
// let bounce = 0.5

// let canvas = document.getElementById('canvas')
// let ctx = canvas.getContext('2d')

// canvas.width = Math.min(700, window.innerWidth)
// canvas.height = 400

// ctx.strokeStyle = '#555'

// let mouse = {
//   cut: 8,
//   influence: 36,
//   down: false,
//   button: 1,
//   x: 0,
//   y: 0,
//   px: 0,
//   py: 0
// }

// class Point {
//   constructor (x, y) {
//     this.x = x
//     this.y = y
//     this.px = x
//     this.py = y
//     this.vx = 0
//     this.vy = 0
//     this.pinX = null
//     this.pinY = null

//     this.constraints = []
//   }

//   update (delta) {
//     if (this.pinX && this.pinY) return this

//     if (mouse.down) {
//       let dx = this.x - mouse.x
//       let dy = this.y - mouse.y
//       let dist = Math.sqrt(dx * dx + dy * dy)

//       if (mouse.button === 1 && dist < mouse.influence) {
//         this.px = this.x - (mouse.x - mouse.px)
//         this.py = this.y - (mouse.y - mouse.py)
//       } else if (dist < mouse.cut) {
//         this.constraints = []
//       }
//     }

//     this.addForce(0, gravity)

//     let nx = this.x + (this.x - this.px) * friction + this.vx * delta
//     let ny = this.y + (this.y - this.py) * friction + this.vy * delta

//     this.px = this.x
//     this.py = this.y

//     this.x = nx
//     this.y = ny

//     this.vy = this.vx = 0

//     if (this.x >= canvas.width) {
//       this.px = canvas.width + (canvas.width - this.px) * bounce
//       this.x = canvas.width
//     } else if (this.x <= 0) {
//       this.px *= -1 * bounce
//       this.x = 0
//     }

//     if (this.y >= canvas.height) {
//       this.py = canvas.height + (canvas.height - this.py) * bounce
//       this.y = canvas.height
//     } else if (this.y <= 0) {
//       this.py *= -1 * bounce
//       this.y = 0
//     }

//     return this
//   }

//   draw () {
//     let i = this.constraints.length
//     while (i--) this.constraints[i].draw()
//   }

//   resolve () {
//     if (this.pinX && this.pinY) {
//       this.x = this.pinX
//       this.y = this.pinY
//       return
//     }

//     this.constraints.forEach((constraint) => constraint.resolve())
//   }

//   attach (point) {
//     this.constraints.push(new Constraint(this, point))
//   }

//   free (constraint) {
//     this.constraints.splice(this.constraints.indexOf(constraint), 1)
//   }

//   addForce (x, y) {
//     this.vx += x
//     this.vy += y
//   }

//   pin (pinx, piny) {
//     this.pinX = pinx
//     this.pinY = piny
//   }
// }

// class Constraint {
//   constructor (p1, p2) {
//     this.p1 = p1
//     this.p2 = p2
//     this.length = spacing
//   }

//   resolve () {
//     let dx = this.p1.x - this.p2.x
//     let dy = this.p1.y - this.p2.y
//     let dist = Math.sqrt(dx * dx + dy * dy)

//     if (dist < this.length) return

//     let diff = (this.length - dist) / dist

//     if (dist > tearDist) this.p1.free(this)

//     let mul = diff * 0.5 * (1 - this.length / dist)

//     let px = dx * mul
//     let py = dy * mul

//     !this.p1.pinX && (this.p1.x += px)
//     !this.p1.pinY && (this.p1.y += py)
//     !this.p2.pinX && (this.p2.x -= px)
//     !this.p2.pinY && (this.p2.y -= py)

//     return this
//   }

//   draw () {
//     ctx.moveTo(this.p1.x, this.p1.y)
//     ctx.lineTo(this.p2.x, this.p2.y)
//   }
// }

// class Cloth {
//   constructor (free) {
//     this.points = []

//     let startX = canvas.width / 2 - clothX * spacing / 2

//     for (let y = 0; y <= clothY; y++) {
//       for (let x = 0; x <= clothX; x++) {
//         let point = new Point(startX + x * spacing, 20 + y * spacing)
//         !free && y === 0 && point.pin(point.x, point.y)
//         x !== 0 && point.attach(this.points[this.points.length - 1])
//         y !== 0 && point.attach(this.points[x + (y - 1) * (clothX + 1)])

//         this.points.push(point)
//       }
//     }
//   }

//   update (delta) {
//     let i = accuracy

//     while (i--) {
//       this.points.forEach((point) => {
//         point.resolve()
//       })
//     }

//     ctx.beginPath()
//     this.points.forEach((point) => {
//       point.update(delta * delta).draw()
//     })
//     ctx.stroke()
//   }
// }

// function setMouse (e) {
//   let rect = canvas.getBoundingClientRect()
//   mouse.px = mouse.x
//   mouse.py = mouse.y
//   mouse.x = e.clientX - rect.left
//   mouse.y = e.clientY - rect.top
// }

// canvas.onmousedown = (e) => {
//   mouse.button = e.which
//   mouse.down = true
//   setMouse(e)
// }

// canvas.onmousemove = setMouse

// canvas.onmouseup = () => (mouse.down = false)

// canvas.oncontextmenu = (e) => e.preventDefault()

// let cloth = new Cloth()

// function zeroG() {
//   gravity = 0
//   cloth = new Cloth(true)
// }

// ;(function update (time) {
//   ctx.clearRect(0, 0, canvas.width, canvas.height)

//   cloth.update(0.016)

//   window.requestAnimFrame(update)
// })(0)
<head>
<link href="style.scss" rel="stylesheet" type="text/css">
</head>
<body>

<canvas id="canvas"></canvas>

<div>
  <label><input type="radio" name="dragmode" onchange="setMode(true)" checked /> Pull</label>
  <label><input type="radio" name="dragmode" onchange="setMode(false)" /> Cut</label>
  <label><input type="checkbox" onchange="zeroG()" /> Zero G</label>
  <p>
    <button onclick="reset()">Reset</button>
    <!--button onclick="zeroG()">Zero G!</button-->
  </p>
</div>

<script type="text/javascript" src="drag-tracker.js"></script>

<script type="text/javascript" src="Cloth.js"></script>

</body>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 流体仿真是计算机图形学中的一个重要研究方向之一,它主要关注如何使用计算机模拟和呈现流体的行为和外观。在流体仿真中,网格是非常重要的一部分,因为它可以将流体分割成离散的区域,便于计算机进行处理。正交网格是一种常用的网格类型,它可以将流体分割成规则的立方体或长方体区域。对于不可压缩流体,正交网格可以通过考虑质量守恒和动量守恒来进行模拟。具体而言,可以使用Navier-Stokes方程组来描述流体的运动,然后使用数值方法(如有限差分或有限元方法)对其进行离散化求解。此外,还需要考虑边界条件和时间步长等问题,以保证模拟结果的精度和稳定性。在Mac平台上,可以使用一些流体仿真软件来进行正交网格下的不可压缩流体图形学模拟,例如Houdini、Blender等。 ### 回答2: 流体仿真是计算机图形学中的重要内容之一,它可以模拟出各种液态和气态的流体运动和效果。在mac网格中,正交网格下的流体仿真主要针对不可压缩流体进行图形学模拟。 正交网格是一种常用的离散化方法,它将计算区域划分为规则的矩形网格单元。在流体仿真中,正交网格可以被用来表示流体的离散化空间,在每个网格单元上存储流体的各种属性,如速度、压力等。同时,正交网格也可以通过有限差分等方法计算流体的运动和相互作用。 不可压缩流体是一种在流体运动中密度近似恒定的流体模拟模型。在图形学中模拟不可压缩流体可以通过Navier-Stokes方程来描述流体的运动规律,并结合约束条件来保证流体的不可压缩性。 在mac网格下,正交网格的流体仿真可以通过求解离散化的Navier-Stokes方程来实现。具体地,可以使用流体的速度和压力场来计算流体的加速度,并根据离散化的时间步长来更新流体的位置。在每个时间步长内,需要根据流体的速度和压力场来计算流体的加速度,然后更新流体的位置和速度。同时,需要使用约束条件来保证流体的不可压缩性,例如使用散度(divergence)来约束流体的速度场。 总之,计算机图形学中的流体仿真涉及到离散化的正交网格和求解流体运动方程,并结合约束条件来模拟不可压缩流体的图形学效果。通过这种方法,可以实现各种流体运动和效果的真实感模拟。 ### 回答3: 计算机图形学流体仿真是通过计算机模拟流体运动行为的一种技术,其中包括了网格方法。正交网格是一种常用的网格类型,它将计算区域划分为一系列正方形或长方形的单元格。 在正交网格下进行不可压缩流体的图形学模拟,首先需要将流体域离散化为网格,并对每个单元格中的流体属性进行计算和存储。对于不可压缩流体,通过求解连续性方程和Navier-Stokes方程来模拟流体的运动。 连续性方程(也称为质量守恒方程)描述了流体在空间和时间上的流动行为,可以通过计算流体的速度和压力来求解。在正交网格下,可以使用有限差分法或有限体积法来进行求解。 Navier-Stokes方程是流体动力学的基本方程,它包括了连续性方程和动量守恒方程。动量守恒方程描述了流体中各个位置上的速度随时间的变化规律。在正交网格下,可以使用差分方法来离散化Navier-Stokes方程,并通过迭代求解来模拟流体的运动。 在正交网格下进行不可压缩流体的图形学模拟需要考虑边界条件、初值条件以及时间步长等因素。通常使用数值方法来求解流体的速度和压力场,并根据得到的结果来更新流体的位置和速度。通过不断迭代计算,可以模拟出流体在正交网格上的运动和形态变化。 总结来说,正交网格是一种常用的网格类型,在计算机图形学流体仿真中可以用于模拟不可压缩流体的行为。通过离散化连续性方程和Navier-Stokes方程,并使用求解方法进行迭代计算,可以实现流体的图形学模拟

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值