maximize e·v
subject to
‖v‖² ≤ 1
u·v ≥ ‖v‖ cos θ
首先,我们可以不失一般性地观察到,‖v‖ = 0 或 ‖v‖ = 1,因为目标函数是线性的,其他点都位于这两个向量的凸组合上。现在让我们关注于后者。
maximize e·v
subject to
‖v‖² = 1
u·v ≥ cos θ
maximize e·(αe + βu)
subject to
‖αe + βu‖² = 1
u·(αe + βu) ≥ cos θ
我们来扩展产品并利用 ‖e‖² = ‖u‖² = 1 这个事实。
maximize α + β(e·u)
subject to
α² + 2αβ(e·u) + β² = 1
α(e·u) + β ≥ cos θ
参考 David 的回答,我实现了这个算法,它似乎运行良好:
Box ComputeSphericalSegmentBoundingBox( const Vec3& u, const float angle )
{
const float cosAngle = cosf( angle ); // cos θ
const auto solveAxis = [&] ( const Vec3& e )
{
const float eu = Vec3::Dot( e, u ); // e·u
if ( eu >= cosAngle )
{
return 1.0f;
}
// solve for a² + 2a(cosθ - a(e·u))(e·u) + (cosθ - a(e·u))² = 1
const float det = ( cosAngle * cosAngle - 1.0f ) / ( eu * eu - 1.0f );
if ( det >= 0.0f )
{
const float a = sqrtf( det );
// maximize x = a + b(e·u)
const float x0 = ( cosAngle - a * eu ) * eu + a;
const float x1 = ( cosAngle + a * eu ) * eu - a;
return Clamp( max( x0, x1 ), 0.0f, 1.0f );
}
return 0.0f;
};
Vec3 boxMin
{
min(0.0f, -solveAxis( Vec3(-1,0,0) ) ),
min(0.0f, -solveAxis( Vec3(0,-1,0) ) ),
min(0.0f, -solveAxis( Vec3(0,0,-1) ) )
};
Vec3 boxMax
{
max(0.0f, solveAxis( Vec3(1,0,0) ) ),
max(0.0f, solveAxis( Vec3(0,1,0) ) ),
max(0.0f, solveAxis( Vec3(0,0,1) ) )
};
return { boxMin, boxMax };
}