# How much to roll Z to make horizon level

I've got a flying camera with some arbitrary rotation. (It also has a translation, but that's not important.) It's flying along its local Z axis, traveling in some direction over the ground with some rate of climb or descent.

I'd like to gently correct it so the horizon is level. Or, equivalently, I'd like to apply rotation around camera's pointing direction (Z) until it's up-vector (Y) is as up as it can be.

That is, I want my camera X and camera Y to adjust, leaving camera Z (forward) exactly the same.

The rotation is represented as a 3x3 matrix. Quaternion solution would be fine, also...

How much do I need to rotate it to get the horizon level?

Any help appreciated!

Edit: I'm currently in c++.

Edit2: Here's a picture further clarifying... I need to rotate in the local XY plane, the grey disk, til X touches the ground plane, Y is up-ish, and Z is unchanged... which sort of suggests an approach...

You would need to know the angle between your camera's up vector and the global up vector. Since you didn't specify what language you're using, I won't provide code. But there should be a function like math.atan2 (that's what it's called in xna) to get the angle between a vector and the x axis. It can easily be used to calculate the angle between two vectors. When you have the angle, rotate your camera in the correct direction just a little bit each frame. Also, if the angle is smaller than 0.001 or something, you might want to snap your cam in place. Otherwise it may end up overrotating and keep shaking around the correct rotation.

Edit: what I forgot to mention is that in xna, which I use, the atan2 function returns a value between -pi and pi. This is critical since you need to know which way to rotate.

• Thanks for answering! I'm using c++, and understand atan2. But the angle I need to roll is not necessarily just the x tilt, since the direction could be anywhere on the xz plane (with y-up). Is it? Sep 4, 2015 at 6:04

calculate the angle between your "up vector" v1 and "world up vector" v2=(0,1,0)

Normalize v1.

So, if v1 and v2 are normalised so that |v1|=|v2|=1, then,

angle = acos(v1•v2)


where :

• = 'dot' product.

acos = arc cos = inverse of cosine function.

• I think I didn't phrase my question clearly enough; will try to edit. After the "roll correction", I want my "forward vector" to be the same, correcting local "up" and "side"... Sep 4, 2015 at 15:35

I found a way that worked. But I'll leave question open a little longer if anyone has a more, I don't know, mathematically clear approach...

From the picture above, cast the tip of red Camera-X downwards along green Camera-Y's direction til it hits the ground plane. (This is a point in the plane of the grey disc.) Then do a dot product between that and the Camera-X.

MeVec3 cameraXVector = this->cameraMatrix.column(0);
MeVec3 cameraYVector = this->cameraMatrix.column(1);

float t = cameraXVector.y / cameraYVector.y;
MeVec3 planePoint = cameraXVector - t * cameraYVector;

// sometimes got 1.003 or -1.0002 from FP error, so...

float t = cameraXVector[upAxis] / cameraYVector[upAxis];