DirectX 11 3d

230516 Deferred Rendering - Light MRT

슬뷔 2023. 5. 17. 01:58

쉐이더 코드에서 inout을 사용한 이유

-> 입력한 값이 들어와서 추가적으로 무언갈 해주고, 반복문을 돌아서 여러번 누적처리를 할 것이기 때문이다.

이 픽셀에 적용되는 광원이 여러개가 겹쳐있을 수도 있다.

 

out만 사용하면, 처음 입력될 때 어떤값을 채워놔도 초기화된 값으로 밀려서 들어간다.

 

Light MRT

1. DiffuseTargetTex

난반사광

2. SpecularTargetTex

정반사광

깊이 텍스쳐 x

광원을 나중에 처리한다는 것은 어떤 의미일까 ?

Directional Light(전역광원)을 사용한다면, 

우리가 보고있는 화면에서 Directional Light을 받아야하는 부분은 화면 전체에 광원을 다 주면 된다..

물체가 어디에 있을지 모르기 때문에!

그런데 그렇게 되면 아무 것도 없는 허공에도(빛을 안받아야되는 부위) 빛이 들어오게 된다..

그럼 화면에 호출시켜야 할 픽셀이 화면 전체가 된다.

 

화면 전체 픽셀을 호출시키려면?

로컬에서 2배 확장해서 -1 ~ 1 사이에 있게 한다..

로컬스페이스상에서 만든 랙트메쉬가 -0.5 ~ 0.5 이기 때문에 2배 확장해서 레스터라이저에 넘긴다.

레스터라이저가 화면 전체영역을 네군데라고 생각하고 그 내부에 들어오는 전체 픽셀을 다 호출시킨다.

 

// ========================
// Directional Light Shader
// mesh : RectMesh
// g_int_0 : Light Index
// g_tex_0 : Position Target
// g_tex_1 : Normal Target
// ========================

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

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

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


struct PS_OUT
{
    float4 vDiffuse     : SV_Target0;
    float4 vSpecular    : SV_Target1;
};

PS_OUT PS_DirLightShader(VS_OUT _in)
{ 
    PS_OUT output = (PS_OUT) 0.f;
    
    // Position Target 
    float3 vViewPos = g_tex_0.Sample(g_sam_0, _in.vUV).xyz;
    
    if (!any(vViewPos.xyz))
        discard;
            
    // Normal Target
    float3 vViewNormal = g_tex_1.Sample(g_sam_0, _in.vUV).xyz;
    
    tLightColor lightcolor = (tLightColor) 0.f;
    CalcLight3D(vViewPos, vViewNormal, g_int_0, lightcolor);
        
    output.vDiffuse = lightcolor.vDiffuse + lightcolor.vAmbient;
    output.vSpecular = lightcolor.vSpecular;    
        
    output.vDiffuse.a = 1.f;
    output.vSpecular.a = 1.f;
    
    return output;
}

 

Forward Shading대비 Deferred Shading의 장점,단점

장점:
1. Light연산이 엄청나게 줄어든다. 그 이유는 Forward Shading에서는 한 pixel위치에 존재하는 여러개의 pixel값들에 Lighting연산을 하고
Output Merger Stage에서 depth test를 통해 한가지 pixel값만 선정해 back buffer에 그리게 되는 반면에,
Deferred Shading에서는 일단 First Pass의 Output Merger Stage에서 depth test를 통해 한 pixel위치에서 'Light 계산하게될 pixel값'을 선정하고 Second Pass에서 해당 pixel값에다가만 Light계산을 취해서 back buffer에 그리게 되기 때문이다. 한 픽셀에 여러개의 Object가 겹쳐있을 수록 Deferred Shading에서의 성능이득이 커진다고 할 수 있다.

단점:
1. Color Blending을 통한 투명한 Object의 렌더링이 불가능하다. 그 이유는 Forward Shading에서는 OM stage에서 한 pixel위치에서 겹치는 위치값을 가지는 pixel값들의 alpha값 비교(Color Blending)을 통해 투명한 Object렌더링이 가능하지만. Deferred Shading에서는 First Pass에서 한 pixel위치에 한가지 값으로 fix하기 위해서 다른 pixel값들을 모두 버리게 된다. 그렇기 때문에 Second Pass에서 투명한 Object를 표현할 수 없다.
2. 하드웨어적으로 지원하는 AA효과가 떨어진다. MSAA같은 anti-aliasing은 하드웨어 Rasterization단계에서 일어나게 된다.
Deferred Shading에서는 Frist Pass에서만 Rasterization이 일어난다(Second Pass는 GBuffer결과물을 사용하기만 하기 때문). 그리고, 이때 AA을 적용할 수는 있다.
하지만, Deferred Shading은 Light계산이 이루어지기 전에 AA을 적용하기 때문에 최종 결과물이 Forward Shading과 다르게 나오는 이슈가 있다. 실제 게임의 예를 들자면, 스타크래프트2가 이러한 이슈 때문에 Deferred Shading을 적용하면서 AA을 적용하지 않는다.
3. 메모리를 더 많이 먹는다. Forward Shading을 쓰면, 1개의 Back Buffer에 값을 쓰기만 하는데. Deferred Shading은 Back Buffer와 같은 해상도를 가지는 Diffuse Texture, Normal Texture, Specullar Texture등등을 다 저장해야한다.

 

2교시 다시 듣기