게임제작기법연구

게임제작기법연구 15주차

ckhyeok 2020. 7. 20. 13:37

Quaternion에서 회전 행렬을 만드는 방법을 정리하고 이를 구현한 코드를 첨부하시오.

사원수의 회전 식

$q_1\cdot q_2 =(w_1w_2-(v1\cdot v2), w_1v_2+w_2v_1 + v_1\times v_2)$

$\quad \space \space \space \space \space \space =(-(\vec{v_1}\cdot \vec{v_1}),(\vec{v_1}\times \vec{v_1}))$

실수부를 통일 시키지 않고 위 식을 적용하면 실수부가 다르므로 다른 공간에 존재 하게 됨.

 

$v' = qvq^* = v + wt + v\times t$

그러므로 위 식을 이용해서 실수부를 통일시켜야 합니다.

 

$v' = q(0, 1, 0, 0)q^*$
$\quad = (w + xi + yj + zk)(i)(w -xi -yj -zk) $
$ \quad = (-x + wi + zj - yk)(w - xi -yj -zk) $
$ \quad = x^2i + xyj + xzk + w^2i -wyij -wzik +wzj - xzji -z^2jk -wyk +xyki +y^2kj $
$ \quad = x^2i + xyj + xzk + w^2i -wyk +wzj +wzj + xzk -z^2i -wyk +xyj -y^2i $
$ \quad= (w^2 + x^2 + -y^2 - z^2)i + 2(xy + wz)j + 2(xz - wy)k $
$ \quad= (1 - 2(y^2 + z^2))i + 2(xy + wz)j + 2(xz - wy)k \quad (w^2 + x^2 = 1 - y^2 -z^2)$

$v'  = q(0, 0, 1, 0)q^*$ 
$ \quad = (w + xi + yj + zk)(j)(w -xi -yj -zk)$
$ \quad = (-y - zi + wj + xk)(w-xi-yj-zk)$
$ \quad = xyi - wzi - wzjk -xykj + y^2j + z^2ik + w^2j - x^2ki + yzk + yzij - wxji + wxk$
$ \quad = xyi - wzi - wzi + xyi + y^2j - z^2j + w^2j - x^2j + yzk + yzk + wxk + wxk$
$ \quad = 2(xy - wz)i + (w^2 - x^2 + y^2 - z^2)j + 2(wx + yz)k$
$ \quad = 2(xy - wz)i + (1 - 2(x^2 + z^2))j + 2(wx + yz)k$

$v' = q(0, 0, 0, 1)q^* $ 
$ \quad = (w + xi + yj + zk)(k)(w -xi -yj -zk) $
$ \quad = (-z + yi - xj + wk)(w-xi-yj-zk) $
$ \quad  = xzi + wyi +xzjk -wykj + yzj - yzik - wxj - wxki + z^2k - y^2ij + x^2ji + w^2k $
$ \quad  = xzi + wyi +xzi +wyi + yzj + yzj - wxj - wxj + z^2k - y^2k - x^2k + w^2k $
$ \quad  = 2(wy + xz)i + 2(yz - wx)j + (1 - 2(x^2 + y^2))k$

 

$M = \begin{bmatrix} 
1- 2(y^2 + z^2) & 2(xy -wz) & 2(xz +wy) & 0 \\ 
2(xy+wz) & 1-2(x^2 - z^2) & 2(yz-wx) & 0\\
2(xz - wy) & 2(yz + wx) & 1 - 2(x^2 - y^2) & 0 \\
0 & 0 & 0 & 1
\end{bmatrix}$

 

FORCEINLINE Matrix4x4 Quaternion::ToRotationMatrix() const
{
	Matrix4x4 result;

	float xs = X * X;
	float ys = Y * Y;
	float zs = Z * Z;
	float wx = W * X;
	float wy = W * Y;
	float wz = W * Z;
	float xy = X * Y;
	float xz = X * Z;
	float yz = Y * Z;

	result.Cols[0] = Vector4(1.f - 2.f * (ys + zs), 2.f * (xy + wz), 2.f * (xz - wy), 0.f);
	result.Cols[1] = Vector4(2.f * (xy - wz), 1.f - 2.f * (xs + zs), 2.f * (wx + yz), 0.f);
	result.Cols[2] = Vector4(2.f * (wy + xz), 2.f * (yz - wx), 1.f - 2.f * (xs + ys), 0.f);
	result.Cols[3] = Vector4(0.f, 0.f, 0.f, 1.f);

	return result;
}

오일러 각 정보를 받아 Quaternion으로 변환하는 수식을 정리하고 이를 구현한 코드를 첨부하시오.

$q_{yaw}, q_{pitch}, q_{roll}$ 을 활용합니다.

$q_y = cos\frac{\theta_y}{2} + sin\frac{\theta_y}{2}j$
$q_p = cos\frac{\theta_p}{2} + sin\frac{\theta_p}{2}i$
$q_r = cos\frac{\theta_r}{2} + sin\frac{\theta_r}{2}k$

 

$q_y \cdot q_p \cdot q_r  = (cos\frac{\theta_y}{2} + sin\frac{\theta_y}{2}j) (cos\frac{\theta_p}{2} + sin\frac{\theta_p}{2}i) (cos\frac{\theta_r}{2} + sin\frac{\theta_r}{2}k) $
$ = (cos\frac{\theta_y}{2}cos\frac{\theta_p}{2} + cos\frac{\theta_y}{2}sin\frac{\theta_p}{2}i + sin\frac{\theta_y}{2}cos\frac{\theta_p}{2}j + sin\frac{\theta_y}{2}sin\frac{\theta_p}{2}ji)(cos\frac{\theta_r}{2} + sin\frac{\theta_r}{2}k) $
$ = (cos\frac{\theta_y}{2}cos\frac{\theta_p}{2} + cos\frac{\theta_y}{2}sin\frac{\theta_p}{2}i + sin\frac{\theta_y}{2}cos\frac{\theta_p}{2}j - sin\frac{\theta_y}{2}sin\frac{\theta_p}{2}k)(cos\frac{\theta_r}{2} + sin\frac{\theta_r}{2}k) $
$ = cos\frac{\theta_y}{2}cos\frac{\theta_p}{2} cos\frac{\theta_r}{2} + cos\frac{\theta_y}{2}cos\frac{\theta_p}{2} sin\frac{\theta_r}{2}k + cos\frac{\theta_y}{2}sin\frac{\theta_p}{2} cos\frac{\theta_r}{2}i $ 
$  \quad + cos\frac{\theta_y}{2}sin\frac{\theta_p}{2} sin\frac{\theta_r}{2}ik + sin\frac{\theta_y}{2}cos\frac{\theta_p}{2} cos\frac{\theta_r}{2}j + sin\frac{\theta_y}{2}cos\frac{\theta_p}{2} sin\frac{\theta_r}{2}jk $
$  \quad - sin\frac{\theta_y}{2}sin\frac{\theta_p}{2} cos\frac{\theta_r}{2}k - sin\frac{\theta_y}{2}sin\frac{\theta_p}{2} sin\frac{\theta_r}{2}kk $
$ = cos\frac{\theta_y}{2}cos\frac{\theta_p}{2} cos\frac{\theta_r}{2} + sin\frac{\theta_y}{2}sin\frac{\theta_p}{2} sin\frac{\theta_r}{2} $
$  \quad + (cos\frac{\theta_y}{2}sin\frac{\theta_p}{2} cos\frac{\theta_r}{2} + sin\frac{\theta_y}{2}cos\frac{\theta_p}{2} sin\frac{\theta_r}{2})i $
$  \quad + (sin\frac{\theta_y}{2}cos\frac{\theta_p}{2} cos\frac{\theta_r}{2} - cos\frac{\theta_y}{2}sin\frac{\theta_p}{2} sin\frac{\theta_r}{2})j $
$ \quad + (cos\frac{\theta_y}{2}cos\frac{\theta_p}{2} sin\frac{\theta_r}{2} - sin\frac{\theta_y}{2}sin\frac{\theta_p}{2} cos\frac{\theta_r}{2})k $
$ = w + xi + yj + zk $

	FORCEINLINE explicit Quaternion(const Rotator & InRotator)
	{
		float sp, sy, sr;
		float cp, cy, cr;
		
		Math::GetSinCos(sy, cy, InRotator.Yaw * 0.5f);
		Math::GetSinCos(sp, cp, InRotator.Pitch * 0.5f);
		Math::GetSinCos(sr, cr, InRotator.Roll * 0.5f);

		W = cy * cp * cr + sy * sp * sr;
		X = cy * sp * cr + sy * cp * sr;
		Y = sy * cp * cr - cy * sp * sr;
		Z = cy * cp * sr - sy * sp * cr;
	}

 

Quaterion에서 오일러 각 정보로 변환하는 수식을 정리하고 이를 구현한 코드를 첨부하시오.

 

$sin\theta_{r}\cdot cos\theta_{p}=2(wz+xy)$

$cos\theta_{r}\cdot cos\theta_{p}=1-2(z^2+x^2)$

$tan\theta_{r}=\frac{2(wz+xy)}{(1-2(z^2+x^2))}$

$\theta_r = atan2(2(wz+xy), (1 - 2(z^2+x^2))) \quad (cos\theta_p \neq 0, cos\theta_r \neq 0)$

$sin\theta_p = 2(wx-yz)$

$\theta_p = asin(2(wx-yz))$  (- $ \theta_p = asin(2(wx-yz)) \quad (-\frac{\pi}{2} < \theta_p <\frac{\pi}{2})$
$sin\theta_y \cdot cos\theta_p = 2(xy+xz)$

$cos\theta_y \cdot cos\theta_p = 1-2(x^2+y^2)$

$tan\theta_y = \frac{2(wy+xz)}{1 - 2(x^2+y^2)}$
$\theta_y = atan2(2(wy+xz), 1 - 2(x^2+y^2))$

 

FORCEINLINE Rotator Quaternion::ToRotator() const
{
	Rotator result;
	
	result.Roll = Math::Rad2Deg(atan2f(2 * (W * Z + X * Y), 1 - 2 * (Z * Z + X * X)));

	const float singularity = 0.499999f;
	float sinp = 2 * W * X - Y * Z;
	if (W * X - Y * Z < -singularity)
	{
		result.Pitch = -90.f;
	}
	else if (W * X - Y * Z > singularity)
	{
		result.Pitch = 90.f;
	}
	else
	{
		result.Pitch = Math::Rad2Deg(asinf(sinp));
	}

	result.Yaw = Math::Rad2Deg(atan2f(2 * (W * Y + X * Z), 1.f - 2 * (X * X + Y * Y)));

	return result;
}

 

Quaternion의 Slerp를 구현을 위한 공식을 정리하고, 이를 구현한 코드를 첨부하시오. 

Lerp 

$q(t)=(1-t)\cdot q_1+t\cdot q_2$

$q'=\frac{q(t)}{|q(t)|}$

 

Slerp

삼각비와 내적을 사용합니다.

$q = cost\theta q_1+sint\theta \frac{q_{2\perp}}{|q_2{\perp}|}$

$q_{2\perp} = q_2 - cos\theta q_1$

$|q_2{\perp}| = \sqrt{(q_2 - cos\theta q_1)(q_2 - cos\theta q_1)} $
$ = \sqrt{q^2_2 - 2cos\theta q_2\cdot q_1 + cos^2\theta q^2_1}$
$ = \sqrt{1 - 2cos^2\theta + cos^2\theta}$

$= \sqrt{1 - cos^2\theta} $
$ = sin\theta $

 

$q =  cost\theta q_1 + sint\theta \frac{q_2 - cos\theta q_1}{sin\theta} $
$ = (\frac{sin\theta cost\theta - cos\theta}{sin\theta})q_1 + (\frac{sint\theta}{sin\theta})q_2 $
$ = (\frac{sin(1 - t)\theta}{sin\theta})q_1 + (\frac{sint\theta}{sin\theta})q_2 $

 

Quaternion Quaternion::Slerp(const Quaternion& q1, const Quaternion& q2, const float& t)
{
	Quaternion result;
	
	float cosTheta = Math::Clamp(q1.W * q2.W + q1.X * q2.X + q1.Y * q2.Y + q1.Z * q2.Z, -1.f, 1.f);

	float s1, s2;
	if (cosTheta < 0.99999f)
	{
		float theta = acosf(cosTheta);
		float invSin = 1.f / sinf(theta);
		s1 = sinf((1.f - t) * theta) * invSin;
		s2 = sinf(t * theta) * invSin;
	}
	else
	{
		return Lerp(q1, q2, t);		
	}
	
	result = q1 * s1 + q2 * s2;

	return result;
}

 

마인크래프트 캐릭터를 계층구조로 만들고 Quaternion Slerp를 사용해 회전시키는 애니메이션을 구현하시오.

 

 

 

 

Front Page

https://ckhyeok.tistory.com/category/%EA%B2%8C%EC%9E%84%EC%A0%9C%EC%9E%91%EA%B8%B0%EB%B2%95%EC%97%B0%EA%B5%AC

 

'게임제작기법연구' 카테고리의 글 목록

 

ckhyeok.tistory.com