p5.js 固定时间步长插值为何模糊,如何保持画面清晰?
数据小许
结论
固定时间步长配合位置插值,通常无法产生真正的运动模糊。你给出的代码中,lastPos1 = pos1 只是复制了对象引用,lastPos1 和 pos1 实际指向同一个 p5.Vector。物理循环修改 pos1.x 时,lastPos1 也会随之改变,插值因此没有两个独立的端点。
保存独立的前后位置
每次执行物理步进前,先复制当前位置,再更新当前位置。渲染时,在这两个独立的位置之间插值:
while (acc >= step) {
lastPos1 = pos1.copy();
pos1.x += 100 * step;
acc -= step;
}
const alpha = acc / step;
const interpPos = p5.Vector.lerp(lastPos1, pos1, alpha);
rect(interpPos.x, interpPos.y, 200, 50);
初始化时,也要为两个位置分别创建向量:
pos1 = createVector(0, height / 2 - 100);
lastPos1 = pos1.copy();
换用其他语言时,可以复制坐标值,或新建位置对象,避免两个状态共用同一个对象。如果一个渲染帧内执行了多个物理步进,就像上面的例子一样,在循环中每次更新前保存上一状态。
插值的作用和限制
alpha = acc / step 表示累计时间距离下一个物理步进还差多少。用它在上一物理状态和当前物理状态之间插值,可以让渲染频率高于物理更新频率时,画面运动更平滑。显示位置会比最新物理状态最多落后一个物理步长。step = 1 / 60 时,这个间隔约为 16.7 毫秒。
插值后的坐标可能落在两个像素之间,画布抗锯齿会让矩形边缘显得柔和。修复引用问题后,如果边缘仍然发虚,可以尝试在绘制时将坐标取整:
rect(Math.round(interpPos.x), Math.round(interpPos.y), 200, 50);
取整可能让运动略有跳动,是否使用要看画面效果。还可以检查 canvas 是否被 CSS 缩放到非整数尺寸,这也可能让边缘显得模糊。仅凭这段代码无法判断画布缩放等因素是否也造成了模糊。
备注:内容仅供参考。
