webphysics-avbd-engine

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

webphysics-avbd-engine

webphysics-avbd-engine

Skill by ara.so — Daily 2026 Skills collection.
ara.so提供的技能——2026每日技能合集。

What It Does

功能介绍

webphysics
is an experimental WebGPU-accelerated rigid-body and soft-body physics engine implementing the AVBD (Augmented Vertex Block Descent) solver from Giles et al. (2025). It runs entirely on the GPU using WebGPU compute shaders and supports:
  • Rigid-body simulation with contacts, friction, and joints
  • GPU broad-phase collision detection via LBVH (Linear BVH)
  • Narrow-phase manifold generation with warm-start persistence
  • Graph-coloring-based parallel body solves
  • Springs and soft-body constraints
  • Body sleeping/diagnostics
Browser support: Chrome only (requires WebGPU). This is an experimental proof-of-concept, not a production library.
webphysics
是一款实验性的WebGPU加速刚体与软体物理引擎,实现了Giles等人(2025年)提出的**AVBD(增强顶点块下降法)**求解器(论文链接:Giles et al. (2025))。它完全通过WebGPU计算着色器在GPU上运行,支持以下特性:
  • 包含接触、摩擦与关节的刚体模拟
  • 基于LBVH(线性包围体层次结构)的GPU宽阶段碰撞检测
  • 带热启动持久化的窄阶段流形生成
  • 基于图着色的并行物体求解
  • 弹簧与软体约束
  • 物体休眠/诊断功能
**浏览器支持:**仅Chrome浏览器(需WebGPU支持)。这是一个实验性概念验证项目,并非生产级库。

Installation & Setup

安装与设置

sh
git clone https://github.com/jure/webphysics.git
cd webphysics
npm install
npm run dev        # development server
npm run build      # production build
The dev server typically starts at
http://localhost:5173
(Vite-based).
sh
git clone https://github.com/jure/webphysics.git
cd webphysics
npm install
npm run dev        # 开发服务器
npm run build      # 生产构建
开发服务器通常启动于
http://localhost:5173
(基于Vite)。

Project Structure

项目结构

src/
├── physics/
│   ├── PhysicsEngine.ts          # Main orchestration: substep loop, init, step
│   └── gpu/
│       ├── avbdState.ts          # Primal/dual solve, coloring, velocity finalization
│       ├── broadPhase.ts         # LBVH broad-phase candidate generation
│       ├── contactGeneration.ts  # Narrow-phase manifolds, per-body constraint lists
│       ├── contactRecord.ts      # Warm-start state persistence
│       └── avbdState.ts          # Inertial targets, primal init, iteration
├── lvbh/
│   └── GPULBVHBuilder.ts         # GPU LBVH construction
└── ...
src/
├── physics/
│   ├── PhysicsEngine.ts          # 主编排模块:子步循环、初始化、步进
│   └── gpu/
│       ├── avbdState.ts          # 原始/对偶求解、着色、速度最终化
│       ├── broadPhase.ts         # LBVH宽阶段候选生成
│       ├── contactGeneration.ts  # 窄阶段流形、逐物体约束列表
│       ├── contactRecord.ts      # 热启动状态持久化
│       └── avbdState.ts          # 惯性目标、原始初始化、迭代
├── lvbh/
│   └── GPULBVHBuilder.ts         # GPU LBVH构建
└── ...

Core API Usage

核心API使用

Initializing the Physics Engine

初始化物理引擎

typescript
import { PhysicsEngine } from './src/physics/PhysicsEngine';

// Requires an existing GPUDevice
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

const engine = new PhysicsEngine(device);
await engine.init();
typescript
import { PhysicsEngine } from './src/physics/PhysicsEngine';

// 需要已有的GPUDevice
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

const engine = new PhysicsEngine(device);
await engine.init();

Adding Rigid Bodies

添加刚体

typescript
// Add a static ground plane
engine.addBody({
  type: 'box',
  position: [0, -1, 0],
  rotation: [0, 0, 0, 1],   // quaternion [x, y, z, w]
  halfExtents: [10, 0.5, 10],
  mass: 0,                   // 0 = static/infinite mass
  restitution: 0.3,
  friction: 0.5,
});

// Add a dynamic rigid box
engine.addBody({
  type: 'box',
  position: [0, 5, 0],
  rotation: [0, 0, 0, 1],
  halfExtents: [0.5, 0.5, 0.5],
  mass: 1.0,
  restitution: 0.2,
  friction: 0.6,
});
typescript
// 添加静态地面平面
engine.addBody({
  type: 'box',
  position: [0, -1, 0],
  rotation: [0, 0, 0, 1],   // 四元数 [x, y, z, w]
  halfExtents: [10, 0.5, 10],
  mass: 0,                   // 0 = 静态/无限质量
  restitution: 0.3,
  friction: 0.5,
});

// 添加动态刚体盒子
engine.addBody({
  type: 'box',
  position: [0, 5, 0],
  rotation: [0, 0, 0, 1],
  halfExtents: [0.5, 0.5, 0.5],
  mass: 1.0,
  restitution: 0.2,
  friction: 0.6,
});

Stepping the Simulation

运行模拟步进

typescript
const TIMESTEP = 1 / 60;
const SUBSTEPS = 10;

function gameLoop(dt: number) {
  engine.step(dt, SUBSTEPS);
  // Read back positions for rendering
  const bodyStates = engine.getBodyStates();
  renderBodies(bodyStates);
  requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);
typescript
const TIMESTEP = 1 / 60;
const SUBSTEPS = 10;

function gameLoop(dt: number) {
  engine.step(dt, SUBSTEPS);
  // 读取位置用于渲染
  const bodyStates = engine.getBodyStates();
  renderBodies(bodyStates);
  requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);

Reading Body State for Rendering

读取物体状态用于渲染

typescript
// After engine.step(), retrieve updated transforms
const states = engine.getBodyStates();
for (const state of states) {
  const { position, rotation, bodyIndex } = state;
  // position: [x, y, z]
  // rotation: quaternion [x, y, z, w]
  updateMeshTransform(bodyIndex, position, rotation);
}
typescript
// 在engine.step()之后,获取更新后的变换
const states = engine.getBodyStates();
for (const state of states) {
  const { position, rotation, bodyIndex } = state;
  // position: [x, y, z]
  // rotation: 四元数 [x, y, z, w]
  updateMeshTransform(bodyIndex, position, rotation);
}

Adding Joints / Constraints

添加关节/约束

typescript
// Distance joint between two bodies
engine.addJoint({
  type: 'distance',
  bodyA: 0,
  bodyB: 1,
  anchorA: [0, 0.5, 0],   // local-space anchor on body A
  anchorB: [0, -0.5, 0],  // local-space anchor on body B
  restLength: 1.0,
  stiffness: 1e4,
});
typescript
// 两个物体间的距离关节
engine.addJoint({
  type: 'distance',
  bodyA: 0,
  bodyB: 1,
  anchorA: [0, 0.5, 0],   // 物体A本地空间锚点
  anchorB: [0, -0.5, 0],  // 物体B本地空间锚点
  restLength: 1.0,
  stiffness: 1e4,
});

Adding Springs (Soft Bodies)

添加弹簧(软体)

typescript
engine.addSpring({
  bodyA: 2,
  bodyB: 3,
  anchorA: [0, 0, 0],
  anchorB: [0, 0, 0],
  restLength: 0.8,
  stiffness: 500,
  damping: 10,
});
typescript
engine.addSpring({
  bodyA: 2,
  bodyB: 3,
  anchorA: [0, 0, 0],
  anchorB: [0, 0, 0],
  restLength: 0.8,
  stiffness: 500,
  damping: 10,
});

AVBD Pipeline Reference

AVBD管线参考

The solver follows Algorithm 1 from the AVBD paper:
1. collision detection (x^t)
2. broad phase (LBVH)         → src/lvbh/GPULBVHBuilder.ts
3. narrow phase + warm start  → src/physics/gpu/contactGeneration.ts
4. per-body constraint lists  → src/physics/gpu/avbdState.ts
5. graph coloring             → src/physics/gpu/avbdState.ts
6. inertial target y, primal init, warm-start α/γ
7. [loop] colored primal body solve (approx Hessian)
8. [loop] dual + stiffness update
9. finalize velocities
Key files per stage:
StageFile
Orchestration
src/physics/PhysicsEngine.ts
Broad phase
src/physics/gpu/broadPhase.ts
Narrow phase
src/physics/gpu/contactGeneration.ts
Contact records
src/physics/gpu/contactRecord.ts
AVBD solve
src/physics/gpu/avbdState.ts
LBVH builder
src/lvbh/GPULBVHBuilder.ts
求解器遵循AVBD论文中的算法1:
1. 碰撞检测 (x^t)
2. 宽阶段(LBVH)         → src/lvbh/GPULBVHBuilder.ts
3. 窄阶段 + 热启动        → src/physics/gpu/contactGeneration.ts
4. 逐物体约束列表        → src/physics/gpu/avbdState.ts
5. 图着色                → src/physics/gpu/avbdState.ts
6. 惯性目标y、原始初始化、热启动α/γ
7. [循环] 着色原始物体求解(近似海森矩阵)
8. [循环] 对偶 + 刚度更新
9. 最终化速度
各阶段对应的关键文件:
阶段文件
编排
src/physics/PhysicsEngine.ts
宽阶段
src/physics/gpu/broadPhase.ts
窄阶段
src/physics/gpu/contactGeneration.ts
接触记录
src/physics/gpu/contactRecord.ts
AVBD求解
src/physics/gpu/avbdState.ts
LBVH构建
src/lvbh/GPULBVHBuilder.ts

Configuration Patterns

配置模式

Solver Parameters

求解器参数

typescript
// Passed during engine construction or step
engine.step(dt, substeps, {
  gravity: [0, -9.81, 0],
  iterations: 10,          // AVBD inner iterations per substep
  restitutionThreshold: 1.0,
});
typescript
// 在引擎构造或步进时传入
engine.step(dt, substeps, {
  gravity: [0, -9.81, 0],
  iterations: 10,          // 每个子步的AVBD内部迭代次数
  restitutionThreshold: 1.0,
});

Tuning Stability

稳定性调优

  • Increase
    substeps
    (e.g., 20) for stiff stacks or fast-moving bodies
  • Increase
    iterations
    for better constraint convergence
  • Use
    mass: 0
    for static bodies (never moves, acts as infinite mass)
  • Lower
    stiffness
    values for softer, more stable joints
  • Set
    restitution: 0
    + high
    friction
    for non-bouncy stacking
  • 增加
    substeps
    (如20)以处理刚性堆叠或高速运动物体
  • 增加
    iterations
    以提升约束收敛效果
  • 静态物体设置
    mass: 0
    (不会移动,视为无限质量)
  • 降低关节
    stiffness
    值以获得更柔软、稳定的关节
  • 设置
    restitution: 0
    + 高
    friction
    以实现无弹跳堆叠

Common Patterns

常见模式

Stack of Boxes

盒子堆叠

typescript
const groundIndex = engine.addBody({
  type: 'box',
  position: [0, 0, 0],
  halfExtents: [5, 0.25, 5],
  mass: 0,
  friction: 0.7,
  restitution: 0.1,
});

for (let i = 0; i < 8; i++) {
  engine.addBody({
    type: 'box',
    position: [0, 0.5 + i * 1.05, 0],
    halfExtents: [0.5, 0.5, 0.5],
    mass: 1.0,
    friction: 0.5,
    restitution: 0.1,
  });
}
typescript
const groundIndex = engine.addBody({
  type: 'box',
  position: [0, 0, 0],
  halfExtents: [5, 0.25, 5],
  mass: 0,
  friction: 0.7,
  restitution: 0.1,
});

for (let i = 0; i < 8; i++) {
  engine.addBody({
    type: 'box',
    position: [0, 0.5 + i * 1.05, 0],
    halfExtents: [0.5, 0.5, 0.5],
    mass: 1.0,
    friction: 0.5,
    restitution: 0.1,
  });
}

Pendulum Chain with Distance Joints

带距离关节的摆链

typescript
let prevIndex = engine.addBody({
  type: 'box', position: [0, 5, 0],
  halfExtents: [0.1, 0.1, 0.1], mass: 0,
  friction: 0, restitution: 0,
});

for (let i = 1; i <= 5; i++) {
  const curr = engine.addBody({
    type: 'box', position: [0, 5 - i, 0],
    halfExtents: [0.15, 0.15, 0.15], mass: 1.0,
    friction: 0.1, restitution: 0,
  });
  engine.addJoint({
    type: 'distance',
    bodyA: prevIndex, bodyB: curr,
    anchorA: [0, -0.15, 0], anchorB: [0, 0.15, 0],
    restLength: 0.7,
    stiffness: 1e5,
  });
  prevIndex = curr;
}
typescript
let prevIndex = engine.addBody({
  type: 'box', position: [0, 5, 0],
  halfExtents: [0.1, 0.1, 0.1], mass: 0,
  friction: 0, restitution: 0,
});

for (let i = 1; i <= 5; i++) {
  const curr = engine.addBody({
    type: 'box', position: [0, 5 - i, 0],
    halfExtents: [0.15, 0.15, 0.15], mass: 1.0,
    friction: 0.1, restitution: 0,
  });
  engine.addJoint({
    type: 'distance',
    bodyA: prevIndex, bodyB: curr,
    anchorA: [0, -0.15, 0], anchorB: [0, 0.15, 0],
    restLength: 0.7,
    stiffness: 1e5,
  });
  prevIndex = curr;
}

Integrate with Three.js Rendering

与Three.js渲染集成

typescript
import * as THREE from 'three';

const meshes: THREE.Mesh[] = [];

function syncPhysicsToRender() {
  const states = engine.getBodyStates();
  states.forEach((state, i) => {
    if (!meshes[i]) return;
    meshes[i].position.set(...state.position);
    meshes[i].quaternion.set(
      state.rotation[0], state.rotation[1],
      state.rotation[2], state.rotation[3]
    );
  });
}

function animate() {
  engine.step(1 / 60, 10);
  syncPhysicsToRender();
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}
typescript
import * as THREE from 'three';

const meshes: THREE.Mesh[] = [];

function syncPhysicsToRender() {
  const states = engine.getBodyStates();
  states.forEach((state, i) => {
    if (!meshes[i]) return;
    meshes[i].position.set(...state.position);
    meshes[i].quaternion.set(
      state.rotation[0], state.rotation[1],
      state.rotation[2], state.rotation[3]
    );
  });
}

function animate() {
  engine.step(1 / 60, 10);
  syncPhysicsToRender();
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

Troubleshooting

故障排除

WebGPU Not Available

WebGPU不可用

Error: navigator.gpu is undefined
  • Only Chrome 113+ supports WebGPU by default
  • Enable via
    chrome://flags/#enable-unsafe-webgpu
    on older versions
  • Firefox/Safari do not currently support WebGPU
Error: navigator.gpu is undefined
  • 仅Chrome 113+默认支持WebGPU
  • 旧版本可通过
    chrome://flags/#enable-unsafe-webgpu
    开启
  • Firefox/Safari目前不支持WebGPU

Simulation Explodes / Bodies Flying Off

模拟崩溃/物体飞散

  • Reduce timestep or increase
    substeps
  • Lower joint
    stiffness
    values
  • Ensure static bodies have
    mass: 0
  • Check that
    halfExtents
    are positive and non-zero
  • 减小时间步长或增加
    substeps
  • 降低关节
    stiffness
  • 确保静态物体设置
    mass: 0
  • 检查
    halfExtents
    为正数且非零

Bodies Sinking Through Ground

物体穿透地面

  • Increase
    iterations
    (try 15–20)
  • Increase
    substeps
  • Check collision shape sizing matches visual mesh
  • 增加
    iterations
    (尝试15–20)
  • 增加
    substeps
  • 确保碰撞形状尺寸与可视网格匹配

Performance Issues

性能问题

  • This is a Chrome-only WebGPU project; GPU driver issues can cause slowdowns
  • Reduce body count or iteration count
  • Check
    chrome://gpu
    to ensure hardware acceleration is active
  • 这是仅支持Chrome的WebGPU项目;GPU驱动问题可能导致性能下降
  • 减少物体数量或迭代次数
  • 查看
    chrome://gpu
    确保硬件加速已激活

Build Errors

构建错误

sh
undefined
sh
undefined

Ensure Node.js >= 18

确保Node.js >= 18

node --version
node --version

Clear cache

清除缓存

rm -rf node_modules dist npm install npm run build
undefined
rm -rf node_modules dist npm install npm run build
undefined

Limitations & Roadmap Notes

限制与路线图说明

  • Chrome only — no Firefox/Safari support yet
  • Not a drop-in npm package; must clone and integrate manually
  • Double-buffered position updates (for same-color conflict safety) not yet implemented — current path uses in-place colored body solve in
    avbdState.ts
  • Experimental API — breaking changes expected
  • No TypeScript type declarations exported for external use yet
  • 仅支持Chrome——暂不支持Firefox/Safari
  • 并非开箱即用的npm包;必须克隆后手动集成
  • 双缓冲位置更新(用于同色冲突安全)尚未实现——当前路径在
    avbdState.ts
    中使用原地着色物体求解
  • API为实验性——可能会有破坏性变更
  • 尚未导出供外部使用的TypeScript类型声明

References

参考资料