텍스처

2 minute read

텍스처

텍스처와 자원의 개괄

장면을 텍스처에 렌더링한 후 그 텍스처를 셰이더 자원으로 사용하는 기법을 텍스처 대상 렌더링이라고 부른다.

하나의 텍스처를 렌더 대상이자 셰이더 자원으로 사용하려면 그 텍스처 자원에 대한 뷰 서술자를 두 개 만들어야 한다.

하나는 렌더 대상 힙에 두고 다른 하나는 셰이더 자원 힙에 두어야한다.

텍스처 좌표

텍스처를 구성하는 요소들을 텍셀이라고 부른다.

텍셀 하나는 0 <= u, v <= 1 인 텍스처 좌표(u, v)로 식별한다.(v축은 위에서 아래로 향함)

좌표는 항상 [0, 1] 구간에 속해야한다.

텍스처 생성 및 활성화

이 책의 저자가 작성한 CreateDDSTextureFromFile12 함수를 통해 텍스처를 DDS파일을 불러올 수 있다.

auto fireCrateTex = std::make_unique<Texture>();
fireCrateTex->Name = "fireCrateTex";
fireCrateTex->Filename = L"../../../Exercise Media/flarealpha.dds";
ThrowIfFailed(DirectX::CreateDDSTextureFromFile12(md3dDevice.Get(),
		mCommandList.Get(), fireCrateTex->Filename.c_str(),
		fireCrateTex->Resource, fireCrateTex->UploadHeap));

mTextures[fireCrateTex->Name] = std::move(fireCrateTex);

와 같이 텍스처를 생성한다.

텍스처 생성 후에는 SRV 힙을 생성하고 SRV 서술자를 생성한다.

SRV 서술자는 D3D12_SHADER_RESOURCE_VIEW_DESC 구조체를 이용하여 생성한다.

auto fireCreateTex = mTextures["fireCrateTex"]->Resource;

hDescriptor.Offset(1, mCbvSrvDescriptorSize);
srvDesc.Format = fireCreateTex->GetDesc().Format;
srvDesc.Texture2D.MipLevels = fireCreateTex->GetDesc().MipLevels;
md3dDevice->CreateShaderResourceView(fireCreateTex.Get(), &srvDesc, hDescriptor);

와 같은 방법으로 서술자를 힙에 생성한다.

DrawRenderItems함수 내부에서 아래와 같은 방법으로 텍스처를 파이프라인에 묶는다.

CD3DX12_GPU_DESCRIPTOR_HANDLE tex(mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
		tex.Offset(ri->Mat->DiffuseSrvHeapIndex, mCbvSrvDescriptorSize);
cmdList->SetGraphicsRootDescriptorTable(0, tex);

필터

화면이 텍스처보다 클 경우 텍스처의 확대가 일어난다.

텍셀 점과 정확히 일치하지 않는 텍스처 좌표를 가진 픽셀들이 생기는데 이 때 지원하는 두 종류의 보간 방법이 상수 보간과 선형 보간이다.

선형 보간은 상수 보간보다 매끄럽지만 보간으로 유도한 자료가 아닌 더 높은 해상도의 텍스처를 사용하면 품질이 떨어진다.

너무 많은 텍셀이 너무 적은 픽셀들에 사상되면 축소가 일어난다.

이를 효율적으로 하기 위해 밉매핑을 사용한다.

밉맵은 하향표본화한 여러개의 작은 텍스처들을 만들어 이용하는 것이다.

밉맵 수준들을 생성하는 작업은 렌더링 이전에 미리 수행된다.

비등방 필터링

다각형의 법선벡터와 카메라의 시건 벡터 사이의 각도가 클때 발생하는 왜곡 현상이 완화된다.

비용이 가장 비싼 필터링이다.

텍스처 좌표 지정 모드

순환 모드(wrap), 테두리 색상 모드(border color), 한정 모드(clamp), 반사 모드(mirror)

표본추출기 객체

셰이더에서 텍스처 표본 추출

나무 상자 예제

텍스처 변환

텍스처 입힌 언덕과 파도 예제

예제 연습

연습문제 1번 - 텍스처의 좌표지정 모드, 필터링 옵션

정점의 텍스처 좌표를 기존보다 크게하여 좌표지정 모드를 적용하였습니다.

image1 image2

연습문제 3번 - 다중 텍스처 적용

두개의 텍스처 성분을 곱하여 하나의 텍스처로 결합하였습니다.

LoadTextures() 함수 내부에 아래의 내용을 추가하여 텍스처를 로드하였습니다.

auto fireCrateTex = std::make_unique<Texture>();
fireCrateTex->Name = "fireCrateTex";
fireCrateTex->Filename = L"../../../Exercise Media/flarealpha.dds";
ThrowIfFailed(DirectX::CreateDDSTextureFromFile12(md3dDevice.Get(),
		mCommandList.Get(), fireCrateTex->Filename.c_str(),
		fireCrateTex->Resource, fireCrateTex->UploadHeap));

mTextures[fireCrateTex->Name] = std::move(fireCrateTex);

BuildDescriptorHeaps() 내부에 아래의 내용을 추가하여 텍스처를 서술자 힙에 추가하였습니다.

auto fireCreateTex = mTextures["fireCrateTex"]->Resource;

hDescriptor.Offset(1, mCbvSrvDescriptorSize);
srvDesc.Format = fireCreateTex->GetDesc().Format;
srvDesc.Texture2D.MipLevels = fireCreateTex->GetDesc().MipLevels;
md3dDevice->CreateShaderResourceView(fireCreateTex.Get(), &srvDesc, hDescriptor);

Default.hlsl파일의 내부에 Texture2D 변수를 추가하고 PS에서 두개의 텍스처를 결합하였습니다.

Texture2D    gDiffuseMap2 : register(t1);

float4 diffuseAlbedo = gDiffuseMap.Sample(gsamLinear, pin.TexC) * gDiffuseMap2.Sample(gsamLinear, pin.TexC) * gDiffuseAlbedo;

image3

연습문제 4번 - 화염구 회전하기

default.hlsl 파일 PS 내부에 아래의 내용을 추가하여 텍스처를 회전하였습니다.

float2 rotateTex;

pin.TexC.x -= 0.5f;
pin.TexC.y -= 0.5f;
rotateTex.x = pin.TexC.x * cos(gTotalTime) - pin.TexC.y * sin(gTotalTime);
rotateTex.y = pin.TexC.y * cos(gTotalTime) + pin.TexC.x * sin(gTotalTime);
rotateTex.x += 0.5f;
rotateTex.y += 0.5f;

float4 diffuseAlbedo = gDiffuseMap.Sample(gsamLinear, rotateTex) * gDiffuseMap2.Sample(gsamLinear, rotateTex) * gDiffuseAlbedo;

아래의 이미지는 화염구가 회전하는 이미지입니다. image4 image5