8
\$\begingroup\$

I want my first-person camera to smoothly change its viewing direction from direction d1 to direction d2. The latter direction is indicated by a target position t2.

So far I have implemented a rotation that works fine but the speed of the rotation slows down the closer the current direction gets to the desired one. This is what I want to avoid.

Here are the two very simple methods I have written so far:

// this method initiates the direction change and sets the parameter
public void LookAt(Vector3 target) {

        _desiredDirection = target - _cameraPosition;
        _desiredDirection.Normalize();

        _rotation = new Matrix();

        _rotationAxis = Vector3.Cross(Direction, _desiredDirection);

        _isLooking = true;
    }


// this method gets execute by the Update()-method if _isLooking flag is up.
private void _lookingAt() {

        dist = Vector3.Distance(Direction, _desiredDirection);

        // check whether the current direction has reached the desired one.
        if (dist >= 0.00001f) {

            _rotationAxis = Vector3.Cross(Direction, _desiredDirection);
            _rotation = Matrix.CreateFromAxisAngle(_rotationAxis, MathHelper.ToRadians(1));


            Direction = Vector3.TransformNormal(Direction, _rotation);
        } else {

            _onDirectionReached();
            _isLooking = false;
        }
    }

Again, rotation works fine; camera reaches its desired direction. But the speed is not equal over the course of movement -> it slows down.

How to achieve a rotation with constant speed ?

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$
_rotationAxis = Vector3.Cross(Direction, _desiredDirection);
_rotation = Matrix.CreateFromAxisAngle(_rotationAxis, MathHelper.ToRadians(1));

As Direction & _desiredDirection change to be pointing in nearly the same direction (as they converge), The smaller the magnitude of _rotationAxis will be. It will still be pointing in the proper direction to be the axis but will be a shorter length. That is the nature of the cross calculation.

The guts of the CreateFromAxisAngle method uses the length of the axis as a factor in the amount of rotation it results in. When the axis has a length of 1, it results in the correct amount of rotation.

So if you were to normalize _rotationAxis between the two lines I quoted above, you would get a constant rotational rate.

\$\endgroup\$
6
\$\begingroup\$

I'd suggest letting the framework do all of the work for you. Start by calculating a rotation matrix for your start and end orientations, and convert them both to quaternions. You only do this once at the start of the movement and store the values.

Matrix start = /* calculate current rotation matrix */;
Matrix end = /* calculate desired rotation matrix */;
Quaternion startQ = Quaternion.CreateFromRotationMatrix(start);
Quaternion endQ = Quaternion.CreateFromRotationMatrix(end);

Now interpolate between these two orientations using spherical linear interpolation. There's a method for it so you don't have to implement anything yourself:

// Animate the progress parameter between 0 and 1
Quaternion currentQ = Quaternion.Slerp(startQ, endQ, progress);

Finally recalculate your direction using the quaternion above, or convert it back to a rotation matrix. Something like this for instance:

Vector3 direction = Vector3.Transform(Vector3.Forward, currentQ);
\$\endgroup\$
2
  • \$\begingroup\$ Thank you very much for your detailed advise! I will try that after lunch ;) \$\endgroup\$ Oct 9, 2012 at 11:20
  • \$\begingroup\$ Quaternion.Slerp Is it just me, or does that sound like a name in either a really bad or really good fantasy novel? Minus the dot, of course. \$\endgroup\$
    – anon
    Jan 15, 2015 at 19:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .