forward shader
결과물인 render target view를 제외한 중간 계산 결과들이 휘발성으로 없어짐
UI를 그릴 때 보통 사용
<=>
deferred shader
카메라의 결과물을 텍스쳐에 저장하여 해당 텍스쳐를 활용할 수 있음
=>
렌더링의 중간 결과값(depth, normal, color 등)들을 텍스쳐에 저장하여 활용
대부분의 물체를 그릴 때 사용
쉐이더 파일
struct PS_OUT
{
float4 position : SV_Target0;
float4 normal : SV_Target1;
float4 color : SV_Target2;
};
PS_OUT PS_Main(VS_OUT input)
{
...
output.position = float4(input.viewPos.xyz, 0.f);
output.normal = float4(viewNormal.xyz, 0.f);
output.color = color;
return output;
}
쉐이더에서 텍스쳐에 저장할 자료 구조를 정의
이제는 픽셀 쉐이더에서 광원을 계산하지 않음
광원 계산에 필요한 정보들을 output에 넣어 결과로 반환
RenderTargetGroup 클래스
swap chain에서 사용하던 RTV 버퍼와 쉐이더 정보 등의 텍스쳐들을 통합하여 관리하는 클래스
enum class RENDER_TARGET_GROUP_TYPE : uint8
{
SWAP_CHAIN,
G_BUFFER,
END,
};
해당 클래스에서 관리할 텍스쳐 타입들 정의
G_BUFFER: geometry buffer
struct RenderTarget
{
shared_ptr<Texture> target;
float clearColor[4];
};
redner target 데이터에는 데이터가 저장될 텍스쳐를 가르키는 포인터와
프레임마다 텍스쳐를 어떤 색상으로 초기화시킬지에 대한 색상 정보를 정의
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();
...
};
command queue에 있던 output merger의 render target을 설정하는 부분과
RTV를 초기화하단 부분을 render target group에서 하도록 정의
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));
...
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);
}
}
render target group을 만들 때 render target 들의 정보들을 가지고 descripotr heap table을 만든다.
swap chain과 depth stencil view 등에서 사용하는 텍스쳐를 texture 클래스로 통합
class Texture : public Object
{
...
virtual void Load(const wstring& path) override;
public:
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());
void CreateFromResource(ComPtr<ID3D12Resource> tex2D);
...
};
texture는 3가지 방법으로 만들어 지게 됨
생성시 함수 종류와 함수의 플래그를 통하여 DSV 단독, SRV, RTV + SRV 3가지 텍스쳐가 생성 된다.
engine 클래스 수정
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);
...
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);
}
engine에서 initialize 할 때 render target group을 만들어 준다.
deferred shader를 위한 텍스쳐들 또한 생성
shader 클래스 수정
enum class SHADER_TYPE : uint8
{
DEFERRED,
FORWARD,
};
struct ShaderInfo
{
SHADER_TYPE shaderType = SHADER_TYPE::FORWARD;
RASTERIZER_TYPE rasterizerType = RASTERIZER_TYPE::CULL_BACK;
DEPTH_STENCIL_TYPE depthStencilType = DEPTH_STENCIL_TYPE::LESS;
D3D12_PRIMITIVE_TOPOLOGY_TYPE topologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
};
UI와 일반적인 오브젝트를 그릴 때 쉐이더의 종류가 다르기 때문에 타입을 정의
ShaderInfo 데이터에 type을 추가
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;
}
...
DEVICE->CreateGraphicsPipelineState(&_pipelineDesc, IID_PPV_ARGS(&_pipelineState));
}
쉐이더를 초기화할 때 쉐이더 타입에 따라 pipe line을 shader 파일과 맞도록 구현
scene 클래스 수정
void Scene::Render()
{
...
int8 backIndex = GEngine->GetSwapChain()->GetBackBufferIndex();
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::SWAP_CHAIN)->ClearRenderTargetView(backIndex);
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::G_BUFFER)->ClearRenderTargetView();
for (auto& gameObject : _gameObjects)
{
...
gameObject->GetCamera()->SortGameObject();
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::G_BUFFER)->OMSetRenderTargets();
gameObject->GetCamera()->Render_Deferred();
GEngine->GetRTGroup(RENDER_TARGET_GROUP_TYPE::SWAP_CHAIN)->OMSetRenderTargets(1, backIndex);
gameObject->GetCamera()->Render_Forward();
}
}
render 시 swap chian과 rtv를 초기화 후
forward 쉐이더를 사용할 물체들과 deferred 쉐이더를 사용할 물체들을 정렬한다.
이 후 output merger가 사용할 render target을 설정 후 해당 물체들을 렌더한다.