[DirectX11 3D] Deferred Rendering 2 - Deferred Shader, Merge

한창희·2024년 5월 17일

DirecX3D 엔진 만들기

목록 보기
8/14

📌Deferred Rendering 구조 만들기

enum class SHADER_DOMAIN
{
	// Deferred
	DOMAIN_DEFERRED,		// 지연 렌더링
	DOMAIN_DECAL,
	DOMAIN_LIGHTING,		// 광원 연산

	// Forward
	DOMAIN_OPAQUE,			// 불투명
	DOMAIN_MASKED,			// 불투명 or 투명
	DOMAIN_TRANSPARENT,		// 반투명
	DOMAIN_POSTPROCESS,		// 후처리

	DOMAIN_DEBUG,
};

Deferred 단계에서 그려질 물체를 분류할 목적으로 SHADER_DOMAIN에 deferred 도메인을 추가하고 카메라는 domain에 맞춰서 물체를 분류한다.

void CCamera::render()
{
	// 계산한 view 행렬과 proj 행렬을 전역변수에 담아둔다.
	g_Transform.matView = m_matView;
	g_Transform.matProj = m_matProj;

	// Domain 순서대로 렌더링
	CRenderMgr::GetInst()->GetMRT(MRT_TYPE::DEFERRED)->OMSet();
	render(m_vecDeferred);
	
	CRenderMgr::GetInst()->GetMRT(MRT_TYPE::SWAPCHAIN)->OMSet();
	render(m_vecOpaque);	
	render(m_vecMaked);
	render(m_vecTransparent);

	// 후처리 작업
	render_postprocess();
}

그리고 render 함수에서 deferred 물체를 그리기전에 MRT를 deferred MRT로 교체한 뒤 물체를 그리게한다.


📌Shader Code

#ifndef _STD_DEFERED
#define _STD_DEFERED

#include "value.fx"


// ======================
// Std3D_Deferred Shader
// MRT : Deferred MRT
#define ColorTexture        g_tex_0
#define NormalMap           g_tex_1
#define ColorTextureCheck   g_btex_0
#define NormalMapCheck      g_btex_1
// ======================


struct VS_IN
{
    float3 vPos : POSITION;
    float2 vUV : TEXCOORD;
    
    float3 vTangent : TANGENT;
    float3 vNormal : NORMAL;
    float3 vBinormal : BINORMAL;
};

struct VS_OUT
{
    float4 vPosition : SV_Position;
    float2 vUV : TEXCOORD;
    
    float3 vViewPos : POSITION;
    float3 vViewTangent : TANGENT;
    float3 vViewNormal : NORMAL;
    float3 vViewBinormal : BINORMAL;    
};

VS_OUT VS_Std3D_Deferred(VS_IN _in)
{
    VS_OUT output = (VS_OUT) 0.f;

    output.vPosition = mul(float4(_in.vPos, 1.f), g_matWVP);
    output.vUV = _in.vUV;
    
    output.vViewPos = mul(float4(_in.vPos, 1.f), g_matWV);    
    output.vViewTangent  = normalize(mul(float4(_in.vTangent, 0.f), g_matWV));
    output.vViewNormal   = normalize(mul(float4(_in.vNormal, 0.f), g_matWV));
    output.vViewBinormal = normalize(mul(float4(_in.vBinormal, 0.f), g_matWV));           
    
    return output;
}


struct PS_OUT
{
    float4 vColor : SV_Target0;
    float4 vPosition : SV_Target1;
    float4 vNormal : SV_Target2;
    float4 vData : SV_Target3;
};


PS_OUT PS_Std3D_Deferred(VS_OUT _in) : SV_Target
{
    PS_OUT output = (PS_OUT) 0.f;

    float4 vOutColor = float4(1.f, 0.f, 1.f, 1.f);    
    if (ColorTextureCheck)
    {
        vOutColor = ColorTexture.Sample(g_sam_0, _in.vUV);
    }
    
    output.vColor = vOutColor;    
    output.vPosition = float4(_in.vViewPos, 1.f);
        
    float3 vViewNormal = _in.vViewNormal;
    
    if (NormalMapCheck)
    {
        float3 vNormal = NormalMap.Sample(g_sam_0, _in.vUV).rgb;
        vNormal = vNormal * 2.f - 1.f;
        
        float3x3 matRot =
        {
            _in.vViewTangent,
            _in.vViewBinormal,
            _in.vViewNormal,
        };
        
        vViewNormal = normalize(mul(vNormal, matRot));
    }
        
    output.vNormal = float4(vViewNormal, 1.f);
    output.vData = float4(0.f, 0.f, 0.f, 1.f);
        
    return output;
}

#endif

vertex shader 단계에서는 물체에 대한 Position과 UV를 계산하여 넘겨주고, 라이팅 단계에서 필요한 view space에서의 정보를 같이 넘겨준다.

pixel shader단계에서는 넘겨 받은 값과 노멀 맵, color texture의 유무를 판단하여 각각의 정보를 계산하여 다른 렌더타겟에 나누어 담아준다.


📌Merge

enum class SHADER_DOMAIN
{
	// Deferred
	DOMAIN_DEFERRED,		// 지연 렌더링
	DOMAIN_DECAL,
	DOMAIN_LIGHTING,		// 광원 연산

	// Merge
	DOMAIN_MERGE,			// Deferred 정보를 SwapChain 타겟으로 이동

	// Forward
	DOMAIN_OPAQUE,			// 불투명
	DOMAIN_MASKED,			// 불투명 or 투명
	DOMAIN_TRANSPARENT,		// 반투명
	DOMAIN_POSTPROCESS,		// 후처리

	DOMAIN_DEBUG,
};

Deferred 물체가 모두 그려진 뒤 merge하기 위해 DOMAIN_MERGE를 추가한다.

void CCamera::render()
{
	// 계산한 view 행렬과 proj 행렬을 전역변수에 담아둔다.
	g_Transform.matView = m_matView;
	g_Transform.matProj = m_matProj;

	// Domain 순서대로 렌더링

	// Deferred 물체 렌더링
	CRenderMgr::GetInst()->GetMRT(MRT_TYPE::DEFERRED)->OMSet();
	render(m_vecDeferred);
	
	// Deferred 정보를 SwapChain 으로 병함
	CRenderMgr::GetInst()->GetMRT(MRT_TYPE::SWAPCHAIN)->OMSet();

	Ptr<CMesh>	   pRectMesh = CAssetMgr::GetInst()->FindAsset<CMesh>(L"RectMesh");
	Ptr<CMaterial> pMergeMtrl = CAssetMgr::GetInst()->FindAsset<CMaterial>(L"MergeMtrl");

	pMergeMtrl->SetTexParam(TEX_PARAM::TEX_0, CAssetMgr::GetInst()->FindAsset<CTexture>(L"NormalTargetTex"));
	pMergeMtrl->UpdateData();
	pRectMesh->render();

	// Foward 렌더링
	render(m_vecOpaque);
	render(m_vecMaked);
	render(m_vecTransparent);

	// 후처리 작업
	render_postprocess();
}

Camera Render에서 Deferred 물체 랜더링 이후 MRT를 스왚체인 MRT로 변경하여 Merge 단계를 수행하도록 한다.

#ifndef _MERGE
#define _MERGE

#include "value.fx"


// ===============
// Merge Shader
// MRT : SwapChain
// Mesh : RectMesh
#define ColorTargetTex      g_tex_0
#define ColorTargetCheck    g_btex_0
// ===============

struct VS_IN
{
    float3 vPos : POSITION;
    float2 vUV : TEXCOORD;
};

struct VS_OUT
{
    float4 vPosition : SV_Position;
    float2 vUV : TEXCOORD;
};

VS_OUT VS_Merge(VS_IN _in)
{
    VS_OUT output = (VS_OUT) 0.f;
    
    output.vPosition = float4(_in.vPos * 2.f, 1.f);
    output.vUV = _in.vUV;    
    
    return output;
}

float4 PS_Merge(VS_OUT _in) : SV_Target
{
    float4 vOutColor = (float4) 0.f;
    
    if (ColorTargetCheck)
    {
        vOutColor = ColorTargetTex.Sample(g_sam_0, _in.vUV);
    }
    
    return vOutColor;
}

#endif

Merge 단계에서는 화면에 출력되는 모든 픽셀당 한번씩만 계산되면 되므로 vertex shader에서는 로컬좌표를 2배로 늘려(로컬에서 rect매쉬의 크기가 0.5이기 때문에 NDC 좌표계랑 사이즈를 맞추기 위해 2배 해야함) 그대로 출력하고, pixel shader 단계에서는 deferred 단계에서 저장해놓은 여러가지 값들을 이용해 화면에 출력될 픽셀의 색을 결정한다. 우선은 Color를 그대로 출력하도록 했다.


💻결과

0개의 댓글