수업
주제
- DirectX11 기반 3D 엔진에서 UV 좌표를 활용한 텍스처 매핑 시스템 구현을 목표로 한다.
- 정점(Vertex)에 UV 정보를 추가하고, 해당 UV를 기준으로 텍스처 이미지를 렌더링에 반영하는 전체 흐름을 실습한다.
- Shader(HLSL)에서 텍스처 샘플링을 수행하고, ResourceManager를 통해 텍스처 자원을 효율적으로 관리한다.
개념
- 텍스처(Texture): 3D 모델의 표면에 입히는 2D 이미지.
- UV 좌표: 텍스처 이미지의 상대 좌표계. 0.0~1.0 범위를 가지며 U는 가로, V는 세로를 의미.
- VertexTextureData: 정점의 위치(Vec3)와 UV 좌표(Vec2)를 포함한 구조체.
- 텍스처 매핑: 정점에 정의된 UV 좌표를 기반으로 해당 위치의 텍스처 이미지를 샘플링하여 픽셀 셰이더에서 색상을 출력하는 과정.
- ShaderResourceView(SRV): 텍스처를 GPU에 전달하고 HLSL Shader에서 접근할 수 있도록 돕는 인터페이스.
- SamplerState: 텍스처를 어떻게 샘플링할 것인가(보간, 래핑 등)를 정의하는 상태.
- ResourceManager: 텍스처 리소스를 효율적으로 로드하고 공유하는 관리자 클래스.
용어정리
| 용어 | 설명 |
|---|
| VertexTextureData | 위치(Vec3) + UV(Vec2)를 포함한 텍스처용 정점 구조체 |
| UV 좌표 | 텍스처 좌표계 (0,0) ~ (1,1), 왼쪽 위 기준 |
| Texture2D | HLSL 셰이더에서 2D 텍스처를 의미 |
| SamplerState | 샘플링 필터링, 테두리 처리 방식 등을 지정 |
| ShaderResourceView | GPU 리소스를 셰이더에서 접근할 수 있게 하는 객체 |
| RESOURCES | ResourceManager의 싱글톤 인스턴스를 간편하게 접근할 수 있게 하는 매크로 |
| CreateQuad | 4개의 정점과 6개의 인덱스를 생성해 사각형을 만드는 헬퍼 함수 |
코드 분석
✅ Vertex 구조 정의
struct VertexTextureData
{
Vec3 position = { 0, 0, 0 };
Vec2 uv = { 0, 0 };
};
- 정점의 위치와 UV 좌표를 모두 포함해 텍스처 매핑에 필요한 정보를 담는다.
✅ GeometryHelper::CreateQuad
void GeometryHelper::CreateQuad(shared_ptr<Geometry<VertexTextureData>> geometry)
{
vector<VertexTextureData> vtx(4);
vtx[0].position = Vec3(-0.5f, -0.5f, 0.f); vtx[0].uv = Vec2(0.f, 1.f);
vtx[1].position = Vec3(-0.5f, 0.5f, 0.f); vtx[1].uv = Vec2(0.f, 0.f);
vtx[2].position = Vec3( 0.5f, -0.5f, 0.f); vtx[2].uv = Vec2(1.f, 1.f);
vtx[3].position = Vec3( 0.5f, 0.5f, 0.f); vtx[3].uv = Vec2(1.f, 0.f);
geometry->SetVertices(vtx);
geometry->SetIndices({ 0, 1, 2, 2, 1, 3 });
}
- 시계 방향으로 4개의 정점 배치
- UV 좌표를 정확히 설정해 텍스처가 왜곡 없이 사각형 전체에 맵핑되도록 한다
✅ Shader - 04. World.fx
Texture2D Texture0;
SamplerState Sampler0;
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;
}
float4 PS(VertexOutput input) : SV_TARGET
{
return Texture0.Sample(Sampler0, input.uv);
}
- VS: 정점 위치를 World → View → Projection 변환하고 UV 좌표 전달
- PS: 전달받은 UV 좌표를 기준으로 Texture0 텍스처에서 샘플링하여 픽셀 색상 결정
✅ TextureDemo.h
shared_ptr<Geometry<VertexTextureData>> _geometry;
shared_ptr<VertexBuffer> _vertexBuffer;
shared_ptr<IndexBuffer> _indexBuffer;
Matrix _world = Matrix::Identity;
shared_ptr<GameObject> _camera;
shared_ptr<Texture> _texture;
- 텍스처 렌더링을 위한 구성 요소를 선언
_texture는 실제 텍스처 데이터를 관리한다
✅ TextureDemo::Init
_shader = make_shared<Shader>(L"04. World.fx");
_geometry = make_shared<Geometry<VertexTextureData>>();
GeometryHelper::CreateQuad(_geometry);
_vertexBuffer = make_shared<VertexBuffer>();
_vertexBuffer->Create(_geometry->GetVertices());
_indexBuffer = make_shared<IndexBuffer>();
_indexBuffer->Create(_geometry->GetIndices());
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform();
_camera->AddComponent(make_shared<Camera>());
_camera->AddComponent(make_shared<CameraScript>());
_camera->GetTransform()->SetPosition(Vec3(0.f, 0.f, -2.f));
_texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
- Camera + CameraScript 조립
- ResourceManager를 통해 텍스처 로드 및 캐싱 처리
✅ TextureDemo::Render
_shader->GetMatrix("World")->SetMatrix((float*)&_world);
_shader->GetMatrix("View")->SetMatrix((float*)&Camera::S_MatView);
_shader->GetMatrix("Projection")->SetMatrix((float*)&Camera::S_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, _indexBuffer->GetCount(), 0, 0);
- ShaderResourceView를 통해 Texture0과 텍스처 자원 연결
- 정점/인덱스 버퍼 바인딩 후 텍스처가 입혀진 사각형 렌더링
핵심
- VertexTextureData 구조체로 위치 + UV 정보를 정의해 텍스처 매핑을 가능하게 한다
- CreateQuad 함수에서 UV 좌표를 지정해 정확한 텍스처 위치 매핑을 수행한다
- Shader에서는 Texture2D와 SamplerState를 통해 UV 위치에 따라 색상 샘플링
- 픽셀 셰이더는 Texture0.Sample(Sampler0, uv)로 색상 결정
- ResourceManager를 통해 텍스처 중복 로딩을 방지하고 리소스를 효율적으로 재사용
- ShaderResourceView를 통해 텍스처와 셰이더 변수(Texture0)를 연결
- Render 함수에서 World/View/Projection 행렬과 텍스처 리소스를 셰이더에 바인딩 후 그리기