-
[DirectX11] TextureBuffer2024년 03월 12일
- 유니얼
-
작성자
-
2024.03.12.:23
728x90DirectX 11로 게임 엔진 아키텍처 만들기
DirectX11과 같은 고급 그래픽 API를 학습하며 게임 엔진 아키텍처의 구성 요소를 이해하는 것은 게임 개발자에게 필수적인 과정입니다. 이러한 과정을 통해, 개발자들은 복잡한 3D 환경을 생성, 조작, 렌더링하는 데 필요한 다양한 기술과 원리를 배울 수 있습니다. 특히, 효과적인 데이터 관리와 접근 방법을 이해하는 것은 성능 최적화와 높은 품질의 그래픽 출력을 달성하는 데 중요한 역할을 합니다. 이번 블로그 포스팅에서는 DirectX11에서 중요한 데이터 관리 메커니즘 중 하나인 TextureBuffer에 대해 알아보고자 합니다.
참고강의 링크:
TextureBuffer 소개
DirectX11에서 TextureBuffer는 GPU에서의 텍스처 데이터 관리와 접근을 용이하게 하는 중요한 리소스입니다. 이는 텍스처를 통한 복잡한 데이터의 저장과 접근을 가능하게 하며, 그래픽과 컴퓨트 셰이더에서 다양한 형태의 데이터를 활용할 수 있도록 지원합니다. TextureBuffer는 기본적으로 이미지 데이터를 저장하지만, 다양한 형태의 정보를 저장하고 활용하는 데 사용될 수 있습니다.
TextureBuffer의 특징과 활용
TextureBuffer는 픽셀 데이터를 다차원 배열로 저장하며, 각 픽셀은 여러 개의 컴포넌트로 구성됩니다. 이러한 구조는 이미지 데이터뿐만 아니라, 다양한 종류의 정보를 효율적으로 저장하고 접근하는 데 유리합니다. 예를 들어, 텍스처 버퍼는 렌더링에 필요한 색상 정보, 노멀 맵, 스페큘러 맵 등 다양한 종류의 텍스처 데이터를 저장할 수 있습니다.
또한, TextureBuffer는 셰이더에서 직접 접근할 수 있으며, GPU의 텍스처 샘플러를 통해 다양한 필터링 방법을 적용하여 데이터를 읽을 수 있습니다. 이는 그래픽 렌더링뿐만 아니라, GPU를 활용한 고급 수치 계산에도 사용될 수 있습니다.
예제 코드: 텍스처 버퍼의 생성과 사용
DirectX11에서 RawBuffer를 구현하기 위해서는 ID3D11Buffer 인터페이스를 사용하여 버퍼를 생성하고, 적절한 버퍼 설명자(D3D11_BUFFER_DESC)와 서브리소스 데이터(D3D11_SUBRESOURCE_DATA)를 설정해야 합니다. 다음은 간단한 RawBuffer 생성 예제 코드입니다:
// 입력 데이터를 저장하는 2D 텍스처 배열 Texture2DArray<float4> Input; // 출력 데이터를 저장할 2D 텍스처 배열로, 읽기와 쓰기가 가능합니다. RWTexture2DArray<float4> Output; // 컴퓨트 셰이더의 각 스레드가 실행할 함수를 정의합니다. // numthreads 어트리뷰트는 각 디스패치에서 스레드 그룹의 크기를 지정합니다. [numthreads(32, 32, 1)] void CS(uint3 id : SV_DispatchThreadID) // 스레드 식별자 { // 입력 텍스처에서 현재 스레드에 해당하는 픽셀의 색상을 로드합니다. float4 color = Input.Load(int4(id, 0)); // 아래의 세 가지 방법 중 하나를 선택하여 출력 텍스처에 색상 값을 쓸 수 있습니다. // 1. 입력 텍스처의 색상을 그대로 출력 텍스처에 저장합니다. //Output[id] = color; // 2. 입력 텍스처의 색상을 반전시켜 출력 텍스처에 저장합니다. Output[id] = 1.0f - color; // 3. 입력 텍스처의 색상의 평균 값을 계산하여, 출력 텍스처에 그레이스케일 색상으로 저장합니다. //Output[id] = float4((color.r + color.g + color.b) / 3.0f, (color.r + color.g + color.b) / 3.0f, (color.r + color.g + color.b) / 3.0f, color.a); } // 컴퓨트 셰이더를 사용하는 테크닉을 정의합니다. technique11 T0 { pass P0 { // 컴퓨트 셰이더를 제외하고는 버텍스 셰이더나 픽셀 셰이더를 사용하지 않으므로 NULL로 설정합니다. SetVertexShader(NULL); SetPixelShader(NULL); // 컴퓨트 셰이더를 컴파일하여 설정합니다. SetComputeShader(CompileShader(cs_5_0, CS())); } };
이 예제에서는 텍스처 버퍼를 선언하고, 픽셀 셰이더에서 이를 사용하여 텍스처 샘플링을 수행합니다. myTexture는 텍스처 데이터를 저장하는 Texture2D 버퍼이며, mySampler는 텍스처 샘플링 시 적용할 샘플러 상태를 정의합니다. PS 함수는 픽셀 셰이더로, 입력 UV 좌표를 기반으로 텍스처 버퍼에서 색상 값을 샘플링하고 이를 반환합니다.
TextureBuffer
TextureBuffer 클래스는 Direct3D 11을 사용하여 텍스처 처리 작업에 필요한 기본적인 구성요소를 제공합니다. TextureBuffer 클래스는 입력 텍스처를 받아 그것을 처리하고 결과를 출력 텍스처로 저장하는 과정을 담당합니다. 이 과정에는 셰이더 리소스 뷰(SRV)와 비순차적 액세스 뷰(UAV) 생성이 포함되며, 이는 GPU에서 텍스처 데이터를 읽고 쓰는 데 사용됩니다.
TextureBuffer.h
#pragma once class TextureBuffer { public: // 생성자: 원본 텍스처를 인자로 받습니다. TextureBuffer(ComPtr<ID3D11Texture2D> src); // 소멸자: 자원을 해제합니다. ~TextureBuffer(); public: // 버퍼를 생성하는 주 함수입니다. void CreateBuffer(); private: // 입력 텍스처를 설정합니다. void CreateInput(ComPtr<ID3D11Texture2D> src); // 입력 텍스처에 대한 셰이더 리소스 뷰(SRV)를 생성합니다. void CreateSRV(); // 출력 텍스처를 생성합니다. void CreateOutput(); // 출력 텍스처에 대한 비순차적 액세스 뷰(UAV)를 생성합니다. void CreateUAV(); // 처리 결과를 저장할 텍스처를 생성합니다. void CreateResult(); public: // 텍스처의 너비, 높이, 배열 크기를 반환하는 접근자 함수들입니다. uint32 GetWidth() { return _width; } uint32 GetHeight() { return _height; } uint32 GetArraySize() { return _arraySize; } // 출력 텍스처와 그에 대한 SRV를 반환하는 접근자 함수입니다. ComPtr<ID3D11Texture2D> GetOutput() { return (ID3D11Texture2D*)_output.Get(); } ComPtr<ID3D11ShaderResourceView> GetOutputSRV() { return _outputSRV; } public: // 입력 및 출력 텍스처에 대한 SRV와 UAV를 반환하는 접근자 함수입니다. ComPtr<ID3D11ShaderResourceView> GetSRV() { return _srv; } ComPtr<ID3D11UnorderedAccessView> GetUAV() { return _uav; } private: // 입력 및 출력 텍스처와 관련 자원을 저장하는 멤버 변수입니다. ComPtr<ID3D11Texture2D> _input; ComPtr<ID3D11ShaderResourceView> _srv; // 입력 텍스처의 SRV ComPtr<ID3D11Texture2D> _output; ComPtr<ID3D11UnorderedAccessView> _uav; // 출력 텍스처의 UAV private: // 텍스처의 너비, 높이, 배열 크기 및 포맷을 저장하는 멤버 변수입니다. uint32 _width = 0; uint32 _height = 0; uint32 _arraySize = 0; DXGI_FORMAT _format; ComPtr<ID3D11ShaderResourceView> _outputSRV; // 출력 텍스처의 SRV };
TextureBuffer.cpp
#include "pch.h" // 프리컴파일 헤더 #include "TextureBuffer.h" // TextureBuffer 클래스 정의 포함 // 생성자: 소스 텍스처를 받아서 입력 텍스처를 생성하고, 관련 버퍼를 모두 생성합니다. TextureBuffer::TextureBuffer(ComPtr<ID3D11Texture2D> src) { CreateInput(src); // 입력 텍스처 생성 CreateBuffer(); // 나머지 버퍼(SRV, 출력, UAV, 결과) 생성 } // 소멸자 TextureBuffer::~TextureBuffer() { // Direct3D 자원 해제는 ComPtr에 의해 자동으로 관리됩니다. } // 전체 버퍼 생성 작업을 수행합니다. void TextureBuffer::CreateBuffer() { CreateSRV(); // 입력 텍스처에 대한 SRV 생성 CreateOutput(); // 출력 텍스처 생성 CreateUAV(); // 출력 텍스처에 대한 UAV 생성 CreateResult(); // 결과 데이터를 저장할 텍스처 생성 (주로 읽기 전용) } // 입력 텍스처 생성 void TextureBuffer::CreateInput(ComPtr<ID3D11Texture2D> src) { D3D11_TEXTURE2D_DESC srcDesc; src->GetDesc(&srcDesc); // 소스 텍스처의 설명 가져오기 // 입력 텍스처의 기본 정보를 설정합니다. _width = srcDesc.Width; _height = srcDesc.Height; _arraySize = srcDesc.ArraySize; _format = srcDesc.Format; D3D11_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); // 메모리 초기화 desc.Width = _width; desc.Height = _height; desc.ArraySize = _arraySize; desc.Format = _format; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; // SRV로 사용됨을 명시 desc.MipLevels = 1; desc.SampleDesc.Count = 1; // 입력 텍스처 생성 CHECK(DEVICE->CreateTexture2D(&desc, NULL, _input.GetAddressOf())); // 소스 텍스처의 데이터를 입력 텍스처로 복사합니다. DC->CopyResource(_input.Get(), src.Get()); } // 입력 텍스처에 대한 SRV 생성 void TextureBuffer::CreateSRV() { D3D11_TEXTURE2D_DESC desc; _input->GetDesc(&desc); // 입력 텍스처의 설명 가져오기 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC)); // 메모리 초기화 srvDesc.Format = desc.Format; // 포맷 설정 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; // 텍스처 배열로 사용 srvDesc.Texture2DArray.MipLevels = 1; srvDesc.Texture2DArray.ArraySize = _arraySize; // SRV 생성 CHECK(DEVICE->CreateShaderResourceView(_input.Get(), &srvDesc, _srv.GetAddressOf())); } // 출력 텍스처 생성 void TextureBuffer::CreateOutput() { D3D11_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC)); // 메모리 초기화 desc.Width = _width; desc.Height = _height; desc.ArraySize = _arraySize; desc.Format = _format; desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; // UAV 및 SRV로 사용됨 desc.MipLevels = 1; desc.SampleDesc.Count = 1; // 출력 텍스처 생성 CHECK(DEVICE->CreateTexture2D(&desc, nullptr, _output.GetAddressOf())); } // 출력 텍스처에 대한 UAV 생성 void TextureBuffer::CreateUAV() { D3D11_TEXTURE2D_DESC desc; _output->GetDesc(&desc); // 출력 텍스처의 설명 가져오기 D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; ZeroMemory(&uavDesc, sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC)); // 메모리 초기화 uavDesc.Format = DXGI_FORMAT_UNKNOWN; // 포맷 자동 결정 uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; // 텍스처 배열로 사용 uavDesc.Texture2DArray.ArraySize = _arraySize; // UAV 생성 CHECK(DEVICE->CreateUnorderedAccessView(_output.Get(), &uavDesc, _uav.GetAddressOf())); } // 결과 데이터를 저장할 텍스처 생성 (주로 읽기 전용 SRV를 위해 사용됩니다) void TextureBuffer::CreateResult() { D3D11_TEXTURE2D_DESC desc; _output->GetDesc(&desc); // 출력 텍스처의 설명 가져오기 D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC)); // 메모리 초기화 srvDesc.Format = desc.Format; // 포맷 설정 srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; // 텍스처 배열로 사용 srvDesc.Texture2DArray.MipLevels = 1; srvDesc.Texture2DArray.ArraySize = _arraySize; // 결과용 SRV 생성 CHECK(DEVICE->CreateShaderResourceView(_output.Get(), &srvDesc, _outputSRV.GetAddressOf())); }
프로젝트 호출
TextureBufferDemo.h
#pragma once #include "IExecute.h" class TextureBufferDemo :public IExecute { public: void Init() override; void Update() override; void Render() override; private: shared_ptr<Shader> _shader; private: ComPtr<ID3D11ShaderResourceView> MakeComputeShaderTexture(); };
TextureBufferDemo.cpp
#include "pch.h" #include "TextureBufferDemo.h" #include "GeometryHelper.h" #include "Camera.h" #include "GameObject.h" #include "CameraScript.h" #include "MeshRenderer.h" #include "Mesh.h" #include "Material.h" #include "Model.h" #include "ModelRenderer.h" #include "ModelAnimator.h" #include "Mesh.h" #include "Transform.h" #include "VertexBuffer.h" #include "IndexBuffer.h" #include "Light.h" #include "TextureBuffer.h" void TextureBufferDemo::Init() { auto newSrv = MakeComputeShaderTexture(); _shader = make_shared<Shader>(L"23. RenderDemo.fx"); // Camera { auto camera = make_shared<GameObject>(); camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f }); camera->AddComponent(make_shared<Camera>()); camera->AddComponent(make_shared<CameraScript>()); CUR_SCENE->Add(camera); } // Light { auto light = make_shared<GameObject>(); light->AddComponent(make_shared<Light>()); LightDesc lightDesc; lightDesc.ambient = Vec4(0.4f); lightDesc.diffuse = Vec4(1.f); lightDesc.specular = Vec4(0.1f); lightDesc.direction = Vec3(1.f, 0.f, 1.f); light->GetLight()->SetLightDesc(lightDesc); CUR_SCENE->Add(light); } // Mesh // Material { shared_ptr<Material> material = make_shared<Material>(); material->SetShader(_shader); auto texture = make_shared<Texture>(); texture->SetSRV(newSrv); material->SetDiffuseMap(texture); MaterialDesc& desc = material->GetMaterialDesc(); desc.ambient = Vec4(1.f); desc.diffuse = Vec4(1.f); desc.specular = Vec4(1.f); RESOURCES->Add(L"Veigar", material); } for (int32 i = 0; i < 100; i++) { auto obj = make_shared<GameObject>(); obj->GetOrAddTransform()->SetLocalPosition(Vec3(rand() % 100, 0, rand() % 100)); obj->AddComponent(make_shared<MeshRenderer>()); { obj->GetMeshRenderer()->SetMaterial(RESOURCES->Get<Material>(L"Veigar")); } { auto mesh = RESOURCES->Get<Mesh>(L"Sphere"); obj->GetMeshRenderer()->SetMesh(mesh); obj->GetMeshRenderer()->SetPass(0); } CUR_SCENE->Add(obj); } //RENDER->Init(_shader); } void TextureBufferDemo::Update() { } void TextureBufferDemo::Render() { } Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> TextureBufferDemo::MakeComputeShaderTexture() { auto shader = make_shared<Shader>(L"26. TextureBufferDemo.fx"); auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg"); shared_ptr<TextureBuffer> textureBuffer = make_shared<TextureBuffer>(texture->GetTexture2D()); shader->GetSRV("Input")->SetResource(textureBuffer->GetSRV().Get()); shader->GetUAV("Output")->SetUnorderedAccessView(textureBuffer->GetUAV().Get()); uint32 width = textureBuffer->GetWidth(); uint32 height = textureBuffer->GetHeight(); uint32 arraySize = textureBuffer->GetArraySize(); uint32 x = max(1, (width + 31) / 32); uint32 y = max(1, (height + 31) / 32); shader->Dispatch(0, 0, x, y, arraySize); return textureBuffer->GetOutputSRV(); }
결론
TextureBuffer는 DirectX11에서 중요한 리소스 유형 중 하나로, 이미지 데이터뿐만 아니라 다양한 형태의 데이터를 효율적으로 저장하고 접근하는 데 활용됩니다. 텍스처 버퍼의 사용은 그래픽 렌더링의 품질을 향상시키는 동시에, GPU를 활용한 다양한 계산 작업에도 큰 이점을 제공합니다. TextureBuffer를 통해, 개발자는 GPU의 강력한 처리 능력을 최대한 활용하여 더욱 다채롭고 동적인 그래픽과 계산 애플리케이션을 개발할 수 있습니다.
반응형다음글이전글이전 글이 없습니다.댓글