652 文字
3 分
正規直交基底の構築 (2)
Pixar の方法
Duff et al. [2017] では, Frisvad の方法の問題点と改善案を提示している. 特異点付近で大きな誤差が生じることから, 行列式が負になることもあり, 誤った座標フレームが使用される. が に近くない場合は適切に動作することから, の場合は のフレームを計算し, その結果を反転することで対処している.
Frisvad の方法では, 基底は次のように定義される.
の場合は, に を代入し, を反転すると次のようになる.
NOTE四元数を基礎として得られた接ベクトルの方向の一貫性を失う.
実装は次のようになる.
// Listing 2. The code of Listing 1, revised to avoid catastrophic cancellation.
void revisedONB(const Vec3f &n, Vec3f &b1, Vec3f &b2)
{
if(n.z<0.){
const float a = 1.0f / (1.0f - n.z);
const float b = n.x * n.y * a;
b1 = Vec3f(1.0f - n.x * n.x * a, -b, n.x);
b2 = Vec3f(b, n.y * n.y*a - 1.0f, -n.y);
}
else{
const float a = 1.0f / (1.0f + n.z);
const float b = -n.x * n.y * a;
b1 = Vec3f(1.0f - n.x * n.x * a, b, -n.x);
b2 = Vec3f(b, 1.0f - n.y * n.y * a, -n.y);
}
}
// Listing 3. A branchless version of Listing 2, using copysignf to eliminate the test.
void branchlessONB(const Vec3f &n, Vec3f &b1, Vec3f &b2)
{
float sign = copysignf(1.0f, n.z); // n.z>=0.0f ? 1.0f : -1.0f
const float a = -1.0f / (sign + n.z);
const float b = n.x * n.y * a;
b1 = Vec3f(1.0f + sign * n.x * n.x * a, sign * b, -sign * n.x);
b2 = Vec3f(b, sign + n.y * n.y * a, -n.y);
}
Comparing ONB Accuracy で違いを確認するには, 次の箇所を変更しコンパイルする.
/**
* 0: frisvad vs max
* 1: frisvad vs pixar
* 2: max vs pixar
*/
#define COMPARISON_MODE 1
Pros and Cons
- Pros
- Frisvad の方法と遜色のない速度を維持する
- 特異点がなく平均誤差も小さい
- Cons
- 四元数を基礎として得られた接ベクトルの方向の一貫性を失う
接空間の 軸に対称な分布に適用するような場合は問題ない
- 四元数を基礎として得られた接ベクトルの方向の一貫性を失う
References
- Duff, T., Burgess, J., Christensen, P., Hery, C., Kensler, A., Liani, M., & Villemin, R. (2017). Building an orthonormal basis, revisited. Journal of Computer Graphics Techniques (JCGT), 6(1), 1–8. http://jcgt.org/published/0006/01/01/