하고자하는 것은
현재는 그려지는 화면이 하나인데 현 화면을 그리기 전 필요한 데이터로 화면을 나눠 그려보자.
이걸 왜 해야하나 싶은데, Rendering중 나오는 변수를 별도로 저장/관리 할 수 있음을 보여주기 위함이다.
지금까지 최종 랜더링 방식(forward shader)을 살펴보자면
void CommandQueue::RenderBegin(const D3D12_VIEWPORT* vp, const D3D12_RECT* rect)
{
//...
// Specify the buffers we are going to render to.
D3D12_CPU_DESCRIPTOR_HANDLE backBufferView = _swapChain->GetBackRTV();
// 후면 버퍼를 비워주고
_cmdList->ClearRenderTargetView(backBufferView, Colors::Black, 0, nullptr);
D3D12_CPU_DESCRIPTOR_HANDLE depthStencilView = GEngine->GetDepthStencilBuffer()->GetDSVCpuHandle();
// OM(Output merger)단계에서 후면 버퍼에 최종결과물을 그려달라고 명령한다.
_cmdList->OMSetRenderTargets(1, &backBufferView, FALSE, &depthStencilView);
_cmdList->ClearDepthStencilView(depthStencilView, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
}
이 방식을 Forward Shading 이라 한다
여기서 OMSetRenderTargets
에서 RenderTarget이란 말이 나오는데 어디에 그릴지에 관한 정보이다.
기존의 예제에서는 후면버퍼에 그려주고 있었는데 꼭 후면 버퍼에만 그려야 하는것은 아니다.
RenderTarget은 결국 2차원 벡터이기에 2차원 벡터라면 어디든 그릴수 있다.
우선 RenderTarget에 대해 더 설명하기 전, 기존 Forward Shading의 문제에 대해 설명하려한다.
Forward Shading에서는 Vertex, Pixel Shading 연산 중 발생한 변수를 재 사용이 불가능하다
VS_OUT VS_Main(VS_IN input)
{
VS_OUT output = (VS_OUT)0;
output.pos = mul(float4(input.pos, 1.f), g_matWVP);
output.uv = input.uv;
// viewPos, viewNormal, viewTangent, viewBinormal 변수모두
// 엄청난 연산을 통해 나온 변수들인데 재사용이 불가능하다
output.viewPos = mul(float4(input.pos, 1.f), g_matWV).xyz;
output.viewNormal = normalize(mul(float4(input.normal, 0.f), g_matWV).xyz);
output.viewTangent = normalize(mul(float4(input.tangent, 0.f), g_matWV).xyz);
output.viewBinormal = normalize(cross(output.viewTangent, output.viewNormal));
return output;
}
특히 빛에 관한 연산은 자원의 손실이 엄청난데 재활용이 불가능하단 단점이 있다.
float4 PS_Main(VS_OUT input) : SV_Target
{
// ...
LightColor totalColor = (LightColor)0.f;
for (int i = 0; i < g_lightCount; ++i)
{
LightColor color = CalculateLightColor(i, viewNormal, input.viewPos);
totalColor.diffuse += color.diffuse;
totalColor.ambient += color.ambient;
totalColor.specular += color.specular;
}
// ...
이 단점을 보완하기 위해서 Deferred Rendering이 나타남
Deferred Rendering은 연산중 나온 변수를 저장할 수 있다.
기존 Forward Rendering의 경우 쉐이더(VS(Vertex), GS(Geometry), FS(Pixel))을 거치고 랜더타겟에 넘겨 그리게 된다.
Deferred Rendering은 쉐이더를 거친후(단, 빛정보는 제외) 쉐이더를 거친정보를 일단 Multi Render Target에 저장 후 이후 Final Render Target을 통해 그리게 되는데
이렇게 하는이유는 Render Target에 필요한 정보를 중간 저장하기 위해서이다.
일단, Deferred Rendering은 다음 장에서 학습하고 여기서는 Render Target을 여러개 구성하는 방법에 대해서 배운다.
Deferred Shader 먼저 만들어보자
// PS_Main에서 당장 출력될 정보를 주는게 아니라 내가 필요한 정보를 넘긴다가 핵심
struct PS_OUT
{
// Texture 타겟을 0, 1, 2 번을 받게한다.
float4 position : SV_Target0;
float4 normal : SV_Target1;
float4 color : SV_Target2;
};
PS_OUT PS_Main(VS_OUT input)
{
PS_OUT output = (PS_OUT)0;
float4 color = float4(1.f, 1.f, 1.f, 1.f);
if (g_tex_on_0)
color = g_tex_0.Sample(g_sam_0, input.uv);
float3 viewNormal = input.viewNormal;
if (g_tex_on_1)
{
// [0,255] 범위에서 [0,1]로 변환
float3 tangentSpaceNormal = g_tex_1.Sample(g_sam_0, input.uv).xyz;
// [0,1] 범위에서 [-1,1]로 변환
tangentSpaceNormal = (tangentSpaceNormal - 0.5f) * 2.f;
float3x3 matTBN = { input.viewTangent, input.viewBinormal, input.viewNormal };
viewNormal = normalize(mul(tangentSpaceNormal, matTBN));
}
// Texture 타겟에 데이터를 넣는다
output.position = float4(input.viewPos.xyz, 0.f);
output.normal = float4(viewNormal.xyz, 0.f);
output.color = color;
// 빛 연산은 모두 삭제함
return output;
}
PS_Main에서 RenderTarget을 묶어서 리턴했는데 그 RenderTarget을 관리하는 클래스가 필요
RenderTargetGroup
여러 랜더타겟을 뭉쳐서 사용하는 클래스를 생성
Texture가 이미지 자체도 Texture로 표현이 가능하나, Position, normal, depth stencil 등도 모두 Texture로 표현이 가능
따라서 기존에 관리하던 Swap Chain의 Texture 등 모두 RenderTargetGroup에 모은다
// RenderTargetGroup
enum class RENDER_TARGET_GROUP_TYPE : uint8
{
SWAP_CHAIN, // (스왑체인 정보) BACK_BUFFER, FRONT_BUFFER
G_BUFFER, // (위치(Geometry) 정보) POSITION, NORMAL, COLOR
END,
};
// ...
struct RenderTarget
{
shared_ptr<Texture> target;
float clearColor[4]; // 초기값 색상
};
class RenderTargetGroup
{
public:
void Create(RENDER_TARGET_GROUP_TYPE groupType, vector<RenderTarget>& rtVec, shared_ptr<Texture> dsTexture);
void OMSetRenderTargets(uint32 count, uint32 offset);
void OMSetRenderTargets();
void ClearRenderTargetView(uint32 index);
void ClearRenderTargetView();
shared_ptr<Texture> GetRTTexture(uint32 index) { return _rtVec[index].target; }
shared_ptr<Texture> GetDSTexture() { return _dsTexture; }
private:
RENDER_TARGET_GROUP_TYPE _groupType;
vector<RenderTarget> _rtVec; // 여러 RenderTarget을 관리
uint32 _rtCount;
shared_ptr<Texture> _dsTexture; // Depth Stencil은 별도로 관리
ComPtr<ID3D12DescriptorHeap> _rtvHeap;
private:
uint32 _rtvHeapSize;
D3D12_CPU_DESCRIPTOR_HANDLE _rtvHeapBegin;
D3D12_CPU_DESCRIPTOR_HANDLE _dsvHeapBegin;
};
// RenderTargetGroup
void RenderTargetGroup::Create(RENDER_TARGET_GROUP_TYPE groupType, vector<RenderTarget>& rtVec, shared_ptr<Texture> dsTexture)
{
_groupType = groupType;
_rtVec = rtVec;
_rtCount = static_cast<uint32>(rtVec.size());
_dsTexture = dsTexture;
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
heapDesc.NumDescriptors = _rtCount;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
heapDesc.NodeMask = 0;
DEVICE->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&_rtvHeap));
_rtvHeapSize = DEVICE->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
_rtvHeapBegin = _rtvHeap->GetCPUDescriptorHandleForHeapStart();
_dsvHeapBegin = _dsTexture->GetDSV()->GetCPUDescriptorHandleForHeapStart();
for (uint32 i = 0; i < _rtCount; i++)
{
uint32 destSize = 1;
D3D12_CPU_DESCRIPTOR_HANDLE destHandle = CD3DX12_CPU_DESCRIPTOR_HANDLE(_rtvHeapBegin, i * _rtvHeapSize);
uint32 srcSize = 1;
ComPtr<ID3D12DescriptorHeap> srcRtvHeapBegin = _rtVec[i].target->GetRTV();
D3D12_CPU_DESCRIPTOR_HANDLE srcHandle = srcRtvHeapBegin->GetCPUDescriptorHandleForHeapStart();
DEVICE->CopyDescriptors(1, &destHandle, &destSize, 1, &srcHandle, &srcSize, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
}
}
#pragma once
#include "Object.h"
class Texture : public Object
{
public:
Texture();
virtual ~Texture();
// 이제 이미지 파일을 불러서 Texture를 만들때만사용됨
virtual void Load(const wstring& path) override;
public:
// Texture를 처음부터 만들어야 하는 경우
void Create(DXGI_FORMAT format, uint32 width, uint32 height,
const D3D12_HEAP_PROPERTIES& heapProperty, D3D12_HEAP_FLAGS heapFlags,
D3D12_RESOURCE_FLAGS resFlags, Vec4 clearColor = Vec4());
// 이미 Resource는 있는데 Texture를 만들어야 하는경우(SwapChain)
void CreateFromResource(ComPtr<ID3D12Resource> tex2D);
public:
ComPtr<ID3D12Resource> GetTex2D() { return _tex2D; }
ComPtr<ID3D12DescriptorHeap> GetSRV() { return _srvHeap; }
ComPtr<ID3D12DescriptorHeap> GetRTV() { return _rtvHeap; }
ComPtr<ID3D12DescriptorHeap> GetDSV() { return _dsvHeap; }
D3D12_CPU_DESCRIPTOR_HANDLE GetSRVHandle() { return _srvHeapBegin; }
private:
ScratchImage _image;
ComPtr<ID3D12Resource> _tex2D;
// srv(shader resource view) : 그림출력용
// rtv(render target view)
// dsv(depth stencil view)
// 이제 texture(render target)이 단순 그림출력용이 아니라 여려 용도로 쓰이기에 할당을 어려개를 함.
ComPtr<ID3D12DescriptorHeap> _srvHeap;
ComPtr<ID3D12DescriptorHeap> _rtvHeap;
ComPtr<ID3D12DescriptorHeap> _dsvHeap;
private:
D3D12_CPU_DESCRIPTOR_HANDLE _srvHeapBegin = {};
};
void Texture::Create(DXGI_FORMAT format, uint32 width, uint32 height,
const D3D12_HEAP_PROPERTIES& heapProperty, D3D12_HEAP_FLAGS heapFlags,
D3D12_RESOURCE_FLAGS resFlags, Vec4 clearColor)
{
D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Tex2D(format, width, height);
desc.Flags = resFlags;
D3D12_CLEAR_VALUE optimizedClearValue = {};
D3D12_RESOURCE_STATES resourceStates = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON;
if (resFlags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
{
// Depth Sencil인지
resourceStates = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_DEPTH_WRITE;
optimizedClearValue = CD3DX12_CLEAR_VALUE(DXGI_FORMAT_D32_FLOAT, 1.0f, 0);
}
else if (resFlags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
{
// Render Target인지
resourceStates = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_RENDER_TARGET;
float arrFloat[4] = { clearColor.x, clearColor.y, clearColor.z, clearColor.w };
optimizedClearValue = CD3DX12_CLEAR_VALUE(format, arrFloat);
}
// Create Texture2D
HRESULT hr = DEVICE->CreateCommittedResource(
&heapProperty,
heapFlags,
&desc,
resourceStates,
&optimizedClearValue,
IID_PPV_ARGS(&_tex2D));
assert(SUCCEEDED(hr));
CreateFromResource(_tex2D);
}
void Texture::CreateFromResource(ComPtr<ID3D12Resource> tex2D)
{
_tex2D = tex2D;
D3D12_RESOURCE_DESC desc = tex2D->GetDesc();
// 주요 조합
// - DSV 단독 (조합X) -> Depth Stencil
// - SRV -> 일반적 이미지 Texture
// - RTV + SRV
if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
{
// DSV
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
heapDesc.NumDescriptors = 1;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
heapDesc.NodeMask = 0;
DEVICE->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&_dsvHeap));
D3D12_CPU_DESCRIPTOR_HANDLE hDSVHandle = _dsvHeap->GetCPUDescriptorHandleForHeapStart();
DEVICE->CreateDepthStencilView(_tex2D.Get(), nullptr, hDSVHandle);
}
else
{
if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
{
// RTV
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
heapDesc.NumDescriptors = 1;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
heapDesc.NodeMask = 0;
DEVICE->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&_rtvHeap));
D3D12_CPU_DESCRIPTOR_HANDLE rtvHeapBegin = _rtvHeap->GetCPUDescriptorHandleForHeapStart();
DEVICE->CreateRenderTargetView(_tex2D.Get(), nullptr, rtvHeapBegin);
}
// SRV
D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
srvHeapDesc.NumDescriptors = 1;
srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
DEVICE->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&_srvHeap));
_srvHeapBegin = _srvHeap->GetCPUDescriptorHandleForHeapStart();
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Format = _image.GetMetadata().format;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
DEVICE->CreateShaderResourceView(_tex2D.Get(), &srvDesc, _srvHeapBegin);
}
}
void Engine::CreateRenderTargetGroups()
{
// DepthStencil
shared_ptr<Texture> dsTexture = GET_SINGLE(Resources)->CreateTexture(L"DepthStencil",
DXGI_FORMAT_D32_FLOAT, _window.width, _window.height,
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
// SwapChain Group
{
vector<RenderTarget> rtVec(SWAP_CHAIN_BUFFER_COUNT);
for (uint32 i = 0; i < SWAP_CHAIN_BUFFER_COUNT; ++i)
{
wstring name = L"SwapChainTarget_" + std::to_wstring(i);
ComPtr<ID3D12Resource> resource;
_swapChain->GetSwapChain()->GetBuffer(i, IID_PPV_ARGS(&resource));
rtVec[i].target = GET_SINGLE(Resources)->CreateTextureFromResource(name, resource);
}
_rtGroups[static_cast<uint8>(RENDER_TARGET_GROUP_TYPE::SWAP_CHAIN)] = make_shared<RenderTargetGroup>();
_rtGroups[static_cast<uint8>(RENDER_TARGET_GROUP_TYPE::SWAP_CHAIN)]->Create(RENDER_TARGET_GROUP_TYPE::SWAP_CHAIN, rtVec, dsTexture);
}
// Deferred Group
{
vector<RenderTarget> rtVec(RENDER_TARGET_G_BUFFER_GROUP_MEMBER_COUNT);
rtVec[0].target = GET_SINGLE(Resources)->CreateTexture(L"PositionTarget",
DXGI_FORMAT_R32G32B32A32_FLOAT, _window.width, _window.height,
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
rtVec[1].target = GET_SINGLE(Resources)->CreateTexture(L"NormalTarget",
DXGI_FORMAT_R32G32B32A32_FLOAT, _window.width, _window.height,
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
rtVec[2].target = GET_SINGLE(Resources)->CreateTexture(L"DiffuseTarget",
DXGI_FORMAT_R8G8B8A8_UNORM, _window.width, _window.height,
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
_rtGroups[static_cast<uint8>(RENDER_TARGET_GROUP_TYPE::G_BUFFER)] = make_shared<RenderTargetGroup>();
_rtGroups[static_cast<uint8>(RENDER_TARGET_GROUP_TYPE::G_BUFFER)]->Create(RENDER_TARGET_GROUP_TYPE::G_BUFFER, rtVec, dsTexture);
}
}
쉐이더 작업
UI의 경우 기존과 동일하게 Forward로 작업
다른 오브젝트는 deferred로 작업
void Shader::Init(const wstring& path, ShaderInfo info)
{
// ...
switch (info.shaderType)
{
case SHADER_TYPE::DEFERRED:
_pipelineDesc.NumRenderTargets = RENDER_TARGET_G_BUFFER_GROUP_MEMBER_COUNT;
_pipelineDesc.RTVFormats[0] = DXGI_FORMAT_R32G32B32A32_FLOAT; // POSITION
_pipelineDesc.RTVFormats[1] = DXGI_FORMAT_R32G32B32A32_FLOAT; // NORMAL
_pipelineDesc.RTVFormats[2] = DXGI_FORMAT_R8G8B8A8_UNORM; // COLOR
break;
case SHADER_TYPE::FORWARD:
_pipelineDesc.NumRenderTargets = 1;
_pipelineDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
break;
}
// ...
void Scene::Render()
{
PushLightData();
// SwapChain Group 초기화
int8 backIndex = GEngine->GetSwapChain()->GetBackBufferIndex();
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::SWAP_CHAIN)->ClearRenderTargetView(backIndex);
// Deferred Group 초기화
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::G_BUFFER)->ClearRenderTargetView();
for (auto& gameObject : _gameObjects)
{
if (gameObject->GetCamera() == nullptr)
continue;
// 내가 그려야할 오브젝트 정리
gameObject->GetCamera()->SortGameObject();
// Deferred OMSet
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::G_BUFFER)->OMSetRenderTargets();
gameObject->GetCamera()->Render_Deferred();
/*
void Camera::Render_Deferred()
{
S_MatView = _matView;
S_MatProjection = _matProjection;
for (auto& gameObject : _vecDeferred)
{
gameObject->GetMeshRenderer()->Render();
}
}
*/
// Light OMSet
// Swapchain OMSet
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::SWAP_CHAIN)->OMSetRenderTargets(1, backIndex);
gameObject->GetCamera()->Render_Forward();
}
}
아직까지는 Deferred로 position, normal, color로 나눠둔 이점을 잘 모르겠는데 …
다음강에서 자세히 살펴보자.
헷갈릴수 있는 부분이
- UI에 어떻게 Render Target을 그리냐 하는 부분
- Shader에서 넘어온 Render Target을 어떻게 받냐 하는 부분
1. UI에 어떻게 Render Target을 그리냐 하는 부분
shared_ptr<Scene> SceneManager::LoadTestScene()
{
// ...
#pragma region UI_Test
// UI를 세 개 만들며
for (int32 i = 0; i < 3; i++)
{
shared_ptr<GameObject> sphere = make_shared<GameObject>();
sphere->SetLayerIndex(GET_SINGLE(SceneManager)->LayerNameToIndex(L"UI")); // UI
sphere->AddComponent(make_shared<Transform>());
sphere->GetTransform()->SetLocalScale(Vec3(100.f, 100.f, 100.f));
sphere->GetTransform()->SetLocalPosition(Vec3(-350.f + (i * 160), 250.f, 500.f));
shared_ptr<MeshRenderer> meshRenderer = make_shared<MeshRenderer>();
{
shared_ptr<Mesh> mesh = GET_SINGLE(Resources)->LoadRectangleMesh();
meshRenderer->SetMesh(mesh);
}
{
shared_ptr<Shader> shader = GET_SINGLE(Resources)->Get<Shader>(L"Forward");
// 각 UI는 GetRTTexture를 통해 Texture를 받아와 그린다.
shared_ptr<Texture> texture = GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::G_BUFFER)->GetRTTexture(i);
shared_ptr<Material> material = make_shared<Material>();
material->SetShader(shader);
material->SetTexture(0, texture);
meshRenderer->SetMaterial(material);
}
sphere->AddComponent(meshRenderer);
scene->AddGameObject(sphere);
}
#pragma endregion
// ...
class RenderTargetGroup
{
// ...
shared_ptr<Texture> GetRTTexture(uint32 index) { return _rtVec[index].target; }
// ...
vector<RenderTarget> _rtVec;
그럼 vector<RenderTarget> _rtVec;
에 Render Target을 어떻게 넣는지만 알면 2.는 자연스럽게 해결됨.
2. Shader에서 넘어온 Render Target을 어떻게 받냐 하는 부분
void RenderTargetGroup::Create(RENDER_TARGET_GROUP_TYPE groupType, vector<RenderTarget>& rtVec, shared_ptr<Texture> dsTexture)
{
_groupType = groupType;
// Create시에 vector<RenderTarget>& rtVec의 주소를 받음
_rtVec = rtVec;
// ...
void Engine::CreateRenderTargetGroups()
{
// ...
// Deferred Group
{
vector<RenderTarget> rtVec(RENDER_TARGET_G_BUFFER_GROUP_MEMBER_COUNT);
rtVec[0].target = GET_SINGLE(Resources)->CreateTexture(L"PositionTarget",
DXGI_FORMAT_R32G32B32A32_FLOAT, _window.width, _window.height,
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
rtVec[1].target = GET_SINGLE(Resources)->CreateTexture(L"NormalTarget",
DXGI_FORMAT_R32G32B32A32_FLOAT, _window.width, _window.height,
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
rtVec[2].target = GET_SINGLE(Resources)->CreateTexture(L"DiffuseTarget",
DXGI_FORMAT_R8G8B8A8_UNORM, _window.width, _window.height,
CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
_rtGroups[static_cast<uint8>(RENDER_TARGET_GROUP_TYPE::G_BUFFER)] = make_shared<RenderTargetGroup>();
_rtGroups[static_cast<uint8>(RENDER_TARGET_GROUP_TYPE::G_BUFFER)]->Create(RENDER_TARGET_GROUP_TYPE::G_BUFFER, rtVec, dsTexture);
}
}
shared_ptr<Texture> Resources::CreateTexture(const wstring& name, DXGI_FORMAT format, uint32 width, uint32 height,
const D3D12_HEAP_PROPERTIES& heapProperty, D3D12_HEAP_FLAGS heapFlags,
D3D12_RESOURCE_FLAGS resFlags, Vec4 clearColor)
{
shared_ptr<Texture> texture = make_shared<Texture>();
texture->Create(format, width, height, heapProperty, heapFlags, resFlags, clearColor);
Add(name, texture);
return texture;
}
이렇게 해서 어떻게 넣는다는 거지?
// deferred.fx
// ...
struct PS_OUT
{
float4 position : SV_Target0;
float4 normal : SV_Target1;
float4 color : SV_Target2;
};
PS_OUT PS_Main(VS_OUT input)
{
PS_OUT output = (PS_OUT)0;
float4 color = float4(1.f, 1.f, 1.f, 1.f);
if (g_tex_on_0)
color = g_tex_0.Sample(g_sam_0, input.uv);
float3 viewNormal = input.viewNormal;
if (g_tex_on_1)
{
// [0,255] 범위에서 [0,1]로 변환
float3 tangentSpaceNormal = g_tex_1.Sample(g_sam_0, input.uv).xyz;
// [0,1] 범위에서 [-1,1]로 변환
tangentSpaceNormal = (tangentSpaceNormal - 0.5f) * 2.f;
float3x3 matTBN = { input.viewTangent, input.viewBinormal, input.viewNormal };
viewNormal = normalize(mul(tangentSpaceNormal, matTBN));
}
// Render Target에 그릴 정보가 position, normal, color에 담기는데??
output.position = float4(input.viewPos.xyz, 0.f);
output.normal = float4(viewNormal.xyz, 0.f);
output.color = color;
return output;
}
// OMSetRenderTargets : 어디다 랜더타겟을 그릴것인지 정보인데
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::G_BUFFER)->OMSetRenderTargets();
void RenderTargetGroup::OMSetRenderTargets()
{
// _rtCount개수만큼 다중으로 그려달라
CMD_LIST->OMSetRenderTargets(_rtCount, &_rtvHeapBegin, TRUE/*다중*/, &_dsvHeapBegin);
// 여기까지하면
/*
// Render Target에 그릴 정보가 position, normal, color에 담기는데??
output.position = float4(input.viewPos.xyz, 0.f);
output.normal = float4(viewNormal.xyz, 0.f);
output.color = color;
// 에 랜더타겟이 담기게 된다.
*/
}
이제 그리는 부분이 남음
// RenderTargetGroup
void RenderTargetGroup::Create(RENDER_TARGET_GROUP_TYPE groupType, vector<RenderTarget>& rtVec, shared_ptr<Texture> dsTexture)
{
_groupType = groupType;
_rtVec = rtVec;
_rtCount = static_cast<uint32>(rtVec.size());
_dsTexture = dsTexture;
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
heapDesc.NumDescriptors = _rtCount;
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
heapDesc.NodeMask = 0;
DEVICE->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&_rtvHeap));
_rtvHeapSize = DEVICE->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
_rtvHeapBegin = _rtvHeap->GetCPUDescriptorHandleForHeapStart();
_dsvHeapBegin = _dsTexture->GetDSV()->GetCPUDescriptorHandleForHeapStart();
for (uint32 i = 0; i < _rtCount; i++)
{
uint32 destSize = 1;
D3D12_CPU_DESCRIPTOR_HANDLE destHandle = CD3DX12_CPU_DESCRIPTOR_HANDLE(_rtvHeapBegin, i * _rtvHeapSize);
uint32 srcSize = 1;
ComPtr<ID3D12DescriptorHeap> srcRtvHeapBegin = _rtVec[i].target->GetRTV();
D3D12_CPU_DESCRIPTOR_HANDLE srcHandle = srcRtvHeapBegin->GetCPUDescriptorHandleForHeapStart();
DEVICE->CopyDescriptors(1, &destHandle, &destSize, 1, &srcHandle, &srcSize, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
}
}