Imaginantia

思ったことを書きます

表現のための数学 #3

続き

今回は数学よりも認知寄りの表現に関する、「重み」の話をしてみる。

動き

f:id:phi16_ind:20180909122159g:plain

2秒で始点から終点へ移動するアニメーションが4つある。これらは全て異なる印象を与える。 点が通っている空間は全て [0,1] である。では何が違うか。

物体の運動というのは本質的に位置だけでなく速度が関わっている。速度は動きに意味を与える重要な情報である。 速度というのは時間に関する位置の依存関係で、つまりは「動きを表現するには時間を考える必要がある」という話である。

「あるときの位置」から速度を計算することは一般には出来ない。何故なら速度は時間に関わるからであり、ある固定されたタイミングでの位置は速度情報を与えない。 では速度を知るにはどれくらいの情報があればいいのか?ほんのちょっとでも時間に関する依存関係がわかれば実は速度がわかる。 速度は 局所的 (Local) な概念で、全体空間の情報を知る必要は無いが、ある瞬間の情報だけでは足りないということが往々にしてあるのである。

ほんのちょっと「周りが見える」という概念を表現するのが 開集合 (Open set) である。多分。

さて、これを行う計算方法が 微分 (Differential) なわけで、微分を扱えれば速度が扱える。実用的な扱い方をちょっと書いておく。

  • 物体が止まっている時、速度は 0 である。動いている時、速度は 0 ではない。
  • 「物体が動き始める」ことは、速度が 0 から単調に変化することで表現できる。
  • 「物体が止まろうとする」ことは、速度が 0 に収束していくことで表現できる。
  • 「物体が動き続けている」ことは、速度が 0 にならないことで表現できる。

一番最初の4つのアニメーションは以下の作り方で出来ている。空間は [0,1] \to [0,1] で捉えている。

  • x\mapsto 3(3x^2-2x^3)^2-2(3x^2-2x^3)^3 (smoothstep(0,1,smoothstep(0,1,x)))
  • x\mapsto x^{0.3} (pow(x,0.3))
  • x\mapsto x^4 (pow(x,4))
  • x\mapsto x (x)

f:id:phi16_ind:20180909135727p:plain

「ある瞬間に止まっていること」は微分を計算すればわかる: (x^4)' = 4x^3 だから x=0 で速度 0(x^{0.3})' = 0.3x^{-0.7} だから x=1 で速度 0.3。 だから x^4 は「始まり」、x^{0.3} は「終わり」を表現できる。ついでに smoothstep は「始まって終わる」ようにできている。 なお終点で正しく速度を 0 にしたいならx\mapsto x^4区間を反転して 1-(1-x)^4 などを使えば良い。

正確な表記ではないが [1,0] というような区間を許容してみれば、[0,1] \to [1,0] という同型は x\mapsto 1-x で与えられる。 これを時間と空間共に行うと [1,0] \to [1,0] という写像になり、これは結局の所 [0,1] \to [0,1] のことである。

何もしないと線形で物を動かしてしまうかもしれないが、その動きに意思が棲んでいるならその意思を表現するべきである。 始点と終点を指定するだけでなく、その間の動きを含めて制御してあげることで「意思の点った自然な動き」が表現できる。

ちなみにこれは「位置」だけの話ではなく、「大きさ」「色」「透明度」「形状」もみんな関連する。 時間に依存した変化は全て速度が関わってくる。アニメーションを曲線で指定したくなるのはそういうことである。

なお周期的アニメーションでは始点と終点を一致させる (連続性) のは勿論、始点と終点の速度を一致させる (微分可能性) のもまた重要である。 あえて速度を一致させないのもまた重要である。速度が連続でないことは、「急激な変化」を表現してくれる。

余談: 速度の空間

\mathbb{R}^n の点の運動を考えた時、ある時点における点の速度は \mathbb{R}^n で表現している。が、これは自明ではない同じ空間でないことは明らかだろうか?端的に言えば原点が違う。

f:id:phi16_ind:20180909142502p:plain

そしてスケールも違う。速度の表現方法は時間に依存するが、位置の表現方法は時間に依存しないので「大きさ」はこの2空間では独立の概念である。

では同型にならなるか?というとそうでもない

f:id:phi16_ind:20180909142810p:plain

S^1 上の点の速度の成す空間は \mathbb{R} である。ついでに S^2 上の点の速度の成す空間は \mathbb{R}^2 である。同型ですらない。 つまり空間自体はほぼ関係ない。だが、その空間の成す次元は一致している

この現象は局所性によって説明できる。全体空間そのものは大域的 (Global) であるが、速度を考えるときには「ある点の近くのほんのちょっとの領域」で十分であった。 だから、S^1 上の点の速度を考えるときにはその点の周りを拡大して扱っても良い。そうなるとこの空間は1次元の線と区別がつかない。 元々 S^1 は (直観的に)「1次元の空間」である。結局 n 次元の空間におけるある点の速度の成す空間は \mathbb{R}^n である、ということになる。

n 次元の空間」という直観は「n 次元多様体」という語で説明される。速度の成す空間はその多様体の接空間 (接ベクトル空間 (Tangent vector space)) である。 接空間は多様体各点に定められている。つまり違う点における速度は本来比較できない。それを比較するために 接続 (Connection) という構造を考えたりする。 接続によって異なる空間の間の関係がわかると、それをかき集めて「任意の点におけるある速度、の成す空間」を考えられる (接束 (Tangent bundle))。\mathbb{R}^n はこれが明らかな構造をしているから速度が扱いやすい。

\mathbb{R}^n における速度の構造が自明である、ことは自明でない、気がする。だから違和感があって良いのだと思う。

密度

今度は時間に関する重みではなく空間に関する重みの話をしよう。端的に言えばGPUパーティクルの話である。

点を思い通りに散らばせたいと思う。そのためには思った密度に従ってランダムに点を与える必要がある。「密度」も「ランダム」も考える必要のある概念である。 密度というのは「領域あたりの点の数」である。そしてこれは局所的な概念だから、微分が基本となる。それに対して「密度で以って分布させる」には全体の領域が必要だから、逆に密度を積分することで得られる。 実際に点を配置する (サンプルを取る (Sampling)) ときには、入力された点に対して位置を与える操作になる。その位置は、なんらかの点固有の値に依存して決定される。

何にも依存せずに「ランダム」な値を与えることはできない。入力が x だけの写像は同じ x に対して同じ値を返さなきゃいけない。 だから「ランダム性」を与えるための値が必要になる。それはオリジナルの座標であったり、時間、UV座標、法線など様々である。 時間でランダム性を与えようとすると点群はみんな同じ値を得ることに注意しなければならない。それはそれで使い所がある。

さて、もうちょっと具体的な話をしよう。様々な密度を考える前に、「重みの違いが無い分布」一様分布 (Uniform distribution) を考える必要がある。 分布を考える上で最も簡単な空間は [0,1] である。ほぼ全ての分布はこれを加工することで生まれる。

空間全体の点の数を 1 とすることにすれば、[0,1] 内の [a,b]\ (0 \le a\lt b \le 1) に存在する点の数は b-a である。感覚的に密度は  (b-a)/(b-a) = 1 で一定である。微分で計算しても密度一定であることがわかる。

「あるにおける点の数」は 0 になる。\mathbb{R} に近い空間では「サンプルした点が特定の有限個の点のどれかに当たる確率」は 0 である。だから [0,1][0,1) の区別は意味がなかったりする。この辺は 測度論 (Measure theory) の話題である。

コンピュータで一様分布に従うように点を配置するのは結構難しく、誰かが作ったものを参考にするのが一番である。離散空間 (有限点) では有名な Mersenne twister とかがあるわけだが、とりあえず [0,1] の一様分布を取れる、と仮定しよう。

Shaderでよく使われている乱数生成器 (\mathsf{rand}\colon \mathbb{R}^2 \to [0,1]) はuniformではない。けど実用上だいたい問題ない。ちょうど 0 を返したり、違う入力に対して同じ値を返すことがよくあるが。

さて、そうすると明らかに [a,b] の一様分布が構成できる。いつもの同相 x \mapsto (b-a)x+a で良い。この写像は「つながり」を保つどころか「全体に対する密度」も保つのである。 さらに [0,1]^n の一様分布も作れる。この空間の一様分布は「各軸が独立」であることを要求するが、独立な一様分布を n 個拾ってくっつければそれを満たす。

f:id:phi16_ind:20180909164817p:plain

S^1 の分布は [0,2\pi]/_{0=2\pi} を使えば良い。この変換もちゃんと密度を保ついいやつである。 では、円板 D^1 = \{p\colon\mathbb{R}^2\mid \lVert p \rVert \le 1\} はどうだろうか? 角度と半径に分解して、即ち D^1 = S^1 \times [0,1] と考えてサンプリングするとこうなる。

f:id:phi16_ind:20180909165443p:plain

どう見ても一様ではない。何故なら、毎度のごとく、空間の形が違うからである。半径が大きいほど点は多くあるべきで、特に半径 0 の空間が潰れてないのがおかしい。では具体的にどうするか。 円周方向はこれでいいので半径方向について考える。半径 r の円周には r 個の点があるべきである。

f:id:phi16_ind:20180909170433p:plain

積分すると r^2/2 、すると全体で 1/2 になってしまうので 1 にしてやると積分の線形性で r^2。これが「半径 r 以内の点の量」である。 実はこのとき逆関数を適用すると一様分布になることが知られている (逆関数法)。つまり一様乱数 x を使って半径を \sqrt{x} で決定する。

f:id:phi16_ind:20180909174115p:plain

密度の歪みを打ち消してあげる感じである。ついでに言えば今回は r に沿って1次元の領域を動かしたが、n 次元の領域を [0,1] に沿って拡大縮小しつつ動かして空間をつくるとき、\sqrt[n+1]{x} でサンプルすると同じ原理で一様になる。

そうすると円錐が一様分布に従う値 a,b,c\colon[0,1] を用いて \sqrt[3]{c}(\sqrt{b}\cos(2\pi a),1,\sqrt{b}\sin(2\pi a)) で一様サンプリングできることがわかる。

ちなみに球面 S^2a\colon[0,2\pi], b\colon[-1,1] で一様分布に従うとして (\sqrt{1-b^2}\cos(a),b,\sqrt{1-b^2}\sin(a)) で一様サンプリングできる。べんり。

重みを持った密度

一様な密度が扱えればあとはどうでもなる。それこそ「動き」の調節に関わる技術はそのまま使える。

f:id:phi16_ind:20180909181554p:plain

  • r = pow(r,0.4)
  • y = pow(y,3.0)

f:id:phi16_ind:20180909182314p:plain

  • y = exp(-100.0*v)

2つの一様乱数を足した結果は一様分布に従わないことに注意すべきである (大数の法則)。

f:id:phi16_ind:20180909183059p:plain

  • y = (r1)/1
  • y = (r1 + r2)/2
  • y = (r1 + r2 + r3)/3
  • y = (r1 + r2 + r3 + r4)/4

どちらかというと「あとは職人芸」なので、いろんな分布をつくって遊ぶのが良いとは思う。表現のためには経験をつまなければ。