사전 오클루전 컬링
개념

오클루전 컬링

gabury1고친 사람 github-actions[bot]

오클루전 컬링은 앞의 물체에 가려 화면에 안 나올 물체를 그리기 전에 빼는 일입니다. 큰 건물 뒤에 늘어선 거리, 벽 너머 옆방의 가구가 그 대상입니다. 어차피 안 보일 것을 계산하느라 버리는 시간을 아낍니다.

쉽고 빠른 이해

오클루전 컬링은 가려진 물체를 그릴 목록에서 빼는 일입니다. 게임 속 눈앞의 벽 너머에 방이 열 개 있어도 화면에는 벽만 나옵니다. 그 방 열 개를 목록에서 빼도 화면은 같습니다.

빼지 않으면 그래픽 카드가 방 열 개를 전부 계산합니다. 벽 뒤라는 것은 그 계산이 거의 끝난 뒤에야 드러납니다. 결과는 버려지고 한 화면을 그리는 시간만 길어집니다.

  1. 벽이나 건물처럼 크게 가리는 물체를 고릅니다
  2. 나머지 물체마다 그 뒤에 완전히 숨는지 따집니다
  3. 확실히 숨는 것만 빼고, 애매하면 남깁니다

숨는지 따지는 계산에도 시간이 듭니다. 트인 벌판처럼 가리는 것이 적은 장면에서는 아끼는 시간보다 따지는 시간이 더 길어집니다.

상세

연극 무대 안쪽에는 앞 세트에 가려 객석에서 안 보이는 뒷무대가 있습니다. 거기 놓인 소품은 칠하지 않습니다. 앞 세트가 가려 주니 어느 자리의 관객도 그 소품을 못 봅니다. 아낀 물감과 시간은 보이는 곳에 씁니다.

오클루전 컬링은 이 생각을 화면 그리기에 옮긴 것입니다. 이 절은 먼저 화면 한 장을 그리는 데 무엇이 드는지 봅니다. 그다음 가려졌는지 판정하는 큰 갈래 둘, 곧 미리 계산해 두는 갈래와 그리면서 정하는 갈래를 차례로 봅니다.

화면 한 장을 그리는 데 드는 일

게임 화면은 한 장을 그리고 끝나지 않습니다. 카메라가 움직일 때마다 새 장을 처음부터 다시 그립니다. 이 한 장을 프레임이라고 부릅니다. 보통 1초에 수십 장을 그립니다.

화면 속 물체는 작은 삼각형 여럿을 이어 붙여 만듭니다. 이 삼각형 묶음을 메시라고 합니다. 건물 하나에 삼각형이 수천 개 들기도 합니다.

삼각형을 화면에 칠하는 일은 GPU(Graphics Processing Unit, 그래픽 처리 장치), 곧 그래픽 카드가 합니다. 무엇을 그릴지 정해 GPU 에 넘기는 일은 CPU(Central Processing Unit, 중앙 처리 장치)에서 도는 게임 코드가 합니다.

CPU 가 GPU 에 물체 하나를 그려 달라고 넘기는 요청 한 번이 드로 콜입니다. 물체마다 한 번씩 가므로 그릴 물체가 늘면 드로 콜도 따라 늡니다.

그러면 양쪽이 다 바빠집니다. CPU 는 드로 콜을 준비하느라, GPU 는 삼각형을 칠하느라 시간을 씁니다. 가려진 물체도 목록에 들어 있는 한 이 비용을 똑같이 치릅니다.

컬링 가운데 오클루전 컬링

안 보일 것을 그리기 전에 빼는 일을 통틀어 컬링이라고 합니다. 무엇 때문에 안 보이는지에 따라 이름이 갈립니다.

이름 안 보이는 까닭
절두체 컬링 카메라가 보는 범위 밖에 있다
오클루전 컬링 범위 안에 있지만 앞의 물체에 가렸다
백페이스 컬링 삼각형이 카메라를 등졌다

셋 중 오클루전 컬링의 판정이 가장 까다롭습니다. 범위 밖인지는 물체 하나만 보면 압니다. 가렸는지는 다른 물체와의 관계라서 그 물체만 봐서는 모릅니다.

그래서 대개 절두체 컬링으로 먼저 줄인 뒤, 남은 물체만 오클루전 컬링으로 따집니다. 범위 밖 물체까지 가림을 따지면 판정 비용만 늘어납니다.

깊이 테스트만으로는 모자란 까닭

가려진 것이 화면에 안 나오게 막는 장치는 이미 따로 있습니다. 화면 칸마다 지금까지 칠한 것이 카메라에서 얼마나 먼지 적어 둡니다. 이 값을 깊이라고 합니다. 깊이를 칸마다 모아 둔 표가 깊이 버퍼입니다.

새로 칠할 점이 거기 적힌 깊이보다 멀면 버립니다. 이 검사가 깊이 테스트입니다. 덕분에 벽 뒤의 방은 화면에 한 점도 안 나옵니다.

걸리는 것은 검사하는 시점입니다. 깊이 테스트는 삼각형을 화면 칸으로 쪼개는 래스터화가 끝난 뒤에 돕니다. 벽 뒤의 방도 드로 콜을 받고, 삼각형 위치를 계산하고, 칸으로 쪼개진 다음에야 버려집니다.

flowchart TD
    A["장면 속 물체"] --> OC["오클루전 컬링 · 가려진 물체를 뺀다"]
    OC --> DC["드로 콜"]
    DC --> V["삼각형 위치 계산"]
    V --> R["래스터화"]
    R --> DT["깊이 테스트 · 가려진 점을 버린다"]
    DT --> P["화면"]

그림에서 오클루전 컬링은 맨 위에, 깊이 테스트는 맨 아래에 있습니다. 위에서 뺀 물체는 그 아래 네 단계를 전부 건너뜁니다.

깊이 테스트는 화면을 맞게 만들 뿐, 앞 단계에서 이미 쓴 시간은 못 돌려받습니다. 벽 뒤의 방이 벽보다 먼저 그려지면 손해가 더 큽니다. 방이 칸을 다 칠한 뒤에 벽이 같은 칸을 다시 칠합니다. 같은 칸을 여러 물체가 겹쳐 칠하는 이 낭비가 오버드로입니다.

오클루전 컬링은 드로 콜을 보내기 전에 벽 뒤의 방을 뺍니다. 그래서 앞 단계의 시간과 오버드로를 함께 줄입니다.

가리는 물체와 판정 대상

판정에는 두 역할이 있습니다. 앞에서 가리는 물체를 오클루더라고 부릅니다. 나머지는 그 뒤에 숨는지 따져 볼 판정 대상입니다.

오클루더로는 크고 단순한 물체를 고릅니다. 벽, 큰 건물, 언덕 같은 지형이 그렇습니다. 작은 물체는 가리는 넓이가 좁아서 뒤에 숨는 것이 거의 없습니다.

오클루더를 고르는 것도 비용입니다. 모든 물체를 오클루더로 쓰면 가림을 따지는 계산이 그리기만큼 무거워집니다. 그래서 장면을 만드는 사람이 오클루더를 따로 표시해 두거나, 크기로 몇 개만 고릅니다.

벽 두 개가 나란히 서서 함께 한 물체를 가리는 경우도 있습니다. 어느 한 벽만으로는 물체가 삐져나와 보입니다. 여러 오클루더를 합쳐서 판정하는 것을 오클루더 퓨전이라고 부릅니다.

헐거운 쪽으로 잡는 판정

오클루전 컬링은 물체의 삼각형을 하나하나 따지지 않습니다. 물체를 감싸는 직육면체를 하나 씌우고 그것만 따집니다. 이 상자를 바운딩 박스라고 합니다.

상자가 오클루더 뒤에 완전히 숨으면 안에 든 물체도 숨습니다. 상자가 조금이라도 삐져나오면 물체를 남깁니다. 물체 자체는 숨었는데 상자 모서리만 삐져나와 남는 경우도 생깁니다.

판정이 틀리는 방향은 둘입니다. 둘의 결과는 전혀 다릅니다.

판정 화면에 생기는 일
가려졌는데 남겼다 쓸데없이 그립니다. 화면은 맞게 나옵니다
보이는데 뺐다 물체가 사라집니다. 화면이 깨집니다

앞쪽 틀림은 시간만 잃고, 뒤쪽 틀림은 화면을 망칩니다. 그래서 오클루전 컬링은 확실히 숨을 때만 빼고 애매하면 남깁니다. 이렇게 한쪽으로만 틀리게 잡는 판정을 보수적 판정이라고 부릅니다.

미리 계산해 두는 갈래

첫째 갈래는 게임을 내놓기 전에 가림 관계를 계산해 둡니다. 장면을 작은 구역으로 나눕니다. 구역마다, 카메라가 그 안 어디에 서든 보일 수 있는 물체를 목록으로 적어 둡니다.

이 목록을 잠재 가시 집합(Potentially Visible Set, PVS)이라고 부릅니다. 「잠재」가 붙는 까닭은 구역 안 한 지점에서라도 보일 수 있으면 목록에 넣기 때문입니다. 보수적 판정이 목록을 만들 때부터 들어가 있는 셈입니다.

그릴 때는 카메라가 선 구역을 찾아 그 목록만 읽습니다. 그리는 도중에 가림을 따지는 계산이 없습니다. 목록을 찾아 읽는 비용만 남습니다.

대가는 셋입니다. 계산이 오래 걸려서 장면을 고칠 때마다 다시 돌려야 합니다. 구역마다 목록을 들고 있어야 해서 저장 공간이 듭니다. 미리 계산할 때 없던 모양, 곧 움직이는 문이나 부서지는 벽은 오클루더로 쓸 수 없습니다.

실내 장면에는 이 목록을 미리 계산하지 않아도 되는 기법이 있습니다. 방과 방은 문이나 창으로만 이어지므로 옆방은 그 구멍을 통해서만 보입니다. 이 구멍을 포털이라고 합니다.

카메라에서 포털을 거쳐 보이는 방만 그리는 기법이 포털 컬링입니다. 장면을 방과 포털로 나눠 두는 일만 미리 하고, 어느 방이 보이는지는 프레임마다 따집니다. 두 갈래에 한 발씩 걸친 기법입니다.

그리면서 정하는 갈래

둘째 갈래는 프레임마다 그때그때 판정합니다. 움직이는 물체도 오클루더로 쓸 수 있습니다. 대신 판정 비용을 프레임마다 치릅니다.

이 갈래의 기법은 판정 장치에 따라 셋입니다. GPU 에 묻는 기법, 깊이 버퍼를 줄여 쌓아 두고 견주는 기법, CPU 에서 직접 그려 보는 기법입니다. 아래 세 소절이 하나씩 봅니다.

셋 다 오클루더를 깊이 버퍼 하나에 그려 놓고 판정합니다. 나란히 선 벽 두 개도 한 깊이 버퍼에 함께 그려지므로, 두 벽을 합친 가림이 그대로 판정에 들어갑니다. 오클루더 퓨전이 따로 애쓰지 않아도 되는 셈입니다.

GPU 에 묻는 오클루전 쿼리

GPU 는 「방금 그린 것 가운데 깊이 테스트를 통과한 점이 몇 개인가」를 세어 달라는 요청을 받습니다. 이 요청을 오클루전 쿼리라고 부릅니다.

쓰는 순서는 이렇습니다. 오클루더를 먼저 그려 깊이 버퍼를 채웁니다. 그다음 판정 대상의 바운딩 박스를 그립니다. 이때 화면에는 아무것도 안 남기고 통과한 점의 개수만 셉니다. 개수가 0 이면 상자가 완전히 가려졌으니 물체를 뺍니다.

곤란한 것은 답이 늦게 온다는 점입니다. GPU 는 CPU 가 보낸 일을 줄 세워 두고 차례로 처리합니다. CPU 가 답을 받으려고 멈춰 서면 그동안 다음 일을 못 보내서 GPU 가 놀게 됩니다. 한쪽이 다른 쪽을 기다리느라 흐름이 끊기는 이 상태를 파이프라인 스톨이라고 부릅니다.

그래서 흔히 한 프레임 늦은 답을 씁니다. 이번 프레임에 물어 둔 것을 다음 프레임에 읽습니다. 그 답으로 뺄지 정합니다. 답이 오기 전인 이번 프레임에는 그 물체를 일단 그립니다.

sequenceDiagram
    participant C as CPU · 게임 코드
    participant G as GPU
    C->>G: 프레임 1 · 상자를 그려 통과한 점을 센다
    C->>G: 프레임 1 · 답이 없으니 물체도 그린다
    G-->>C: 프레임 1 의 답 · 통과한 점 0개
    Note over C: 프레임 2 · 이 물체를 뺀다

한 프레임 늦은 답은 보수적 판정을 깨뜨립니다. 모퉁이 뒤에서 방금 걸어 나온 물체는 지난 프레임에 가려져 있었으므로 이번 프레임에 빠집니다. 이렇게 물체가 한 프레임 늦게 불쑥 나타나는 현상을 팝핑이라고 부릅니다.

깊이 피라미드로 견주기

둘째 기법은 GPU 에 개수를 묻지 않습니다. 깊이 버퍼를 줄인 사본을 만들어 두고, 그 사본과 상자를 직접 견줍니다. 사본을 만드는 일도 견주는 일도 대개 GPU 가 합니다.

깊이 값은 0 이 가장 가깝고 1 이 가장 멉니다. 아무것도 안 그린 빈 칸은 1 입니다.

깊이 버퍼는 칸이 화면 픽셀 수만큼, 곧 수백만 개라서 하나씩 견주면 느립니다. 그래서 깊이 버퍼를 단계적으로 줄인 사본을 만들어 둡니다.

줄이는 규칙은 하나입니다. 가로 둘, 세로 둘로 붙은 네 칸을 한 칸으로 합칩니다. 합친 칸에는 넷 가운데 가장 먼 깊이를 남깁니다. 이것을 되풀이하면 칸 수가 한 층마다 4분의 1 로 줄어든 층이 쌓입니다.

flowchart TD
    subgraph L0["원래 깊이 버퍼 · 여덟 칸"]
        A1["0.2"]
        A2["0.3"]
        A3["0.4"]
        A4["0.3"]
        B1["0.1"]
        B2["0.2"]
        B3["1.0 · 빈 칸"]
        B4["0.2"]
    end
    subgraph L1["한 층 위 · 네 칸 중 가장 먼 값"]
        A["0.4"]
        B["1.0"]
    end
    A1 --> A
    A2 --> A
    A3 --> A
    A4 --> A
    B1 --> B
    B2 --> B
    B3 --> B
    B4 --> B

앞 네 칸은 오클루더로 전부 덮여 가장 먼 값 0.4 가 남았습니다. 뒤 네 칸에는 빈 칸이 하나 섞여 1.0 이 남았습니다. 이렇게 쌓은 층 묶음을 깊이 피라미드라고 부릅니다.

깊이 값은 Z 라고도 부릅니다. 화면의 가로를 x, 세로를 y 로 두면 화면 안쪽으로 들어가는 방향이 z 이기 때문입니다. 그래서 깊이 피라미드로 판정하는 기법은 계층적 Z 컬링이라고도 부릅니다.

판정할 때는 바운딩 박스가 화면에서 덮는 넓이에 맞는 층을 고릅니다. 상자가 크면 위층의 칸 하나로 넓은 범위를 한 번에 봅니다. 그 칸의 값과 상자에서 카메라에 가장 가까운 점의 깊이를 견줍니다.

상자의 가장 가까운 점이 칸의 가장 먼 값보다도 멀면, 상자는 그 범위 어디서든 오클루더 뒤에 있습니다. 가장 먼 값을 남긴 까닭이 이것입니다. 가장 먼 칸보다 뒤라면 나머지 칸보다도 뒤입니다.

아래는 그 비교 한 번입니다. far 는 피라미드 칸의 값이고, near 는 상자에서 카메라에 가장 가까운 점의 깊이입니다.

C
far = 0.4;           // 앞 네 칸을 합친 값

near = 0.7;          // 벽 뒤의 상자
hidden = near > far; // 1 · 뺀다

near = 0.3;          // 벽 앞의 상자
hidden = near > far; // 0 · 남긴다

빈 칸이 섞인 뒤 네 칸은 값이 1.0 입니다. 어떤 상자도 1.0 보다 멀 수는 없으므로 그 범위에서는 아무 물체도 안 뺍니다. 가림이 확실하지 않은 범위에서는 판정이 저절로 남기는 쪽으로 기웁니다.

피라미드를 만들 깊이 버퍼는 두 곳에서 얻습니다. 이번 프레임에 오클루더만 먼저 그려서 얻거나, 지난 프레임에 그린 깊이 버퍼를 빌려 씁니다. 빌려 쓰면 오클루전 쿼리처럼 한 프레임 늦은 판정이 되어 팝핑이 생길 수 있습니다.

지난 프레임 것을 빌려 쓸 수 있는 것은 이어진 두 프레임이 거의 같은 장면이기 때문입니다. 이 성질을 시간 일관성이라고 부릅니다.

CPU 에서 그려 보는 소프트웨어 래스터화

셋째 기법은 GPU 를 거치지 않습니다. CPU 가 작은 깊이 버퍼를 따로 두고, 거기에 오클루더를 직접 그립니다. GPU 가 하던 래스터화를 코드로 흉내 내는 것이라 소프트웨어 래스터화라고 부릅니다.

CPU 는 GPU 보다 칠하는 속도가 훨씬 느립니다. 그래서 화면보다 훨씬 작은 깊이 버퍼를 씁니다. 오클루더도 삼각형 수를 크게 줄인 단순한 모양으로 그립니다. 장면을 만드는 사람이 이 단순한 모양을 오클루더마다 따로 만들어 두기도 합니다.

판정은 앞의 깊이 피라미드와 같습니다. 다른 점은 이번 프레임의 오클루더를 CPU 가 직접 그린다는 것입니다. CPU 가 그리고 CPU 가 읽으므로 같은 프레임 안에서 답이 나옵니다. 지난 프레임 것을 빌리지도, GPU 의 답을 기다리지도 않아서 늦은 답 때문에 생기는 팝핑이 없습니다.

대가는 CPU 시간입니다. 드로 콜을 준비하느라 이미 바쁜 CPU 에 그리기 일을 하나 더 얹습니다.

네 기법 견주기

미리 계산하는 갈래의 기법 하나와 그리면서 정하는 갈래의 기법 셋을 합치면 넷입니다. 네 기법은 언제 판정하는지와 무엇을 대가로 치르는지에서 갈립니다. 아래 표는 앞의 소절들을 한 줄씩 줄인 것입니다.

기법 판정 시점 움직이는 오클루더 약점
잠재 가시 집합 게임을 내놓기 전 못 쓴다 계산 시간 · 저장 공간
오클루전 쿼리 그리는 도중 · GPU 쓴다 답이 늦어 팝핑
깊이 피라미드 그리는 도중 · 대개 GPU 쓴다 지난 프레임 깊이를 빌리면 팝핑
소프트웨어 래스터화 그리는 도중 · CPU 쓴다 CPU 시간

포털 컬링은 표에서 뺐습니다. 준비는 미리 하고 판정은 그리면서 해서 두 갈래에 걸쳐 있습니다. 또 방과 포털로 나뉘는 실내 장면에만 맞습니다.

한 게임이 둘 이상을 섞어 쓰기도 합니다. 벽처럼 안 움직이는 구조는 미리 계산해 두고, 움직이는 것은 그리면서 판정하는 식입니다.

손해로 돌아서는 장면

오클루전 컬링이 아끼는 양은 가려진 물체의 양을 따라갑니다. 도시 골목이나 건물 실내처럼 벽이 시야를 자주 막는 장면에서 가장 많이 아낍니다.

트인 벌판에서는 거꾸로입니다. 가리는 것이 거의 없어 빠지는 물체가 적습니다. 판정 비용만 남아서 끄는 편이 빠를 수 있습니다.

물체 하나를 그리는 비용이 작을 때도 손해가 납니다. 풀 한 포기를 그리는 비용과 그 풀이 가려졌는지 따지는 비용이 비슷하면 따질 까닭이 없습니다. 그래서 작은 물체는 판정 없이 그리거나, 여럿을 묶어 한 번에 판정합니다.

오클루전 컬링은 그릴 물체의 개수를 줄일 뿐, 물체 하나를 그리는 비용은 안 줄입니다. 멀리 있는 물체를 삼각형이 적은 모양으로 바꿔 그리는 일은 레벨 오브 디테일이 맡습니다.

관련 항목

오클루전 컬링이 속하는 상위 분류

컬링 · 가시성 판정 · 은면 제거 · 렌더링 최적화

오클루전 컬링과 나란히 거는 다른 컬링

절두체 컬링 · 백페이스 컬링 · 포털 컬링 · 디테일 컬링 · 작은 삼각형 컬링 · 클러스터 컬링

가려졌는지 판정하는 기법

잠재 가시 집합 · 오클루전 쿼리 · 조건부 렌더링 · 계층적 Z · 깊이 피라미드 · 소프트웨어 래스터화 · 오클루더 퓨전 · 오클루더

판정에 쓰는 깊이 정보와 기하 요소

깊이 버퍼 · 깊이 테스트 · 바운딩 볼륨 · 바운딩 박스 · 뷰 절두체 · 밉맵

판정 대상을 미리 갈라 두는 공간 분할 구조

공간 분할 · BSP 트리 · 옥트리 · BVH · 포털 · 씬 그래프

늦은 판정이 부르는 문제와 기대는 성질

팝핑 · 파이프라인 스톨 · 시간 일관성 · 지연 시간

오클루전 컬링이 줄이려는 낭비와 그 지표

오버드로 · 드로 콜 · 프레임 시간 · 필레이트 · GPU 바운드 · CPU 바운드

같은 목적으로 그릴 양을 줄이는 기법

레벨 오브 디테일 · 얼리 Z · Z 프리패스 · 인스턴싱 · 임포스터 · 디퍼드 셰이딩

오클루전 컬링이 쓰이는 상위 분야

그래픽스 · 실시간 렌더링 · 게임 개발 · 게임 엔진 · 렌더 파이프라인 · 그래픽스 파이프라인

다른 이름: occlusion culling