三角法を避ける (2013)
概要
- 3Dグラフィックス内部で三角関数を多用することへの懸念
- ベクトル演算(内積・外積)によるシンプルで堅牢な実装の提案
- 典型的な回転行列生成の非効率な方法とその問題点の解説
- 三角関数を排除した効率的な回転行列計算方法の提示
- ベクトル演算を中心とした直感的で高性能なコードの推奨
3Dグラフィックスにおける三角関数の過剰利用について
- プロジェクションやリフレクション、ベクトル演算の理解が深まるごとに、3Dアルゴリズム内部で三角関数を見かけるたびに違和感を覚えるようになった経験
- sin, cos, tan, asin, acos, atan などの三角関数が3Dエンジン内部で現れる場合、非効率でエラーを招きやすい設計である可能性
- 三角関数はデータ入力やアルゴリズムの外部インターフェースでは便利だが、内部処理ではベクトル演算で置き換えるべきである主張
- 内積(dot product)と外積(cross product)は、cosineとsineの情報を完全に内包しているため、ベクトルとジオメトリの操作で十分
- 角度や三角関数を途中で持ち出す設計は、複雑化・冗長化・エラーの温床となる懸念
非効率な回転行列生成の例
- 多くの3Dエンジンやライブラリで見られる、軸と角度から回転行列を生成する関数 rotationAxisAngle()
- 例:
mat3x3 rotationAxisAngle(const vec3 &v, float a)
- 例:
- オブジェクトのz軸を任意の方向ベクトルdに合わせる際、zとdの内積からacos()で角度を求め、cross()で回転軸を算出し、normalize()して回転行列を生成する典型的な流れ
- この手法では、dot→acos→cos/sin→回転行列 という非効率な計算チェーンが発生
- acos()の直後にcos()を使うと「cos(acos(x)) = x」となり、無駄な計算
- clampやnormalizeも必要になり、パフォーマンスや精度の悪化を招く
ベクトル演算による効率的な回転行列の導出
- sin(θ)は、zとdが単位ベクトルならcross(z, d)の長さと一致
- これにより、三角関数を使わずにcos(θ)とsin(θ)を求められる
- 三角関数やnormalize、clamp、sqrtを排除した回転行列生成コードの例
- まずは sin, cos をベクトル演算で算出
- さらに式変形により sqrtも排除し、k = 1/(1+c) の形に簡略化
- 最終的な回転行列生成関数(k = 1/(1+c))
- ベクトル演算のみで安定・高速な実装
結論と推奨
- 三角関数やその逆関数、clamp、normalize、sqrtを排除した実装は、安定性・パフォーマンス両面で優れる
- ベクトル同士の問題はベクトル演算のみで解決するのが本質的
- 多くの三角関数の公式は、実はベクトルの特定の配置に関する記述であり、ベクトル演算の直感を身につければ暗記も不要
- 外部APIや既存ライブラリの設計に引きずられず、ベクトル演算中心の設計を推奨
- パフォーマンス・安定性・可読性の観点からも、3Dグラフィックス内部では三角関数の利用を極力減らすべき
まとめ
- 3Dレンダリングエンジン内部での三角関数多用は、冗長・不安定・非効率であることが多い
- ベクトル演算(内積・外積)中心の設計により、シンプルかつ堅牢なコード実現
- 既存APIの設計に惑わされず、本質的なベクトル処理を心がけるべき
- 三角関数を使わずに回転行列や方向ベクトルの処理を行うノウハウの重要性