微軟DX庫里提供了對Quaternion進行球面四邊形內插的接口,能夠在多個Quaternion之間平滑插值。這里涉及到兩個接口:
void D3DXQuaternionSquadSetup(
__out D3DXQUATERNION *pAOut,
__out D3DXQUATERNION *pBOut,
__out D3DXQUATERNION *pCOut,
__in const D3DXQUATERNION *pQ0,
__in const D3DXQUATERNION *pQ1,
__in const D3DXQUATERNION *pQ2,
__in const D3DXQUATERNION *pQ3
);
D3DXQUATERNION* D3DXQuaternionSquad(
__inout D3DXQUATERNION *pOut,
__in const D3DXQUATERNION *pQ1,
__in const D3DXQUATERNION *pA,
__in const D3DXQUATERNION *pB,
__in const D3DXQUATERNION *pC,
__in FLOAT t
);
其中,D3DXQuaternionSquadSetup用于返回內插的控制點。它們具體的實現公式和用法,有興趣的同學可以參考MSDN。在此需要說明的是,
D3DXQuaternionSquad使用了Slerp作為內部實現,會導致在兩個夾角為180°左右的Quaternion之間插值會出現斷裂的問題。下面代碼通過
實現一個考慮了上述情況的Slerp版本,在q1和q2夾角在0°或者180°時,使用線性內插而非球面,來解決該問題。
- Quaternion QuatSlerpNoInvert(const Quaternion& q1 , const Quaternion& q2 , float t)
- {
- ????float cosAngle = DotProduct(q1, q2);
-
- ????float c1, c2;
- ????// Linear interpolation for close orientations
- ????if ((1.0f - fabs(cosAngle)) < 1e-5f)
- ????{
- ????????c1 = 1.0f - t;
- ????????c2 = t;
- ????}
- ????else
- ????{
- ????????// Spherical interpolation
- ????????float angle????= acos(fabs(cosAngle));
- ????????float sinAngle = sin(angle);
- ????????c1 = sin(angle * (1.0f - t)) / sinAngle;
- ????????c2 = sin(angle * t) / sinAngle;
- ????}
-
- ????Quaternion q = q1 * c1 + q2 * c2;
- ????q.Normalize();
-
- ????return q;
- }
-
- Quaternion QuatSquad(const Quaternion& p1 , const Quaternion& p2 , const Quaternion& p3 , const Quaternion& p4 , float t)
- {
- ????static Quaternion a , b , c;
-
- ????D3DXQuaternionSquadSetup((D3DXQUATERNION*)&a , (D3DXQUATERNION*)&b , (D3DXQUATERNION*)&c ,
- ????????(D3DXQUATERNION*)&p1 , (D3DXQUATERNION*)&p2 , (D3DXQUATERNION*)&p3 , (D3DXQUATERNION*)&p4);
-
- ????return QuatSlerpNoInvert(QuatSlerpNoInvert(p2 , c , t) , QuatSlerpNoInvert(a , b , t) , 2 * t * (1-t));
- }
參考:
[1] http://msdn.microsoft.com/en-us/library/bb205419(v=vs.85).aspx
[2] http://msdn.microsoft.com/en-us/library/bb205420(v=vs.85).aspx