w除算の結合律について
久々に小ネタ。現代のGPUにおける行列演算にはw除算と呼ばれる仕様が存在するが、これは結合律を満たすのか?という疑問について。
結論から言うと満たす。というか満たしてくれないと困るのだが、その証明を書く。
w除算とは
ググれば出てくるのだが端的に書く。
4次元ベクトルに4x4行列をかけた場合、普通の数学ならこうなる。
$$ \begin{pmatrix} A_{11} & A_{12} & A_{13} & A_{14} \\ A_{21} & A_{22} & A_{23} & A_{24} \\ A_{31} & A_{32} & A_{33} & A_{34} \\ A_{41} & A_{42} & A_{43} & A_{44} \\ \end{pmatrix} \begin{pmatrix}x \\ y \\ z \\ w \end{pmatrix} =\begin{pmatrix}A_{11} x + A_{12} y + A_{13} z + A_{14} w \\ A_{21} x + A_{22} y + A_{23} z + A_{24} w \\ A_{31} x + A_{32} y + A_{33} z + A_{34} w \\ A_{41} x + A_{42} y + A_{43} z + A_{44} w\end{pmatrix} $$
が、GPUにおけるシェーダなどではこうなる。
$$ \begin{pmatrix} A_{11} & A_{12} & A_{13} & A_{14} \\ A_{21} & A_{22} & A_{23} & A_{24} \\ A_{31} & A_{32} & A_{33} & A_{34} \\ A_{41} & A_{42} & A_{43} & A_{44} \\ \end{pmatrix}\ast \begin{pmatrix}x \\ y \\ z \\ w \end{pmatrix}= \begin{pmatrix}x'/w' \\ y'/w' \\ z'/w' \\ w'/w' \end{pmatrix}= \begin{pmatrix}(A_{11} x + A_{12} y + A_{13} z + A_{14}) / (A_{41} x + A_{42} y + A_{43} z + A_{44} w) \\ (A_{21} x + A_{22} y + A_{23} z + A_{24} w) / (A_{41} x + A_{42} y + A_{43} z + A_{44} w) \\ (A_{31} x + A_{32} y + A_{33} z + A_{34} w) / (A_{41} x + A_{42} y + A_{43} z + A_{44} w) \\ 1\end{pmatrix} $$
普通の行列のかけ算の後に全体がwで割られる。これがw除算である。キモいがこういうものなので仕方がない。これのおかげでパースをかけたりすることが出来る。
w除算の結合性について
MVP変換などでは複数の行列のかけ算を連鎖させることはよくある。
$P_w = P \ast V \ast M \ast P_m$
このとき、以下のようにかける順序によって計算結果が違ったらどうなるだろうか。
$P_w = P \ast (V \ast (M \ast P_m))$
$P_w = ((P \ast V) \ast M) \ast P_m$
まあ、だいぶ嫌な気持ちになると思う。値が固定されているところだけ先に乗じておくみたいなことが出来なくなる。普通の線形代数学において、行列積は交換法則は満たさないが結合法則は満たす。 とすればw除算を含む行列積でも結合法則は満たしてほしいものだ。
結論から言うと、満たす。
証明
普通の行列のかけ算を「\( \cdot \)」、w除算を行う行列のかけ算を「\( \ast \)」で表すとしよう。
4次元ベクトル \(x\) の4番目の項を \(x_w\) と表すことにする。
w除算のある(4x4行列)×(4次元ベクトル)の積は以下のように表せる。
$A\ast x = (A\cdot x) / (A \cdot x)_w$
なお、4x4行列同士の積にw除算はない。
$A\ast B = A \cdot B$
満たされてほしい性質はこれだ。
$(A\ast B) \ast x = A \ast (B \ast x)$
$A,\ B: 4\times 4行列$
$x: 4次元ベクトル$
計算
左結合:
$(A\ast B) \ast x = (A\cdot B) \ast x$
$ = (A\cdot B \cdot x) / (A\cdot B \cdot x)_w$
右結合:
$A \ast (B\ast x) = A\ast ((B\cdot x) / (B\cdot x)_w)$
$=(A\cdot ((B\cdot x) / (B\cdot x)_w)) / (A\cdot ((B\cdot x) / (B\cdot x)_w))_w$
$=(A\cdot B\cdot x) / (B\cdot x)_w / ((A\cdot B\cdot x) / (B\cdot x)_w)_w$
$=(A\cdot B\cdot x) / (B\cdot x)_w / ((A\cdot B\cdot x)_w / (B\cdot x)_w)$
$=(A\cdot B\cdot x) / (A\cdot B\cdot x)_w$
一致することが確認できた。
なお、\((B\cdot x)_w = 0\) になる場合とかは知らない。