포워드 렌더링(Forward Rendering)과 디퍼드 렌더링(Deferred Rendering) 비교 및 아키텍처 분석

게임 그래픽 엔진을 설계하거나 최적화할 때 가장 중요한 아키텍처 선택 중 하나는 바로 어떤 렌더링 방식(Rendering Path)을 사용할 것인가입니다. 전통적인 방식인 포워드 렌더링부터 다수의 광원을 효율적으로 처리하는 디퍼드 렌더링까지, 각 방식의 내부 동작 원리와 소스 코드 레벨에서의 차이를 이해하는 것은 그래픽스 최적화의 기본입니다.

이 글에서는 포워드 렌더링과 디퍼드 렌더링의 개념을 명확히 정의하고, 픽셀 셰이더와 프레임 버퍼 관리 관점에서 두 방식의 장단점을 비교해 보겠습니다.

1. 포워드 렌더링 (Forward Rendering)이란?

포워드 렌더링은 가장 직관적이고 전통적인 렌더링 방식입니다. 화면에 보이는 각 오브젝트를 개별적으로 렌더링하면서, 그 순간에 영향을 주는 광원(Light)들의 계산을 직접 수행하여 최종 색상을 결정합니다.

유니티(Unity)나 언리얼(Unreal) 등의 엔진에서 기본적으로 씬에 배치된 오브젝트들은 각 메쉬를 그릴 때마다 셰이더 내부에서 광원 계산을 거칩니다.

High-level shader language

// 포워드 렌더링 방식의 기본 셰이더 구조 (HLSL)
struct v2f {
    float4 pos : SV_POSITION;
    float3 normal : TEXCOORD0;
    float3 worldPos : TEXCOORD1;
};

float4 frag(v2f i) : SV_Target {
    // 오브젝트 픽셀마다 씬 내의 모든 활성 광원과의 거리 및 각도를 직접 계산
    float3 finalColor = float3(0, 0, 0);
    for(int lightIdx = 0; lightIdx < NUM_LIGHTS; ++lightIdx) {
        finalColor += CalculateLighting(i.worldPos, i.normal, _Lights[lightIdx]);
    }
    return float4(finalColor, 1.0);
}

포워드 렌더링의 특징

  • 장점: 투명(Transparent) 오브젝트 처리가 직관적이며, MSAA(Multi-Sample Anti-Aliasing) 같은 하드웨어 안티앨리어싱을 쉽게 적용할 수 있습니다.
  • 단점: 만약 화면 안에 수십 개의 광원이 존재한다면, 픽셀 셰이더가 모든 광원에 대해 반복 연산을 수행해야 하므로 (오브젝트 수 $\times$ 광원 수)에 비례하여 연산량이 폭발적으로 증가합니다.

2. 디퍼드 렌더링 (Deferred Rendering)이란?

디퍼드 렌더링(지연 렌더링)은 라이팅 연산을 화면에 바로 수행하지 않고 뒤로(Deferred) 미루는 방식입니다.

화면에 그려질 기하학적 정보(위치, 법선, 알베도 색상 등)를 먼저 렌더 타겟에 기록한 뒤, 마지막 단계에서 화면 전체를 덮는 풀스크린 쿼드(Full-screen Quad)를 그리며 한꺼번에 라이팅 연산을 수행합니다. 이 중간 단계의 버퍼들을 통칭하여 지오메트리 버퍼(G-Buffer)라고 부릅니다.

C++

// 디퍼드 렌더링의 1단계: 지오메트리 패스 (G-Buffer에 데이터 기록)
struct GBufferOutput {
    float4 albedo : COLOR0;     // 물체의 기본 색상
    float3 normal : COLOR1;     // 법선 벡터
    float3 position : COLOR2;   // 월드 좌표
};

GBufferOutput geomFrag(v2f i) {
    GBufferOutput o;
    o.albedo = tex2D(_MainTex, i.uv);
    o.normal = normalize(i.normal);
    o.position = i.worldPos;
    return o;
}

G-Buffer 이후의 라이팅 패스

G-Buffer에 기록된 픽셀 데이터 정보를 바탕으로, 실제 라이팅은 화면 공간(Screen Space)에서 일괄 처리됩니다. 이로 인해 씬에 수백 개의 광원이 있더라도 실제 화면 픽셀에 영향을 주는 광원만 연산하므로 부하를 대폭 줄일 수 있습니다.

High-level shader language

// 디퍼드 렌더링의 2단계: 라이팅 패스 (G-Buffer를 읽어와 광원 계산)
Texture2D _GBufferAlbedo;
Texture2D _GBufferNormal;
Texture2D _GBufferPosition;

float4 lightingFrag(V2F_FullQuad i) : SV_Target {
    // 화면 픽셀 좌표에 해당하는 G-Buffer 정보를 샘플링
    float3 albedo = _GBufferAlbedo.Load(int3(i.pos.xy, 0)).rgb;
    float3 normal = _GBufferNormal.Load(int3(i.pos.xy, 0)).rgb;
    float3 pos = _GBufferPosition.Load(int3(i.pos.xy, 0)).rgb;

    // 광원 계산을 단 한 번의 화면 공간 연산으로 처리
    return float4(CalculateScreenSpaceLight(pos, normal, albedo), 1.0);
}

3. 포워드 vs 디퍼드 핵심 비교

구분포워드 렌더링 (Forward)디퍼드 렌더링 (Deferred)
광원 처리 능력광원 수가 늘어나면 성능 급감 ($O(N \times M)$)다수의 광원이 있어도 성능 저하가 적음 ($O(N)$)
메모리 대역폭낮음 (즉시 프레임 버퍼 기록)높음 (여러 장의 G-Buffer 텍스처 생성 필요)
투명(Transparent) 오브젝트자연스럽고 완벽한 처리 가능처리가 까다로워 별도의 포워드 패스 혼용 필요
안티앨리어싱 (MSAA)하드웨어 MSAA 지원 용이G-Buffer 구조상 하드웨어 MSAA 직접 적용 어려움 (후처리기법 사용)

요약 및 결론

포워드 렌더링은 단순한 구조와 투명 오브젝트 처리에 유리한 반면, 디퍼드 렌더링은 다수의 동적 광원이 난무하는 고사양 3D 게임 환경에서 압도적인 효율을 자랑합니다.

프로젝트를 기획할 때는 모바일 환경이나 단순한 캐주얼 게임이라면 가벼운 포워드 렌더링을, PC 및 콘솔 기반의 복잡한 실시간 라이팅 환경이라면 디퍼드 렌더링 구조를 채택하는 것이 기술적 최적화의 핵심입니다.

댓글 남기기