본문 바로가기
공부/DirectX3D

[DirectX3D] 뷰행렬을 사용한 회전(CreateLookAt()함수사용)

by MY블로그 2023. 9. 14.

기존에는 몬스터가 플레이어를 추적할때 플레이어 방향으로 각도를 맞추어 줄 수 있도록 X축과 Z축의 두개축을 가지고 atan2f() 함수를 사용하여 Y축의 회전 각도를 보간하였습니다.

기존의 방식은 평변을 기준으로 계산하기때문에 3개의 축을 한번에 계산하기는 조금 번거로웠습니다.

 

하지만 행렬과 CreateLookAt() 함수를 사용하여 X,Y, Z 3개의 모든축의 회전 각도를 보간할수 있는 방법을 정리하고자 합니다.

[사용 예시]

아래의 코드는 플레이어객체(player)가 항상 타겟(boss)을 향하고 있도록 회전시키는 코드입니다.

Matrix M = Matrix::CreateLookAt(player->GetWorldPos(), boss->root->GetWorldPos(), Vector3(0, 1, 0));
M = M.Transpose();
Quaternion Q = Quaternion::CreateFromRotationMatrix(M);
player->rotation.y = Util::QuaternionToYawPtichRoll(Q).y;

 

플레이어는 X,Z 축으로 이동만 시키고있으나 플레이어를 추적하고 있는 몬스터를 계속 바라보고 있습니다.

 

1. 전치행렬관계로 만들어져있는 뷰행렬를 생성합니다.

Matrix M = Matrix::CreateLookAt(회전을적용할객체의좌표, 목표대상객체의좌표, UP벡터);
inline Matrix Matrix::CreateLookAt(const Vector3& eye, const Vector3& target, const Vector3& up) noexcept
{
    using namespace DirectX;
    Matrix R;
    XMVECTOR eyev = XMLoadFloat3(&eye);
    XMVECTOR targetv = XMLoadFloat3(&target);
    XMVECTOR upv = XMLoadFloat3(&up);
    XMStoreFloat4x4(&R, XMMatrixLookAtLH(eyev, targetv, upv));
    return R;
}

2. 뷰행렬을 전치행렬로 변환합니다.

M = M.Transpose();
inline Matrix Matrix::Transpose() const noexcept
{
    using namespace DirectX;
    XMMATRIX M = XMLoadFloat4x4(this);
    Matrix R;
    XMStoreFloat4x4(&R, XMMatrixTranspose(M));
    return R;
}

3. 행렬(Matrix)을 쿼터니언(Quaternion)으로 변환합니다.

Quaternion Q = Quaternion::CreateFromRotationMatrix(M);
inline Quaternion Quaternion::CreateFromRotationMatrix(const Matrix& M) noexcept
{
    using namespace DirectX;
    XMMATRIX M0 = XMLoadFloat4x4(&M);

    Quaternion R;
    XMStoreFloat4(&R, XMQuaternionRotationMatrix(M0));
    return R;
}

4. 회전시킬 객체의 회전값에 QuaternionToYawPtichRoll()함수를 사용하여 회전을 적용합니다.

회전시킬객체->rotation.y = Util::QuaternionToYawPtichRoll(Q).y;
Vector3 Util::QuaternionToYawPtichRoll(Quaternion q1)
{
	float sqw = q1.w * q1.w;
	float sqx = q1.x * q1.x;
	float sqy = q1.y * q1.y;
	float sqz = q1.z * q1.z;
	float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor

	float test = q1.x * q1.w - q1.y * q1.z;
	Vector3 v;
	if (test > 0.4995f * unit) { // singularity at north pole

		v.y = 2.0f * atan2f(q1.y, q1.x);
		v.x = PI / 2.0f;
		v.z = 0;
		return NormalizeAngles(v);
	}

	if (test < -0.4995f * unit) { // singularity at south pole

		v.y = -2.0f * atan2f(q1.y, q1.x);
		v.x = -PI / 2.0f;
		v.z = 0;
		return NormalizeAngles(v);
	}

	Quaternion q = Quaternion(q1.w, q1.z, q1.x, q1.y);
	// Yaw
	v.y = atan2f(2.0f * q.x * q.w + 2.0f * q.y * q.z, 1.0f - 2.0f * (q.z * q.z + q.w * q.w));
	// Pitch
	v.x = asinf(2.0f * (q.x * q.z - q.w * q.y));
	// Roll
	v.z = atan2f(2.0f * q.x * q.y + 2.0f * q.z * q.w, 1.0f - 2.0f * (q.y * q.y + q.z * q.z));

	return NormalizeAngles(v);
}

댓글