10. 3차원 공간

CJB_ny·2023년 3월 2일
0

이득우 겜수

목록 보기
7/9

정리 글


10-1

좌표계

왼손 좌표계, 오른손 좌표계가 무엇인지 파악을 먼저 한다.

z축이 모니터 밖으로 향하는게 "오른손 좌표계"

z축이 모니터 안으로 향하는게 "왼손 좌표계"이다.

유니티랑 언리얼은 "왼손 좌표계"를 사용한다.

근데 언리얼도 왼손 좌표계를 사용한다고했는데

언리얼에서는 z축이 위로 향하는데 어떻게 왼손좌표계이냐 하면은

엔진마다 축방향을 다르게 사용하기 때문이다.

유니티는 "y업 왼손 좌표계", 언리얼을 "z업 왼손 좌표계"이다. 둘다 같은 왼손 좌표계임.

Euler's Angle

오일러각은 표준기저벡터를 중심으로 회전하는 각의 크기로 지정된다.

세 표준기저벡터를 중심으로 회전하는 각을 모으면 3차원 벡터로 표현이 가능하다.

  • 왜 오일러각 사용하나?

    오일러각은 모든 프로그램에서 범용적으로 사용하기 힘들다. 왜냐하면 소프트웨어마다 x, y, z축의 용도가 다르기때문. 이런 문제를 해결하기 위해서 x, y, z축 회전 대신 회전의 움직임으로 회전동작을 구분하고 각을 지정하는 방법을 사용한다.

표준기저벡터를 축으로하는 회전의 움직임은 방향에 따라 Yaw, Roll, Pitch로 불린다.

회전방향
Yawy
Rollz
Pitch오른쪽x

움직임으로 오일러각 지정하면 서로다른 좌표계를 사용하는 프로그램간에 데이터를 쉽게 변환할 수 있다. 그래서 Vector3를 사용하지 않고 Rotator구조체를 별도로 정의헤서 사용함.

R = Ryaw • Rpitch • Rroll

z -> y -> x 순서로 회전행렬곱 만든다 (언리얼도 이 순서)

10-1 실습

이를 통해 실습예제를 구현을 하면

// 최초 씬 로딩을 담당하는 함수
void SoftRenderer::LoadScene3D()
{
	GameEngine& g = Get3DGameEngine();

	// 플레이어
	constexpr float playerScale = 100.f;

	// 플레이어 설정
	GameObject& goPlayer = g.CreateNewGameObject(PlayerGo);
	goPlayer.SetMesh(GameEngine::CubeMesh);
	goPlayer.GetTransform().SetPosition(Vector3::Zero);
	goPlayer.GetTransform().SetScale(Vector3::One * playerScale);
	goPlayer.GetTransform().SetRotation(Rotator(0.f, 0.f, 0.f));
	goPlayer.SetColor(LinearColor::Blue);

	// 카메라 설정
	CameraObject& mainCamera = g.GetMainCamera();
	mainCamera.GetTransform().SetPosition(Vector3(0.f, 0.f, 500.f));
	mainCamera.GetTransform().SetRotation(Rotator(180.f, 0.f, 0.f));

}

// 게임 로직과 렌더링 로직이 공유하는 변수

// 게임 로직을 담당하는 함수
void SoftRenderer::Update3D(float InDeltaSeconds)
{
	// 게임 로직에서 사용하는 모듈 내 주요 레퍼런스
	GameEngine& g = Get3DGameEngine();
	const InputManager& input = g.GetInputManager();

	// 게임 로직의 로컬 변수
	static float moveSpeed = 500.f;
	static float rotateSpeed = 180.f;

	// 게임 로직에서 사용할 게임 오브젝트 레퍼런스
	GameObject& goPlayer = g.GetGameObject(PlayerGo);
	CameraObject& camera = g.GetMainCamera();

	// 입력에 따른 플레이어 트랜스폼 변경
	goPlayer.GetTransform().AddPosition(Vector3::UnitZ * input.GetAxis(InputAxis::ZAxis) * moveSpeed * InDeltaSeconds);
	goPlayer.GetTransform().AddPitchRotation(-input.GetAxis(InputAxis::WAxis) * rotateSpeed * InDeltaSeconds);

	// 입력에 따른 카메라 트랜스폼의 변경
	camera.GetTransform().AddYawRotation(-input.GetAxis(InputAxis::XAxis) * rotateSpeed * InDeltaSeconds);
	camera.GetTransform().AddPitchRotation(-input.GetAxis(InputAxis::YAxis) * rotateSpeed * InDeltaSeconds);
}

LoadScene에서 플레이어와 Camera생성해준다.

Rotation설정할 때 Rotator로 설정하는거 볼 수 있다.

Camera의 경우 Yaw방향으로 180도를 돌려서 설정했는데

이는 월드공간에 배치된 오브젝트를 카메라가 바라볼 때 보편적으로 인지하는 2차원 데카르트 좌표계와 다르기때문이다.

그래서 y축으로 180도를 회전시켜 "뷰공간"을 구성해야한다.

그리고 Update3D부분에서는 방향키 X, Y입력에는 Camera가 회전할 수 있도록 했다.

방향키 X : 카메라 Yaw회전
방향키 Y : 카메라 Pitch회전

즉 X키 누르면 카메라가 Yaw y축을 중심으로 빙빙돈다.

Y키는 Pitch 회전, 즉 물체를 중심으로 도는것처럼 느껴진다. 실제로 물체를 중심으로 돌지는 않는다.

현재 "원근감"이 없어 이게 제대로 도는지 아닌지는 좌표계를 보고 판단을 해야한다.

3차원 공간에서 원근감을 주면서 렌더링할려면 "원근 투영 변환"을 적용해야한다.

10-2

오일러각을 사용해 회전을 구현할 수 있지만 장단점 명확히 존재한다.

  • 장점

    1.직관적인 인터페이스 -> 사용자가 사용하기 쉽다.
    2.사용자 입장에서 물체의 회전을 설정할 때 가장 적합하다.

  • 단점

    1.짐벌락 현상
    특정상황에서 회전 움직임이 제한이 됨.

그래서 "로드리게스" 나 "사원수"를 사용하는데

게임엔진 내부에서는 "사원수"를 통해서 모든 회전을 처리하고 사용자 인터페이스는 오일러각을 입력받아 이것을 내부에서 "사원수"로 변환을 해 계산을 한다.

"사원수"가 행렬로 변환도 쉽고 벡터 내적/외적으로 식이 도출되기 때문에 컴퓨터 입장에서는 좋다.


추가적으로 회전보간에 대해서 MMORPG같은 경우 하나의 기저를 사용하는 회전은 오일러각으로도 충분하지만

비행기 같은 게임의 경우 2개 이상의 기저를 사용한 회전의 경우에는 오일러각으로 회전보간을 구현하기 어렵다.

그래서 사원수를 통해서 비행기 게임같은 부분의 회전을 구현을 한다.

profile
https://cjbworld.tistory.com/ <- 이사중

0개의 댓글