from 방향에서 to 방향으로 향하도록 회전시키는 Quaternionout 에 담아 돌려줍니다.

Syntax

Quaternion.fromTo(from, to);
Quaternion.fromTo(from, to, out);

Parameters

from

회전하기 전의 방향을 나타내는 Vector3 . 반드시 정규화된 벡터일 필요는 없습니다.

to

회전한 후의 방향을 나타내는 Vector3 . 반드시 정규화된 벡터일 필요는 없습니다.

out

결과를 담을 Quaternion . 인자를 주지 않으면 임시 변수가 생성됩니다.

Return value

$(\vec{from}\times\vec{to})$ 를 회전축으로 하며, 시계방향으로 $\vec{from}, \vec{to}$ 의 사이각 $\theta^\circ$ 만큼 회전시키는 Quaternion . out 인자를 주었다면 out 에 결과를 저장하며 out 을 그대로 돌려줍니다.

Description

Quaternion.angleAxis() 와 마찬가지로 회전 사원수 $q = (cos(\frac{\theta}{2}), \text{ } sin(\frac{\theta}{2})\cdot \vec{n})$ 을 생성하는 것은 같지만, 그 전에 두 벡터 $\vec{from}, \vec{to}$ 의 사이각 $\theta$ 와 회전축 $\vec{n}$ 을 구하는 추가 과정이 필요합니다.

두 벡터의 사이각 $\theta$ 은 내적 $(\vec{from}\cdot\vec{to})$ 을 사용하여 구할 수 있는데, 이는 내적이 기하학적으로 $\|\vec{from}\|\cdot\|\vec{to}\|\cdot cos\theta$ 를 의미하기 때문입니다. 내적에서 두 벡터의 크기가 $1$ 이었다면, $cos\theta$ 만이 남게되며 $acos()$ 함수를 통해 각도를 구할 수 있습니다:

$$ (\vec{from}\cdot\vec{to}) = \|\vec{from}\|\cdot\|\vec{to}\|\cdot cos\theta \\

\|\vec{from.normalized}\|\cdot \|\vec{to.normalized}\|\cdot cos\theta = cos\theta \\

\theta = acos(cos\theta)

$$

유의해야할 점은 실수(real number)에서 평행한 두 벡터 간의 내적은 항상 $-1/1$ 이지만, 부동 소수점 수(floating point number)에서는 반올림 오차(rounding error)로 인해 $acos()$ 의 정의역 $[-1, 1]$ 을 벗어날 수 있음을 고려해야 합니다. 그렇기에 프로그래밍 구현에서는 MyMath.clamp(cos, -1, 1) 를 해주어야 합니다:

const cos   = Vector3.dot(from.normalized, to.normalized);
const acos  = Math.acos(MyMath.clamp(cos,-1,1) );
const angle = acos * MyMath.RAD2DEG;

$acos()$ 의 치역은 $[0, \pi]$ 이므로, 육십분법(angle in degree)으로 생각하면 $[0^\circ, 180^\circ]$ 가 됩니다. 여기서 시계방향/반시계 방향으로 회전할지는 고민할 필요가 없습니다. Quaternion.mulVector() 에서 세웠던 회전공식은 항상 $\vec{from}$ 이 시계방향으로 $\theta$ 도 만큼 회전해서 $R(\vec{from})$ 이 되도록 설계되었기 때문입니다. 즉 각도는 항상 부호없는 각도를 사용합니다.