Two-Pass HZB Occlusion Culling

2026. 4. 10. 22:25·Graphics

Two-Pass HZB Occlusion Culling

Two-Pass HZB(Hierarchical Z-Buffer) Occlusion Culling은 가려진 지오메트리를 효율적으로 제거하기 위한 기법이다. 현재 Unreal Engine 5의 Nanite를 비롯해 다수의 최신 렌더링 파이프라인에서 표준적인 오클루전 컬링 방식으로 자리잡고 있다.

핵심 아이디어는 단순하다. "이전 프레임에 보였던 오브젝트는 이번 프레임에도 거의 대부분 보일 것"이라는 가정을 활용하여 1차 패스에서 신뢰할 수 있는 HZB를 먼저 만들고, 이 HZB로 2차 패스에서 나머지 오브젝트의 가시성을 재검증하는 구조이다.

 

기존 HZB Occlusion Culling (One-Pass) 방식의 한계

기존의 HZB 오클루전 컬링은 다음과 같이 동작한다.

  1. 현재 프레임의 Depth Buffer를 기반으로 mip chain HZB를 생성한다 (각 mip 레벨은 하위 레벨 2x2 텍셀의 max depth를 저장).
  2. 오브젝트의 AABB를 스크린 공간에 투영하여 픽셀 크기에 맞는 Mip level을 계산하여 해당 영역의 HZB 샘플을 조회한다.
  3. 오브젝트의 min Z가 HZB의  2x2 텍셀의 max Z보다 멀면 컬링되었다고 판정하여 드로우에서 제외한다.

위와 같은 방식으로 동작하는데 가장 큰 문제는 현재 프레임의 Depth Buffer를 구성할 때 현재 보여지는 객체를 판별할 방법이 없기에 현재 렌더를 해야하는 모든 객체를 렌더링을 해보는 수 밖에 없었다. 이는 고정 비용으로 가장 부하가 큰 영역이기에 개선이 필요하다.

 

최적화 방식 아이디어

Occluder를 사전에 지정하여 해당 오브젝트만 HZB를 구성

월드에 배치된 오브젝트 중 크기가 큰 오브젝트만 사전에 지정하여 해당 오브젝트만 HZB를 구성하여 모든 오브젝트를 HZB 테스트를 진행하고 통과된 오브젝트들을 기준으로 Depth Prepass를 실행한다. 이는 수동으로 Occluder를 명시적으로 태깅하는 작업을 해줘야하며 이는 유지보수 비용이 크고 씬마다 튜닝이 필요하다.

 

Depth Reprojection

현재 프레임의 Depth Buffer를 계산하기 위해 이전 프레임의 Depth Buffer를 Reprojection하는 방식도 널리 사용되었으나 재투영된 깊이는 근사값이므로 컬링이 보수적이지 않게 되어 갑자기 오브젝트가 나타나는 현상이 발생할 수 있다.

 

Two-Pass HZB Occlusion Culling의 동작 원리

HZB Test

오클루전 컬링을 GPU에서 수행하는 컴퓨트 셰이더이다. 씬의 모든 오브젝트 바운딩 박스에 대해 프러스텀 컬링 + HZB 테스트를 수행하고, 각 오브젝트의 가시성 여부를 1비트로 표현하여 VisBits 버퍼에 압축 저장한다.

sMask는 Group 전체가 공유하는 단 하나의 32비트 shared memory이다. 각 스레드가 자신의 가시성 비트를 이 마스크에 OR해서 병합한다.

#include "Common.fxh"
#include "BuildHZBStructures.fxh"
#include "HZBCull.fxh"

StructuredBuffer<HZBBound> HZBBounds;

// 32 objects per uint
RWStructuredBuffer<uint> VisBits;

cbuffer cbVisiblityHZBAttribs
{
    VisiblityHZBAttribs_CS g_cbVisiblityHZBAttribs;
};

groupshared uint sMask;

[numthreads(32, 1, 1)]
void HZBTestObjectsCS(uint3 GTid : SV_GroupThreadID, uint3 Gid : SV_GroupID)
{
    uint baseObj = Gid.x * 32;
    uint objId = baseObj + GTid.x;

    if (GTid.x == 0)
        sMask = 0;
        
    GroupMemoryBarrierWithGroupSync();

    bool bVisible = false;

    if (objId < g_cbVisiblityHZBAttribs.ObjectCount)
    {
        HZBBound Bound = HZBBounds[objId];

        FFrustumCullData Cull = BoxCullFrustumPerspective(Bound.Center.xyz, Bound.Extent.xyz, g_cbVisiblityHZBAttribs.ViewProj, g_cbVisiblityHZBAttribs.Proj);

        if (Cull.bIsVisible && !Cull.bCrossesNearPlane)
        {
            FScreenRect Rect = GetScreenRect(int4(0, 0, g_cbVisiblityHZBAttribs.ViewRect.xy), Cull.RectMin, Cull.RectMax, 0);
            Cull.bIsVisible = IsVisibleHZB(Rect, g_cbVisiblityHZBAttribs.HZBSize);
        }

        bVisible = Cull.bIsVisible;
    }

    if (bVisible)
    {
        InterlockedOr(sMask, 1u << GTid.x);
    }

    GroupMemoryBarrierWithGroupSync();

    if (GTid.x == 0)
    {
        VisBits[Gid.x] = sMask;
    }
}

 

HZB First Pass

이전 프레임에서 1픽셀이라도 그린 오브젝트를 기준으로 Depth Buffer를 구성한다. 이전에 안보였던 물체는 모두 그리지 않는다. 이후 HZB를 구성하여 현재 Scene에 포함된 모든 오브젝트들을 HZB Test를 진행한다.

 

HZB Second Pass

First Pass에서 이전 프레임에 보이지 않았지만 HZB Test에 통과한 객체 즉, 현재 프레임에 처음 Visible 판정이 이루어진 객체에 대해 Depth Prepass를 추가로 실행한다. 이 과정까지 끝나게 되면 현재 프레임에 그려져야할 객체들은 Depth Buffer에 기록이 완료된 상태이므로 최종 HZB를 재구성하고 Test를 진행하여 그려질 오브젝트를 선별한다.

 

Overdraw 비교

01
Depth PrePass ( One-Pass / Two-Pass )

 

적용 영상

 

저작자표시 비영리 변경금지 (새창열림)

'Graphics' 카테고리의 다른 글

Volumetric Ray Marching  (0) 2026.04.26
Deferred Decal Tangent Space Normal Blend  (1) 2026.04.13
Animation Compression  (0) 2025.04.22
Animation Retargeting  (0) 2025.04.16
Texture Block Compression  (0) 2025.04.15
'Graphics' 카테고리의 다른 글
  • Volumetric Ray Marching
  • Deferred Decal Tangent Space Normal Blend
  • Animation Compression
  • Animation Retargeting
KyuHwang
KyuHwang
  • KyuHwang
    DirectX Engine
    KyuHwang
  • 전체
    오늘
    어제
    • 분류 전체보기 (53)
      • C++ (4)
      • Graphics (35)
      • DLL (2)
      • Shader (7)
      • Project (4)
      • ETC (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • YouTube
  • 공지사항

  • 인기 글

  • 태그

    C ++
    dll
    DirectX 2D
    Shader Resource 관리
    std::enable_if
    Order Independent Transparency
    Implicit Linking
    texture block compression
    rigging chain
    shader
    animation constraint
    animation retargeting
    nvtt
    Return Type Operator
    mobile format
    Explicit Linking
    Win API
    Shader Reflection
    RunTime Compile Shader
    Alpha Blending
    bc format
    std::is_base_of
    hlsl
    Define Function
    Shader Macro
    Define Macro
    Project
    Hash Code
    DLL Interface
    Directx11
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
KyuHwang
Two-Pass HZB Occlusion Culling
상단으로

티스토리툴바