본문 바로가기
DirectX/[Inflearn_rookiss] Part2: DirectX12

13. Translation 정리 & Camera 클래스 추가

by 헛둘이 2023. 1. 24.

*인라인 파일(.inl)의 존재 이유?

- 인라인 함수의 경우 헤더 파일에 다 구현이 있어야 하는데 그러면 너무 지저분하니까 따로 뺀 것

 

*라이브러리에서 구현된 벡터는 우리가 직접 만든 벡터보다 float연산에 최적화된 레지스터를 사용한다(SIMD)

- 그래서 따로 구현하는 것보다 만들어진걸 쓰는게 더 성능이 좋음

 

*예제에서 행렬과 관련된 계산은 SimpleMath 라이브러리에서 지원해주는 함수를 통해 사용함

- 역행렬, 전치행렬 등 행렬연산에 필요한 여러 헬퍼함수들이 많이 준비되어 있음

- 이 라이브러리에서 주의할 점은 오른손좌표계 기준으로 되어있다보니 Forward가 -Z방향을 가리킴(즉, Backward가 Look)

 

Transform이 가진 좌표는 월드좌표?

- 항상 월드를 기준으로 나타내는게 아니다

- 어떤 오브젝트에 귀속된 경우 (오브젝트가 다른 오브젝트를 소유하는 개념)면 부모의 Translation를 기준으로 상대좌표를 가진다.

- 자식이 월드좌표로 변환될때는 부모의 좌표계로 변환을 거친 후 월드 좌표로 변환된다 (유니티 방식)

 

*따라서 첫 번째 큐브(객체)에 구속된 오브젝트들이 있다면 첫 번째 큐브의 움직임에 따라 나머지도 움직인다

->직속 부모의 좌표를 기준으로 한 상대좌표 개념

 

*그럼 최상위 부모는 뭘 기준으로 월드좌표로 변환하지?

-> 최상위 부모는 자기 자신의 좌표를 월드 좌표로 변환

 

*정리하자면 Local -> if( IsParent() ){ 직속 부모 좌표로 변환 } -> World

 

*view, projection은 변하지 않지만, world는 오브젝트마다 가지고 있으므로 오브젝트 단위로 관리되어야 함

void Transform::FinalUpdate()
{
	// Scale 계산
	Matrix matScale = Matrix::CreateScale(_localScale);

	// Rotation 계산
	Matrix matRotation = Matrix::CreateRotationX(_localRotation.x);
	matRotation *= Matrix::CreateRotationY(_localRotation.y);
	matRotation *= Matrix::CreateRotationZ(_localRotation.z);
	// 나중가면 쿼터니언으로 바뀜
	// 극한의 확률로 문제를 일으키는경우가 있음

	// Translation 계산
	Matrix matTranslation = Matrix::CreateTranslation(_localPosition);

	// 세 행렬를 하나의 행렬로 취합
	_matLocal = matScale * matRotation * matTranslation;
	_matWorld = _matLocal;

	// 부모님이 있다면 부모님의 월드행렬을 곱해준다
	shared_ptr<Transform> parent = GetParent().lock();
	if (nullptr != parent)
	{
		_matWorld *= parent->GetLocalToWorldMatrix();
	}
}

- 월드좌표 계산

- SRT를 구해서 곱해준다.

- R의 경우 나중에 쿼터니언을 배우면 쿼터니언으로 바꿀 예정

- 아래 문단에서 부모 여부에 따라 부모의 좌표계로 추가적인 변환이 발생한다.

 

쉐이더에 넘기는 인자의 타입 변경

struct TransformParams
{
	Matrix matWVP;
};

쉐이더 코드에서도 이 Matrix를 받아 줄 수 있도록 타입 변경

cbuffer TRANSFORM_PARAMS : register(b0)
{
    row_major matrix matWVP;
}

row_major가 뭐지?

- 우리가 생각하는 행렬 순서와 쉐이더 코드의 행렬 순서가 다르기 때문

- 다렉에서는 11 12 13 14로 접근하는데, 쉐이더에서는 11 21 31 41로 접근하기 때문에 이걸 맞춰주는 것


Camera 클래스 추가

- 카메라의 로컬좌표는 그대로 View가 되므로 View 행렬도 카메라가 관리하고,

- Projection 행렬도 카메라가 관리하도록 한다 (유니티와 동일하게)

 

void Camera::FinalUpdate()
{
	// 뷰행렬이라는 자체가 현재 카메라의 로컬좌표계로 다른 물체들을 끌어오는 것이기 때문
	_matView = GetTransform()->GetLocalToWorldMatrix().Invert();

	float width = static_cast<float>(GEngine->GetWindow().width);
	float height = static_cast<float>(GEngine->GetWindow().height);

	// Matrix 기반으로 구현된 헬퍼함수는 오른손좌표계기준이라서 dx에서 지원해주는 함수를 사용
	if (PROJECTION_TYPE::PERSPECTIVE == _type)
		_matProjection = ::XMMatrixPerspectiveFovLH(_fov, width / height, _near, _far);
	else
		_matProjection = ::XMMatrixOrthographicLH(width * _scale, height * _scale, _near, _far);

	S_MatView = _matView;
	S_MatProjection = _matProjection;

}

- 예제에서는 설명의 간소함을 위해 static 변수로 view행렬과 projection행렬을 빼 두었다.

- 맨 위에 월드행렬을 구하고 그 역행렬을 구하는데 그게 View행렬로 변환하는 행렬이기 때문

 

void Camera::Render()
{
	shared_ptr<Scene> scene = GET_SINGLE(SceneManager)->GetActiveScene();

	const vector<shared_ptr<GameObject>>& gameObjects = scene->GetGameObjects();

	for (auto& gameObject : gameObjects)
	{
		if (nullptr == gameObject->GetMeshRenderer())
			continue;

		gameObject->GetMeshRenderer()->Render();
	}
}

- Camera의 Render에서는 오브젝트들을 씬에서 다 가져와서 MeshRenderer 컴포넌트를 가진 오브젝트를 렌더링한다.

 

 

MeshRenderer에서 해주는 일

void MeshRenderer::Render()
{
    // 위치 정보를 GPU로 쏴준다
	GetTransform()->PushData();
    
    // 정점 정보, 인덱스 정보, 텍스쳐를 GPU로 쏴준다. 
	_material->PushData();
    
    // 테이블에 적힌 내용을 토대로 RenderTarget에 그린다.
	_mesh->Render();
}

 

'DirectX > [Inflearn_rookiss] Part2: DirectX12' 카테고리의 다른 글

15. Lighting 기본 개념  (0) 2023.01.25
14. Resources & 3D Cube 생성  (0) 2023.01.25
12. Projection, Screen 변환 행렬  (2) 2023.01.23
11. World, View 변환 행렬  (0) 2023.01.22
10. 좌표계 변환 행렬  (0) 2023.01.22

댓글