Strange twitching with an orbit camera

Started by
6 comments, last by pseudomarvin 8 years, 10 months ago

I have implemented an orbit camera and it seems to work but there is a strange twitch that happens when I rotate around the Y-axis too fast. I have recorded and posted it to youtube for your viewing pleasure :). If I move slowly it seems okay, but if I move the mouse quickly right/left, the entire scene becomes kind of skewed. The problem is most prominent in the angle recorded on the video and does not happen if the camera direction is parallel with the floor. I am not sure what could be causing it.

The camera code:


void CameraControl::Update(Camera *camera, UserInput *input)
{
	// Rotation
        // position and previousPosition are the positions of the mouse
	float yaw = (input->position.x - input->previousPosition.x) * -0.005f;
	float pitch = (input->position.y - input->previousPosition.y) * -0.005f;

	Vector3 targetToCameraVector = camera->position - camera->target;
	float length = Math::Length(targetToCameraVector);

	targetToCameraVector = Math::Normalize(targetToCameraVector);

	Vector3 forward = -targetToCameraVector;
	Vector3 right = Math::CrossProduct(forward, Vector3(0, 1, 0));
	right = Math::Normalize(right);
	Vector3 up = Math::CrossProduct(right, forward);
	up = Math::Normalize(up);

	Vector4 rotatedTargetToCameraVector = Math::GetRotation(pitch, right) *
		Math::GetRotation(yaw, Vector3(0, 1, 0)) * Vector4(targetToCameraVector, 0.0f);

	camera->position = camera->target + Math::Normalize(Vector3(rotatedTargetToCameraVector)) * length;

	camera->viewMatrix = Math::LookAt(camera->position, camera->target, up);
}

I would welcome any suggestions. Thanks.

Advertisement
Have you eliminated the possibility that it is in your input-handling code?


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

It seems to me that you should be recalculating your 'up' vector after you do the rotation before you make your LookAt matrix. I think you're making a matrix that takes the previous camera position's "up" vector and applying it after you've moved your camera's position.

This also explains the symptoms you're seeing.

	Vector3 up = Math::CrossProduct(right, forward);
	up = Math::Normalize(up);
…
	camera->viewMatrix = Math::LookAt(camera->position, camera->target, up);

He seems to be doing it.
However I believe that function is actually supposed to take Vector3(0, 1, 0) as input (the function itself should re-derive “up” internally based on this). But the result will be the same either way.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

All of the code prior to the last three statements is operating on the old camera matrix.

Then he changes the camera position, but doesn't fix the right or up vectors. They still have values that are only correct from the old position.

Then he makes a matrix using: LookAt(new camera position, same camera target, OLD up vector);


I believe you're right that you can pass some constant up such as (0,1,0) to the LookAt function and that the function fixes everything internally. I remember doing that, but was never sure if I was actually supposed to or not.

Thanks for your help guys. Nypyren you were exactly right, recalculating the up vector solved the problem. If I could have a couple more questions:

The code now looks like this:


	// Rotation velocity
	float decceleration = 0.9f;
	float rotationSpeed = 0.0007f;
	static float yaw = 0.0f;
	static float pitch = 0.0f;
	yaw = yaw * decceleration + (input->position.x - input->previousPosition.x) * -rotationSpeed;
	pitch = pitch * decceleration + (input->position.y - input->previousPosition.y) * -rotationSpeed;

	// Rotation
	Vector3 targetToCameraVector = camera->position - camera->target;
	float cameraDistance = Math::Length(targetToCameraVector);
	
	targetToCameraVector = Math::Normalize(targetToCameraVector);

	Vector3 forward = -targetToCameraVector;
	Vector3 right = Math::CrossProduct(forward, Vector3(0, 1, 0));
	right = Math::Normalize(right);
	Vector3 up = Math::CrossProduct(right, forward);
	up = Math::Normalize(up);

	Vector3 oldPosition = camera->position;
	
	Vector4 rotatedTargetToCameraVector = Math::GetRotation(pitch, right) *
		Math::GetRotation(yaw, Vector3(0, 1, 0)) * Vector4(targetToCameraVector, 0.0f);

	camera->position = camera->target + Math::Normalize(Vector3(rotatedTargetToCameraVector)) * cameraDistance;

	// Recalculate up for the View Matrix
	forward = camera->target - camera->position;
	right = Math::CrossProduct(forward, Vector3(0, 1, 0));
	right = Math::Normalize(right);
	up = Math::CrossProduct(right, forward);
	up = Math::Normalize(up);

	// Check whether the camera is going to get parallel with the (0, 1, 0) vector
	// (either looking straight up or down) if so, send it the other way
	if (Math::Dot(-Math::Normalize(forward), Vector3(0, 1, 0)) > 0.95f)
	{
		yaw = 0.0f;
		pitch = 0.08f;
	}
	else if (Math::Dot(-Math::Normalize(forward), Vector3(0, -1, 0)) > 0.95f)
	{
		yaw = 0.0f;
		pitch = -0.08f;
	}

1. Is there a smarter way of calculating the forward, up and right vectors? It seems ike I am currently it doing it twice, although it also looks like it is unavoidable (at least the way my code is set up currently).

2. If the camera tries to look at the object from straight up or down, the rotation pushes it over the edge and the scene gets flipped. I tried to counter that by setting a limit on how parallel with the (0, 1, 0) vector the camera direction ray can get and if it that boundary is passed, the rotation is reversed. But it has the side effect that you cannot look straight down at the object and I don't like that. Is there an obvious solution to this problem?

Thanks.

I'd keep {targetPos, distanceFromTarget, pitch, yaw} as my camera state. I'd update those when new input arrives. Then I'd use those four variables to calculate the view matrix from scratch: Either make some matrices representing the translation and rotation operations you'd use to transform the camera to the right position/orientation and multiply them together, or hand-calculate the equations for each element of the entire matrix. Constructing the matrix in this way will keep your right/up/forward vectors aligned properly at all times.

From prior threads, it seems that view matrices can be made without using Math::LookAt or other view-matrix helper functions by simply making a transform that you'd use to position a mesh at the camera, then take the inverse of that matrix.

Here's a link that might help explain what's inside a view matrix: http://stackoverflow.com/questions/349050/calculating-a-lookat-matrix

All right, thanks for the suggestion I will definitely look into that.

This topic is closed to new replies.

Advertisement