Euler vs Quaternion Interpolation

// Vector3.Lerp – linear interpolation
euler3 = Vector3.Lerp(euler1, euler2, alpha);
cube1.transform.rotation = Quaternion.Euler(euler3);
// Quaternion.Slerp – quaternion spherical linear interpolation
quat3 = Quaternion.Slerp(quat1, quat2, alpha);
cube2.transform.rotation = quat3;

Euler Rotation in Unity

// Create a Ra matrix Euler(30, 60, 45)
Matrix4x4 ra = Matrix4x4.Rotate(Quaternion.Euler(30, 60, 45));
// Create a Rb matrix rz -> rx -> ry
Matrix4x4 rz = Matrix4x4.Rotate(Quaternion.Euler(0, 0, 45));
Matrix4x4 rx = Matrix4x4.Rotate(Quaternion.Euler(30, 0, 0));
Matrix4x4 ry = Matrix4x4.Rotate(Quaternion.Euler(0, 60, 0));
Matrix4x4 rb = ry * rx * rz; // Z → X → Y

// ra == rb

// Create a Rc matrix ry -> rx -> rz
Matrix4x4 rc = rz * rx * ry; // Y → X → Z

// rb != rc

Composing Transformation

// white cube at origin
cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.GetComponent().material.color = new Color(1.0f, 1.0f, 1.0f, 1.0f);
cube.transform.parent = this.gameObject.transform;

// translation, rotation, scale
Vector3 translation = new Vector3(1.5, 0, 0);
Quaternion rotation = Quaternion.Euler(0, 0, 45);
Vector3 scale = new Vector3(0.2, 0.2, 0.2);

// white cube with S->R->T
cube.transform.localScale = scale;
cube.transform.rotation = rotation;
cube.transform.position = translation;
Matrix4x4 matrix = cube.transform.localToWorldMatrix;
Debug.Log(“cube.transform.localToWorldMatrix=\n” + matrix);

// Create a composing transformation Scale -> Rotation -> Translation
Matrix4x4 m1 = Matrix4x4.TRS(translation, rotation, scale);
// Create a composing transformation Scale -> Rotation -> Translation
Matrix4x4 m2 = Matrix4x4.Translate(translation) *
Matrix4x4.Rotate(rotation) * Matrix4x4.Scale(scale); // Scale -> Rotation -> Translation
if (m1 == m2) Debug.Log(“m1 == m2”);
if (m1 == matrix) Debug.Log(“m1 == matrix”);

// Create a composing transformation Translation -> Rotation ->Scale
Matrix4x4 m3 = Matrix4x4.Scale(scale) *
Matrix4x4.Rotate(rotation) * Matrix4x4.Translate(translation); 
if (m1 != m3) Debug.Log(“m1 != m3”);

Rotate a point about an arbitrary axis

Rotate a point about an arbitrary axis (3 dimensions)
Written by Paul Bourke

https://paulbourke.net/geometry/rotate/


/*
Rotate a point p by angle around an arbitrary line segment p1-p2
Return the rotated point.
Positive angles are anticlockwise looking down the axis
towards the origin. Assume right hand coordinate system.
*/
public static Vector3 ArbitraryRotate(Vector3 p, float angle, Vector3 p1, Vector3 p2)
{
Vector3 u, q1, q2, q3, q4, q5, q6, q7;
float d;

// (1) translate space so that the rotation axis passes through the origin
q1 = p – p1;

// (2) rotate space about the x axis so that the rotation axis lies in the xz plane
u = p2 – p1; // rotation-axis vector
u = Vector3.Normalize(u);
d = Mathf.Sqrt(u.y*u.y + u.z*u.z);
if (d != 0) {
q2.x = q1.x;
q2.y = q1.y * u.z / d – q1.z * u.y / d;
q2.z = q1.y * u.y / d + q1.z * u.z / d;
} else {
q2 = q1;
}

// (3) rotate space about the y axis so that the rotation axis lies along the z axis
q3.x = q2.x * d – q2.z * u.x;
q3.y = q2.y;
q3.z = q2.x * u.x + q2.z * d;

// (4) perform the desired rotation by theta about the z axis
float theta = Mathf.Deg2Rad * angle;
q4.x = q3.x * Mathf.Cos(theta) – q3.y * Mathf.Sin(theta);
q4.y = q3.x * Mathf.Sin(theta) + q3.y * Mathf.Cos(theta);
q4.z = q3.z;

// (5) apply the inverse of step (3)
q5.x = q4.x * d + q4.z * u.x;
q5.y = q4.y;
q5.z = -q4.x * u.x + q4.z * d;

// (6) apply the inverse of step (2)
if (d != 0) {
q6.x = q5.x;
q6.y = q5.y * u.z / d + q5.z * u.y / d;
q6.z = -q5.y * u.y / d + q5.z * u.z / d;
} else {
q6 = q5;
}

// (7) apply the inverse of step (1)
q7 = q6 + p1;
return q7;
}

/*
Rotate a point p by angle around an arbitrary axis
Return the rotated point.
Positive angles are anticlockwise looking down the axis
towards the origin. Assume right hand coordinate system.
*/
public static Vector3 ArbitraryRotate(Vector3 p, float angle, Vector3 axis)
{
Vector3 q = Vector3.zero;
float costheta, sintheta;
float theta = Mathf.Deg2Rad * angle;

axis = Vector3.Normalize(axis);
costheta = Mathf.Cos(theta);
sintheta = Mathf.Sin(theta);

q.x += (costheta + (1 – costheta) * axis.x * axis.x) * p.x;
q.x += ((1 – costheta) * axis.x * axis.y – axis.z * sintheta) * p.y;
q.x += ((1 – costheta) * axis.x * axis.z + axis.y * sintheta) * p.z;

q.y += ((1 – costheta) * axis.x * axis.y + axis.z * sintheta) * p.x;
q.y += (costheta + (1 – costheta) * axis.y * axis.y) * p.y;
q.y += ((1 – costheta) * axis.y * axis.z – axis.x * sintheta) * p.z;

q.z += ((1 – costheta) * axis.x * axis.z – axis.y * sintheta) * p.x;
q.z += ((1 – costheta) * axis.y * axis.z + axis.x * sintheta) * p.y;
q.z += (costheta + (1 – costheta) * axis.z * axis.z) * p.z;

return q;
}

Unity Rotation

적용 순서는 Z → X → Y, 행렬 곱으로 쓰면 R = Ry · Rx · Rz

적용 순서 (월드 축 기준, extrinsic) Unity는  Z축으로 z°, X축으로 x°, Y축으로 y° 순서대로 회전

곱 순서 (열벡터 기준, v’ = R·v) v’ = Ry · Rx · Rz · v

Quaternion.Euler(x, y, z) == Quaternion.AngleAxis(y, Vector3.up) * Quaternion.AngleAxis(x, Vector3.right) * Quaternion.AngleAxis(z, Vector3.forward);

같은 회전을 로컬 축 기준(intrinsic) Y → X’ → Z’’ 순서. 먼저 Y로 yaw를 돌리고, 돌아간 로컬 X로 pitch, 다시 로컬 Z로 roll을 돌리는 것으로, 곱의 모양 Ry·Rx·Rz는 동일