Rigidbody - Interpolation

計算と予測
明日の朝、友達と遊びに行く予定があるとしましょう。何を着ていくか決めるために天気予報を確認します。明日の午前9時は10°、10時は14°だそうです。朝は少し肌寒いですが、時間が経つにつれて気温が上がりそうです。上着を着ていくか迷いますね。しかし残念ながら、天気予報は10時までしか提供されていません。与えられた情報だけで判断しなければなりません。
現在わかっていることは以下の通りです。
午前9時: 10°午前10時: 14°
では、午前9時30分の気温はどのくらいでしょうか?
おそらく12°くらいと予測できますね。かなり正確だと考えられます。
では、午前11時の気温はどのくらいでしょうか?
与えられた情報だけで判断すると、9時から10時の間に4°上昇したので、同じ傾向が続くと仮定すれば、午前11時は約18°くらいと予想できます。
ここで質問です。
午前9時30分の予測気温と、午前11時の予測気温、どちらがより正確でしょうか?
内挿と外挿
おそらく午前9時30分の予測気温の方が正確だと思われたのではないでしょうか。
では、なぜそのような判断をしたのでしょうか?
その理由は、2つのケースが異なる範囲にあるからです。
午前9時30分は9時と10時の間、つまり既知のデータの範囲内にあります。
一方、午前11時は与えられたデータの範囲外の値です。
したがって、9時30分の場合は既存の情報に基づいた計算値であり、11時はその傾向に基づいた予測値であるため、精度に差が出ます。
先ほどの例をもとに整理すると次の通りです。
- 午前9時〜10時の間の値を予測したもの → 内挿(補間)
Interpolation - 午前11時のように範囲外の値を予測したもの → 外挿
Extrapolation
これが、この記事で取り上げる内挿と外挿の概念です。
韓国、日本、中国などの開発やグラフィックス分野では、「内挿」よりも「補間」という表現が一般的に使われています。ここからは補間という用語を使います。
では、この概念をUnityのRigidbodyと結びつけてみましょう。
Rigidbody - Interpolation
Unityには毎フレーム実行されるUpdateと、固定間隔で実行されるFixedUpdateがあります。物理演算はFixedUpdateで処理され、デフォルトは毎秒50回です。一方、Updateはフレームレートに応じて可変的に実行されます。
Update, FixedUpdate
この2つのループは常に同じタイミングで実行されるわけではありません。物理位置はFixedUpdateごとに更新されますが、画面に描画されるのはUpdate(レンダリング)のタイミングです。では、Interpolationプロパティについて見ていきましょう。
比較
百聞は一見にしかず。3つのオプションを直接比較してみましょう。各キューブのInterpolationを異なる設定にし、TimeScaleを下げてスローモーションで確認すると、どのように見えるでしょうか?
違いがわかりますか?私が見た印象をそのままお伝えします。
- None : 動くときにガタつきが発生します。
- Interpolate : 滑らかですが、他のキューブより少し遅れて到着するように見えます。
- Extrapolate : 滑らかですが、床に触れたときによりめり込んで見えます。
なぜこのような違いが生じるのでしょうか?
None
補間処理は行いません。レンダリング時点で最新のFixedUpdateで計算された物理位置をそのままレンダリングします。FixedUpdateとレンダリングのタイミングがずれると、オブジェクトの位置が不連続に変化し、ジッター(Jitter) が発生します。つまり、物理位置自体は正確ですが、レンダリングのタイミングと合わないため、位置がカクカクと飛んで見えるのです。
None - Uses the latest FixedUpdate position
Interpolate
直前2回のFixedUpdateの位置を使用し、その間を補間します。
Uses the previous two FixedUpdate results and renders one step behind
直前2回のFixedUpdate位置をA、Bとすると、AからBまでの区間の中でレンダリング時点がどこに該当するかの比率(α) を求め、AとBの間の位置を補間してレンダリングします。FixedUpdateが毎秒50回しか実行されなくても、毎フレーム少しずつ異なる位置をレンダリングするため、動きが滑らかに見えます。ただし、常にすでに完了した過去2つの位置を基準に補間するため、レンダリング位置は実際の物理位置より1 FixedUpdateステップ分遅れます。
上の映像でInterpolateに設定されたキューブが遅れて落ちた理由がわかりますか?
Extrapolate
現在の速度を基に次の位置を予測(外挿) します。天気予報で10時までのデータから11時の気温を予測したのと同じです。与えられたデータ範囲を超える予測であるため、速度や方向が急激に変わる状況では予測が外れ、オブジェクトが実際の位置を超えてから戻る現象が発生することがあります。
Predicts the next position based on current velocity and renders ahead
Extrapolateに設定したキューブが床に触れたとき、なぜ他のキューブより潰れて見えたかわかりますか?レンダラーはキューブが引き続き下に落ちると予測して描画しますが、突然の床との衝突は予測できなかったということです。
まとめ
映像をもう一度見てみましょう。今回は最終結果に注目します。
Physics 実際に床と衝突した時間
Extrapolate Cube: 0.920sNone Cube: 0.920sInterpolate Cube: 0.920s
Render 描画が床に到達した時間
Extrapolate Cube: 0.893sNone Cube: 0.904sInterpolate Cube: 0.914s
ここで注目すべき点は、実際の物理衝突は3つのキューブすべて同じ時間に発生したということです。Interpolation設定は物理演算自体には影響しません。そうです。違いが出るのはレンダリングのタイミングだけです。
物理衝突は同じなのに、レンダリングの到着時間が異なる理由を整理するとこうなります。
Extrapolate: 未来を予測するため、実際より先に描画される。そのため床をより多く貫通する。Interpolate: 過去2つの位置を補間するため、実際より遅れて描画される。None:FixedUpdateの位置をそのまま使用するため、その中間。
では、いつ、どれを使うべきでしょうか?
| オプション | 状況 |
|---|---|
| None | 物理オブジェクトがカメラに映っていない、またはジッターが目立たない場合 |
| Interpolate | プレイヤーキャラクター、カメラが追従するオブジェクトなど、滑らかな動きが重要な場合 |
| Extrapolate | Interpolateのわずかな遅延すら許容できない場合。ただし、急激な方向転換時に誤差が発生する可能性あり |
ほとんどの場合、Interpolateを使えば問題ありません。演算コストが心配なら大丈夫です。Interpolateの中核演算は線形補間(Lerp)です。
前の位置A、直前の位置B、進行比率αがあれば、足し算と掛け算数回で終わるO(1) 演算です。Rigidbody1つにつきレンダリングフレームごとに1回だけ実行されるため、物理シミュレーション自体のコストに比べれば無視できるレベルです。特に理由がなければ、Interpolateをデフォルトにしておくのがよいでしょう。