# Yaw rotation not translating to Quaternion

I'm trying to implement the gimbal lock fix described in this question, but I end up getting weird behavior where when I rotate on the X axis (or try to at least) it rotates on both the X and Y axis at the same time, but when i try and rotate on the Y axis nothing happens at all.

I'm keeping total orientation in a quaternion and (trying to) update it with a rotation matrix constructed by forward, right, and up vectors. I'm deriving these vectors like so:

// From https://learnopengl.com/Getting-started/Camera

_forward.Normalize();
_right = Vector3.Cross(_forward, _worldUp).Normalized();
_up    = Vector3.Cross(_right, _forward)


and updating the _rotation values like this:

public void RotateX(float rotation) {
_rotation.X += rotation * _sens;
}

public void RotateY(float rotation) {
_rotation.Y += rotation * _sens;
}


To get the rotation matrix I'm just putting the 3 vectors into a Matrix3 object:

var r1 = new Vector3(_right.X, _up.X, _forward.X);
var r2 = new Vector3(_right.Y, _up.Y, _forward.Y);
var r3 = new Vector3(_right.Z, _up.Z, _forward.Z);

var mat = new Matrix3(r1, r2, r3);

if (mat != _check) {
_orientation = Quaternion.FromMatrix(mat) * _orientation;
}

_orientation.Normalize();


and then I update the orientation quaternion.

The _check property is just a matrix that represents what should be created if there is no change in rotation so _orientation isn't changed when it doesn't have to be.

This is a gif of what happens when I just use the RotateX function, so it just rotates on some weird axis. Any clue what I'm doing wrong? Am I initializing the rotation matrix wrong or is it something else entirely?

Camera pitch now works without affecting Yaw and Roll:

public void Update() {

// _forward.Normalize();
_right = Vector3.Cross(_forward.Normalized(), _worldUp);
_up    = Vector3.Cross(_right.Normalized(), _forward.Normalized());

Console.Out.WriteLine("forward: {0}", _forward);
Console.Out.WriteLine("right: {0}", _right);
Console.Out.WriteLine("up: {0}", _up);

var r1 = new Vector3(_right.X, _up.X * _worldUp.X, _forward.X);
var r2 = new Vector3(_right.Y, _up.Y * _worldUp.Y, _forward.Y);
var r3 = new Vector3(_right.Z, _up.Z * _worldUp.Z, _forward.Z);

var mat = new Matrix3(r1, r2, r3);

if (mat != _check) { // Change in orientation.
_orientation = Quaternion.FromMatrix(mat) * _orientation;
Console.Out.WriteLine(_orientation.ToEulerAngles());
}

_orientation.Normalize();

Console.Out.WriteLine("Position: {0}", _position);

_rotation = Vector3.Zero;
}


But no rotation occurs when trying to rotate about Y axis.

• Why are you multiplying your up vector by the world up, componentwise, when forming your r1, r2, and r3 values? Also, you can skip the matrix step entirely by just computing a pitch quaternion (x: sin(pitch/2), w: cos(pitch/2)) and a yaw quaternion (y:sin(yaw/2), w:cos(yaw/2)) and then multiplying them. Commented Oct 9, 2022 at 22:27
• My thinking with the worldUp multiplication was that the 0s from that would cancel out the local up pitch and roll so it wouldn't get contaminated. But I see that was completely unneseccary now. Commented Oct 10, 2022 at 0:05

Thanks to DMGregory for correcting my quaternion understanding. All is working fine now. I just make a quaternion for pitch and yaw then multiply them with my existing oreientation quaternion like so:

// Values in _rotation are already converted to Radians.
var pitchQuat = new Quaternion((float) MathHelper.Sin(_rotation.X / 2f), 0f, 0f,
(float) MathHelper.Cos(_rotation.X / 2f));

var yawQuat = new Quaternion(0f, (float) MathHelper.Sin(_rotation.Y / 2f), 0f,
(float) MathHelper.Cos(_rotation.Y / 2f));

_orientation = pitchQuat * _orientation * yawQuat;


This replaces the whole Matrix thing I was going with and actually makes the camera work like I wanted. Thanks again to DMGregory for just being cool.

Result(idk why the colors are freaking out it wasn't actually doing that it's just recording artifact i guess):