Company
교육 철학

텍스처(Texture) - DirectXTex lib

개요

Texture(텍스처)는 3D 오브젝트의 표면에 적용되는 2D 이미지 데이터입니다. DirectX 11에서 텍스처는 리소스로 취급되며, 렌더링 파이프라인에서 뷰(View)를 통해 접근합니다. 이 문서에서는 DirectXTex 라이브러리를 사용한 텍스처 로드와 관리 방법을 다룹니다.

Part 1: 텍스처의 개념과 역할

텍스처란?

정의
텍스처는 3D 모델의 표면에 입히는 2D 이미지로, 오브젝트에 색상, 패턴, 디테일을 부여합니다.
텍스처의 용도
Albedo/Diffuse: 기본 색상 정보
Normal Map: 표면의 미세한 굴곡 정보
Roughness/Metallic: 재질의 물리적 속성
Ambient Occlusion: 주변 폐색 정보
Emissive: 자체 발광 정보

DirectX의 텍스처 아키텍처

텍스처는 다른 자원과 마찬가지로 렌더링 파이프라인에서 [뷰]를 통해 액세스됩니다. [뷰]는 [텍스처 리소스]가 렌더링 파이프라인으로부터 어떠한 데이터로 보이게 할 것인가를 정의합니다.
텍스처 시스템의 3요소
1.
Texture Resource: 실제 이미지 데이터가 저장된 GPU 메모리
2.
View: 리소스에 접근하는 인터페이스
3.
Sampler: 텍스처 샘플링 방법을 정의
뷰(View)의 종류
Shader Resource View (SRV): 셰이더에서 읽기 전용으로 사용
Render Target View (RTV): 렌더링 결과를 저장하는 대상
Depth Stencil View (DSV): 깊이/스텐실 버퍼로 사용
Unordered Access View (UAV): 읽기/쓰기 모두 가능 (컴퓨트 셰이더)

Part 2: 텍스처 처리 과정

기본 처리 순서

텍스처를 다루는 기본적인 순서는 다음과 같습니다:
텍스처 리소스 생성 → 텍스처 이미지 로드 → 텍스처에 액세스하는 뷰 생성
단계별 설명
1단계: 이미지 파일 로드
디스크에서 이미지 파일 읽기 (PNG, JPG, BMP 등)
메모리에 임시 이미지 데이터 저장
DirectXTex 라이브러리 사용
2단계: 텍스처 리소스 생성
GPU 메모리에 텍스처 할당
ID3D11Texture2D 인터페이스 획득
이미지 데이터를 GPU로 전송
3단계: 뷰 생성
Shader Resource View (SRV) 생성
렌더링 파이프라인에서 접근 가능하도록 설정
셰이더 슬롯에 바인딩

DirectXTex 라이브러리

우리는 이미지 로드를 하기 위해서 DirectX에서 제공되는 로드 함수 대신에 DirectXTex라는 이미지 로드 라이브러리를 사용합니다. PNG, BMP, JPG 등등 다양한 이미지 확장자 로드를 제공해주는 라이브러리입니다.
DirectXTex의 장점
다양한 포맷 지원: PNG, JPG, BMP, TGA, DDS, HDR, EXR 등
자동 포맷 변환: 다양한 픽셀 포맷 간 변환
밉맵 생성: 자동 밉맵 체인 생성
압축 텍스처: BC1~BC7 압축 포맷 지원
이미지 처리: 리사이즈, 회전, 플립 등
DirectX 기본 함수 대비 이점

Part 3: Texture 클래스 구현

Texture 클래스 구조

#pragma once #include <DirectXTex.h> #include <DirectXTex.inl> #include <DirectXTexEXR.h> #include "yaResource.h" #include "yaGraphicDevice_DX11.h" namespace ya::graphics { class Texture : public Resource { public: Texture(); ~Texture(); virtual HRESULT Save(const std::wstring& path) override; virtual HRESULT Load(const std::wstring& path) override; void Bind(eShaderStage stage, UINT startSlot); //std::wstring private: ScratchImage mImage; D3D11_TEXTURE2D_DESC mDesc; Microsoft::WRL::ComPtr<ID3D11Texture2D> mTexture; Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> mSRV; Microsoft::WRL::ComPtr<ID3D11RenderTargetView> mRTV; }; }
C++
복사

클래스 멤버 상세 설명

ScratchImage mImage
DirectXTex의 이미지 컨테이너
파일에서 로드된 원본 이미지 데이터 보관
CPU 메모리에 저장되며, GPU 리소스 생성의 소스 역할
밉맵, 배열, 큐브맵 등 다양한 형태 지원
D3D11_TEXTURE2D_DESC mDesc
텍스처의 속성을 정의하는 구조체
크기, 포맷, 밉맵 레벨, 사용 용도 등 정보 포함
텍스처 생성 시 필요한 메타데이터
ID3D11Texture2D* mTexture
실제 GPU 메모리에 할당된 텍스처 리소스
2D 텍스처 인터페이스
ScratchImage의 데이터가 GPU로 전송되어 저장됨
직접 접근하지 않고 뷰를 통해 사용
ID3D11ShaderResourceView* mSRV
Shader Resource View (셰이더 리소스 뷰)
셰이더에서 텍스처를 읽기 전용으로 사용하기 위한 뷰
가장 일반적으로 사용되는 뷰 타입
픽셀 셰이더에서 텍스처 샘플링에 사용
ID3D11RenderTargetView* mRTV
Render Target View (렌더 타겟 뷰)
렌더링 결과를 저장하는 대상으로 사용하기 위한 뷰
오프스크린 렌더링, 포스트 프로세싱에 활용
동적으로 생성되는 텍스처에 사용

Resource 상속

class Texture : public Resource
C++
복사
Resource 클래스의 역할
리소스 매니저에 의한 중앙 집중식 관리
파일 경로, 이름, 타입 등 메타데이터 관리
Save/Load 인터페이스 제공
참조 카운팅 또는 공유 리소스 지원

Part 4: 텍스처 로드 과정

CreateShaderResourceView 함수

ScratchImage는 이미지를 로드해서 보관해두는 용도로 사용이 되고, Texture2D에 텍스처 리소스를 가져와서 보관해둘 것입니다. 그리고 마지막으로 ID3D11ShaderResourceView에 텍스처 리소스에 액세스할 수 있는 뷰를 생성할 것입니다.
HRESULT __cdecl CreateShaderResourceView( _In_ ID3D11Device* pDevice, _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _Outptr_ ID3D11ShaderResourceView** ppSRV) noexcept;
C++
복사
함수의 역할
CreateShaderResourceView는 셰이더 리소스 뷰를 생성함과 동시에 리소스도 같이 생성합니다. 이 함수 하나로 텍스처 리소스 생성과 뷰 생성을 한 번에 처리할 수 있습니다.
매개변수 설명
pDevice: DirectX 11 디바이스
srcImages: 소스 이미지 배열 (ScratchImage에서 가져옴)
nimages: 이미지 개수
metadata: 텍스처 메타데이터 (크기, 포맷 등)
ppSRV: 생성된 SRV 반환

텍스처 로드 구현 예시

HRESULT Texture::Load(const std::wstring& path) { // 1. 파일 확장자 확인 std::wstring ext = std::filesystem::path(path).extension(); HRESULT hr = S_OK; // 2. 확장자에 따라 적절한 로드 함수 호출 if (ext == L".dds" || ext == L".DDS") { hr = LoadFromDDSFile(path.c_str(), DDS_FLAGS_NONE, nullptr, mImage); } else if (ext == L".tga" || ext == L".TGA") { hr = LoadFromTGAFile(path.c_str(), nullptr, mImage); } else if (ext == L".hdr" || ext == L".HDR") { hr = LoadFromHDRFile(path.c_str(), nullptr, mImage); } else // PNG, JPG, BMP 등 { hr = LoadFromWICFile(path.c_str(), WIC_FLAGS_NONE, nullptr, mImage); } if (FAILED(hr)) return hr; // 3. Shader Resource View 생성 (동시에 Texture2D도 생성됨) hr = CreateShaderResourceView( GetDevice()->GetID3D11Device(), mImage.GetImages(), mImage.GetImageCount(), mImage.GetMetadata(), mSRV.GetAddressOf() ); if (FAILED(hr)) return hr; // 4. 생성된 텍스처 리소스 가져오기 mSRV->GetResource((ID3D11Resource**)mTexture.GetAddressOf()); return S_OK; }
C++
복사

리소스 가져오기

리소스 뷰에 할당된 리소스를 가져올 때는:
mSRV->GetResource((ID3D11Resource**)mTexture.GetAddressOf());
C++
복사
해당 리소스 뷰에서 GetResource 함수를 통해서 리소스를 가져올 수 있습니다.
중요한 점
GetResource는 참조 카운트를 증가시킴
ComPtr 사용 시 자동으로 Release 처리
수동 관리 시 반드시 Release() 호출 필요
리소스와 뷰의 관계

Part 5: 셰이더 바인딩

Bind 함수 구현

해당 리소스를 셰이더에서 사용하려면 파이프라인에 세팅해주어야 합니다.
void Texture::Bind(eShaderStage stage, UINT startSlot) { graphics::GetDevice()->SetShaderResource(stage, startSlot, mSRV.GetAddressOf()); }
C++
복사
매개변수 설명
stage: 어떤 셰이더 스테이지에 바인딩할지 (VS, PS, GS 등)
startSlot: 텍스처 레지스터 슬롯 번호 (t0, t1, t2 등)

셰이더 스테이지와 슬롯

Shader Stage (셰이더 단계)
enum class eShaderStage { VS, // Vertex Shader HS, // Hull Shader DS, // Domain Shader GS, // Geometry Shader PS, // Pixel Shader CS, // Compute Shader };
C++
복사
Texture Slot (텍스처 슬롯)
DirectX 11은 각 셰이더 스테이지당 최대 128개의 텍스처 슬롯 제공
슬롯 번호는 HLSL의 register(t#)와 대응
여러 텍스처를 동시에 사용 가능

SetShaderResource 내부 구현

void GraphicDevice_DX11::SetShaderResource(eShaderStage stage, UINT startSlot, ID3D11ShaderResourceView* const* srv) { switch (stage) { case eShaderStage::VS: mContext->VSSetShaderResources(startSlot, 1, srv); break; case eShaderStage::HS: mContext->HSSetShaderResources(startSlot, 1, srv); break; case eShaderStage::DS: mContext->DSSetShaderResources(startSlot, 1, srv); break; case eShaderStage::GS: mContext->GSSetShaderResources(startSlot, 1, srv); break; case eShaderStage::PS: mContext->PSSetShaderResources(startSlot, 1, srv); break; case eShaderStage::CS: mContext->CSSetShaderResources(startSlot, 1, srv); break; } }
C++
복사

Part 6: HLSL에서 텍스처 사용

셰이더 코드 예시

파이프라인 슬롯에 맞게 세팅해주었다면 HLSL에서 해당 레지스터 번호에 맞게 데이터가 세팅(바인딩)될 것입니다.

HLSL 코드 분석

Texture2D 선언
Texture2D: 2D 텍스처 타입
albedo: 변수 이름
register(t0): 텍스처 레지스터 0번 슬롯
C++ Bind 함수의 startSlot과 대응
SamplerState 선언
텍스처 샘플링 방법 정의
필터링, 어드레싱 모드 등 설정
샘플러도 별도의 레지스터 사용 (s0, s1, ...)
텍스처 샘플링
Sample(): 텍스처에서 색상 값 읽기
defaultSampler: 사용할 샘플러
input.uv: 텍스처 좌표 (0~1 범위)
반환값: RGBA 색상 (float4)

여러 텍스처 사용 예시

C++ 바인딩
albedoTexture->Bind(eShaderStage::PS, 0); // t0 normalTexture->Bind(eShaderStage::PS, 1); // t1 roughnessTexture->Bind(eShaderStage::PS, 2); // t2 metallicTexture->Bind(eShaderStage::PS, 3); // t3
C++
복사

Part 7: 리소스 관리

메모리 관리

텍스처 리소스는 기본적으로 Release 시켜줄 필요는 없습니다. (단, GetResource로 얻어온 인터페이스는 Release 시켜주어야 합니다.)
ComPtr 사용 시
Microsoft::WRL::ComPtr<ID3D11Texture2D> mTexture; Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> mSRV;
C++
복사
ComPtr이 자동으로 참조 카운트 관리
소멸자에서 자동으로 Release 호출
메모리 누수 방지
수동 관리 시 주의
ID3D11Texture2D* texture = nullptr; mSRV->GetResource((ID3D11Resource**)&texture); // 사용 후 반드시 Release if (texture) { texture->Release(); texture = nullptr; }
C++
복사

텍스처 언바인딩

사용 후 정리
void Texture::Clear(eShaderStage stage, UINT slot) { ID3D11ShaderResourceView* nullSRV = nullptr; graphics::GetDevice()->SetShaderResource(stage, slot, &nullSRV); }
C++
복사
언바인딩이 필요한 경우
텍스처를 Render Target으로 전환할 때
같은 텍스처를 다른 용도로 재사용할 때
리소스 충돌 방지

Part 8: 고급 텍스처 사용

Render Target Texture

추가적으로 렌더 타겟, CPU에서 사용할 수 있는 텍스처도 생성이 가능합니다. 이것은 추후에 사용해보도록 하겠습니다.
Render Target View 생성
HRESULT Texture::CreateRenderTargetView() { if (!mTexture) return E_FAIL; D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = mDesc.Format; rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; rtvDesc.Texture2D.MipSlice = 0; return GetDevice()->GetID3D11Device()->CreateRenderTargetView( mTexture.Get(), &rtvDesc, mRTV.GetAddressOf() ); }
C++
복사
Render Target 텍스처 사용 예시
오프스크린 렌더링
화면이 아닌 텍스처에 렌더링
반사, 그림자 맵, 미니맵 등에 활용
포스트 프로세싱
렌더링 결과를 텍스처에 저장
블룸, 블러, 색보정 등 후처리 효과
여러 패스를 거쳐 최종 결과 생성
동적 텍스처
런타임에 생성되는 텍스처
파티클 시스템, 데칼, UI 렌더링

CPU 액세스 가능 텍스처

Staging Texture
// CPU에서 읽을 수 있는 텍스처 생성 D3D11_TEXTURE2D_DESC stagingDesc = mDesc; stagingDesc.Usage = D3D11_USAGE_STAGING; stagingDesc.BindFlags = 0; stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; ID3D11Texture2D* stagingTexture = nullptr; GetDevice()->GetID3D11Device()->CreateTexture2D(&stagingDesc, nullptr, &stagingTexture); // GPU에서 CPU로 복사 GetDevice()->GetContext()->CopyResource(stagingTexture, mTexture.Get()); // CPU에서 데이터 읽기 D3D11_MAPPED_SUBRESOURCE mapped; GetDevice()->GetContext()->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mapped); // mapped.pData에서 픽셀 데이터 읽기 // ... GetDevice()->GetContext()->Unmap(stagingTexture, 0); stagingTexture->Release();
C++
복사
사용 사례
스크린샷 저장
텍스처 데이터 분석
CPU 기반 이미지 처리

Part 9: 최적화 및 모범 사례

텍스처 로딩 최적화

비동기 로딩
// 별도 스레드에서 텍스처 로드 std::future<HRESULT> loadFuture = std::async(std::launch::async, [this, path]() { return this->Load(path); }); // 메인 루프는 계속 진행 // 로딩 완료 시 결과 확인 if (loadFuture.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) { HRESULT hr = loadFuture.get(); // 로딩 완료 처리 }
C++
복사
텍스처 압축
DDS 포맷 사용 (BC1~BC7 압축)
메모리 사용량 1/4~1/8 감소
로딩 속도 향상
밉맵 생성
// 로드 시 밉맵 자동 생성 if (!mImage.GetMetadata().mipLevels > 1) { ScratchImage mipChain; GenerateMipMaps( mImage.GetImages(), mImage.GetImageCount(), mImage.GetMetadata(), TEX_FILTER_DEFAULT, 0, // 모든 레벨 생성 mipChain ); mImage = std::move(mipChain); }
C++
복사

텍스처 캐싱

리소스 매니저 활용
// 중복 로딩 방지 Texture* ResourceManager::LoadTexture(const std::wstring& path) { // 이미 로드된 텍스처 확인 auto it = mTextures.find(path); if (it != mTextures.end()) { return it->second; } // 새로 로드 Texture* texture = new Texture(); if (SUCCEEDED(texture->Load(path))) { mTextures[path] = texture; return texture; } delete texture; return nullptr; }
C++
복사

메모리 관리

텍스처 풀링
자주 사용하는 텍스처는 메모리에 유지
사용 빈도가 낮은 텍스처는 언로드
LOD(Level of Detail)에 따른 동적 로딩
메모리 예산 관리
// 텍스처 메모리 사용량 추적 size_t GetTextureMemoryUsage() { size_t total = 0; for (auto& tex : mTextures) { D3D11_TEXTURE2D_DESC desc; tex.second->GetTexture()->GetDesc(&desc); size_t size = desc.Width * desc.Height; size *= GetBytesPerPixel(desc.Format); size *= desc.MipLevels; total += size; } return total; }
C++
복사

결론

핵심 요약

Texture (텍스처)
3D 오브젝트 표면에 적용되는 2D 이미지
GPU 메모리에 저장되며 뷰를 통해 접근
다양한 용도로 활용 (Albedo, Normal, Render Target 등)
DirectXTex 라이브러리
다양한 이미지 포맷 지원
자동 밉맵 생성 및 포맷 변환
최신 DirectX 권장 방식
텍스처 시스템 구조
ScratchImage: CPU 메모리의 임시 이미지 데이터
Texture2D: GPU 메모리의 실제 텍스처 리소스
SRV: 셰이더에서 텍스처를 읽기 위한 뷰
RTV: 렌더링 결과를 저장하기 위한 뷰

실전 적용 가이드

텍스처 로딩
1.
DirectXTex로 파일에서 이미지 로드
2.
CreateShaderResourceView로 리소스와 뷰 생성
3.
Bind 함수로 셰이더 슬롯에 바인딩
4.
HLSL에서 register(t#)로 접근
최적화
1.
텍스처 압축 (DDS, BC 포맷)
2.
밉맵 생성으로 품질과 성능 확보
3.
리소스 매니저로 중복 로딩 방지
4.
비동기 로딩으로 프레임 드롭 최소화
고급 활용
1.
Render Target 텍스처로 오프스크린 렌더링
2.
포스트 프로세싱 효과 구현
3.
동적 텍스처 생성 (파티클, 데칼 등)
4.
CPU 액세스로 픽셀 데이터 분석
텍스처는 3D 그래픽의 핵심 요소이며, DirectXTex 라이브러리와 적절한 리소스 관리를 통해 효율적이고 강력한 텍스처 시스템을 구축할 수 있습니다. 이는 게임의 시각적 품질을 결정하는 가장 중요한 요소 중 하나입니다.