(사실 이해했는지 모르겠다. 어렴풋이 느껴질 뿐... 그래도 그 느낌을 정리한다.)
1. D3D에서 Texture 입히기
- 점 하나에 색 하나면, 진짜 점을 말도 안되게 찍어야 할 것이다.
- 그 대신에 어떤 텍스쳐 이미지를 이용해서 Pixel을 찍는 Texturing에 대해서 연습했다.
- 방법은 Texture와 Vertex의 UV좌표고, 그걸 가능케 하는 DirectX 기능은 ID3DResource와 Sampler 이다.
(.... 그냥 생각나는 걸 적었는데, 당연히 중간중간 다른 기능들이 많이 쓰인다.)
- 일단 예제에서는 GPU 친화적인 이미지 파일 포멧인 DDS와 그것을 Resource로 로드 해주는 업로드 함수를 사용하였다.
(추후에 Toolkit이나 DirectXTex를 써서 다른 이미지 포멧도 사용할 수 있어야할 것이다...)
(음... 사용법을 차례대로 그냥 적어보면서 중간중간 내용을 끼워 넣는 식으로 해보겠다.)
- Texture Image를 ID3DResource로 로드 한다. 예제에서 사용하는 텍스쳐는 일반적인 이미지 이므로 Dimension은 D3D11_RESOURCE_DIMENSION_TEXTURE2D이다.
(D3D12는 온갖게 다 ID3DResource으로 사용하는 듯 하다. 리소스를 생성할 때 D3D12_RESOURCE_DESC에서 타입이니 차원이니를 다 설정하여서 사용하는 커스텀... 타입 느낌)
- 이제 리소스를 Shader에 넘겨주하는데...어떻게 넘겨줄까?
- 또 Shader는 이 Texture Resource를 이용해서 어떻게 화면에 점을 찍어야 하는데...Shader는 어떻게 작동할까?
- 일단 리소스를 넘겨주는 걸 먼저 보면Shader에서는 2d 텍스쳐에 관한 Type이 존재한다. -> Texture2D
- 그리고 이것을 위한 레지스터 번호도 따로 존재한다. -> t[n]
- 예제에서는 Texture Resource를 Shader 에게 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV 전용 힙을 만들어서 Table로 넘겨준다. (Buffer로 넘겨주려면 넘겨줄 수 있겠지만, 예제에서는 그렇게 하지 않았다.)
- D3D12_SHADER_RESOURCE_VIEW_DESC을 Texture2D에 맞게 맴버를 채워주고 Heap에 View를 만들어준다.
- 이제 shader가 Texture2D를 가지고 어떻게 화면을 찍는지 보자.
- UV 좌표가 왼쪽 위 가생이 부터 시작하는 정의역 [0, 1]을 가지고 있고, 해당 Vertex가 가지고 있는 UV값에 의해 Texture 위에 있는 색을 가져온다는 것은 그냥 믿음으로 넘어가자.
- 여기서 Sampler라는 친구가 나온다. 이 친구는 Texture의 색을 뽑아서 어떻게 화면이 찍을지 고민(?)해주는 친구이다. 예를 들면 Texture의 해상도가, 점으로 찍힐 화면보다 크거나 작을때 어떻게... 아니면 UV 좌표를 벗어나는 값이 나왔을 때 어떡할지 등등. 타입은 -> SamplerState을 가지게 된다.
- Sampler가 어떻게 작동할지는 D3D12_SAMPLER_DESC 구조체를 채워서 결정할 수 있다.
- 원칙 적으로 Sampler는 App에서 만들어준 다음에, 이 친구도 D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER 전용 힙을 만들어서 Table로서 Shader에 넘겨준다.
- 다만 예제에서는 Static Sampler라고 View Heap을 만들지 않고, 배열로써 바로 RootSignature에 연결 시킬 수 있는 방법을 사용하였다. (약간의 제한사항이 있지만, 비용이 매우 적고 간편하기 때문에 몇개를 미리 만들어 놓고 사용한다고 한다.)
- 이제 Root Signature를 만드는데... Sampler는 Static Sampler로 사용한다고 했다.
- 미리 App단에서 D3D12_SAMPLER_DESC 을 가지는 배열을 만들어서 CD3DX12_ROOT_SIGNATURE_DESC 구조체에 넣어준다.
- Texture를 받을 Table과 이전에 있었던 Constant Buffer를 받는 Root Constant를 가지는 Root Signature를 만든다.
- 이제 geometry를 생성하는데, Texture가 잘 입혀질 수있도록 UV 좌표 또한 Vertex 정보에 들어가도록 만들어야 한다.
- 예제에서 가지는 Material 구조, Geometry, Texture View (Index)를 Render Item 마다 설정을 해주고
- 루프를 돌면서, ID3D12CommandList::SetGraphicsRootDescriptorTable / ID3D12CommandList:: ::SetGraphicsRootConstantBufferView을 RenderItem에 등록된 대로 bind를 해주면서 ID3D12CommandList ::DrawIndexedInstanced 을 걸면 된다.
2. 쉐이더에서 텍셀(Texel) 가져와서 화면에 찍기
- Shader에 Texture2D 타입의 텍스쳐 리소스와, SamplerState 타입의 샘플러가 넘어온다고 했다.
- 여기서 텍스쳐 리소스의 값을 이용해서, Vertex에 맞게, View에 맞게, Camera에 맞게 화면에 찍어야 한다.
- Texture2D 의 Sample 함수를 이용해서 UV 좌표에 맞는, 혹은 그 값에 맞게 Sampler가 고민(보간, mipmap 등등) 해서 가져온 색을 가져올 수 있다.
3.언급할 만한 예제에서 사용한 방법(구조)
- 이전과 비슷하다. RenderItem 구조와 Material 구조를 이용해서 리소스는 공유하면서, 기록은 편하게 하고, 사용도 편하게 한다.
4. 연습 문제
(클릭하면 커집니다.)
연습 문제 1:
연습 문제 2 :
- D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER 으로 힙을 만들고, Sampler View를 채운다.
- 그리고 CD3DX12_DESCRIPTOR_RANGE에서 D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER을 성분으로 넣어주고, Shader 레지스터 번호에 맞게 table을 채워준다.
- Heap Increment는 ID3D12Device:: GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) 으로 얻는다.
연습 문제 3 :
연습 문제 4:
UV 좌표가 시작되는 부분이 어딘지 생각하고 조심해야한다.
연습 문제 6 :
(책에서 제공하는 코드에 있는 내용은 따로 캡쳐해서 올리지 않겠다.)
(이미 있는 Skull을 또 없애는건 또 가오가 상해서)
- 해골이 대충 원형으로 생겼다고 스스로에게 최면을 건 다음에, 점 위치를 대충 원으로 근사한다고 가정하고, 데카르트 좌표계를 구하는 식을 뒤집어서 Phi(피)와 Theta(세타) 값을 각각 U와 V로 넣어주었다.
- Get UV from Vertex Approximatly (using Spherical Coordinate System)
namespace Prac3
{
XMFLOAT2 VertexToApproxSphericalRadian(const XMFLOAT3& _ver)
{
XMFLOAT2 uv = { 0.f, 0.f };
XMVECTOR vec = { _ver.x, _ver.y, _ver.z, 0.f };
vec = XMVector3Normalize(vec);
XMFLOAT3 ver;
XMStoreFloat3(&ver, vec);
float theta = acosf(ver.y);
if (theta > 0.0f)
{
float sinPhi = ver.z / sin(theta);
float phi = asinf(sinPhi);
uv.x = phi;
uv.y = theta;
}
return uv;
}
void Prac3VerticesNIndicies(const wstring& _Path, vector<Vertex>& _outVertices, vector<uint32_t>& _outIndices)
{
ifstream fin;
fin.open(_Path);
string TrashBin;
UINT VertexCount;
UINT IndicesCount;
float px, py, pz, nx, ny, nz;
uint32_t index;
if (fin.is_open())
{
fin >> TrashBin;
fin >> VertexCount;
fin >> TrashBin;
fin >> IndicesCount;
IndicesCount *= 3;
_outVertices.reserve(VertexCount);
_outIndices.reserve(IndicesCount);
getline(fin, TrashBin);
getline(fin, TrashBin);
fin >> TrashBin;
for (UINT i = 0; i < VertexCount; i++)
{
fin >> px >> py >> pz >> nx >> ny >> nz;
XMFLOAT3 pos(px, py, pz);
XMFLOAT3 norm(nx, ny, nz);
XMFLOAT2 uv = VertexToApproxSphericalRadian(pos);
Vertex skullVert = { pos, norm, uv };
_outVertices.push_back(skullVert);
}
fin >> TrashBin;
fin >> TrashBin;
fin >> TrashBin;
for (UINT i = 0; i < IndicesCount; i++)
{
fin >> index;
_outIndices.push_back(index);
}
fin >> TrashBin;
}
fin.close();
}
}
책 : DirectX 12를 이용한 3D 게임 프로그래밍 입문
'Programming > D3D12' 카테고리의 다른 글
[책공부] Stencil 예제 + chap 11 연습 문제 (0) | 2024.01.23 |
---|---|
[책공부] Blend 예제 + chap 10 연습 문제 (0) | 2024.01.16 |
[책공부] Lighting 예제 + chap 8 연습 문제 (0) | 2024.01.11 |
[책공부] FrameResource 예제 ( + Root Constant) (0) | 2024.01.08 |
[책공부] Chap7 연습 문제 (0) | 2024.01.08 |