사전 렌더 타깃
개념

렌더 타깃

gabury1고친 사람 github-actions[bot]

렌더 타깃은 그리기의 결과를 받아 두는 메모리입니다. 그리기를 내기 전에 어느 메모리에 받을지 걸어 둡니다. 화면으로 곧장 나가는 메모리일 때도 있습니다. 화면에 안 보이게 따로 마련해 둔 메모리일 때도 있습니다.

쉽고 빠른 이해

그리기의 결과를 받아 두는 메모리입니다. 거울에 비친 장면을 거울 표면에 붙이려면 그 장면을 화면이 아니라 따로 둔 메모리에 먼저 그립니다. 그 메모리가 렌더 타깃입니다.

그리는 쪽 코드는 결과가 어디로 가는지 몰라도 됩니다. 받는 곳만 바꿔 끼우면 같은 그리기가 화면으로도 가고 화면 밖에 따로 둔 메모리로도 갑니다. 이 갈아 끼움이 없으면 거울과 그림자를 만들 때마다 그리기 코드를 새로 짜야 합니다.

어떻게 도는가:

  1. 그림을 받을 메모리를 마련하고 가로세로 칸 수와 한 칸에 담을 값의 형식을 정합니다
  2. 그리기 명령을 내기 전에 그 메모리를 타깃으로 걸어 둡니다
  3. 그리기가 끝나면 걸어 둔 메모리에 결과가 남습니다. 다음 그리기가 그것을 읽어 씁니다

대가가 있습니다. 타깃 한 장이 화면 한 장만큼의 메모리를 먹습니다. 방금 그린 타깃을 곧바로 읽으려 하면 앞의 그리기가 끝날 때까지 기다리게 됩니다.

상세

화가는 붓을 들기 전에 어느 종이에 그릴지 먼저 고릅니다. 벽에 걸 큰 캔버스일 때도 있습니다. 나중에 큰 그림에 오려 붙일 작은 종이일 때도 있습니다. 붓질은 똑같고 받는 종이만 다릅니다.

그리기가 결과를 내려놓는 메모리

그래픽스 파이프라인은 3차원 장면을 화면 칸의 색으로 바꾸는 단계들입니다. 이 단계들이 마지막에 내놓는 값은 어딘가에 써넣어야 남습니다. 그 써넣을 곳으로 걸어 둔 메모리가 렌더 타깃입니다.

메모리의 모양은 화면과 같은 격자입니다. 가로세로 칸 수가 정해져 있습니다. 칸마다 값이 하나씩 들어갑니다.

칸에 든 숫자를 무엇으로 읽을지는 그 타깃에 정해 둔 형식이 정합니다. 붉은색 · 초록색 · 파란색을 각각 8비트씩 담기로 정했다면 한 칸이 24비트를 씁니다.

타깃을 거는 일은 그리기 명령과 따로 놉니다. 먼저 어느 메모리에 받을지 걸어 둡니다. 그다음 장면을 그리라고 명령합니다. 같은 그리기 명령이라도 앞에 걸어 둔 타깃이 다르면 결과가 다른 메모리에 남습니다.

둘을 가른 덕에 그리는 코드는 결과가 어디로 가는지 몰라도 됩니다. 받는 곳만 바꿔 끼우면 화면에 내보낼 그림과 나중에 재료로 쓸 그림을 같은 코드로 그립니다. 이 갈아 끼움이 없으면 그림자와 반사를 만들 때마다 그리기 코드를 따로 짜야 합니다.

flowchart TD
    C["같은 그리기 명령"]
    C --> S1["화면으로 나가는 메모리를 걸어 두면"]
    C --> T1["화면 밖 메모리를 걸어 두면"]
    S1 --> S2["눈에 보이는 그림"]
    T1 --> T2["다음 그리기가 읽을 재료"]

프레임버퍼와 어떻게 갈리나

프레임버퍼는 그리기 한 판이 같이 쓰는 메모리를 묶은 한 벌입니다. 색만 담는 것이 아니라 그리는 동안 판정에 쓰는 값까지 한 벌에 들어갑니다.

한 벌에 드는 것은 셋입니다. 색 버퍼는 칸마다 색을 담습니다. 깊이 버퍼는 칸마다 그 점이 카메라에서 얼마나 떨어져 있는지를 담습니다. 스텐실 버퍼는 칸마다 그리기를 통과시킬지 막을지 표시해 두는 값을 담습니다.

렌더 타깃은 그 한 벌 가운데 색을 받는 쪽을 가리키는 말입니다. 깊이 테스트가 견줘 보는 깊이 값은 화면에 보일 그림이 아닙니다. 어느 것이 앞을 막았는지 판정하는 데만 쓰는 값이라 보통 렌더 타깃이라고 부르지 않습니다.

flowchart TD
    RT["렌더 타깃이라 부르는 것"]
    subgraph FB["프레임버퍼 한 벌"]
        CB["색 버퍼 · 칸마다 색을 담는다"]
        DB["깊이 버퍼 · 판정에만 쓰는 값 · 화면에 안 보인다"]
        SB["스텐실 버퍼 · 통과시킬지 막을지 표시한다"]
        CB ~~~ DB
        DB ~~~ SB
    end
    RT --> CB

부르는 말은 그래픽 표준마다 갈립니다. OpenGL은 이 메모리를 프레임버퍼에 붙여 쓰는 것으로 보고 첨부라고 부릅니다. 이름이 달라도 가리키는 것은 같습니다.

화면 밖에 그리는 타깃

화면으로 이어진 타깃에 그리면 그림이 곧 눈에 보입니다. 화면과 이어지지 않은 메모리로 타깃을 바꿔 걸면, 같은 그리기가 눈에 안 보이는 곳에 그림을 남깁니다. 이렇게 그리는 것을 오프스크린 렌더링이라고 합니다.

화면 밖 타깃은 대개 텍스처로 잡습니다. 텍스처는 물체 표면에 붙이는 그림입니다. 그리는 동안 셰이더가 읽을 수 있다는 것이 텍스처로 잡는 까닭입니다. 셰이더는 그래픽 하드웨어가 그리기 도중에 돌리는 짧은 프로그램입니다.

그리기의 결과를 텍스처로 받아 두면 다음 그리기가 그것을 표면에 붙일 재료로 씁니다.

거울이 그 예입니다. 먼저 거울에서 본 장면을 텍스처에 그립니다. 그다음 눈에 보일 화면을 그릴 때 거울 표면에 그 텍스처를 붙입니다. 한 장면을 두 번 그리는 셈입니다. 첫 번째 그리기가 남긴 그림이 두 번째 그리기의 재료가 됩니다.

flowchart TD
    A["첫 번째 그리기 · 거울에서 본 장면"] --> B["렌더 타깃 · 텍스처"]
    B --> C["두 번째 그리기 · 거울 표면에 그 텍스처를 붙인다"]
    C --> D["렌더 타깃 · 화면으로 나가는 메모리"]

그림자도 같은 꼴입니다. 빛이 있는 쪽에서 장면을 한 번 그려 거리 값만 텍스처에 받아 둡니다. 눈에 보일 화면을 그릴 때 그 텍스처를 읽어, 어느 칸이 빛에 가려졌는지 가릅니다.

이때 거리 값은 깊이 버퍼에 들어가지 않습니다. 색을 담는 칸에 숫자로 적어 넣습니다. 그래서 이 그림자용 텍스처는 렌더 타깃이 맞습니다.

한 번에 여러 장을 거는 그리기

셰이더 가운데 화면 칸 하나마다 한 번씩 도는 것이 프래그먼트 셰이더입니다. 이 셰이더가 내놓는 값을 받을 타깃을 여러 장 걸어 두면, 한 번 도는 동안 값을 여러 장에 나눠 씁니다. 이것을 다중 렌더 타깃이라고 부릅니다.

이렇게 하면 같은 장면을 여러 번 안 그려도 됩니다. 색 · 표면이 향한 방향 · 표면의 거칠기를 따로 받고 싶다고 해 봅시다. 타깃 세 장을 걸면 그리기 한 번으로 셋이 다 채워집니다. 한 장씩 받으려면 같은 장면을 세 번 그려야 합니다.

flowchart TD
    subgraph ONE["타깃 한 장씩 · 같은 장면을 세 번 그린다"]
        D1["첫 번째 그리기"] --> A1["색"]
        D2["두 번째 그리기"] --> A2["표면이 향한 방향"]
        D3["세 번째 그리기"] --> A3["표면의 거칠기"]
    end
    subgraph MANY["타깃 세 장 · 한 번 그린다"]
        S["프래그먼트 셰이더 · 칸 하나를 계산한다"]
        S --> B1["색"]
        S --> B2["표면이 향한 방향"]
        S --> B3["표면의 거칠기"]
    end
    ONE ~~~ MANY

이렇게 받아 둔 여러 장을 나중에 한꺼번에 읽어 빛을 계산하는 방식이 지연 셰이딩입니다. 빛이 수백 개 있는 장면에서 물체마다 빛을 다시 계산하지 않으려고 씁니다.

타깃을 늘릴 때 치르는 비용

첫째는 메모리입니다. 가로 1920 · 세로 1080 짜리 타깃이면 칸이 200만 개쯤 됩니다. 칸마다 32비트를 쓰면 8메가바이트가 넘습니다. 타깃을 세 장 걸면 그만큼 곱절로 듭니다.

둘째는 메모리를 오가는 양입니다. 타깃이 늘면 칸마다 써넣는 값도 그만큼 늘어납니다. 화면을 가득 덮는 물체가 여럿이면 이 오감이 메모리 대역폭을 먼저 채웁니다.

셋째는 갈아 끼우는 비용입니다. 타깃을 바꿔 걸 때 그래픽 하드웨어는 앞의 그리기가 끝났는지 확인하고 내부 상태를 정리합니다. 한 장면에서 이 갈아 끼움을 수백 번 하면 그 정리가 쌓입니다. 그래서 같은 타깃에 그릴 것을 모아 한 번에 내는 식으로 짭니다.

방금 그린 타깃을 곧바로 읽는 것은 따로 조심할 대목입니다. 그리기는 명령을 낸다고 그 순간 끝나지 않습니다. 아직 그리는 중인 타깃을 읽으려 하면 그 그리기가 끝날 때까지 기다리게 됩니다. 그동안 그래픽 하드웨어는 다음 일을 못 합니다.

sequenceDiagram
    participant P as 그리기를 내는 쪽
    participant G as 그래픽 하드웨어
    P->>G: 이 타깃에 그려라
    P->>G: 그 타깃을 읽어라
    Note over P,G: 앞의 그리기가 끝날 때까지 기다린다
    G-->>P: 다 그려진 값

그래서 화면에 곧바로 그려도 되는 그림은 타깃을 따로 두지 않습니다. 결과를 다음 그리기의 재료로 다시 읽을 때만 화면 밖 타깃을 둡니다.

관련 항목

그리기 한 판이 같이 걸어 두는 메모리

프레임버퍼 · 색 버퍼 · 깊이 버퍼 · 스텐실 버퍼 · 누적 버퍼 · 프레임버퍼 객체

렌더 타깃에 값을 써넣는 처리 단계

그래픽스 파이프라인 · 래스터화 · 프래그먼트 셰이더 · 깊이 테스트 · 스텐실 테스트 · 블렌딩 · 출력 병합기

렌더 타깃으로 잡는 이미지 메모리

텍스처 · 깊이 텍스처 · 렌더버퍼 · 밉맵

화면 밖 타깃을 재료로 쓰는 기법

오프스크린 렌더링 · 지연 셰이딩 · 그림자 매핑 · 그림자 맵 · 환경 매핑 · 후처리

타깃 한 칸에 표본을 여럿 담는 방식

멀티샘플링 · 슈퍼샘플링 · 안티에일리어싱 · 리졸브

타깃 한 장의 크기와 한 칸의 형식을 정하는 값

해상도 · 픽셀 · 픽셀 포맷 · 색 깊이 · RGBA · 깊이 스텐실 형식

타깃을 늘릴 때 먼저 차는 한계

메모리 대역폭 · VRAM · 오버드로 · 필레이트

다 그린 타깃을 화면으로 내보내는 경로

스왑 체인 · 더블 버퍼링 · 백 버퍼 · 프런트 버퍼 · 버퍼 스왑 · 스캔아웃

렌더 타깃을 걸고 푸는 그래픽 표준

OpenGL · Vulkan · WebGL · Direct3D · Metal

렌더 타깃이 속하는 상위 분류와 장치

그래픽스 · 렌더링 · GPU · 셰이더

다른 이름: render target · 렌더 타겟