Grahpics
[Graphics] DirectX12 - Texture Mapping
송만덕
2022. 1. 28. 21:26
Texture Mapping
- 텍스처 : 3D 그래픽에서 폴리곤으로 제작된 오브젝트에 덧씌워 다양한 색상이나 질감 등을 표현하는데 쓰이는 2D 이미지
- 텍스처(Texture)는 3D 그래픽의 게임에서 폴리곤(Polygon)으로 제작한 3D 오브젝트에 UV 좌표 값을 맞춰 렌더링 했을 때 보이게 되는 2D 이미지를 뜻한다.
- 텍스처는 폴리곤만으로는 표현할 수 없는 세부적인 형태나 질감, 색상(Color) 등에 대한 정보를 담고 있으며, 텍스처가 3D 오브젝트를 감싸는 형태가 된다.
- 텍스처는 용도에 따라 다양한 종류가 있다. 색상과 질감 같은 색에 관한 정보를 포함한 ‘컬러 텍스처’(Color Texture), 적은 폴리곤 위에 두드러진 요철이나 세밀한 형태를 포함해 주는 ‘노멀 텍스처’(Normal Texture), 빛의 반사를 표현해 재질 표현을 극대화하기 위한 ‘하이라이트 텍스처’(Highlight Texture), 물체(Object) 주변의 반사를 표현하는 ‘환경 텍스처’ 등이 있다.
- 텍스처 매핑 : 가상의 3차원 물체의 표면에 세부적인 질감의 묘사를 하거나 색을 칠하는 기법.
- 간단하게 물체의 표면에 텍스처를 입힌다고 생각하면 된다.
<UV좌표>
- UV 좌표계는 3차원 공간에 폴리곤에 텍스쳐를 입히기 위한 기준이 되는 2차원 좌표계.
- UV 좌표는 최소0 최대1의 좌표를 가지고 있음.
- UV 좌표는 0에서 1의 좌표를 가지고 있으며 1을 넘어가거나 0미만이 될 경우 다시 텍스쳐가 반복되어 나오게 된다.
<Game.cpp>
더보기
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
#include "pch.h"
#include "Game.h"
#include "Engine.h"
shared_ptr<Mesh> mesh = make_shared<Mesh>();
shared_ptr<Shader> shader = make_shared<Shader>();
shared_ptr<Texture> texture = make_shared<Texture>();
void Game::Init(const WindowInfo& info)
{
GEngine->Init(info);
vector<Vertex> vec(4);
vec[0].pos = Vec3(-0.8f, 0.8f, 0.5f);
vec[0].color = Vec4(1.f, 0.f, 0.f, 1.f);
vec[0].uv = Vec2(0.f, 0.f);
vec[1].pos = Vec3(0.8f, 0.8f, 0.5f);
vec[1].color = Vec4(0.f, 1.f, 0.f, 1.f);
vec[1].uv = Vec2(1.f, 0.f);
vec[2].pos = Vec3(0.8f, -0.8f, 0.5f);
vec[2].color = Vec4(0.f, 0.f, 1.f, 1.f);
vec[2].uv = Vec2(1.f, 1.f);
vec[3].pos = Vec3(-0.8f, -0.8f, 0.5f);
vec[3].color = Vec4(0.f, 1.f, 0.f, 1.f);
vec[3].uv = Vec2(0.f, 1.f);
vector<uint32> indexVec;
{
indexVec.push_back(0);
indexVec.push_back(1);
indexVec.push_back(2);
}
{
indexVec.push_back(0);
indexVec.push_back(2);
indexVec.push_back(3);
}
mesh->Init(vec, indexVec);
shader->Init(L"..\\Resources\\Shader\\default.hlsli");
texture->Init(L"..\\Resources\\Texture\\Manduk.png");
GEngine->GetCmdQueue()->WaitSync();
}
void Game::Update()
{
GEngine->RenderBegin();
shader->Update();
{
Transform t;
t.offset = Vec4(0.f, 0.f, 0.f, 0.f);
mesh->SetTransform(t);
mesh->SetTexture(texture);
mesh->Render();
}
GEngine->RenderEnd();
}
|
cs |
- 정점을 생성할 때 추가적으로 uv값을 저장하는 방식이 추가되었으며
- 43번째 줄에서 추가로 texture를 init 해주면서, 인자로 경로를 넘겨주는 것을 확인할 수 있다.
<Texture.cpp>
더보기
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
#include "pch.h"
#include "Texture.h"
#include "Engine.h"
void Texture::Init(const wstring& path)
{
CreateTexture(path);
CreateView();
}
void Texture::CreateTexture(const wstring& path)
{
// 파일 확장자 얻기
wstring ext = fs::path(path).extension();
if (ext == L".dds" || ext == L".DDS")
::LoadFromDDSFile(path.c_str(), DDS_FLAGS_NONE, nullptr, _image);
else if (ext == L".tga" || ext == L".TGA")
::LoadFromTGAFile(path.c_str(), nullptr, _image);
else // png, jpg, jpeg, bmp
::LoadFromWICFile(path.c_str(), WIC_FLAGS_NONE, nullptr, _image);
HRESULT hr = ::CreateTexture(DEVICE.Get(), _image.GetMetadata(), &_tex2D);
if (FAILED(hr))
assert(nullptr);
vector<D3D12_SUBRESOURCE_DATA> subResources;
hr = ::PrepareUpload(DEVICE.Get(),
_image.GetImages(),
_image.GetImageCount(),
_image.GetMetadata(),
subResources);
if (FAILED(hr))
assert(nullptr);
const uint64 bufferSize = ::GetRequiredIntermediateSize(_tex2D.Get(), 0, static_cast<uint32>(subResources.size()));
D3D12_HEAP_PROPERTIES heapProperty = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);
ComPtr<ID3D12Resource> textureUploadHeap;
hr = DEVICE->CreateCommittedResource(
&heapProperty,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(textureUploadHeap.GetAddressOf()));
if (FAILED(hr))
assert(nullptr);
::UpdateSubresources(RESOURCE_CMD_LIST.Get(),
_tex2D.Get(),
textureUploadHeap.Get(),
0, 0,
static_cast<unsigned int>(subResources.size()),
subResources.data());
GEngine->GetCmdQueue()->FlushResourceCommandQueue();
}
void Texture::CreateView()
{
D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
srvHeapDesc.NumDescriptors = 1;
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
DEVICE->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&_srvHeap));
_srvHandle = _srvHeap->GetCPUDescriptorHandleForHeapStart();
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = _image.GetMetadata().format;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Texture2D.MipLevels = 1;
DEVICE->CreateShaderResourceView(_tex2D.Get(), &srvDesc, _srvHandle);
}
|
cs |
- 경로를 통해서 파일 포맷에 따른 이미지를 로드한 뒤 _image 생성
- CreateTexture()를 통해 _tex2D를 GPU 영역에 생성. //즉 이미지 원본파일을 통해 텍스쳐를 생성.
- 이후 업로드 힙을 생성하여 업로드를 해주게 된다.
<Default Shader>
더보기
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
cbuffer TEST_B0 : register(b0)
{
float4 offset0;
};
cbuffer TEST_B1 : register(b1)
{
float4 offset1;
};
Texture2D tex_0 : register(t0);
SamplerState sam_0 : register(s0);
struct VS_IN
{
float3 pos : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
struct VS_OUT
{
float4 pos : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
VS_OUT VS_Main(VS_IN input)
{
VS_OUT output = (VS_OUT)0;
output.pos = float4(input.pos, 1.f);
output.color = input.color;
output.uv = input.uv;
return output;
}
float4 PS_Main(VS_OUT input) : SV_Target
{
float4 color = tex_0.Sample(sam_0, input.uv);
return color;
}
|
cs |
- 이전과는 다르게 텍스처를 저장하는 레지스터 t0가 추가되었으며
- Sampler State 레지스터 s0가 추가되었다.
- Sampler State가 추가된 이유로는 추후에 레스터라이저 단계에서 uv값에 따라 픽셀셰이더에서 색상을 골라주게 되는데, 색상을 고르는 방법을 정하기 위한 정책과 같은 역할을 해준다.
- 또 버텍스 구조체에 UV값을 저장하기 위한 TEXCOORD가 추가되었으며
- 위에서 설명한 것과 같이 PS_Main 부분에서 uv값에 따른 색상정보를 Sample이라는 함수를 통해 Sampler State의 정책에 따라 변경시켜 준다.위의 코드는 texture의 색상을 입히는 방식이다.
결과
이전에 평범하게 사각형을 띄우고 정점의 색상좌표에 따라서 색상이 입혀지던것과는 달리
경로에 따른 이미지 파일을 읽은 뒤 그 이미지를 텍스처화 하였고,
그 텍스처의 색상을 오브젝트의 uv 좌표에 따라서 입혀줌으로써
정점의 색상이 아닌 텍스처의 색상이 출력되는 것을 확인할 수 있다.