jessejuicer
jessejuicer

Reputation: 107

Orbit camera around sphere while looking at the center (using quaternions)

I have looked at a ton of quaternion examples on several sites including this one, but found none that answer this, so here goes...

I want to orbit my camera around a large sphere, and have the camera always point at the center of the sphere. To make things easy, the sphere is located at {0,0,0} in the world. I am using a quaternion for camera orientation, and a vector for camera position. The problem is that the camera position orbits the sphere perfectly as it should, but always looks one constant direction straight forward instead of adjusting to point to the center as it orbits.

This must be something simple, but I am new to quaternions... what am I missing?

I'm using C++, DirectX9. Here is my code:

// Note: g_camRotAngleYawDir and g_camRotAnglePitchDir are set to either 1.0f, -1.0f according to keypresses, otherwise equal 0.0f
// Also, g_camOrientationQuat is just an identity quat to begin with.

float theta = 0.05f;
D3DXQUATERNION g_deltaQuat( 0.0f, 0.0f, 0.0f, 1.0f );
D3DXQuaternionRotationYawPitchRoll(&g_deltaQuat, g_camRotAngleYawDir * theta, g_camRotAnglePitchDir * theta, g_camRotAngleRollDir * theta);
D3DXQUATERNION tempQuat = g_camOrientationQuat;
D3DXQuaternionMultiply(&g_camOrientationQuat, &tempQuat, &g_deltaQuat);

D3DXMatrixRotationQuaternion(&g_viewMatrix, &g_camOrientationQuat);
g_viewMatrix._41 = g_camPosition.x;
g_viewMatrix._42 = g_camPosition.y;
g_viewMatrix._43 = g_camPosition.z;

g_direct3DDevice9->SetTransform( D3DTS_VIEW, &g_viewMatrix );

[EDIT - Feb 13, 2012]

Ok, here's my understanding so far:

move the camera using an angular delta each frame. Get a vector from center to camera-pos. Call quaternionRotationBetweenVectors with a z-facing unit vector and the target vector. Then use the result of that function for the orientation of the view matrix, and the camera-position goes in the translation portion of the view matrix.

Here's the new code (called every frame)...

//  This part orbits the position around the sphere according to deltas for yaw, pitch, roll
D3DXQuaternionRotationYawPitchRoll(&deltaQuat, yawDelta, pitchDelta, rollDelta);

D3DXMatrixRotationQuaternion(&mat1, &deltaQuat);

D3DXVec3Transform(&g_camPosition, &g_camPosition, &mat1);

// This part adjusts the orientation of the camera to point at the center of the sphere

dir1 = normalize(vec3(0.0f, 0.0f, 0.0f) - g_camPosition);

QuaternionRotationBetweenVectors(&g_camOrientationQuat, vec3(0.0f, 0.0f, 1.0f), &dir1);


D3DXMatrixRotationQuaternion(&g_viewMatrix, &g_camOrientationQuat);

g_viewMatrix._41 = g_camPosition.x;
g_viewMatrix._42 = g_camPosition.y;
g_viewMatrix._43 = g_camPosition.z;


g_direct3DDevice9->SetTransform( D3DTS_VIEW, &g_viewMatrix );

...I tried that solution out, without success. What am I doing wrong?

Upvotes: 2

Views: 4413

Answers (2)

kloffy
kloffy

Reputation: 2928

It should be as easy as doing the following whenever you update the position (assuming the camera is pointing along the z-axis):

direction = normalize(center - position)
orientation = quaternionRotationBetweenVectors(vec3(0,0,1), direction)

It is fairly easy to find examples of how to implement quaternionRotationBetweenVectors. You could start with the question "Finding quaternion representing the rotation from one vector to another".

Here's an untested sketch of an implementation using the DirectX9 API:

D3DXQUATERNION* quaternionRotationBetweenVectors(__inout D3DXQUATERNION* result, __in const D3DXVECTOR3* v1, __in const D3DXVECTOR3* v2)
{
    D3DXVECTOR3 axis;

    D3DXVec3Cross(&axis, v1, v2);
    D3DXVec3Normalize(&axis, &axis);

    float angle = acos(D3DXVec3Dot(v1, v2));

    D3DXQuaternionRotationAxis(result, &axis, angle);

    return result;
}

This implementation expects v1, v2 to be normalized.

Upvotes: 3

rasmus
rasmus

Reputation: 3216

You need to show how you calculate the angles. Is there a reason why you aren't using D3DXMatrixLookAtLH?

Upvotes: 0

Related Questions