*인라인 파일(.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 |
댓글