Resources Numerical accuracy Calculation policy

Skinning on the graphics card

Vertex skinning by bones on the GPU for the real-time view and the path tracer.

How the precision is kept

The WGSL kernel and its bone packing work in 32-bit and feed the renderers. The CPU path in rig.js is the reference the exports use.

Calculations under this policy

2 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

anim/skin-gpu.js ModelCraft 0.8.1 32-bit, display only 2 calculations Reviewed

Tests None found by the review

Bone data for the card

32-bit, display only

Packs the bone matrices (or dual quaternions) for the kernel.

Float32Array.from rounds every 64-bit matrix entry to 32-bit.

  • anim/skin-gpu.js
  • lines 81 to 85
  • ModelCraft 0.8.1
export function packBones(M, mode) {
  const nb = M.length / 16;
  if (mode === 'dqs') return Float32Array.from(dualQuats(M, nb));
  return Float32Array.from(M);
}

Skinned points read back

32-bit, display only

Runs one skin on the card and reads the points back.

The points come back from the GPU buffer as a Float32Array. The code comment says this call is for tests.

  • anim/skin-gpu.js
  • lines 114 to 127
  • ModelCraft 0.8.1
async skin({ rest, joints, weights, n, max = 4, M, mode = 'lbs' }) {
  const bonesData = packBones(M, mode);
  const b = buffersFor({ rest, joints, weights, n, max, bones: M.length / 16 });
  device.queue.writeBuffer(b.params, 0, new Uint32Array([n, mode === 'dqs' ? 1 : 0, max, 0]));
  device.queue.writeBuffer(b.bones, 0, bonesData);
  const enc = device.createCommandEncoder();
  encode(enc, b, n);
  enc.copyBufferToBuffer(b.out, 0, b.read, 0, n * 12);
  device.queue.submit([enc.finish()]);
  await b.read.mapAsync(GPUMapMode.READ);
  const out = new Float32Array(b.read.getMappedRange().slice(0));
  b.read.unmap();
  return out;
},

In detail

Method
Linear blend or dual quaternion skinning in one compute pass.
Number types
Bone matrices are Float64Array on the CPU and are converted to Float32Array for the card; the read-back is a Float32Array.
Limits
The read-back in skin() is for tests; the renderers keep the result on the card.

Approach: Display only

Computed to draw the screen, often in 32-bit on the graphics processor. No number a person reads, saves or exports comes from it.

Every policy that takes this approach