일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Stored Procedure
- MYSQL
- 스토어드 프로시저
- Two Points
- Brute Force
- SQL
- union find
- binary search
- Hash
- DP
- String
- 다익스트라
- 그래프
- Dijkstra
- 이진탐색
- two pointer
- Trie
- Today
- Total
codingfarm
27. 텍스처 적용(Texturing) 본문
학습목표
- Pixel Shader에서 texture의 표본을 추출 해 pixel의 색상으로 쓸 수 있도록 texture를 shader resource로 binding하여 쓰는 방식을 알아본다.
버퍼 및 텍스처에 대해 배울때 정리한 바 있지만 여기서 다시한번 상세히 설펴본다
텍스처링은 크게 3가지 요소를 통해 이루어진다.
- 텍스처 자원
- 텍스처 필터링
- 좌표 지정 모드
이 중 텍스처 필터링과 좌표 지정 모드에 대한 정보가 표본 추출기 객체에 묶이게 된다.
1. 텍스처(Texture)
- D3D12_RESOURCE_DESC::Dimension이 D3D12_RESOURCE_DIMENSION_TEXTURE2D 인 IDTD12RESOURCE 로 대표되는 2차원 텍스처 객체
- 다양한 종류의 data를 담기 위한 저장공간
- 픽셀 정보 : 색상, 법선 벡터, depth 정보...
- 밉맵 수준(mipmap level)
- DXGI_FORMAT 형식의 자료 원소만 담을 수 있음
- GPU를 활용한 연산(필터링, 다중 표본화)을 적용하기 위해
- 텍스처 자원은 모든 종류의 셰이더(정점, 픽셸, 기하)가 사용할 수 있다.
SRV(Shader Resource View)
- 정의 : 파이프라인의 프로그램 가능 셰이더 단계가 읽는 자원에 대해 서술
- 우리는 여기서 텍스처 자원에 대해 서술할것이다.
- 역할 : shader가 texture에 접근 가능하게끔 wrapping 한다.
무형식(Typeless)
- 메모리 확보만 하고 해석방법은 pipeline에 binding 할 때 지정(일종의 캐스팅)
- ex) DXGI_FORMAT_R8G8B8A8_TYPELESS
텍스처 바인딩(Texture Binding)
- Pipeline에서 texture를 쓰기 위해 binding 하는 작업
- 텍스처 바인딩은 크게 2가지 목적으로 이루어짐
- RTV 로 binding : Direct3D 가 장면을 terture에 rendering 하기 위해
- Shader 자원으로 묶기 : Shader에서 texture 추출
- 하나의 자원을 render target 겸 shader resource로 쓰는것이 가능하나, 동시에 사용하는것은 불가능함
텍스처 대상 렌더링(texture to rendering)
장면을 RTV로 binding된 texture에 렌더링 한 후 그 텍스처를 shader resource로 쓰는 기법
텍스처 대상 렌더링을 위해선 texutre 자원에 대한 view descriptor를 두개 만들어야 한다.
1
2
3
4
5
|
// render target으로 묶기
OMSetRenderTargets(...);
// Shader Resource로 묶기
SetGraphicsRootDescriptorTale(...);
|
cs |
2. 텍스처 좌표(Texture Coordinates)
Direct3D는 $u$축이 가로방향이고 $v$축이 세로방향인 텍스처 좌표계를 사용한다.
텍셀(texel; texture element) : 텍스처를 구성하는 요소
텍셀 하나는 $0 \leq u, \; v \leq 1$ 인 텍스처 좌표 $(u, \; v)$로 식별한다.
$[0, \; 1]$로 정규화된 좌표성분을 사용하여 텍스처 이미지와는 독립적인 구간의 좌표들을 다룰 수 있게 한다.
ex) $(0.5 \; 0.5)$ 는 텍스처의 크기에 무관하고 항상 정중앙의 텍셀을 가져온다
구간 바깥의 값을 가져오는것도 가능은 하다.
텍스처 매핑
- 텍스처 매핑(Texture Mapping) : mesh의 삼각형에 이미지 자료를 입히는 기법
물체에 텍스처를 입히려면 , 물체의 각 3차원 삼각형마다 그 삼각형에 입힐 텍스처 이미지상의 삼각형을 지정해야한다.
$p_n$ : 3차원 삼각형의 정점
$q_n$ : $p_n$에 해당하는 텍스처 좌표
위와 같이 정의하면, 3차원 삼각형 면에 존재하는 임의의 좌표 $(x,y,z)$가 $s \geq 0, \; t \geq 0, \; s+t \leq 1$을 따르는 매개변수 $s,\; t$를 포함하는 아래 공식을 따른다.
$$(x,\;y,\;z)= p = p_0 + s(p_1 - p-0) + t(p_2 - p_0)$$
그럼 이 3차원 좌표에 대응되는 텍스처 좌표 $(u, \; v)$는 아래와 같다.
$$(u, \; v) = q = q_0 + s(q_1 - q_0) + t(q_2 - q_0)$$
위 보간에 의해, 삼각형의 모든 점마다 그에 대응되는 텍스처 좌표가 결정된다.
파이프라인에서 이런 보간을 적용하기 위해, 매개변수 $s, \; t$ 에 대응되는필드를 Vertex struct에 추가해준다.
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
|
struct Vertex{
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT3 Normal;
DirectX::XMFLOAT2 TexC; //s, t 매개변수
}
mInputLayout =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24,
D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
...
struct VertexIn
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 TexCL : TEXCOORD;
};
|
cs |
3. 텍스처 생성 및 활성화
BMP나 DDS, TGA, PNG 같은 이미지 파일을 게임 적재 시점에 ID3D12Resouce 객체로 load 할 수 있다.
텍스처 파일을 GPU 자원(ID3D12Resource)로 바꾸기 위해 DirectXTex 라이브러리를 사용한다.
해당라이브러리에서는 다양한 타입의 텍스처 파일을 load 하기 위해 아래와 같은 함수들을 제공한다
- LoadFromDDSFile: Loads DDS files from disk. This function supports loading of many legacy Direct3D 9 and all Direct3D 10/11 DDS encoded files.
- LoadFromHDRFile: Loads HDR files from disk. This function supports loading of RGBE (Radiance HDR) files.
- LoadFromTGAFile: Loads HDR files from disk. This function supports loading of Targa Truvision (TGA) files.
- LoadFromWICFile: Loads BMP, PNG, GIF, TIFF, JPEG, and JPEG-XR / HD Photo images using the Windows Imaging Component (WIC) library. This function is used when the file extension is not DDS, HDR, or TGA.
DDS는 DirectX12에서 지원하지 않기에 DDS의 load는 루나책을 참고한다.
텍스처 load의 전반적인 절차는 아래와 같다
- 텍스처를 저장하기 위한 ID3D12Resource 생성
- 이미지 파일로부터 Load 한 후 텍스처를 생성
- load : 이미지 파일 확장자에 따라 다름
- 텍스처 생성 : CreateTexture 함수
- SRV heap 생성
- descriptor 생성
3-1. 리소스 생성
이미지 파일에서 직접 load 해야 할 경우에는 불필요한 작업이다.
텍스처를 저장하기 위한 ID3D12Resource는 앞서 말했듯이 D3D12_RESOURCE_DESC::Dimension을 D3D12_RESOURCE_DIMENSION_TEXTURE2D 로 설정하고 생성해야한다.
그리고 D3D12_RESOURCE_DESC::Flag 에 적절한 bit값을 넣어 생성될 리소스를 위한 view의 생성을 사전에 허가해준다. SRV에 대해서는 deny 하는 bit 옵션만 있으므로, D3D12_RESOURCE_FLAG_NONE 설정을 주면된다
(앞서 우리는 텍스처 대상 렌더링에 대해선 RTV와 SRV 양쪽 모두에 대한 descriptor가 필요함을 설명했다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
D3D12_RESOURCE_DESC m_tDesc;
m_tDesc.MipLevels = 1;
m_tDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
m_tDesc.Width = WIDTH; // 1280
m_tDesc.Height = HEIGHT; // 768
m_tDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
m_tDesc.DepthOrArraySize = 1;
m_tDesc.SampleDesc.Count = 1;
m_tDesc.SampleDesc.Quality = 0;
m_tDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
m_tDesc.Layout = D3D12_TEXTURE_LAYOUT::D3D12_TEXTURE_LAYOUT_UNKNOWN;
HRESULT hr = md3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&m_tDesc,
D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(&m_pTex2D));
|
cs |
3-2. 이미지 파일 load
이미지 파일의 확장자 종류에 따라 load 방법은 상이하다.
여기서는 png 파일을 예로든다.
아래 링크의 예제코드를 바탕으로 작성하였다.
CreateTexture · microsoft/DirectXTex Wiki · GitHub
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
|
// 2. load image file
DirectX::LoadFromWICFile(_strFullPath.c_str(), DirectX::WIC_FLAGS_FORCE_RGB, nullptr, m_Image);
hr = CreateTexture(md3dDevice.Get(), m_Image.GetMetadata(), &m_pTex2D);
std::vector<D3D12_SUBRESOURCE_DATA> vecSubresources;
hr = PrepareUpload(md3dDevice.Get()
, m_Image.GetImages()
, m_Image.GetImageCount()
, m_Image.GetMetadata()
, vecSubresources);
if (FAILED(hr))
assert(nullptr);
// upload is implemented by application developer. Here's one solution using <d3dx12.h>
const UINT64 uploadBufferSize =
GetRequiredIntermediateSize(m_pTex2D.Get(),
0, static_cast<unsigned int>(vecSubresources.size()));
Microsoft::WRL::ComPtr<ID3D12Resource> textureUploadHeap;
hr = md3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(textureUploadHeap.GetAddressOf()));
if (FAILED(hr))
assert(nullptr);
UpdateSubresources(mCommandList.Get(),
m_pTex2D.Get(),
textureUploadHeap.Get(),
0, 0,
static_cast<unsigned int>(vecSubresources.size()),
vecSubresources.data());
mCommandList->Close();
ID3D12CommandList* ppCommandLists[] = { mCommandList.Get() };
mCommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
FlushCommandQueue();
mDirectCmdListAlloc->Reset();
mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr);
|
cs |
3-3. SRV heap
SRV 서술자를 생성하기에 앞서 heap을 먼저 생성한다.
그래야 자원을 shader program이 사용할 root parameter 매개변수 슬롯에 설정할 수 있다.
다음은 CBV 서술자나 SRV 서술자, UAV 서술자를 저장할 수 있는, 그리고 셰이더들이 접근할 수 있는 서술자 하나짜리 힙을 생성하는 코드이다.
1
2
3
4
5
6
7
|
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_pSRV;
D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
srvHeapDesc.NumDescriptors = 1;
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
md3dDevice->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&m_pSRV));
|
cs |
3-4. SRV 서술자
SRV heap에 실제 descriptor를 생성해준다.
이를 위해선 D3D12_SHADER_RESOURCE_VIEW_DESC 구조체를 적절히 채워, 자원의 용도와 기타 정보(형식, 차원, 밉맵 개수 등)을 서술해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
typedef struct D3D12_SHADER_RESOURCE_VIEW_DESC {
DXGI_FORMAT Format;
D3D12_SRV_DIMENSION ViewDimension;
UINT Shader4ComponentMapping;
union {
D3D12_BUFFER_SRV Buffer;
D3D12_TEX1D_SRV Texture1D;
D3D12_TEX1D_ARRAY_SRV Texture1DArray;
D3D12_TEX2D_SRV Texture2D;
D3D12_TEX2D_ARRAY_SRV Texture2DArray;
D3D12_TEX2DMS_SRV Texture2DMS;
D3D12_TEX2DMS_ARRAY_SRV Texture2DMSArray;
D3D12_TEX3D_SRV Texture3D;
D3D12_TEXCUBE_SRV TextureCube;
D3D12_TEXCUBE_ARRAY_SRV TextureCubeArray;
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_SRV RaytracingAccelerationStructure;
};
} D3D12_SHADER_RESOURCE_VIEW_DESC;
|
cs |
1. DXGI_FORMAT Format
- descirptor가 서술하는 자원의 형식
- 자원 생성시 지정했던 DXGI_FORMAT 멤버를 그대로 지정하면된다
- 자원을 무형식으로 지정했다면, 반드시 구체적인 형식에 해당하는 DXGI_FORMAT 멤버를 이 필드에 지정해야 한다
2. D3D12_SRV_DIMENSION ViewDimension
- 자원의 차원
- 자원 생성시 지정했던 dimension을 지정한다.
- 이 예에선 DIMENSION_TEXTURE2D를 지정
3. UINT Shader4ComponentMapping
- 셰이더에서 텍스처 표본을 추출하면 지정된 텍스처 좌표에 있는 텍스처 자료를 담은 벡터가 반환된다.
- 이 필드를 통해 텍스처 추출 시 반환되는 벡터 성분들으 순서를 바꾸는 수단을 제공한다
- ex : 적색 색상 성분과 녹색 색상 성분을 맞바꿈
- 이 책에서는 원래 순서를 돌려주는 D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING을 지정한다.
4. D3D12_TEX2D_SRV Texture2D;
union을 통해 자원에 대해 상세하게 서술할 수 있다.
우리는 SRV를 통해 텍스처를 서술할 예정이므로 D3D12_TEX2D_SRV를 서술해주면 된다.
해당 구조체의 형태는 아래와 같다.
1
2
3
4
5
6
|
typedef struct D3D12_TEX2D_SRV {
UINT MostDetailedMip;
UINT MipLevels;
UINT PlaneSlice;
FLOAT ResourceMinLODClamp;
} D3D12_TEX2D_SRV;
|
cs |
1. UINT MostDetailedMip
- 이 뷰에 대해 세부적인 mipmap level의 색인을 지정
- 0 이상, $MipCount - 1$ 이하의 값을 지정
2. UINT MipLevels
- 이 뷰의 밉맵 수준 갯수
- MostDetailedMip 필드와의 조합으로 이 뷰에 대한 mipmap level 들의 부분 구간을 지정가능
- -1을 지정하면 MostDetailedMip에서 마지막 밉맵 수준까지 모든 밉맵 수준을 포함할 수 있음
3. UINT PlaneSlice
- 평면 색인
- 특정 자원 형식들에서는 이미지가 여러 개의 색상 성분별 평면으로 이루어져 있다.(여기서는 안 다룸)
4. FLOAT ResourceMinLODClamp
- 접근 가능한 최소 밉맵 수준을 지정
- ex) 3.0 지정 : mipmap level 을 3.0 에서 $MipCount - 1$ 까지 접근 가능
- ex) 0.0 지정 : 모든 mipmap level에 접근 가능
1
2
3
4
5
6
7
8
9
|
D3D12_CPU_DESCRIPTOR_HANDLE handle = m_pSRV->GetCPUDescriptorHandleForHeapStart();
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Format = m_Image.GetMetadata().format;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
md3dDevice->CreateShaderResourceView(m_pTex2D.Get(),
&srvDesc, m_pSRV->GetCPUDescriptorHandleForHeapStart());
|
cs |
3-5. binding
이제 텍스처를 파이프라인에 묶으면 된다
Shader Resource View들의 테이블이 0번 슬롯 (t0) 매개변수에 묶이도록 루트 서명을 정의하는 방법은 아래와 같다.
1
2
|
D3D12_GPU_DESCRIPTOR_HANDLE tex = m_pSRV->GetGPUDescriptorHandleForHeapStart();
mCommandList->SetGraphicsRootDescriptorTable(1, tex);
|
cs |
이 SRV의 루트 서명을 생성하는 과정은 추후 살펴보겠다.
4. 필터
텍스처 맵의 요소인 텍셀은 하나의 연속된 이미지로부터 추출하는 이산적인 색상 표본(sample) 이다. 텍셀을 면적을 가진 직사각형이라 정의해선 안된다.
4-1. 확대(magnification)
가령 모니터 해상도가 $1024\; \times \; 1024$ 이고 벽의 텍스처 이미지가 $256 \; \times \; 256$ 이라 할 때, 이 경우 화면이 텍스처보다 크므로 텍스처의 확대(magnification)이 발생한다. 즉, 적은수의 텍셀들로 더 많은 수의 픽셀들을 채우게 된다. 그에 따라 텍셀 점과 정확히 일치 하지 않는 텍스처 좌표를 가진 픽셀들이 존재하게 된다. 그런 픽셀들의 색을 결정하기 위해선 픽셀 부근에 있는 텍셀들의 색들을 보간 하는것이다.
그래픽 하드웨어는 상수 보간(constant interpolation)과 선형 보간(linear interpolation) 두 종류의 보간법을 지원하는데, 선형보간이 즐겨 쓰인다.
[상수 보간]
[선형 보간]
[좌 : 상수 보간 적용 결과, 우 : 선형 보간 적용 결과]
네 개의 텍셀점이 주어젔을 때, 그 사이에 놓인 점의 색상을 근사하기 위한 2차원 선형보간이 존재 하며 이를 이차선형 보간(bilinear interpolation) 이라 한다.
이차 선형 보간법에는 다양한 방법이 존재하며, 루나책에선 위 그림과 같은 방법을 안내한다.
4-2. 축소(minification)
너무 많은 텍셀이 적은 픽셀들에 사상될 경우 발생
예 : $256 \; \times \; 256$의 텍스처를 벽에 입혔을때, 벽이 화면의 $64 \; \times \; 64$픽셀 영역을 차지할 경우 발생
이런경우 커다란 크기의 텍셀을 하향표본화(downsampling) 하여 더 작은 크기로 줄이면, 쓸데 없는 연산의 낭비 및 축소가 일어나는 정도를 줄일 수 있다.
텍스처 초기화 시점(또는 생성 시점에서 주어진 이미지를 하향 표본화해서 텍스처의 더 작은 버전들을 만드는 기법을 밉매핑(mipmapping) 이라 한다.
이런 작은 버전들을 밉맵 수준(mipmap level)이라 부른다.
밉매핑을 통해 만들어진 수많은 밉맵수준들의 집합을 밉맵 사슬(mipmap chain)이라 한다.
[밉맵 사슬]
밉맵 수준들의 생성은 렌더링 이전에 미리 수행된다. 이후 렌더링 도중 그래픽 하드웨어는 응용 프로그램이 제공한 밉맵 수준을 선택하여 텍스처를 적용하는데, 선택 방식은 두가지가 대표된다.
1. 점 필터링(point filtering)
화면에 투영된 기하구조의 해상도에 가장 잘 부합하는 밉맵 수준 하나를 선택해서 텍스처로 사용하는 기법
필요에 따라 상수 보간 및 선형 보간을 적용
2. 선형 필터링(linear filtering)
화면에 투영된 기하구조의 해상도에 가장 가까운 이웃한 두 밉맵수준(화면 기하 구조 해상도보다 큰 것 하나, 작은 것 하나)을 선택한다. 그리고 두 밉맵 수준에 각각 상수 및 선형 필터링을 적용하여 텍스처 색상을 하나 뽑는다. 마지막으로, 두 텍스처 색상을 보간해서 최종 색상을 결정한다.
3. 비등방 필터링(anisortropic filtering)
다각형의 법선 벡터와 카메라의 시선 벡터 사이의 각도가 클때 박생하는 왜곡 현상을 완화하기 위한 필터링 기법
비용이 가장 비싼 필터링 이지만, 왜곡에 의한 결함을 제거하여 품질을 향상시킬 수 있다.
[좌 : 필터링 off, 우 : 필터링 on]
5. 텍스처 좌표 지정 모드
텍스처와 보간의 조합은 하나의 벡터값 함수 $T(u, v) = (r,g,b,a)$ 를 의미한다.
즉, 텍스처 좌표 $(u, v) \in [0, 1]^2$ 이 주어젔을 때 텍스처 함수 $T$는 하나의 색상$(r,g,b,a)$를 돌려준다.
좌표 지정 모드(address mode) : 위 함수의 정의역 바깥의 좌표가 주어졌을 때 처리하는 방식
Direct3D는 4가지 좌표 지정 모드를 지원한다.
1. wrap extends the texture function by repeating the image at every integer junction
2. border color extends the texture function by mapping each (u, v) not in [0, 1]^2 to
some color specified by the programmer
3. clamp extends the texture function by mapping each (u, v) not in [0, 1]^2 to the color
T(u0, v0), where (u0, v0) is the nearest point to (u, v) contained in [0, 1]^2
4. mirror extends the texture function by mirroring the image at every integer junction
기본 모드는 wrap 모드이며, 이 모드를 적용하면 [0, 1] 구간 바깥의 텍스처 좌표는 항상 적절한 의미를 가진다.
이 wrap 모드를 잘 이용하면 하나의 텍스처를 반복해서 적용하는 tiling 효과를 줄 수있다.
Direct3D에서 좌표 지정 모드를 나타내는 데 사용하는 수단은 D3D12_TEXTURE_ADDRESS_MODE 열거형이다.
1
2
3
4
5
6
7
|
typedef enum D3D12_TEXTURE_ADDRESS_MODE {
D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_MIRROR,
D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
D3D12_TEXTURE_ADDRESS_MODE_BORDER,
D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE
} ;
|
cs |
6. 표본 추출기 객체(Sampler Object)
텍스처 자원에서 표본을 추출할 때 구체적으로 어떤 필터링 방식과 좌표 지정모드를 적용할 것인지에 대한 정보를 저장하는 객체
6-1. 표본추출기 서술자 생성
DirectX12는 표본추출기를 총 2가지 방법으로 제공한다.
일반 표본추출기
우선 표본추출기 힙이 필요햐다. 표본추출기 힙의 생성은 D3D12_DESCRIPTOR_HEAP_DESC::Type 를 D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER로 채우면된다.
1
2
3
4
5
6
7
8
|
D3D12_DESCRIPTOR_HEAP_DESC descHeapSampler = {};
descHeapSampler.NumDescriptors = 1;
descHeapSampler.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
descHeapSampler.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mSamplerDescriptorHeap;
md3dDevice->CreateDescriptorHeap(&descHeapSampler,
IID_PPV_ARGS(&mSamplerDescriptorHeap));
|
cs |
표본추출기 서술자의 생성은 D3D12_SAMPLER_DESC 구조체의 여러 필드에 좌표 지정 모드와 필터 종류, 기타 여러 매개 변수를 지정하여 생성한다.
1
2
3
4
5
6
7
8
9
10
11
12
|
typedef struct D3D12_SAMPLER_DESC {
D3D12_FILTER Filter;
D3D12_TEXTURE_ADDRESS_MODE AddressU;
D3D12_TEXTURE_ADDRESS_MODE AddressV;
D3D12_TEXTURE_ADDRESS_MODE AddressW;
FLOAT MipLODBias;
UINT MaxAnisotropy;
D3D12_COMPARISON_FUNC ComparisonFunc;
FLOAT BorderColor[4];
FLOAT MinLOD;
FLOAT MaxLOD;
} D3D12_SAMPLER_DESC;
|
cs |
주로 위에서 4개의 필드를 사용한다.
1. Filter
사용할 필터의 종류를 뜻하는 열거형
2. AddressU/V/W
텍스처의 수평/수직/깊이 방향 좌표 지정 모드
깊이 방향(w)는 3차원 텍스처에만 적용됨
아래는 선형 필터 방식에, 모든 축에 대해 WRAP 좌표 지정 모드를 적용한 표본추출기 서술자를 생성하는 코드이다
1
2
3
4
5
6
7
8
9
10
11
12
13
|
D3D12_SAMPLER_DESC samplerDesc = {};
samplerDesc.Filter = D3D12_FILTER_ANISOTROPIC;
samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.MinLOD = 0;
samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
samplerDesc.MipLODBias = 0.0f;
samplerDesc.MaxAnisotropy = 1;
samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
md3dDevice->CreateSampler(&samplerDesc,
mSamplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
|
cs |
추후 살펴볼것이지만, 일반 표본 추출기의 루트 서명은 descriptor table의 형태로만 제공된다.
정적 표본추출기
Direct3D는 표본추출기 힙을 생성하지 않고도 shader에서 표본추출기를 사용하게 해주는 단축 수단을 제공한다.
D3D12_STATIC_SAMPLER_DESC에 원하는 표본추출기의 특성을 넣은 후, 루트 서명생성시에 필드값에 추가만 해주면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
typedef struct D3D12_STATIC_SAMPLER_DESC {
D3D12_FILTER Filter;
D3D12_TEXTURE_ADDRESS_MODE AddressU;
D3D12_TEXTURE_ADDRESS_MODE AddressV;
D3D12_TEXTURE_ADDRESS_MODE AddressW;
FLOAT MipLODBias;
UINT MaxAnisotropy;
D3D12_COMPARISON_FUNC ComparisonFunc;
D3D12_STATIC_BORDER_COLOR BorderColor;
FLOAT MinLOD;
FLOAT MaxLOD;
UINT ShaderRegister;
UINT RegisterSpace;
D3D12_SHADER_VISIBILITY ShaderVisibility;
} D3D12_STATIC_SAMPLER_DESC;
|
cs |
이 구조체는 앞서 배운 D3D12_SAMPLER_DESC와 유사하지만, 아래와 같은 차이가 있다.
1. 테두리 색상 설정에 제한이 있다, 정적 표본추출기의 테두리 색상은 다음 열거형의 한 멤버여야 한다.
1
2
3
4
5
|
typedef enum D3D12_STATIC_BORDER_COLOR {
D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK,
D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE
} ;
|
cs |
2. 셰이더 레지스터, 레지스터 공간, 셰이더 가시성을 지정하는 추가적인 필드들이 있다.
정적 표본추출기는 최대 2032개만 정의할 수 있다.
정적 표본추출기를 만들어 서명까지 하는 예제 코드는 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
CD3DX12_STATIC_SAMPLER_DESC anisotropicClamp(
0, // shaderRegister
D3D12_FILTER_ANISOTROPIC, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressW
0.0f, // mipLODBias
8); // maxAnisotropy
D3D12_ROOT_SIGNATURE_DESC rootSigDesc;
rootSigDesc.NumParameters = 4;
rootSigDesc.pParameters = slotRootParameter;
rootSigDesc.NumStaticSamplers = 1;
rootSigDesc.pStaticSamplers = &anisotropicClamp;
rootSigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
cs |
6-2. 표본추출기 루트 서명
앞서 정적 표본추출기는 root signature desc에 static sampler desc를 전달하고 서명을 생성하면 됐었지만, 일반 표본 추출기는 추가적인 평번한 서명 작업이 필요하다.
일반 표본 추출기
표본추출기는 셰이더에서 쓰이므로, 이를 위해선 표본추출기 객체에 대한 descriptor를 원하는 shader에 묶어야 한다.
셰이더의 표본 추출기 레지스터 슬롯 0번(s0)에 묶이도록 하는 예제 코드는 아래와 같다.
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
|
void Init(){
D3D12_ROOT_PARAMETER slotRootParameter[3];
D3D12_DESCRIPTOR_RANGE descRange[3];
descRange[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
descRange[0].NumDescriptors = 3;
descRange[0].BaseShaderRegister = 0;
descRange[0].RegisterSpace = 0;
descRange[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
descRange[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
descRange[1].NumDescriptors = 1;
descRange[1].BaseShaderRegister = 0;
descRange[1].RegisterSpace = 0;
descRange[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
descRange[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
descRange[2].NumDescriptors = 1;
descRange[2].BaseShaderRegister = 0;
descRange[2].RegisterSpace = 0;
descRange[2].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
slotRootParameter[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
slotRootParameter[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
slotRootParameter[0].DescriptorTable.NumDescriptorRanges = 1;
slotRootParameter[0].DescriptorTable.pDescriptorRanges = &descRange[0];
slotRootParameter[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
slotRootParameter[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
slotRootParameter[1].DescriptorTable.NumDescriptorRanges = 1;
slotRootParameter[1].DescriptorTable.pDescriptorRanges = &descRange[1];
slotRootParameter[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
slotRootParameter[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
slotRootParameter[2].DescriptorTable.NumDescriptorRanges = 1;
slotRootParameter[2].DescriptorTable.pDescriptorRanges = &descRange[2];
D3D12_ROOT_SIGNATURE_DESC rootSigDesc;
rootSigDesc.NumParameters = 3;
rootSigDesc.pParameters = slotRootParameter;
rootSigDesc.NumStaticSamplers = 0;
rootSigDesc.pStaticSamplers = nullptr;
rootSigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
// create a root signature with a single slot which points to a descriptor range consisting of a single constant buffer
Microsoft::WRL::ComPtr<ID3DBlob> serializedRootSig = nullptr;
Microsoft::WRL::ComPtr<ID3DBlob> errorBlob = nullptr;
D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
if (errorBlob != nullptr)
{
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(&mRootSignature));
}
void Update(){
D3D12_GPU_DESCRIPTOR_HANDLE mCbvHeapHandle = mCbvHeap->GetGPUDescriptorHandleForHeapStart();
mCommandList->SetGraphicsRootDescriptorTable(0, mCbvHeapHandle);
mCbvHeapHandle.ptr += mCbvSrvUavDescriptorSize;
//mCommandList->SetGraphicsRootDescriptorTable(1, mCbvHeapHandle);
D3D12_GPU_DESCRIPTOR_HANDLE tex = m_pSRV->GetGPUDescriptorHandleForHeapStart();
mCommandList->SetGraphicsRootDescriptorTable(1, tex);
mCommandList->SetGraphicsRootDescriptorTable(2,
mSamplerDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
}
|
cs |
SRV와 표본추출기는 pixel 단계에서만 참조가능하도록 분리시켰다. root parameter를 분리시켰다
'computer graphics > DirectX12' 카테고리의 다른 글
28. 혼합(Blending) - 작성 중 (0) | 2021.09.27 |
---|---|
27-1. 텍스처적용 예제 코드 (0) | 2021.09.22 |
26. 좌표 변환 (0) | 2021.08.23 |
24. 색이 변하는 구 (0) | 2021.08.19 |
23. Cube 그리기 (0) | 2021.08.10 |