14. アニメーション2:物理・手続き型・実写合成

第13章では、全ての動きをアニメーターが決めていました。
キーフレーム、カーブ、スケルトン、収録した演技です。
キャラクタには其れで上手く行きますが、1万個の火花、風になびく旗、砕けるガラス、300羽の鳥の群れをキーフレームで付けたい人は居ません。
其の様な物では、動きをコンピュータに計算させます。
物理法則から、又は一度書き下した単純な規則からです。
そして完成したCGを実写の映画のショットに入れる時は、本物のカメラと一緒に動き、本物の画素と混ざり合わなければなりません。

此の章は分野5(アニメーション)の4つのシラバス項目、物理ベースアニメーション、リアルタイムアニメーション、手続き型アニメーション、実写映像との合成を扱います。
つまり、数値積分と其の安定性、パーティクルシステム、バネ質点系、剛体、衝突、流体、フレーム時間の予算、ゲームループ、アニメーションブレンド、ノイズ、群れ(ボイド)、群衆、そしてマッチムーブ、クロマキー、アルファとマット、ロトスコープ、ライティングの一致です。
キーフレーム、スケルトン、スキニング、モーションキャプチャは第13章、画像のフィルタ処理と編集全般は第15章、GPUやリアルタイムシステムのハードウェア側は第17章で扱います。

必要な数学は、ベクトル、長さ、内積(線型代数シリーズ第2章〜第4章)、外積(線型代数シリーズ第5章)、そして少しの学校の物理、つまり速度、加速度、ニュートンの第2法則です。
物理は此の章で復習します。

14.1 物理ベースアニメーション

何故シミュレーションするのか

跳ねるボール1つをキーフレームで付けるのは簡単です。
でも、互いにぶつかって跳ねる50個のボールを、正しい高さとタイミングで付けるのは簡単ではありません。
物理ベースアニメーションは、物体に物理的な性質(質量、硬さ、摩擦)を与え、力を受けてどう動くかをシミュレーションに計算させます。
結果は自動的に物理的にもっともらしくなり、何百万個の物体にも拡張出来ます。

代わりに失うのは制御です。
監督が「2秒後、破片はヒーローの少し左に落ちて欲しい」と言っても、物理は命令を聞きません。
其の為、制作の現場では両方を混ぜます。
アニメーターが主な演技をキーフレームで付け、シミュレーションが二次的な動き(布、髪、破片、しぶき)を加え、アーティストが追加の力、力場、拘束でシミュレーションを誘導します。

状態、力、ニュートンの第2法則

質点について、時刻tでシミュレータが知るべき事は全て、其の状態、つまり位置𝐱と速度𝐯です。
速度は位置の変化率、加速度𝐚は速度の変化率です。
ニュートンの運動の第2法則は、質量mの物体に働く力の合計𝐅と加速度を結び付けます:

𝐅=m𝐚

状態についての規則として書くと、此れが運動方程式です:

d𝐱dt=𝐯,d𝐯dt=𝐚=𝐅(𝐱,𝐯,t)m

力は、物体が何処にあるか、どれだけ速く動いているか、何時かに依存出来ます。
アニメーションで何度も出て来る力は次の通りです:

  • 重力:𝐅=m𝐠、𝐠=(0,−9.8,0) m/s²(yが上)。
  • バネの力:伸びxのバネでF=−kx(14.4節)。
  • 減衰、空気抵抗:𝐅=−c𝐯、動きと逆向き。
  • 風、渦、ノイズ等の力場、及び衝突による接触力(14.6節)。

シミュレーションとは、既知の状態から始めて、力を計算し、小さな時間刻みh(例えばh=160秒)だけ状態を進める事を繰り返す事です。
高さy0から落としたボールには厳密解y(t)=y0−12gt2がありますが、複数の力が絡み合うと式は無くなり、数値的に一歩ずつ進めるしかありません。

14.2 数値積分

陽的オイラー法

一番単純な規則は、1ステップの間、速度と加速度が一定だと仮定します。
此れが陽的オイラー法(前進オイラー法、又は単にオイラー法)です:

𝐱n+1=𝐱n+h𝐯n,𝐯n+1=𝐯n+h𝐚n

どちらの更新も古い状態(添字n)だけを使います。
此れが「陽的」の意味です。

計算例:落下するボール。
ボールをy0=10 mからv0=0で落とします。
g=9.8 m/s²、h=0.1秒です。
加速度は常にa=−9.8なので、ha=−0.98です。

  • ステップ1:y1=10+0.1⋅0=10、v1=0−0.98=−0.98。
  • ステップ2:y2=10+0.1⋅(−0.98)=9.902、v2=−1.96。
  • ステップ3:y3=9.902+0.1⋅(−1.96)=9.706、v3=−2.94。

t=0.3秒での正確な高さは10−4.9⋅0.09=9.559 mなので、オイラー法は0.147 m高過ぎます。
常にステップの始めの速度を使うので、加速し続けるボールには遅過ぎるのです。
一定の時刻での誤差はhに比例します。
ステップを半分にすれば誤差も半分になります。
其の為、オイラー法は1次精度の手法です。

半陰的(シンプレクティック)オイラー法

順番を入れ替えます。
先に速度を更新し、次に新しい速度で動かします:

𝐯n+1=𝐯n+h𝐚n,𝐱n+1=𝐱n+h𝐯n+1

此れが半陰的オイラー法で、シンプレクティックオイラー法とも呼ばれます。
計算量は陽的オイラー法と全く同じです。
落下するボールでは、v1=−0.98、y1=10−0.098=9.902、v2=−1.96、y2=9.706、v3=−2.94、y3=9.412です。
今度は0.147 m低過ぎます。
各ステップの終わりの速度を使うからです。
此れも1次精度なので、落下するボールではどちらも優れていません。
違いは、少し後のバネで現れます。

ベルレ法

ベルレ法は、1967年に分子動力学で使ったループ・ベルレの名前から来ていて、速度を全く保存しません。
直前の2つの位置から次の位置を計算します:

𝐱n+1=2𝐱n−𝐱n−1+h2𝐚n

速度は差𝐱n−𝐱n−1の中に隠れています。
始めるには𝐱1が必要で、例えば𝐱1=𝐱0+h𝐯0+12h2𝐚0で求めます。
落下するボールではy1=10−12⋅9.8⋅0.01=9.951、そして

y2=2⋅9.951−10−0.098=9.804,y3=2⋅9.804−9.951−0.098=9.559

此れは0.1秒、0.2秒、0.3秒での本当の高さと丁度同じです。
ベルレ法は2次精度(誤差がh2に比例)で、加速度が一定なら厳密です。
ゲームの布やロープで人気があります。
拘束(ロープの1区間は長さを保つ等)を満たすには位置を動かすだけで良く、速度は自動的に付いて来ます。

ルンゲ=クッタ法

ルンゲ=クッタ法は、ステップの中の複数の点で傾きを調べて精度を上げます。
状態全体を𝐬=(𝐱,𝐯)、其の変化率を𝐟(𝐬)=(𝐯,𝐚)と書きます。
中点法(RK2)は、半ステップのオイラー法でステップの中央の状態を見積もり、其処での傾きで1ステップ全体を進めます:

𝐬mid=𝐬n+h2𝐟(𝐬n),𝐬n+1=𝐬n+h𝐟(𝐬mid)

古典的な4次のルンゲ=クッタ法(RK4)は4つの傾きを求め、重み1、2、2、1で平均します:

𝐤1=𝐟(𝐬n),𝐤2=𝐟(𝐬n+h2𝐤1)𝐤3=𝐟(𝐬n+h2𝐤2),𝐤4=𝐟(𝐬n+h𝐤3)𝐬n+1=𝐬n+h6(𝐤1+2𝐤2+2𝐤3+𝐤4)

誤差はh4に比例します。
ステップを半分にすると誤差は16分の1になります。
代わりに1ステップで力を4回評価する必要があり、力の評価がシーン全体の衝突計算を意味する時には効いて来ます。

手法 1ステップの力の評価回数 次数(誤差∝) 主な用途
陽的オイラー法 1 h 教育用。実務では稀
半陰的オイラー法 1 h ゲームの物理エンジン、パーティクル
ベルレ法 1 h2 布、ロープ、分子動力学
RK2(中点法) 2 h2 滑らかな力、オフライン
RK4 4 h4 正確で滑らかな動き(軌道、カメラ)
陰的オイラー法 1+連立方程式の解 h 硬い布やソフトボディ

安定性:爆発するバネ

精度は1つの問題ですが、安定性は更に悪い問題です。
質量m=1 kgの物体を、硬さk=1 N/mのバネでx0=1 mまで引っ張って放します(v0=0)。
正確な動きはx(t)=costで、周期2π≈6.28秒で−1と1の間を永遠に振動し、其のエネルギー

E=12mv2+12kx2

はE0=0.5 Jのままです。
加速度はa=−kmx=−xです。
h=0.1秒で、陽的オイラー法は次の様になります:

ステップ x v E
0 1 0 0.5
1 1+0.1⋅0=1 0−0.1⋅1=−0.1 0.505
2 1+0.1⋅(−0.1)=0.99 −0.1−0.1⋅1=−0.2 0.51005
3 0.99−0.02=0.97 −0.2−0.099=−0.299 0.5151505

エネルギーは1ステップごとに丁度1%増えます。
偶然ではありません。
ω2=kmとすると、1ステップは(x,v)を(x+hv,v−hω2x)に移し、

(v−hω2x)2+ω2(x+hv)2=v2−2hω2xv+h2ω4x2+ω2x2+2hω2xv+h2ω2v2=(1+h2ω2)(v2+ω2x2)

なので、各ステップでエネルギーは1+h2ω2倍、此処では1+0.01=1.01倍になります。
200ステップ(20秒)後には、エネルギーは1.01200≈7.32倍、振幅は7.32≈2.7倍になります。
陽的オイラー法は、どんなステップ幅でも減衰の無い振動子にエネルギーを注ぎ込みます。
此の問題に対しては無条件に不安定です。
同じhの半陰的オイラー法は、最初の3ステップでx=0.99,0.9701,0.940499となり、エネルギーは何時までも約0.476と0.526の間で揺れるだけです:

左:バネに付いた質点の20秒間の位置。厳密解、陽的オイラー法、半陰的オイラー法、時間刻み0.1。陽的オイラー法は振幅約2.6まで大きくなり、半陰的オイラー法は厳密解の曲線上に留まる。右:同じ計算の位相平面。陽的オイラー法は外側へ渦を巻き、半陰的オイラー法は単位円の周りの閉じたループに留まる

右半分は位相平面、つまり位置と速度のグラフです。
正確な動きは単位円(エネルギー一定)で、陽的オイラー法は外側へ渦を巻き、半陰的オイラー法は閉じたループに留まります。
非常に長い計算でもエネルギーが有界のままという性質が、実用上の「シンプレクティック」の意味で、殆ど全てのゲームの物理エンジンが半陰的オイラー法を使う理由です。

但し、魔法ではありません。
半陰的オイラー法が安定なのはhω<2の間だけです。
つまり

h<2ω=2mk=Tπ

で、周期T=2πωを使うと、周期の約3分の1です。
硬いバネはωが大きく、とても小さなステップが必要です。
k=100 N/m、m=1 kgならω=10 rad/sです。
h=0.1秒ではhω=1で、半陰的オイラー法は問題ありませんが、陽的オイラー法は毎ステップ、エネルギーを1+1=2倍にするので、10ステップ(1秒)後には210=1024倍、振幅は32倍です。
h=0.25秒ではhω=2.5となり、半陰的オイラー法も爆発します。
xは1、−5.25、21.3、−85.3、…となります。

逃げ道は3つあります:

  • ステップを小さくする(又は1フレームを複数のサブステップに分ける)事で、hωを小さく保ちます。
  • 陰的積分:陰的オイラー法はステップの終わりの力を使い、𝐯n+1=𝐯n+h𝐚(𝐱n+1)とします。各ステップで連立方程式を解く必要がありますが、どんなステップ幅でも安定です。人工的に減衰させます。今のバネではエネルギーが1ステップごとに11+h2ω2倍になり(h=0.1で20秒後には14%しか残りません)、布には許容出来ます(バラフとウィトキン「Large Steps in Cloth Simulation」、1998年)。
  • 減衰:物理的な、又は人工的な減衰で、積分法が加えたエネルギーを取り除きます。

減衰

減衰力−c𝐯は、終わらない振動を減衰する振動に変えます。
バネでは、特別な値c=2kmが臨界減衰で、行き過ぎずに出来るだけ早く静止に戻ります。
k=1、m=1ならc=2です。
減衰が少ないと減衰しながら揺れ(ゼリー、尻尾、遅れて付いて来て落ち着くアンテナ)、多いとドアクローザーの様にゆっくり戻ります。

コード:陽的オイラー法と半陰的オイラー法

此のプログラムは、表のバネを両方の手法で計算し、位置とエネルギーを表示します(1次元なのでlinalg.hhは要りません):

#include <cmath>
#include <cstdio>

// A mass on a spring: m x'' = -k x. The state is the position x and the velocity v.
struct State {
  double x, v;
};

const double m = 1.0, k = 1.0;

double energy(State s) { return 0.5 * m * s.v * s.v + 0.5 * k * s.x * s.x; }

State explicitEuler(State s, double h) {
  double a = -k / m * s.x;               // acceleration from the OLD position
  return {s.x + h * s.v, s.v + h * a};   // both updates use the old state
}

State semiImplicitEuler(State s, double h) {
  double v = s.v + h * (-k / m * s.x);   // velocity first
  return {s.x + h * v, v};               // then position, with the NEW velocity
}

int main() {
  const double h = 0.1;
  State e{1.0, 0.0}, si{1.0, 0.0};
  printf("%4s  %10s  %8s  %10s  %8s\n", "step", "explicit x", "E", "semi x", "E");
  for (int n = 0; n <= 200; ++n) {
    if (n <= 3 || n % 50 == 0) {
      printf("%4d  %10.6f  %8.5f  %10.6f  %8.5f\n", n, e.x, energy(e), si.x, energy(si));
    }
    e = explicitEuler(e, h);
    si = semiImplicitEuler(si, h);
  }
  printf("exact x(20) = cos(20) = %.6f, E = 0.5\n", std::cos(20.0));
  return 0;
}

step  explicit x         E      semi x         E
   0    1.000000   0.50000    1.000000   0.50000
   1    1.000000   0.50500    0.990000   0.49505
   2    0.990000   0.51005    0.970100   0.49035
   3    0.970000   0.51515    0.940499   0.48608
  50    0.343355   0.82232    0.333638   0.51601
 100   -1.408847   1.35241   -0.809385   0.47781
 150   -1.532160   2.22421   -0.796058   0.52573
 200    1.264886   3.65801    0.354578   0.48373
exact x(20) = cos(20) = 0.408082, E = 0.5

半陰的オイラー法のエネルギーは0.5付近に留まりますが、20秒での位置(0.355)は正確な0.408ではありません。
位相平面でのループが少し傾いた楕円で、其れを少し速く回るので、位相がずれて行くのです。
同じステップのRK4は0.408097、エネルギー0.4999986になりますが、計算量は4倍です。

14.3 動くパーティクルシステム

第9章の9.5節では、炎や煙の様なぼんやりした形を表す方法として、リーブス(1983年)のパーティクルシステムを紹介し、動きは此の章で扱うと約束しました。
パーティクル(粒子)は、状態に属性を加えた質点です。
位置、速度、年齢、寿命、色、大きさ、不透明度を持ちます。
毎フレーム、システムは同じループを回します:

  1. 放出:エミッタが、決まった割合(毎秒の粒子数)で、決まった領域(点、線、面、体積)に、平均の速度の周りにランダムなばらつきを持たせて新しい粒子を作ります。
  2. 力を加える:重力、空気抵抗、風、引力源、そして煙を渦巻かせるノイズの場の様な力場(第11章のノイズ)です。
  3. 普通は半陰的オイラー法で積分し、単純な形と衝突させます(14.6節)。
  4. 年を取る:年齢と共に色、大きさ、不透明度を変え(火花は白から暗い赤へ冷えて消えて行きます)、寿命が尽きた粒子を取り除きます。
  5. 描画:普通はカメラの方を向いた小さなテクスチャ付きの四角形(スプライト、ビルボード)として描き、順番が問題にならない様に加算ブレンディングを使う事が多いです(第10章の10.12節)。モーションブラーの為に筋として描く事もあります。

計算例:火花1つ。
火花が(0,1) mから速度(3,8) m/s、寿命1.5秒で放出され、重力だけを受けます。
正確な軌道は

x(t)=3t,y(t)=1+8t−4.9t2

です。
最も高くなるのは鉛直方向の速度8−9.8tが0になるt=89.8≈0.816秒で、其の時x≈2.45 m、y=1+822⋅9.8≈4.27 mです。
t=1.5秒で(4.5,1+12−11.025)=(4.5,1.975)で消えます。
まだ空中です。
地面に届くのはt≈1.75秒の筈でした。
此の寿命で毎秒200個の火花を出すエミッタは、200⋅1.5=300個の火花を生かしておきます。

相互作用しない粒子は全て独立に更新出来るので、GPUはコンピュートシェーダーで何百万個もシミュレーションします(コンピュートシェーダーはGLSLシリーズの第15章で出会います)。
粒子がバネ、衝突、圧力で相互作用すると、次の節以降のシステムになります。

14.4 バネ質点系:布、ソフトボディ、髪

3次元のフックの法則

粒子をバネで繋ぐとバネ質点系になります。
位置𝐱Aと𝐱Bの粒子の間にある、自然長L0、硬さkのバネは、伸びると2つを引き寄せ、縮むと押し離します。
此れがフックの法則です:

𝐅A=k(‖𝐝‖−L0)𝐝^,𝐅B=−𝐅A,𝐝=𝐱B−𝐱A

𝐝^はAからBへの単位ベクトルです(線型代数シリーズ第3章)。
バネが永遠に鳴り続けない様に、ダンパーがバネに沿って相対速度に逆らう力を加えます:

𝐅Adamp=c((𝐯B−𝐯A)⋅𝐝^)𝐝^

計算例。
Aが(0,0)、Bが(3,4)、自然長はL0=4、k=50 N/mです。
𝐝=(3,4)、‖𝐝‖=5、𝐝^=(0.6,0.8)なので、バネは1 m伸びています。

𝐅A=50⋅(5−4)⋅(0.6,0.8)=(30,40),𝐅B=(−30,−40)

どちらも50 Nで互いに引き寄せられます。
BがAから相対速度(1,2) m/sで遠ざかり、c=2なら、伸びる速さは(1,2)⋅(0.6,0.8)=2.2 m/sで、ダンパーはAの力に2⋅2.2⋅(0.6,0.8)=(2.64,3.52)を加え、合計は(32.64,43.52)になります。

布

布は、粒子の格子と3種類のバネで出来ています:

4×4の粒子の格子を3つ。直接の隣同士を結ぶ構造バネ、対角線のせん断バネ、1つ飛ばしで結ぶ曲げバネ

  • 構造バネは直接の隣同士を結び、伸びに抵抗します。
  • せん断バネは対角線に沿い、正方形のマスが菱形に潰れるのに抵抗します。
  • 曲げバネは2つ離れた粒子を結び、折れ曲がりに抵抗します。曲げバネが弱ければ絹、強ければ革になります。

布は安定性が効いて来る所です。
本物の布地は殆ど伸びないので、構造バネはとても硬くなければなりません。
k=50 N/mのバネに付いた10 gの粒子1つでは、ω=50/0.01≈70.7 rad/sで、半陰的オイラー法にはh<270.7≈0.028秒が必要です。
60 Hzのステップ0.0167秒は其れを満たします。
バネを10倍硬く(k=500)するとω≈224 rad/s、h<0.0089秒で、1フレームに2回のサブステップが必要です。
其の為、映画の布は陰的積分を使い、ゲームは位置ベースの動力学(PBD)をよく使います。
硬いバネの代わりに、各ステップの後で粒子を直接動かして距離の拘束を満たす、14.2節のベルレ法の考え方です。
布は体とも自分自身とも衝突しなければならず(自己衝突)、普通は其れが一番重い部分です。

ソフトボディと髪

  • ソフトボディ(弾性体):体積をバネで繋いだ粒子で満たします(又は四面体メッシュと有限要素法、FEMを使います)。揺れるゼリー、脂肪、筋肉、押すと縮むクッションです。
  • 髪の毛(ヘア):1本の毛は、伸びのバネと曲げのバネ(巻き毛ならねじれも)を持つ粒子の鎖です。人の頭には約10万本の毛があるので、シミュレータは数百本のガイドヘアを動かし、残りは其の間を補間します。毛皮も同じです。

布と合わせて、此れらは形が変わる変形体です。
其の反対が剛体です。

14.5 剛体

剛体は決して形が変わりません。
サイコロ、木箱、車体、レンガです。
其の状態には位置以上の物が必要です:

  • 並進運動:重心の位置𝐱と速度𝐯で、粒子と全く同じく𝐅=m𝐚に従います。力が物体の何処に働いても、重心は同じ様に加速されます。
  • 回転運動:姿勢をクォータニオン(線型代数シリーズ第13章)か回転行列で保存し、角速度𝝎を持ちます。角速度の向きは回転軸、長さはrad/sでの速さです。

重心から𝐫だけ離れた点に働く力𝐅は、線型代数シリーズ第5章の外積でトルクも生みます:

𝝉=𝐫×𝐅

𝐅=m𝐚の回転版は𝝉=𝐈𝜶です。
𝜶は角加速度、𝐈は慣性テンソルで、質量が重心の周りにどう分布しているかを表す対称な3×3行列です。
直感的には、軸から遠い質量は其の軸周りの回転を難しくします。
フィギュアスケーターは腕を引き寄せると速く回ります。
質量m、半径rの中身の詰まった球では、𝐈は単位行列の25mr2倍です。
細長い形や変な形では軸ごとに異なります。
シミュレータは𝐈を物体自身の座標で保存し、物体と一緒に回転させます。

計算例。
質量3 kg、長さ2 mの細い棒が、原点を中心にx軸に沿って置かれています。
中心を通るz軸の周りではI=112mL2=112⋅3⋅4=1 kg·m²です。
右端𝐫=(1,0,0)を𝐅=(0,10,0) Nで押します:

𝝉=𝐫×𝐅=(0⋅0−0⋅10,0⋅0−1⋅0,1⋅10−0⋅0)=(0,0,10)

+z周りに10 N·mのトルクで、+zから見て反時計回りです(右手の法則)。
角加速度はα=101=10 rad/s²なので、0.1秒後に棒は1 rad/sで回っています。
同時に重心は上向きに103≈3.33 m/s²で加速します。
1回押すだけで、動きもし、回りもするのです。

剛体のシミュレータは各ステップで、前と同じく𝐯と𝐱を積分し、トルクから𝝎を積分し、姿勢のクォータニオンを𝐪←𝐪+h2(0,𝝎)𝐪で更新して、純粋な回転のままである様に正規化します。

14.6 衝突検出と衝突応答

検出:ブロードフェーズとナローフェーズ

互いにすり抜ける物体はすぐにおかしく見えるので、シミュレータは毎ステップ接触を見つけなければなりません。
此れが衝突検出(衝突判定)です。
全ての組を調べると2乗の手間になり、1,000個の物体で1000⋅9992=499,500組です。
其の為、検出は2段階で行います:

  • ブロードフェーズ:単純なバウンディングボリュームと空間構造を使い、接触するかもしれない組を素早く見つけます。均一格子、スイープ&プルーン(ボックスを1つの軸に沿って並べ替え、区間が重なる物だけを比べる)、又は第12章の12.9節の階層バウンディングボリューム(BVH)を使います。物が動くので、BVHは毎フレーム作り直すか更新します。
  • ナローフェーズ:候補の各組について、本当の形(球、箱、カプセル、凸包、三角形メッシュ)で正確な判定を行い、接触点、接触法線、めり込み量を計算します。凸形状には分離軸判定(SAT)やGJKがよく知られています。

一番単純なバウンディングボリュームは2つです:

境界球は、中心間の距離が半径の和より小さければ重なります。
平方根を避ける為に2乗で比べます:

‖𝐜1−𝐜2‖2<(r1+r2)2

𝐜1=(1,2,2)と𝐜2=(3,3,3.5)にあり、どちらも半径1.5の球では、22+12+1.52=7.25<32=9なので重なっています。
距離は7.25≈2.693、めり込み量は3−2.693=0.307、接触法線は(2,1,1.5)2.693≈(0.743,0.371,0.557)です。

軸平行境界ボックス(AABB)は、全ての軸で区間が重なる時、そして其の時に限り重なります。
xについては

amin,x≤bmax,xandbmin,x≤amax,x

で、y(3次元ならzも)も同じです。
隙間のある軸が1つあれば、分離するには十分です。

格子上の3つのボックス。Aは(0, 0)から(2, 2)、Bは(1.5, 2.5)から(3, 4)、Cは(1, 1)から(3.5, 3)。下にxの区間、左にyの区間を描いてある。AとBのxの区間は重なるが、yの区間には2から2.5の隙間がある

  • A = [0, 2] × [0, 2]とB = [1.5, 3] × [2.5, 4]:xでは0≤3、1.5≤2で重なり、yでは2.5≤2が成り立ちません。xの区間は重なっていても、衝突無しです。
  • AとC = [1, 3.5] × [1, 3]:xが重なり(0≤3.5、1≤2)、yも重なります(0≤3、1≤2)。重なっています。
  • BとC:x(1.5≤3.5、1≤3)とy(2.5≤3、1≤4)の両方が重なります。重なっています。

すり抜けと連続衝突検出

各ステップの終わりだけで判定すると、速くて薄い物を見逃します。
半径0.1 mの弾丸の様なボールが30 m/sで飛ぶと、60 Hzの1ステップで30⋅160=0.5 m進みます。
厚さ0.2 mの壁にボールの直径を足しても0.4 mしかないので、ボールは一度も重ならずに、あるステップで壁の手前、次のステップで壁の向こうに居る事があり得ます。
此れがすり抜けです。
対策は、ステップを小さくする、衝突形状を厚くする、又は連続衝突検出(CCD)です。
CCDはステップの間に通った経路全体(ボールなら古い位置から新しい位置までのカプセル)を調べ、衝突の時刻を求めます。

応答:反発係数と力積

接触が見つかったら、衝突応答で物体が離れる様に速度を変えます。
衝突は殆ど時間が掛からないので、接触法線に沿った瞬間的な運動量の変化、力積jとして扱います。
反発係数eは、近づく速さのどれだけが戻って来るかを表します:

vrel′=−evrel

e=1は完全弾性衝突(エネルギーを失わない)、e=0は完全非弾性衝突(くっ付く)で、本物の材質は其の間です(テニスボールで約0.7〜0.8)。
1次元で、速度v1、v2、相対速度vrel=v1−v2の2つの物体について、運動量を保存しつつ此れを実現する力積は

j=−(1+e)vrel1m1+1m2,v1′=v1+jm1,v2′=v2−jm2

です。

計算例。
m1=2 kgが右へv1=3 m/sで、m2=1 kgが左へv2=−1 m/sで動き、e=0.5です。
vrel=3−(−1)=4なので

j=−1.5⋅40.5+1=−4,v1′=3−42=1,v2′=−1+41=3

確認しましょう。
新しい相対速度は1−3=−2=−0.5⋅4、運動量は前が2⋅3+1⋅(−1)=5、後が2⋅1+1⋅3=5、運動エネルギーは9+0.5=9.5 Jから1+4.5=5.5 Jに減ります。
(無限に重い)地面で跳ねるボールは速さのe倍を保ち、上がる高さは速さの2乗に比例するので、跳ねる度に前の高さのe2倍に届きます。
e=0.8なら、1 mから落としたボールは0.64 mまで戻ります。

3次元では同じ式を接触法線に沿って使い(回転があると慣性テンソルの項が加わります)、摩擦が面に沿った力積を加え、めり込み量だけ物体を押し離します。

拘束

自由ではない物は沢山あります。
ドアは蝶番に付いていて、鎖の輪は繋がっていなければならず、キャラクタの骨は関節で結ばれています。
拘束は動きを制限します。
ボールジョイントは2点を一緒に保ち、ヒンジは1つの軸周りの回転だけを許し、距離拘束は長さを保ちます。
物理エンジンは毎ステップ、全ての接触と拘束をまとめて、普通は反復的に、少しずつ直す数回のパスで解きます。
ラグドールは、キャラクタのスケルトン(第13章)を、角度制限付きの関節拘束で繋いだ剛体に変えた物で、ゲームのキャラクタはこうして説得力のある倒れ方をします。

14.7 流体、煙、炎、破壊

流体の2つの記述方法

水、煙、炎は流体です。
其の動きはナビエ・ストークス方程式で記述されます。
流体の小さな各部分についての𝐅=m𝐚で、圧力、粘性、外力を含み、更に流体が非圧縮性であるという条件が加わります。
導出はしません。
大事なのは、シミュレータが流体を次の2つの方法のどちらかで離散化する事です:

  • 格子法(オイラー的手法):固定されたセルが速度、圧力、密度、温度を保存し、流体は格子の中を通って流れます。煙や炎に自然です。ジョス・スタムの「Stable Fluids」(SIGGRAPH 1999)は、どんな時間刻みでも安定なステップで、格子の流体をグラフィックスで実用的にしました。代償はメモリで、2563セルで既に1,680万個です。
  • 粒子法(ラグランジュ的手法):粒子が質量と速度を運び、流れと一緒に動きます。しぶきを上げる水に自然で、質量は自動的に保存されます。一番有名な手法はSPH(平滑化粒子流体力学法)で、1977年に天体物理学の為に、ギンゴールドとモナハン、そして独立にルーシーが開発しました。各量は近くの粒子の重み付き和です。例えば粒子iでの密度はρi=∑jmjW(‖𝐱i−𝐱j‖)で、Wはある半径で0に落ちる滑らかなカーネルです。ミュラー、チャリパー、グロス(2003年)がSPHをインタラクティブなグラフィックスに持ち込みました。

映画の水は両方を組み合わせる事がよくあります(PIC/FLIPのハイブリッド手法)。
描画では、液体の表面を粒子から陰関数曲面として作り直し、マーチングキューブ法(第9章の9.3節)でポリゴン化します。
煙は関与媒質として、ボリュームレンダリングで描きます(第12章の12.6節)。

破壊

物を壊すのは、普通は剛体のシミュレーションに1つの工夫を加えた物です。
シミュレーションの前に、物体を破片に事前分割し(衝撃点の周りに散らばる3次元のボロノイ図をよく使います)、破片を壊れる拘束で接着しておきます。
力積が拘束の強さを超えると接着が外れ、破片は独立した剛体として飛び、其の上に埃の粒子が加わります。

検定のポイント

  • 陽的オイラー法は古い状態だけを使います。1次精度で、どんなステップ幅でも減衰の無い振動子にエネルギーを加える(1ステップごとに1+h2ω2倍)ので、爆発し得ます。
  • 半陰的(シンプレクティック)オイラー法は先に速度を更新します。計算量は同じで、エネルギーは有界、hω<2で安定です。陰的オイラー法はどんなステップでも安定ですが減衰します。RK4は4次精度で、評価は4回です。硬いバネには小さなステップ、サブステップ、又は陰的積分が必要です。
  • バネ質点系の布:構造バネ、せん断バネ、曲げバネ。フックの法則。
  • 衝突検出:ブロードフェーズの次にナローフェーズ。2つのAABBは全ての軸で重なる時だけ重なります。すり抜けとCCD。応答:反発係数e(1は弾性、0は非弾性)、法線に沿った力積、運動量は保存されます。
  • 流体:格子法(オイラー的)と粒子法(ラグランジュ的、SPH)。

14.8 リアルタイムアニメーション

フレーム時間の予算

リアルタイムアニメーションでは、ディスプレイが次のフレームを欲しがる前に、毎フレームを仕上げなければなりません。
毎秒fフレームなら、フレーム時間の予算は1000f msです:

フレームレート 1フレームの予算
30 fps 33.3 ms
60 fps 16.7 ms
90 fps(VRヘッドセット) 11.1 ms
120 fps 8.33 ms
144 fps 6.94 ms

入力、ゲームロジック、アニメーション、物理、音、描画が全て此の16.7 msを分け合います(CPUとGPUは並列に動くので、其々が大体自分の予算を持ちます)。
オフラインレンダリングは1フレームに何時間も掛けられますが、ゲームはプレイヤーがボタンを押している間に、全てをフレームごとに決めなければなりません。
其の為、重いエフェクトはよくベイクされます。
オフラインでシミュレーションし、アニメーションとして再生するのです。

オフライン(映画) リアルタイム(ゲーム、VR)
1フレームの時間 数分〜数時間 数ミリ秒
入力 事前に決まっている ユーザー、毎フレーム
シミュレーション 高解像度、やり直しと演出が出来る 低解像度、決して爆発してはならない、よくベイクされる
キャラクタ 完全なリグ、どんな変形でも スケルトン+GPUスキニング、LOD

ゲームループと固定時間刻み

ゲームはゲームループを回します。
入力を読み、世界を更新し、描画し、繰り返します。
一番単純な版は、測ったフレーム時間だけシミュレーションを進めます。
すると物理がフレームレートに依存します。
100 msの遅いフレームは巨大なステップになり(爆発するバネを思い出して下さい)、速いコンピュータと遅いコンピュータで結果が変わります。
標準的な対策が固定時間刻みです。
シミュレーションは常に同じhだけ進み、実時間に追い付くのに必要な回数だけ繰り返します。
其の為にアキュムレータを使います:

  1. フレームの実際の経過時間をアキュムレータに足します。
  2. アキュムレータにh以上ある間:現在の状態を前の状態として保存し、1ステップのシミュレーションを行い、hを引きます。
  3. 前の状態と現在の状態の間をα=accumulatorhで補間した状態を描画します:𝐱draw=(1−α)𝐱prev+α𝐱curr。

計算例。
物理は50 Hz(h=20 ms)、ディスプレイは60 fps(1フレーム16.6‾ ms)です。

フレーム 足した後のアキュムレータ ステップ数 残り α
1 16.67 0 16.67 0.83
2 33.33 1 13.33 0.67
3 30.00 1 10.00 0.50
4 26.67 1 6.67 0.33
5 23.33 1 3.33 0.17
6 20.00 1 0.00 0

0から100ミリ秒の時間軸。上に16.7 msごとの描画フレームF1〜F6、下に20 msごとの物理ステップS1〜S5、各フレームでの補間係数アルファは0.83、0.67、0.50、0.33、0.17、0

100 msで6フレーム、20 msのステップが丁度5回です。
シミュレーションは実時間に付いて行けています。
補間しなければ、フレーム1は動きを見せず、フレーム2〜6は1ステップずつで、カクつきます。
補間すれば動きは滑らかになりますが、世界を最大1ステップ遅れて見せる事になります。
1フレームがとても長く掛かった時は、1フレームのステップ数に上限を付けなければなりません。
付けないと、ステップに更に時間が掛かり、次のフレームは更に追い付かなければならず、ゲームが固まります(「死のスパイラル」)。

#include <cstdio>

// A ball falling under gravity, simulated with a FIXED time step and drawn at any frame rate.
struct State {
  double y, v;
};

State step(State s, double h) {           // semi-implicit Euler, g = 9.8 m/s^2
  double v = s.v - 9.8 * h;
  return {s.y + h * v, v};
}

int main() {
  const double h = 0.020;                 // physics: 50 steps per second
  const double frameTimes[] = {1 / 60.0, 1 / 60.0, 1 / 60.0, 1 / 60.0, 1 / 60.0, 0.100, 1 / 60.0};
  const int maxStepsPerFrame = 4;         // protection against the "spiral of death"

  State previous{10.0, 0.0}, current{10.0, 0.0};
  double accumulator = 0.0;
  int frame = 0;
  for (double frameTime : frameTimes) {   // in a game: measured with a clock
    accumulator += frameTime;
    int steps = 0;
    while (accumulator >= h && steps < maxStepsPerFrame) {
      previous = current;
      current = step(current, h);
      accumulator -= h;
      ++steps;
    }
    if (steps == maxStepsPerFrame && accumulator >= h) {
      accumulator = 0.0;                  // too far behind: drop the rest
    }
    double alpha = accumulator / h;       // how far we are between previous and current
    double yDraw = (1 - alpha) * previous.y + alpha * current.y;
    printf("frame %d: steps %d, alpha %.3f, draw y = %.4f\n", ++frame, steps, alpha, yDraw);
  }
  return 0;
}

frame 1: steps 0, alpha 0.833, draw y = 10.0000
frame 2: steps 1, alpha 0.667, draw y = 9.9974
frame 3: steps 1, alpha 0.500, draw y = 9.9922
frame 4: steps 1, alpha 0.333, draw y = 9.9843
frame 5: steps 1, alpha 0.167, draw y = 9.9739
frame 6: steps 4, alpha 0.000, draw y = 9.8902
frame 7: steps 0, alpha 0.833, draw y = 9.8641

フレーム1〜5は表と一致します。
フレーム6は100 msの引っ掛かりです。
103.3 msが溜まっていて本来は5ステップですが、上限で4ステップまでしか許されず、残りは捨てられます。
其の為ゲームは固まる代わりに、一瞬少し遅くなるだけです。
此の様なループはOpenGLシリーズの第26章で自分で書きます。

スケルタルアニメーションの再生

第13章のキーフレームやモーションキャプチャのクリップは、ゲームでは毎フレーム、全ての関節について、各クリップを現在の時刻でサンプリングして再生します。
キャラクタが1つのクリップだけを再生する事は殆どありません:

  • アニメーションブレンド(13.9節のモーションブレンディング):2つのクリップを関節ごとに重みで混ぜ、回転はslerp又は正規化した線型補間で補間します。1.5 m/sの歩きと4.5 m/sの走りを重みw=2.5−1.54.5−1.5=13で混ぜると、2.5 m/sの小走りになります。ある股関節の角度が歩きで20°、走りで50°なら、ブレンドは20°+13(50°−20°)=30°です。2つのサイクルは時間を揃え(同じ位相で同じ足が着地)なければ、足が滑ります。
  • クロスフェード:クリップを切り替える時、跳ぶ代わりに0.2〜0.3秒掛けて古い物から新しい物へ混ぜます。
  • アニメーションステートマシン:待機、歩き、走り、ジャンプ等の状態と、ゲームのイベントや条件(「速度 > 0.1」、「ジャンプが押された」)で起きる遷移です。
  • ブレンドツリー:パラメータで動くブレンドノードの木です。例えば速度と方向で、8つの移動クリップを一度に混ぜます。
  • レイヤーとIK:加算レイヤー(呼吸、反動)、脚が走っている間に狙いを付ける上半身、そして実行時に足を凸凹の地面に置くインバースキネマティクス(第13章)です。

GPUスキニングとアニメーションのLOD

ゲームは第13章(13.8節)のスキニングを、1フレームに1回ボーン行列を送って頂点シェーダーで行います。此れがGPUスキニングです。
頂点数10,000、1頂点あたり4つの影響を持つキャラクタは、1フレームに40,000回の行列・ベクトル積が必要です。
60 fpsで200体なら40000⋅200⋅60=4.8⋅108回/秒で、GPUには楽ですが、CPUには辛い量です。

アニメーションのLODは、第9章の詳細度の考え方を動きに使います。
遠くのキャラクタはスケルトンの更新を減らし、ボーンを減らし、又はベイクした頂点アニメーションに切り替えます。
60本のボーンを持つ200体を60 Hzで更新すると、200⋅60⋅60=720,000回/秒の関節の更新が必要です。
其のうち150体が遠くにあって15 Hzで更新するなら50⋅60⋅60+150⋅60⋅15=180,000+135,000=315,000で、半分以下です。

物理エンジンとインタラクション

ゲームは物理エンジンを使います。
剛体、衝突、拘束、キャラクタコントローラ、乗り物の為のライブラリで、固定時間刻みと半陰的積分を使います。
其の第1の規則は頑丈さです。
プレイヤーが100個積んだ木箱にトラックで突っ込んでも、ゲームのシミュレーションは決して爆発してはなりません。
リアルタイムアニメーションはインタラクティブでもあります。
プレイヤーの入力が毎フレーム動きを変えるので、キャラクタは反応の速いステートマシンと、二次的な動きやラグドールの為の物理を組み合わせます。
衝突付きの簡単な3次元のキャラクタコントローラはOpenGLシリーズの第37章で作ります。
リアルタイムシステムのハードウェア側(GPU、垂直同期、遅延)は第17章です。

14.9 手続き型アニメーション

手続き型アニメーションは、キーや物理シミュレーションの代わりに、規則や関数から動きを計算します。
必要なデータはとても少なく、少数のパラメータで変えられ、全く同じ繰り返しになる事がありません。

関数から作る動き

  • 振動:海に浮かぶブイの上下、y(t)=Asin(2πft)。A=0.2 m、f=0.5 Hzなら、t=0.5秒で0.2 m、t=1秒で0、t=1.5秒で−0.2 mです。
  • 進行波:位置に依存する位相を加えてθ(t,x)=Asin(ωt−kx)とすると、草原や旗の列が、速さωkで進む波として揺れます。ω=2 rad/s、k=0.5 rad/mなら4 m/sです。
  • ノイズによる動き:第11章の11.10節のノイズを、位置の代わりに時間を入力にしてサンプリングします。カメラを0.05(2v(t)−1) m動かす手ぶれは、第11章のバリューノイズvを使うと、t=2.3秒で0.05(2⋅0.3512−1)≈−0.0149 mです(其処の計算例でv(2.3)=0.3512でした)。繰り返さずに滑らかにさまよう動きは、手持ちカメラ、揺らめく炎、待機中の生き物に丁度欲しい物です。複数のオクターブを重ねると、ゆっくりした漂いの上に細かい震えが乗ります。
  • 手続き型の歩行サイクル:足は足跡から足跡へ弧を描き、IK(第13章)で置かれ、腰は1サイクルに2回(1歩に1回)上下します。脚の多い蜘蛛やロボットは、全て此の方法で動かす事もよくあります。

エクスプレッションとドライバー

DCCツール(Maya、Blender、Houdini等)では、どんなパラメータにも小さな式、エクスプレッションやドライバーを付けられます。
典型的な例は、滑らずに転がる車輪です。
距離d進むと、車輪はθ=drラジアン回ります。
半径0.3 mの車輪の車が3 m進むと、車輪は30.3=10 rad ≈573°、約1.59回転します。
アニメーターが車にどんな経路を与えても自動的にです。

群れ:ボイド

1987年、クレイグ・レイノルズは「Flocks, Herds, and Schools: A Distributed Behavioral Model」(SIGGRAPH '87)を発表しました。
群れの経路を台本で決める代わりに、1羽1羽の鳥、ボイド(「bird-oid」から)を独立したエージェントとしてシミュレーションしました。
各ボイドは近くの仲間だけを見て、3つの単純な舵取りの規則に従います:

  1. 分離(レイノルズの言葉では衝突回避):近過ぎる仲間から離れる様に舵を取ります。
  2. 整列(速度の一致):近くの仲間の平均の速度(向きと速さ)に合わせる様に舵を取ります。
  3. 結合(群れの中心へ):近くの仲間の平均の位置(中心)に向かう様に舵を取ります。

レイノルズは此の順番を優先順位として挙げました。
衝突を避ける事が一番大事です。
群れ全体の形を知っているボイドは居らず、リーダーも居ません。
障害物の周りで分かれて又合流する群れの振る舞いは、局所的な規則から創発します。
其の為、行動のアニメーション(ビヘイビアアニメーション)の古典的な例になっています。

計算例。
ボイド0は𝐩=(0,0)にあり、速度は𝐯=(1,0)です。
半径r=3以内を近くの仲間として見て、rs=1より近い仲間を近過ぎると見なします。
他のボイドは次の通りです:

ボイド 位置 速度 ボイド0までの距離
1 (2,1) (1,1) 5≈2.24
2 (1,2) (0,1) 5≈2.24
3 (0.5,−0.5) (1,−1) 0.5≈0.71
4 (0.5,1.5) (2,1) 2.5≈1.58
5 (4,3) (0,−1) 5:近くの仲間ではない

  • 結合:ボイド1〜4の中心は(2+1+0.5+0.5,1+2−0.5+1.5)4=(4,4)4=(1,1)です。舵取りは中心から自分の位置を引いて(1,1)です。
  • 整列:平均の速度は(1+0+1+2,1+1−1+1)4=(1,0.5)です。舵取りは平均から自分の速度を引いて(1,0.5)−(1,0)=(0,0.5)です。
  • 分離:1より近いのはボイド3だけです。距離の2乗の逆数で重み付けして、其処から離れる舵取りは𝐩−𝐩3‖𝐩−𝐩3‖2=(−0.5,0.5)0.5=(−1,1)です。

3つの重みを全て1にすると、舵取りの合計は(1,1)+(0,0.5)+(−1,1)=(0,2.5)で、真上、つまり下のボイド3から離れ、上の仲間に近づく向きです。
此れを加速度としてh=0.1秒の1ステップに使うと、𝐯′=(1,0)+0.1⋅(0,2.5)=(1,0.25)でarctan0.25≈14°左に曲がり、𝐩′=(0,0)+0.1⋅(1,0.25)=(0.1,0.025)です。
実際の実装では分離に一番大きな重みを付け、速さと曲がる速さを制限します。

原点で右を向くボイド0、点線の半径3の円の中の仲間1〜4、其の外のボイド5、ボイド3が中にある半径1の分離の円、そして舵取りのベクトル。ボイド3から離れる分離(−1, 1)、整列(0, 0.5)、仲間の中心へ向かう結合(1, 1)、其の合計(0, 2.5)

此の規則を全てのボイドに毎ステップ適用すると、ばらばらに散らばったボイドが自分達で群れにまとまります。
次の図は、同じ半径を使った48羽のボイドの2次元シミュレーションです(重みは1.5、1、0.6、速さは1.5〜3に制限、h=0.05秒の半陰的オイラー法):

48羽のボイドのシミュレーションの2つのスナップショット。t = 0秒ではボイドはランダムな向きで散らばり、t = 16秒では殆ど同じ向きの1つの群れとして一緒に飛ぶ

コストは近くの仲間を見つける所にあります。
素朴には全てのボイドが他の全てを調べる(n2)ので、実際の群れは14.6節のブロードフェーズの様に、格子等の空間構造を使います。

群衆

群衆シミュレーションは、此の考え方を人に広げます。
各エージェントは目的地を持ち、経路を計画し、他のエージェントを局所的に避け、アニメーションシステムが其の動きに合うクリップを選びます。
映画『ロード・オブ・ザ・リング』の合戦シーンの為に開発されたMassiveの様な群衆ツールは、各エージェントに単純な知覚と判断の規則を与え、アニメーターが1人1人に触れなくても何千人もの兵士が戦える様にしました。
ゲームでは強いLODを使い、遠くの群衆はアニメーションするビルボードにまで落とします。

育つ植物:時間の中のL-system

第9章の9.5節のL-systemの世代は、順に見せると跳びます。
成長をアニメーションさせるには、各モジュールに年齢を持たせ、長さ、太さ、角度を年齢の滑らかな関数にします。
すると植物は連続的に育ち、新しい枝が芽吹く間に古い枝が太くなります。

検定のポイント

  • フレーム時間の予算 = 1000 / fps ms(60 fpsで16.7 ms)。アキュムレータを使う固定時間刻みで、物理をフレームレートから独立させます。描画は最後の2つの状態の間を補間します。
  • リアルタイムのキャラクタ:ブレンド(slerp)、クロスフェード、ステートマシン、ブレンドツリー、頂点シェーダーでのGPUスキニング、アニメーションのLOD。
  • 手続き型アニメーション:規則や関数から作る動き(正弦波、時間のノイズ、θ=d/rの様なエクスプレッション)。
  • ボイド(レイノルズ、1987年):分離、整列、結合。近くの仲間だけを使い、リーダーは居ません。整列は速度を合わせ、結合は中心へ向かいます。

14.10 実写映像との合成

VFXの流れ

視覚効果(VFX)のショットは、本物のカメラで撮った映像にCGを入れます。
本物の街の中の怪物や、本物の役者の後ろのセットの拡張です。
映像はプレート(実写素材)、レイヤーを1枚の画像にまとめる事は合成(コンポジット)と言います。
信じられる結果にするには、CGが第2章のカメラモデルが記述するあらゆる点でプレートと一致しなければなりません。
カメラの位置とレンズ、光、モーションブラー、ピント、ノイズです。

VFXの流れ。実写素材の撮影(HDRプローブも)、カメラとレンズを推定するマッチムーブ、CGのレイアウトとアニメーション、パスに分けたライティングとレンダリング。並行して素材のキーイングとロトスコープ。両方が合成に入り、グレーディングと出力へ続く

撮影現場では、レンズのデータを記録し、歪みを測る為にレンズ格子(グリッド)を撮影し、CGの物体が立つ場所でHDRのライトプローブを撮ります。

マッチムーブ:カメラを復元する

マッチムーブはカメラトラッキングとも呼ばれ、映像から本物のカメラの動きを復元して、CGのカメラが其れを正確に真似出来る様にします。
2つの部分から成ります:

  1. 2次元トラッキング:多くの目立つ点(角、撮影現場に置いたマーカー)を、フレームからフレームへ画像の中で追いかけます。
  2. カメラの解析(ソルブ):各フレームのカメラの位置と向き、及び焦点距離(とレンズ歪み)を、トラッキングした点の3次元の位置と一緒に求めます。3次元の点を第2章の2.2節のピンホールモデルで投影した時、トラッキングした2次元の位置に一致する様にするのです。此れはコンピュータビジョンのstructure from motionの問題です。

解析の品質は再投影誤差で測ります。
カメラが幅36 mmのセンサーに35 mmのレンズを付け、画像の幅が1920ピクセルなら、ピクセル単位の焦点距離はf=3536⋅1920≈1866.7 pxです。
カメラ空間(カメラは−zを向く)で(1,0.5,−5)にある解析済みの3次元の点は、画像の中心から

x=f⋅15≈373.3 px,y=f⋅0.55≈186.7 px

の位置に投影されます。
トラッカーが其の特徴点を(375.0,186.0)で測っていたなら、誤差は(1.67,−0.67)、約1.672+0.672≈1.8 pxです。
解析は全ての点と全てのフレームについて誤差の2乗の和が最小になる様に、全てのカメラと点を調整します。
良い解析は平均誤差が1ピクセルを十分下回り、其れに固定したCGはプレートの上で「滑り」ません。
マッチムーブが復元するのはカメラ(と疎な点群)です。
役者を背景から切り離す事はせず、其れは次の仕事です。

プレートには本物のレンズの歪み(歪曲収差)もありますが、レンダラーのピンホールカメラには歪みがありません。
其の為、トラッキングの為にプレートを測ったモデル(第2章のr′=r(1+kr2))で歪み補正し、CGは歪み無しでレンダリングし、完成したCGを同じモデルで歪ませ直してから元のプレートと合成します。
k=−0.12なら、正規化した半径r=0.8にレンダリングされたCGの点は、本物のレンズが置いたであろう場所0.8⋅(1−0.12⋅0.64)≈0.739に移さなければなりません。

動きが事前に決まっているなら、ロボットの様な装置に載せたモーションコントロールカメラが全く同じ動きを何度も繰り返せます(役者、背景だけ、ミニチュア)。
『スター・ウォーズ』(1977年)の為に作られたダイクストラフレックスが有名な初期の例です。

クロマキー

役者をCGの世界に入れるには、マット、つまりどの画素が役者に属するかを示すアルファチャンネルが必要です(第2章でアルファを4番目のチャンネルとして紹介しました)。
古典的な方法は、役者を一様で強い色のスクリーンの前で撮影し、色からマットを計算する事です。
此れがクロマキーで、マットを抜き出す処理をキーイングと言います。
スクリーンは肌の色から最も遠いグリーンバックかブルーバックです。
緑はベイヤー配列のセンサー(第2章の2.4節、緑の受光素子が2倍)で一番きれいなチャンネルになり、青は衣装が緑の時に使います。

単純で広く使われるキーヤーが色差法です。
画素が他のチャンネルよりどれだけ緑かを見ます:

d=G−max(R,B),α=1−clamp(ddS,0,1)

dSはスクリーンの色についての同じ差です。
スクリーンの色がS=(0.1,0.8,0.15)ならdS=0.8−0.15=0.65です。

  • スクリーン自体:d=0.65、α=0(完全に透明)。
  • 肌(0.8,0.6,0.5):d=0.6−0.8=−0.2を0に切り詰めてα=1(不透明)。
  • 灰色のセーター(0.4,0.4,0.4)が半分だけ覆う縁の画素:カメラは0.5(0.4,0.4,0.4)+0.5(0.1,0.8,0.15)=(0.25,0.6,0.275)と平均するので、d=0.6−0.275=0.325、α=1−0.5=0.5で、丁度正しい値です。
  • 半分が肌、半分がスクリーン:(0.45,0.7,0.325)、d=0.25、α≈0.615で、本当の0.5ではありません。此の方法が正確なのは無彩色だけで、本物のキーヤーには此の様な場合を調整する沢山の設定があります。

縁の画素にはまだスクリーンの緑が半分入っています。
スクリーンの減算で其れを取り除きます。
(1−α)Sを引くと前景の寄与が残ります:

(0.25,0.6,0.275)−0.5(0.1,0.8,0.15)=(0.2,0.2,0.2)=0.5⋅(0.4,0.4,0.4)

此れはセーターの色にアルファを掛けた物、つまり乗算済みの色です(後述)。
新しい青空の背景(0.2,0.3,0.8)に合成すると(0.2,0.2,0.2)+0.5(0.2,0.3,0.8)=(0.3,0.35,0.6)です。

スクリーンで跳ね返った緑の光は、役者の縁や光沢のある面にも色を付けます。
此れがスピルです。
スピル除去は、緑のチャンネルが他のチャンネルから見て高過ぎる所で其れを制限します。
例えば

G′=min(G,R+B2)

です。
肌(0.8,0.6,0.5)はR+B2=0.65≥0.6なので変わらず、スピルの乗った肌(0.8,0.7,0.5)は(0.8,0.65,0.5)になります。

4つのパネル。グリーンバックの前の手続き的に生成した生き物、色差キーヤーで計算したマット、新しい空と地面の背景に合成した生き物(輪郭に緑の縁がある)、スピル除去をした同じ合成(縁が無い)

此の図の生き物、スクリーン、背景は全て生成した物です(写真ではありません)。
素材は、生き物の本当の被覆率と少しむらのあるスクリーンを混ぜ、輪郭の近くに緑のスピルを加えています。
上のキーヤーはマットを画素あたり平均0.01未満の誤差で復元します。
3枚目はスピルが残す緑の縁を、4枚目はスピル除去をした結果を見せています。

同じ手順をフラグメントシェーダーの関数にした物です(Slang、GLSL、HLSLの順):

static const float3 screenColor = float3(0.1, 0.8, 0.15);

float keyAlpha(float3 c) {
  float d = c.g - max(c.r, c.b);
  float dS = screenColor.g - max(screenColor.r, screenColor.b);
  return 1.0 - saturate(d / dS);
}

// The keyed foreground: premultiplied color in rgb, alpha in a.
float4 keyPixel(float3 plate) {
  float a = keyAlpha(plate);
  float3 fg = max(plate - (1.0 - a) * screenColor, float3(0.0, 0.0, 0.0)); // screen subtraction
  fg.g = min(fg.g, 0.5 * (fg.r + fg.b));                                    // spill suppression
  return float4(fg, a);
}

// Premultiplied "over": foreground over an opaque background.
float3 over(float4 fg, float3 background) {
  return fg.rgb + (1.0 - fg.a) * background;
}
const vec3 screenColor = vec3(0.1, 0.8, 0.15);

float keyAlpha(vec3 c) {
  float d = c.g - max(c.r, c.b);
  float dS = screenColor.g - max(screenColor.r, screenColor.b);
  return 1.0 - clamp(d / dS, 0.0, 1.0);
}

// The keyed foreground: premultiplied color in rgb, alpha in a.
vec4 keyPixel(vec3 plate) {
  float a = keyAlpha(plate);
  vec3 fg = max(plate - (1.0 - a) * screenColor, vec3(0.0)); // screen subtraction
  fg.g = min(fg.g, 0.5 * (fg.r + fg.b));                      // spill suppression
  return vec4(fg, a);
}

// Premultiplied "over": foreground over an opaque background.
vec3 over(vec4 fg, vec3 background) {
  return fg.rgb + (1.0 - fg.a) * background;
}
static const float3 screenColor = float3(0.1, 0.8, 0.15);

float keyAlpha(float3 c) {
  float d = c.g - max(c.r, c.b);
  float dS = screenColor.g - max(screenColor.r, screenColor.b);
  return 1.0 - saturate(d / dS);
}

// The keyed foreground: premultiplied color in rgb, alpha in a.
float4 keyPixel(float3 plate) {
  float a = keyAlpha(plate);
  float3 fg = max(plate - (1.0 - a) * screenColor, float3(0.0, 0.0, 0.0)); // screen subtraction
  fg.g = min(fg.g, 0.5 * (fg.r + fg.b));                                    // spill suppression
  return float4(fg, a);
}

// Premultiplied "over": foreground over an opaque background.
float3 over(float4 fg, float3 background) {
  return fg.rgb + (1.0 - fg.a) * background;
}

乗算済みの色にスピル除去を行っても問題ありません。
3つのチャンネルを全てα倍しても、どれが大きいかは変わらないからです。
キーイングは画像の編集全般の一部で、マットをきれいにするフィルタ(ぼかし、収縮、膨張)は第15章で扱います。

マット、アルファ、乗算済みアルファ

第10章の10.12節では、over演算子C=αsCs+(1−αs)Cdで面をフレームバッファの上に混ぜました。
合成には、背景自体も部分的に透明かもしれない(最終画像ではなくレイヤー)完全な版が必要です。
ポーターとダフ(1984年)は其れを乗算済みアルファ(プリマルチプライドアルファ)で定義しました。
各画素を(R,G,B,α)ではなく(αR,αG,αB,α)として保存します。
乗算済みの色を𝐜=αCと書くと、A over Bは

𝐜o=𝐜A+(1−αA)𝐜B,αo=αA+(1−αA)αB

で、4つのチャンネル全てに同じ式です。
乗算済みアルファは、次の3つの理由で、合成ソフトとレンダラーの内部の標準です:

  • フィルタ処理が正しい。 赤い不透明な画素(1,0,0,1)と、見えない色が偶々緑の透明な隣の画素(0,1,0,0)を、拡大縮小やぼかしの様に平均します。ストレートでは(0.5,0.5,0,0.5)で、白の上では0.5(0.5,0.5,0)+0.5(1,1,1)=(0.75,0.75,0.5)、黄色っぽい縁になります。乗算済みでは透明な画素は(0,0,0,0)、平均は(0.5,0,0,0.5)、白の上では(0.5,0,0)+0.5(1,1,1)=(1,0.5,0.5)で、白の上の半分の赤、つまりピンクで、正しい結果です。
  • overが結合的:(A over B) over C=A over (B over C)なので、レイヤーはどんなまとまりでも先に合成しておけます。
  • 加算的な光が使える:α=0で色が0でない画素は、何も覆わずに光を足します(グロー)。

ポーターとダフは、マットを組み合わせるin、out、atop、xorも定義しました。
ガベージマットは、要らない部分(スクリーンの端の照明スタンド等)を捨てる為に手で大まかに描くマットです。
ホールドアウトマットは、本物の物体がCGの前を通る所でCGを切り抜きます。

ロトスコープ

全てのショットにグリーンバックがある訳ではありません。
ロトスコープはマットを手で作ります。
アーティストがキーフレームで物体の周りにスプライン図形を描き、ソフトウェアが其れを補間・追跡し、アーティストがフレームごとに直します。
名前は、マックス・フライシャーのロトスコープ(1917年に特許)、つまり実写のフィルムをアニメの為に1コマずつなぞる装置から来ています。
ロトスコープは遅いですがどんな映像にも使えます。
クロマキーは自動ですが、撮影時に色の付いたスクリーンが必要です。

クロマキー ロトスコープ マッチムーブ
作る物 マット(アルファ) マット(アルファ) カメラ(位置、向き、焦点距離)と3次元の点
元にする物 各画素の色 アーティストが1フレームずつ描く図形 フレームを通して追跡した2次元の特徴点
撮影時に必要な物 ブルーバック又はグリーンバック 特に無し 追跡出来る特徴点、レンズのデータ

光、影、カメラを合わせる

カメラとマットが完璧でも、光がプレートと合っていなければ、CGは貼り付けた様に見えます:

  • 光:撮影現場で撮ったHDRのライトプローブで、第12章の12.13節の様にイメージベーストライティングでCGを照らし、同じ方向から同じ光を受ける様にします。ホワイトバランスと色温度もプレートと一致させなければなりません(第3章の3.8節)。
  • 影と接地:CGの生き物は本物の地面に影を落とさなければなりません。地面の簡単な代役のモデルをシャドウキャッチャーとしてレンダリングします。其れ自体は見えず、CGが其の上に落とす影(と映り込み)だけを記録します。デベヴェックの差分レンダリング(1998年)は、物体が生む差をプレートに加えます。物体のある局所シーンと無い局所シーンをレンダリングし、(ある − 無い)を足すのです。プレートの画素が0.6で、局所モデルが生き物無しで0.5、其の影ありで0.3とレンダリングされるなら、結果は0.6+(0.3−0.5)=0.4です。丁度影の分だけ暗くなり、プレート自身の模様は残ります。
  • レンダーパス(AOV):別々の画像(拡散、鏡面、影、深度、モーションベクトル)にすると、合成担当が再レンダリング無しで各部分を調整出来ます。
  • カメラの効果:CGは完璧にシャープできれいですが、プレートは違います。合成担当はモーションブラー(本物のシャッター時間、第2章の2.3節)、被写界深度、レンズの効果を合わせ、プレートの粒状感(フィルムグレイン)を加えます。最後の色のグレーディングがショットをまとめます。

バーチャルプロダクション

バーチャルプロダクションは、合成の一部を撮影現場に移します。
役者は大きなLEDウォールで囲まれたステージ、LEDボリュームの中に立ちます。
リアルタイムエンジンが、トラッキングしたカメラの視点からCGの環境を正しい視差でLEDウォールに描き、カメラは役者とスクリーンを一緒に撮影します。
「インカメラVFX」です。
『マンダロリアン』(2019年、Unreal Engineでレンダリング)の為のILMのStageCraftが此の手法を有名にしました。
LEDウォールは環境の色で役者を照らし、光沢のある衣装に本物の映り込みを与え、取り除くべき緑のスピルもありません。
一方でウォールの解像度と明るさには限りがあり、カメラがLEDの画素にピントを合わせるとモアレが出ます(又しても標本化です、第2章)。

検定のポイント

  • マッチムーブ(カメラトラッキング)は、映像の中で追跡した2次元の特徴点から、各フレームの本物のカメラの位置、向き、焦点距離(とレンズ歪み)を復元します。品質は再投影誤差で測ります。CGは歪み無しでレンダリングし、其の後で歪ませ直します。
  • クロマキーはブルーバックやグリーンバックの色からマットを計算します。スピルは被写体に当たったスクリーンの光で、緑のチャンネルを制限して除去します。ロトスコープはマットを1フレームずつ手で描き、スクリーンは要りません。
  • 乗算済みアルファはαCを保存します。over:𝐜A+(1−αA)𝐜B。フィルタ処理が正しく、結合的です。
  • ライティングの一致にはHDRのライトプローブ(IBL)を使います。シャドウキャッチャーと差分レンダリングがCGの影をプレートに加えます。モーションブラー、被写界深度、粒状感をプレートと一致させなければなりません。

14.11 まとめ

  • 物理ベースアニメーションは、𝐅=m𝐚と数値積分で状態(位置、速度)を進めます。もっともらしい動きが自動で得られますが、演出は難しくなります。
  • 陽的オイラー法はどんなステップ幅でも振動子にエネルギーを加えます。半陰的オイラー法は同じ計算量で、hω<2の間は有界です。ベルレ法は2次、RK4は4次の精度です。陰的オイラー法は常に安定ですが減衰します。
  • パーティクルシステムは粒子を放出し、動かし、衝突させ、年を取らせ、消します。バネ質点系は布、ソフトボディ、髪を表します。剛体は姿勢、トルク𝐫×𝐅、慣性テンソルを加えます。衝突にはブロードフェーズとナローフェーズが必要で、力積で解決します。流体は格子か粒子(SPH)を使います。
  • リアルタイムアニメーションは、固定時間刻みと補間、クリップのブレンド、GPUスキニング、アニメーションのLODで、フレーム時間の予算(60 fpsで16.7 ms)に収めます。
  • 手続き型アニメーションは規則から動きを計算します。正弦波、時間のノイズ、エクスプレッション、ボイド(分離、整列、結合)、群衆、育つL-systemです。
  • 合成はCGをプレートに合わせます。カメラにはマッチムーブとレンズ歪み、マットにはクロマキーかロトスコープ、乗算済みのover、光と影にはHDRプローブとシャドウキャッチャー、見た目にはモーションブラーと粒状感です。

14.12 用語

English 日本語
physics-based animation 物理ベースアニメーション
simulation シミュレーション
state 状態
equation of motion 運動方程式
time step 時間刻み
numerical integration 数値積分
explicit Euler method 陽的オイラー法(前進オイラー法)
semi-implicit (symplectic) Euler method 半陰的オイラー法(シンプレクティックオイラー法)
Verlet method ベルレ法
Runge–Kutta method ルンゲ=クッタ法
implicit Euler method 陰的オイラー法
stability 安定性
damping 減衰(ダンピング)
critical damping 臨界減衰
particle system パーティクルシステム
emitter エミッタ
mass–spring system バネ質点系
Hooke's law フックの法則
rest length 自然長
structural, shear and bend springs 構造バネ、せん断バネ、曲げバネ
deformable body, soft body 変形体、ソフトボディ(弾性体)
rigid body 剛体
center of mass 重心
angular velocity 角速度
torque トルク
inertia tensor 慣性テンソル
collision detection 衝突検出(衝突判定)
broad phase, narrow phase ブロードフェーズ、ナローフェーズ
bounding sphere 境界球(バウンディングスフィア)
axis-aligned bounding box (AABB) 軸平行境界ボックス(AABB)
tunneling すり抜け
continuous collision detection 連続衝突検出
collision response 衝突応答
impulse 力積
coefficient of restitution 反発係数
constraint 拘束
ragdoll ラグドール
fluid 流体
Navier–Stokes equations ナビエ・ストークス方程式
Eulerian grid, Lagrangian particles 格子法(オイラー的手法)、粒子法(ラグランジュ的手法)
smoothed particle hydrodynamics (SPH) SPH(平滑化粒子流体力学法)
real-time animation リアルタイムアニメーション
frame budget フレーム時間の予算
game loop ゲームループ
fixed time step 固定時間刻み
animation blending アニメーションブレンド
animation state machine アニメーションステートマシン
blend tree ブレンドツリー
GPU skinning GPUスキニング
physics engine 物理エンジン
procedural animation 手続き型アニメーション
expression, driver エクスプレッション、ドライバー
flocking, boids 群れ、ボイド
separation, alignment, cohesion 分離、整列、結合
behavioral animation 行動のアニメーション(ビヘイビアアニメーション)
crowd simulation 群衆シミュレーション
compositing with live action 実写映像との合成
plate プレート(実写素材)
matchmoving, camera tracking マッチムーブ、カメラトラッキング
reprojection error 再投影誤差
motion control camera モーションコントロールカメラ
chroma key, keying クロマキー、キーイング
blue screen, green screen ブルーバック、グリーンバック
spill, spill suppression スピル、スピル除去
matte マット
premultiplied alpha 乗算済みアルファ(プリマルチプライドアルファ)
garbage matte, holdout matte ガベージマット、ホールドアウトマット
rotoscoping ロトスコープ
shadow catcher シャドウキャッチャー
differential rendering 差分レンダリング
render pass (AOV) レンダーパス(AOV)
grain 粒状感(フィルムグレイン)
virtual production バーチャルプロダクション

14.13 課題

  1. ★ ボールをy=0からv=5 m/sで真上に投げます(g=9.8 m/s²、h=0.1秒)。陽的オイラー法と半陰的オイラー法で3ステップ計算し、どちらもt=0.3秒での正確な高さと比べて下さい。どちらが高過ぎますか? 其れは何故ですか?
  2. ★ 30、90、144 fpsでのフレーム時間の予算をmsで求めて下さい。あるゲームは物理を120 Hzで、50 fpsのディスプレイで動かします。14.8節のアキュムレータの方法に従うと、最初の3フレームで其々何回の物理ステップが走り、各フレームの後のαは幾つですか?
  3. ★★ 1次元で2つの物体が正面衝突します。m1=1 kgがv1=4 m/sで、m2=3 kgは静止しています。e=1とe=0の其々で力積と両方の最終速度を求め、各場合の運動量と運動エネルギーを確かめて下さい。
  4. ★★ 1 kgの質点がk=400 N/mのバネに吊るされています。半陰的オイラー法で安定な最大のステップは幾つですか? h=160秒で陽的オイラー法は1ステップごとにエネルギーを何倍にし、1秒後には何倍ですか? k=40000 N/mでは、半陰的オイラー法は60 Hzの1フレームに何回のサブステップが必要ですか?
  5. ★★ (0,0)のボイドが𝐯=(0,1)で飛んでいます(r=3、rs=1、重みは全て1)。他は、(2,0)で速度(1,0)、(−1,1)で(0,1)、(0.4,−0.3)で(1,1)、(−0.6,1.3)で(−2,2)、(5,5)で(1,0)です。結合、整列、分離のベクトルと其の合計を求め、ボイドがどうするかを言葉で説明して下さい。
  6. ★★★ 乗算済みの式を使い、色とアルファの両方について、overが結合的、(A over B) over C=A over (B over C)である事を示して下さい。次に、緑の部分のある衣装が何故クロマキーで問題になるかを説明し、制作側が対処する方法を3つ挙げて下さい。

選択問題

問1 ★ 次のうち、物理ベースアニメーションの数値積分について正しい物はどれか。

あ 陽的オイラー法は、時間刻みに関係なく、減衰の無いバネのエネルギーを正確に一定に保つ。
い 時間刻みを大きくすると、ステップ数が減って誤差の蓄積が少なくなるので、シミュレーションは常に正確になる。
う 陽的オイラー法では減衰の無いバネのエネルギーが毎ステップ増えるので、動きが爆発し得る。時間刻みを小さくするか、半陰的オイラー法を使うと改善する。
え 半陰的オイラー法は、バネがどれだけ硬くても、どんな時間刻みでも安定である。
お 4次のルンゲ=クッタ法は、オイラー法と同じく1ステップに1回しか力を評価しない。

問2 ★ 次のうち、レイノルズのボイドについて正しい物はどれか。

あ 各ボイドは近くの仲間だけを使い、分離・整列・結合に従って舵を取り、群れ全体の振る舞いは此の局所的な規則から創発する。
い リーダーのボイドが群れ全体の経路を計算し、他のボイドに送る。
う 整列とは、近くの仲間の平均の位置に向かって舵を取る事である。
え ボイドのモデルは、1983年にリーブスが炎の為のパーティクルシステムとして最初に発表した。
お ボイドはキーフレームの技法で、アニメーターが全てのキーで全ての鳥の位置を設定する。

問3 ★★ 次のうち、合成の技法と其の働きの組み合わせとして正しい物はどれか。

あ クロマキー:映像の中で追跡した点から、本物のカメラの動きを推定する。
い ロトスコープ:ブルーバックやグリーンバックの色から、マットを自動的に計算する。
う スピル除去:CGをピンホールカメラでレンダリング出来る様に、プレートのレンズ歪みを取り除く。
え マッチムーブ:映像の中で追跡した特徴点から、各フレームの本物のカメラの位置、向き、焦点距離を復元し、CGのカメラが其れを真似出来る様にする。
お HDRライトプローブ:セットの形を密なポリゴンメッシュとして記録する。

14.14 実践課題

ボールを手でシミュレーションし、跳ねさせ、合成する。
紙(方眼紙が便利です)、鉛筆、電卓を用意して下さい。
小さな物理シミュレーションを走らせ、衝突を検出して解決し、フレームを補間し、画素の列をキーイングして合成します。
章全体を1枚の紙の上で行います。
数値を簡単にする為、g=10 m/s²とします。

  1. 陽的と半陰的。 ボール(点)をy=2 mからv=0で落とします。時間刻みはh=0.1秒です。ステップ、t、v、yの列を持つ表を作り、陽的オイラー法と半陰的オイラー法で最初の3ステップを計算します。両方を正確なy=2−5t2と比べて下さい。
  2. 落下と検出。 半陰的オイラー法だけで続けます。各ステップの後で地面との衝突y<0を判定します。初めて衝突するステップとめり込み量を書き留めて下さい。
  3. 応答。 其のステップでy=0とし、反発係数e=0.8を使って速度を−evに置き換えます。ボールが2回目に落ちて来るまで表を続け、其の衝突も同じ様に解決して下さい。
  4. 理論と比べる。 最初に跳ねた後の表の中で一番高いyを見つけます。2 mから落とした時の正確な衝突速度2gh0、理想的な跳ね返りが届く高さ(e2⋅2 m)、表の跳ね返り直後の速さが正確に届く高さ(v22g)を計算し、違いを説明して下さい。
  5. フレームを補間する。 ディスプレイはt=0.25秒、つまりステップ2と3の間でフレームを表示します。αを求め、半陰的オイラー法の状態を補間して描画する高さを求めて下さい。
  6. 列をキーイングする。 グリーンバックの素材の1行に4つの画素(0.1,0.8,0.15)、(0.225,0.75,0.2625)、(0.475,0.65,0.4875)、(0.6,0.6,0.6)があり、スクリーンの色はS=(0.1,0.8,0.15)です。其々についてd=G−max(R,B)、α=1−clamp(d/0.65,0,1)、乗算済みの前景pixel−(1−α)Sを計算して下さい。
  7. 合成する。 キーイングした各画素を、乗算済みのoverで新しい背景(0.2,0.3,0.8)の上に合成します。次に、乗算済みで(0.4,0.1,0.05)、α=0.5のCGのボールの画素を、3番目の画素の上に2通りで重ねます。先ず(ボール over キーイングした画素)over 背景、次にボール over(キーイングした画素 over 背景)です。

自分で確認する方法:

  • 段階1:正確な高さは2つの手法の間にあります。陽的オイラー法は高過ぎ、半陰的オイラー法は同じだけ低過ぎます。
  • 段階2と3:最初の衝突はt=0.6秒で、小さな(0.2 m未満の)めり込みと共に見つかります。跳ねた後の山は全て前の山より低く、全エネルギー12v2+gy(1 kgあたり)は跳ねる時に決して増えません。
  • 段階4:跳ねた後の最初の山は1.28 mより低く、表の跳ね返り直後の速さのv22gよりも低くなります。検出の遅れと山の低さは、どちらも時間刻みが原因です。
  • 段階5:α=0.5で、描画する高さはステップ2と3の高さの間にあります。
  • 段階6:アルファは0、0.25、0.75、1です。乗算済みの前景は全て灰色でα⋅(0.6,0.6,0.6)に等しく、αより大きいチャンネルはありません。
  • 段階7:不透明な画素は背景か灰色を其のまま見せ、段階7の2通りのまとめ方は同じ色(結合性)、結果のアルファは1になります。


コメント(0)

まだコメントはありません。