導入
こんな感じのプログラムについての話です。
かっこが多い!
// normalOS : オブジェクトローカル座標系の法線
// normal : ビュー 座標系の法線
// MY_IT_MV 行列を用いて, normalOS → normal に変換 (最後に正規化している)
float3 normal = normalize(mul((float3x3)MY_IT_MV, normalOS));
この計算式の意味を理解した、みたいな感じです。
その1 - 必要なのは「回転」「拡縮」のみ
CGには、基本の座標操作があります。
- 拡縮
- 回転
- (並行)移動
あとはスキューとかがありますが、今回は考えないです。
さて、法線の変換に移動の情報は要らないです。
「移動の情報」とはつまり、「法線のワールド座標」です。
法線は方向ベクトルですので、いくら並行移動しようが意味はありません。
従って移動の情報は捨てても良いです。
で、その結果として、めっちゃ嬉しいことがあります。
その2 - 線形変換じゃん!
CGでは 4x4 のアフィン変換行列を用いると思います。
それで、そもそもなぜ 3x3 じゃダメなのかというと、
移動があるせいで線形変換じゃなくなり、そのため 3x3 で表現しきれない からです。
アフィン変換行列のテンプレを見てください。
右端の列、移動成分 (tx, ty, tz) がスゴい主張しています。
\left|
\begin{matrix}
a & b & c & <tx> \\
d & e & f & <ty> \\
g & h & i & <tz> \\
0 & 0 & 0 & 1 \\
\end{matrix}
\right|
法線の変換で移動が要らないということは、
つまりアフィン変換に拡張する意味が無くなったということです。
なので、左上 3x3 (線形変換の情報がある部分) を使えば十分です。
// 左上 3x3 を使えば十分
(float3x3)MY_IT_MV
その3 - 逆転置の意味
本題かな?とは思うんですが、他の人が解説しているので飛ばします!!
素晴らしい解説記事
僭越ながら簡単に要約すると、
- オブジェクトの回転はそのまま反映 - 逆行列→転置行列で、回転行列は変化しない
- オブジェクトの拡縮には反比例する - 逆行列→転置行列で、拡縮成分は逆数になる
的な感じです。
// 逆(inverse) → 転置(transport) なので、「IT」がくっついている。
(float3x3)MY_IT_MV
その4 - 最後の仕上げ
ここまでで変換行列が求まったので、適用していきます。
mul((float3x3)MY_IT_MV, normalOS)
拡縮行列のせいで、法線の長さが変わってしまいました。
正規化しましょう。
normalize(mul((float3x3)MY_IT_MV, normalOS))
完成!!
float3 normal = normalize(mul((float3x3)MY_IT_MV, normalOS));
最後に
分かりにくい所があれば、ご指摘お願いします。