일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 백준
- c++
- 수학
- BFS
- Unreal Engine 5
- 알고리즘
- 다이나믹 프로그래밍
- 백트래킹
- 다익스트라
- 유니온 파인드
- 누적 합
- 우선순위 큐
- 브루트포스
- 정렬
- ue5
- 그래프
- 트리
- 투 포인터
- Team Fortress 2
- 문자열
- DFS
- XR Interaction Toolkit
- 그리디 알고리즘
- 스택
- 구현
- 유니티
- VR
- 시뮬레이션
- 재귀
- 자료구조
- Today
- Total
1일1알
DirectX11 학습 - 텍스쳐 입히기, 카메라 본문
이번 시간에는 메쉬에 텍스쳐를 입히고 카메라를 추가하여 카메라의 위치에 따라서 물체가 다르게 보여지도록 하는 코드를 작성한다.
텍스쳐를 입히기 위해서는 UV좌표에 대한 이해가 필요하다. UV좌표란 텍스쳐 이미지를 메쉬에 입히기 위한 텍스쳐의 2D좌표이다. 텍스쳐의 크기에 상관없이 최소 좌표는 (0,0), 최대 좌표는 (1,1)이다.
텍스쳐에서 입히고 싶은 부분을 오려서 메쉬에 적용한다고 생각하면 이해하기 수월하다.
메쉬의 정점에 uv좌표를 포함시켜서 쉐이더에 올리면 Rasterizer 단계에서 uv좌표들이 보간되고 최종적으로 PixelShader 에 보간된 uv좌표가 전달되어서 텍스쳐에서 해당 좌표의 픽셀값을 가져오게 된다.
우리는 게임을 할때 가상의 3D 게임 세상을 2D 화면으로 보고 플레이하고있다. 어떻게 이게 가능할까?
평소에는 아무렇지 않게 생각하고 있었지만 이 질문을 들었을때 신선한 충격으로 다가왔었다.
이것을 이해하기 위해서는 여러 가지 공간에 대한 이해가 필요하다.
이부분을 구체적으로 다루기 위해서는 복잡하고 어려운 수학적인 내용이 많기 때문에 간단하게만 작성하겠다.
- Local Space : 오브젝트마다 가지고 있는 개인적인 공간(이 공간에서 오브젝트의 기준점을 잡고 그 기준점을 바탕으로 정점을 배치한다던지 등의 오브젝트마다 개인적인 공간)
- World Space : 3D세상에 물체들이 배치되려면 하나의 기준이 되는 공간이 있어야 하는데, 그 공간을 World Space라고 함
- View(Camera) Space : 화면에 보여질 장면을 촬영하는 카메라를 기준으로 하는 공간
1차적으로 일단 이렇게 3개의 공간이 있고, 우리의 화면에 보여지는 모습은 카메라에 찍히는 모습을 보여주는 것이기 때문에 배치된 오브젝트들은 View Space를 기준으로 해야 한다.
그렇기 때문에 View Space를 기준으로 하는 좌표 변환 작업이 필요하다.
여기서는 계층구조가 없이 하나의 오브젝트만 있다고 가정하겠다.
Local Space에서 World Space로 변환하려면 SRT(Scale, Rotation, Transpose)행렬을 곱하면 된다. SRT의 값은 결국 World Space를 기준으로 하는 값이기 때문이다.
만약 자신을 자식으로 가지는 부모 오브젝트가 있다면 현재 SRT값은 부모를 기준으로 하고 있기 때문에 SRT행렬을 곱하고 부모의 SRT행렬을 또 곱하면 World Space로 변환이 된다. 이 행렬을 A라고 하겠다.
카메라에서 World Space로 변환하는 행렬은 위와 같이 카메라의 SRT 행렬이다. 결국 카메라고 World를 기준으로 하는 공간에 있기 때문이다. 하지만 우리가 원하는 변환은 World에서 카메라 공간으로 가는 변환이고, 이것은 카메라의 SRT행렬의 역행렬을 구해주면 된다. 이 행렬을 B라고 하겠다.
결론적으로 A,B행렬을 곱해서 카메라를 기준으로 하는 공간으로 좌표변환을 할 수 있다.
카메라를 기준으로 하는 공간으로 들어왔지만 아직 3D공간인 것은 여전하다.
카메라에는 절두체 공간이 있는데, 이 공간 안에 들어오는 물체만 화면에 그려지게 된다.
이제 이 절두체 공간을 2D로 압축해야 한다.
이때 사용되는 변환 행렬을 Projection 행렬이라고 하고, z값에 따른 비율에 따라서 어떻게 그려질지를 정해준다. 물체변환이 완료되면 x,y의 값은 -1 ~ 1, z의 값은 0~1로 표현되는 NDC 좌표계를 기준으로 하는 좌표로 변환이 된다. 이때 뷰포트의 해상도에 따라 보정을 미리 해준다.
여기서 z값은 깊이값으로 활용되어 제일 앞쪽에 있는 픽셀만 그리게 된다.
struct VertexTextureData
{
Vec3 position = { 0, 0, 0 };
Vec2 uv = { 0, 0 };
};
shared_ptr<Geometry<VertexTextureData>> _geometry;
void P_Texture::Init()
{
_shader = make_shared<Shader>(L"_Texture.fx");
// Texture
_texture = RESOURCES->Load<Texture>(L"Wood", L"..\\Resources\\Textures\\Wood.jpg");
_geometry = make_shared<Geometry<VertexTextureData>>();
GeometryHelper::CreateCube(_geometry);
_vertexBuffer = make_shared<VertexBuffer>();
_vertexBuffer->Create(_geometry->GetVertices());
_indexBuffer = make_shared<IndexBuffer>();
_indexBuffer->Create(_geometry->GetIndices());
// Camera
{
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -2.f });
_camera->AddComponent(make_shared<Camera>());
_camera->AddComponent(make_shared<CameraScript>());
_camera->GetTransform()->SetRotation(Vec3(25.f, 0.f, 0.f));
//CUR_SCENE->Add(camera);
}
}
화면을 그리는 과정은 앞의 삼각형 띄우기와 비교하여 color대신 uv값을 전달해준다는 것과 비슷하다.
GeometryHelper라는 기본 도형을 만들어주는 Helper 클래스를 사용하여 큐브 모양을 만들어주었다.
그리고 삼각형 띄우기에서는 NDC좌표계를 기준으로 했지만 이번에는 오브젝트의 Local좌표계를 기준으로 쉐이더에 올려서 Vertex Shader에서 좌표 변환 작업을 할 것이다.
void P_Texture::Render()
{
Matrix _world = Matrix::Identity;
_shader->GetMatrix("World")->SetMatrix((float*)&_world);
_shader->GetMatrix("View")->SetMatrix((float*)&_camera->GetCamera()->_matView);
_shader->GetMatrix("Projection")->SetMatrix((float*)&_camera->GetCamera()->_matProjection);
_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());
uint32 stride = _vertexBuffer->GetStride();
uint32 offset = _vertexBuffer->GetOffset();
DC->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset);
DC->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
_shader->DrawIndexed(0, 0, _geometry->GetIndexCount());
}
World변환 행렬은 일단 기본으로 항등행렬로 넘겨주고 View, Projection 행렬은 카메라의 변화에 따라 달라지는 행렬값을 프레임마다 새로 넘겨줬다.
준비한 텍스쳐도 넘겨주었다.
VertexOutput VS(VertexInput input)
{
VertexOutput output;
output.position = mul(input.position, World);
output.position = mul(output.position, View);
output.position = mul(output.position, Projection);
output.uv = input.uv;
return output;
}
모든 정점에 WVP행렬을 곱해줘서 Camera Space를 기준으로 하는 좌표로 변환을 하고 uv는 그대로 다음단계로 넘겨준다.
SamplerState Sampler0;
float4 PS(VertexOutput input) : SV_TARGET
{
return Texture0.Sample(Sampler0, input.uv);
}
픽셀 쉐이더에서는 들어온 uv값을 이용해 텍스쳐와 매핑하여 픽셀의 색상을 정해주었다.
적용한 텍스쳐와 텍스쳐를 입힌 큐브가 화면에 잘 나타나는 모습이다.
'DirectX11' 카테고리의 다른 글
DirectX11 학습 - Normal Mapping (0) | 2024.03.30 |
---|---|
DirectX11 학습 - Light (0) | 2024.03.30 |
DirectX11 학습 - Normal Vector (0) | 2024.03.28 |
DirectX11 학습 - 삼각형 띄우기 (0) | 2024.03.26 |
DirectX11 학습 - 프로젝트 기본 프레임워크 (1) | 2024.03.26 |