본문으로 건너뛰기
뒤로가기

[Emberlit] Quaternion 곱 순서: 같은 회전이 왼쪽·오른쪽에 따라 월드·로컬이 되는 이유

TL;DR — 쿼터니언 r은 그 자체로 월드 회전도 로컬 회전도 아닙니다. r * q(왼쪽 곱)이면 월드로 나온 뒤 r이 작용하여 월드 축 기준 회전이 되고, q * r(오른쪽 곱)이면 월드로 나가기 전에 r이 작용하여 로컬 축 기준 회전이 됩니다. FPS 카메라에서 yaw를 왼쪽에, pitch를 오른쪽에 곱하는 이유가 여기서 나옵니다.

Table of contents

Open Table of contents

들어가며

쿼터니언으로 회전을 누적할 때, 같은 angleAxis 값을 왼쪽에 곱하느냐 오른쪽에 곱하느냐에 따라 결과가 완전히 달라집니다. 처음에는 “왼쪽 = 월드, 오른쪽 = 로컬”이라고 외웠지만, 왜 그런지를 수식으로 풀어보니 결합법칙에서 자연스럽게 도출되는 구조였습니다.

이 글에서는 column-vector 컨벤션(GLM)에서 쿼터니언 곱 순서가 공간 기준을 결정하는 원리를 수식으로 전개하고, FPS 카메라의 yaw/pitch를 예시로 정리했습니다.

이 글에서 다루는 내용:

사전 지식: 쿼터니언의 기본 연산(켤레, 역, 회전 적용 qvq1q \cdot v \cdot q^{-1})과 GLM의 column-vector 컨벤션을 전제로 합니다.


1. orientation 쿼터니언이 의미하는 것

오브젝트의 현재 자세를 나타내는 orientation 쿼터니언 q는 identity에서 출발하여 회전을 누적한 결과입니다. q는 로컬 공간의 벡터를 월드 공간으로 변환하는 역할을 합니다:

vworld=qvlocalq1v_\text{world} = q \cdot v_\text{local} \cdot q^{-1}

q가 identity이면 로컬과 월드가 일치하고, q에 회전이 쌓일수록 로컬 축이 월드 축에서 벗어납니다. 여기에 새로운 회전 r을 추가할 때, r을 어느 쪽에 곱하느냐에 따라 결과가 달라집니다.


2. 왼쪽 곱 — 월드 축 기준 회전

새 회전 r을 왼쪽에 곱하면 합성 쿼터니언은 r * q가 됩니다:

vworld=(rq)vlocal(rq)1v_\text{world} = (r \cdot q) \cdot v_\text{local} \cdot (r \cdot q)^{-1}

역쿼터니언 성질 (rq)1=q1r1(r \cdot q)^{-1} = q^{-1} \cdot r^{-1}을 적용하면:

vworld=rqvlocalq1r1v_\text{world} = r \cdot q \cdot v_\text{local} \cdot q^{-1} \cdot r^{-1}

괄호로 실행 순서를 나타내면:

vworld=r(qvlocalq1)1단계: v를 월드로 보냄r1v_\text{world} = r \cdot \underbrace{(q \cdot v_\text{local} \cdot q^{-1})}_{\text{1단계: v를 월드로 보냄}} \cdot r^{-1}
  1. 안쪽: qqvlocalv_\text{local}을 월드 공간으로 보냅니다
  2. 바깥쪽: rr이 이미 월드에 있는 결과를 한 번 더 돌립니다

rr은 월드 공간의 벡터에 작용하므로, rr의 축은 월드 축 기준으로 해석됩니다.


3. 오른쪽 곱 — 로컬 축 기준 회전

새 회전 r을 오른쪽에 곱하면 합성 쿼터니언은 q * r이 됩니다:

vworld=(qr)vlocal(qr)1v_\text{world} = (q \cdot r) \cdot v_\text{local} \cdot (q \cdot r)^{-1}

(qr)1=r1q1(q \cdot r)^{-1} = r^{-1} \cdot q^{-1}을 적용하면:

vworld=qrvlocalr1q1v_\text{world} = q \cdot r \cdot v_\text{local} \cdot r^{-1} \cdot q^{-1}

괄호로 실행 순서를 나타내면:

vworld=q(rvlocalr1)1단계: v를 로컬에서 돌림q1v_\text{world} = q \cdot \underbrace{(r \cdot v_\text{local} \cdot r^{-1})}_{\text{1단계: v를 로컬에서 돌림}} \cdot q^{-1}
  1. 안쪽: rrvlocalv_\text{local}을 아직 로컬 공간에서 돌립니다
  2. 바깥쪽: qq가 그 결과를 월드로 보냅니다

rr은 월드로 나가기 전에 작용하므로, rr의 축은 로컬 축 기준으로 해석됩니다.


4. 핵심 인사이트 — r은 월드도 로컬도 아니다

수식을 전개하면서 깨달은 점이 있습니다. r 자체에는 “월드 회전” 또는 “로컬 회전”이라는 성질이 없습니다. r은 단지 “어떤 축으로 얼마만큼 회전”이라는 정보일 뿐입니다.

곱하는 위치가 공간 기준을 결정합니다.

왼쪽 곱 rqr \cdot q오른쪽 곱 qrq \cdot r
rr이 작동하는 시점월드로 나온 월드로 나가기
rr이 보는 공간월드 공간로컬 공간
결과월드 축 기준 회전로컬 축 기준 회전

이 구조는 결합법칙으로 자연스럽게 전개되는 것이지, 별도의 규칙을 외워야 하는 것이 아닙니다.


5. FPS 카메라 예시

FPS 카메라에서 yaw(좌우 회전)와 pitch(상하 회전)는 서로 다른 공간 기준이 필요합니다:

GLM 코드로 표현하면 다음과 같습니다:

glm::quat orientation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); // identity

void rotateWorldY(float degrees) {
    glm::quat qYaw = glm::angleAxis(glm::radians(degrees), glm::vec3(0, 1, 0));
    orientation = glm::normalize(qYaw * orientation);  // 왼쪽 곱 → 월드 축
}

void rotateLocalX(float degrees) {
    glm::quat qPitch = glm::angleAxis(glm::radians(degrees), glm::vec3(1, 0, 0));
    orientation = glm::normalize(orientation * qPitch); // 오른쪽 곱 → 로컬 축
}

같은 glm::angleAxis로 만든 회전이라도, 곱하는 위치가 공간 기준을 결정합니다.

5.1 만약 pitch를 왼쪽에 곱하면?

pitch를 qPitch * orientation으로 왼쪽에 곱하면 월드 XX축 기준 회전이 됩니다. 카메라가 yaw로 90도 돌아간 상태에서 pitch를 적용하면, 카메라 기준 위아래가 아니라 세상 기준 XX축으로 돌아가므로 의도와 다른 회전이 발생합니다.


정리하며

핵심 요약:

참고 자료


이 게시물은 학습한 내용을 바탕으로 초안을 작성한 뒤, LLM의 도움을 받아 내용을 검수하고 다듬어 완성되었습니다.


공유하기:

이전 글
[LLM Workbench] Hooklusion: 에이전트 상태를 작은 신호로 보기
다음 글
[Emberlit] Normal Mapping: TBN 행렬과 Vulkan 구현기