“Building an Orthonormal Basis, Revisited“という、1つの単位ベクトルから正規直交基底を求める方法について書かれた論文を読んでみた。
Revisitedというだけあって、Frisvadが提案した方法だと誤差が大きくなるケースがあるのを修正する、という内容。 そちらも知らなかったのであたってみた。
ナイーブな方法
1つの単位ベクトル から3次元の正規直交基底を求めるナイーブな方法は、平行しない適当なベクトルを選び、直交成分を取り出して正規化したものを とし、また外積を取ったものを として求める:
void naive(const Vec3f& n, Vec3f& b1, Vec3f& b2) { |
Hughes-Möller法
適当なベクトルとして、 のx, y, z成分の中で絶対値が一番小さい成分を0として、残り2つを入れ替え、片方の符号を反転したものを選ぶと に直交する、ということを利用してナイーブな方法よりちょっとお得:
void hughes_moeller(const Vec3f& n, Vec3f& b1, Vec3f& b2) { |
Frisvad法
クォータニオンによる任意軸の回転を利用して、 を回転させた基底を計算する。 正規化をしなくてすむのでお得:
// Listing 2. New way of finding an orthonormal basis from a unit 3D vector. |
Frisvad法の問題点
が-1に近づくと誤差がひどくなり、また行列式が負になることもある。修正方法
z成分が-1に近づくと問題が起きるが、そうでなければ問題ない。 じゃあということでzが負の場合には逆にから任意軸回転させてやればよい:
void revisedONB(const Vec3f &n, Vec3f &b1, Vec3f &b2) { |
しかしこの変更によってFrisvad法より2倍も遅くなってしまう! これはFrisvad法の場合には条件分岐がほとんど成り立たないので分岐予測がほぼ当たるのに対し、修正版では半々になってしまうため。 これを低減するためにcopysignfを使い分岐をなくす:
void branchlessONB(const Vec3f &n, Vec3f &b1, Vec3f &b2) { |
これにより5%~12%遅い程度で済む。
参照
- Tom Duff, James Burgess, Per Christensen, Christophe Hery, Andrew Kensler, Max Liani, Ryusuke Villemin, 2017. Building an Orthonormal Basis, Revisited, http://jcgt.org/published/0006/01/01/.
- FRISVAD, J. R. 2012. Building an orthonormal basis from a 3D unit vector without normalization. J. Graphics Tools 16, 3, 151–159. URL: http://orbit.dtu.dk/files/126824972/onb_frisvad_jgt2012_v2.pdf.
- HUGHES, J. F., AND MöLLER, T. 1999. Building an orthonormal basis from a unit vector. J. Graph. Tools 4, 4, 33–35. URL: http://www.tandfonline.com/doi/abs/10.1080/10867651.1999.10487513.
- MAX, N. 2017. Improved accuracy when building an orthonormal basis. Journal of Computer Graphics Techniques (JCGT) 6, 1 (March), 60–66. URL: http://jcgt.org/published/0006/01/02/.