색 버퍼
고친 사람 github-actions[bot]
색 버퍼는 화면에 내보낼 색을 칸마다 모아 둡니다. 그리는 쪽은 칠할 것이 정해질 때마다 그 칸의 색을 고쳐 씁니다. 한 장을 다 그리고 나면 이 메모리의 내용이 그대로 화면으로 나갑니다.
쉽고 빠른 이해
색 버퍼는 화면 칸마다 "이 점에 지금 어떤 색이 있나"를 담아 둔 색판입니다. 가로 1920 · 세로 1080 짜리 화면이면 색이 200만 개쯤 들어 있습니다.
이게 없으면 계산한 색을 둘 곳이 없습니다. 화면 한 장은 물체를 여러 개 겹쳐 그려서 만들어집니다. 그동안의 중간 결과가 어딘가에 쌓여 있어야 마지막 한 장이 나옵니다.
- 한 장을 그리기 전에 모든 칸을 배경색으로 채웁니다
- 칠하겠다는 것이 검사를 통과할 때마다 그 칸의 색을 고쳐 씁니다. 이미 있던 색과 섞기도 합니다
- 다 그리면 판을 화면 쪽으로 넘깁니다. 다음 장은 다른 판에 그립니다
대가는 메모리와 오가는 값입니다. 화면 크기만 한 판을 두 장 넘게 들고 있어야 합니다. 칠할 것이 올 때마다 쓰기가 붙습니다. 가려질 것까지 칠한 색은 그대로 버려집니다.
상세
이 절은 색 버퍼가 어떤 모양의 메모리인지, 한 장을 그리는 동안 그 안의 색이 어떻게 바뀌어 화면까지 가는지를 봅니다.
화면 한 장은 물체를 여러 개 겹쳐 그려서 만들어집니다. 화면 자신은 앞서 칠한 것을 기억하지 않으므로, 다 그릴 때까지 중간 결과를 받아 둘 메모리가 따로 있어야 합니다. 색 버퍼가 그 메모리입니다.
벽에 붙인 타일판
흰 타일을 벽 가득 붙여 둔 판을 떠올려 보겠습니다. 칸마다 색을 칠할 수 있습니다. 한 칸에 남는 색은 언제나 하나입니다.
여러 사람이 각자 맡은 그림을 이 판에 칠합니다. 나중에 칠하는 사람은 앞 사람의 색 위에 덧칠하기도 합니다. 물감을 묽게 타서 아래 색이 비치게 만들기도 합니다.
다 칠하고 나면 판을 들어 전시장 벽에 겁니다. 보는 사람은 누가 몇 번 덧칠했는지 모른 채 마지막 판만 봅니다.
색 버퍼가 이 타일판에 해당합니다. 타일 한 칸은 화면의 한 점입니다. 판을 벽에 거는 일은 화면에 내보내는 일입니다.
화면과 같은 크기의 색판
화면은 작은 칸이 격자로 늘어선 것입니다. 칸 하나가 픽셀 하나입니다. 몇 줄 몇 칸인지는 화면의 해상도가 정합니다.
색 버퍼는 그 칸마다 색 하나를 담는 메모리입니다. 칸은 번호로 화면의 점과 맞물립니다. 색 버퍼의 셋째 줄 다섯째 칸은 화면의 셋째 줄 다섯째 점이 될 색입니다.
같은 크기의 메모리가 옆에 나란히 섭니다. 깊이 버퍼는 칸마다 그 점에 보이는 것까지의 거리를 담습니다. 스텐실 버퍼는 칸마다 표식 하나를 담습니다. 이렇게 화면 한 장을 이루는 메모리를 한 벌로 묶은 것이 프레임버퍼입니다.
flowchart TD
subgraph F["프레임버퍼 한 벌 · 세 판이 같은 칸 번호로 맞물린다"]
A["색 버퍼 · 셋째 줄 다섯째 칸 = 그 점이 될 색"]
B["깊이 버퍼 · 셋째 줄 다섯째 칸 = 그 점까지의 거리"]
C["스텐실 버퍼 · 셋째 줄 다섯째 칸 = 그 점의 표식"]
end
F --> S["화면의 셋째 줄 다섯째 점"]
세 판은 칸 번호만 같고 담는 값이 서로 다릅니다. 화면으로 나가는 것은 색 버퍼뿐입니다. 나머지 두 판은 그 색을 쓸지 말지를 가리는 데 쓰입니다.
색 버퍼가 언제나 화면으로 나가는 것은 아닙니다. 화면에 안 내보내고 따로 받아 두었다가 다음 그리기의 재료로 쓰는 것도 같은 색 버퍼입니다. 그렇게 결과를 받는 메모리를 렌더 타깃이라고 부릅니다.
한 칸의 색은 숫자 넷으로 적습니다. 이 숫자 하나하나를 채널이라고 부릅니다. 빨강 · 초록 · 파랑의 밝기 셋에, 얼마나 비치는지를 담는 투명도 하나가 붙습니다. 투명도를 담는 넷째 채널이 알파 채널입니다.
packet-beta 0-7: "빨강" 8-15: "초록" 16-23: "파랑" 24-31: "알파"
칸 하나를 32비트로 잡았을 때 비트를 이렇게 나눠 씁니다. 채널 하나에 8비트를 주면 밝기를 256단계로 적을 수 있습니다.
칸을 더 크게 잡으면 밝기를 더 잘게 나눠 담습니다. 그만큼 메모리를 더 먹으므로 쓰임에 맞춰 고릅니다.
| 칸 하나의 크기 | 어떻게 담나 | 어떤 때 고르나 |
|---|---|---|
| 32비트 | 채널 넷을 8비트씩 · 밝기를 256단계로 | 널리 쓰이는 크기. 화면에 바로 내보낼 판 |
| 64비트 | 채널 넷을 16비트씩 · 단계가 훨씬 촘촘하게 | 밝은 곳과 어두운 곳의 차이가 큰 그림 |
| 채널 하나짜리 | 밝기 한 값만 | 그림자·가림처럼 색이 필요 없는 중간 결과 |
셋째 줄은 화면에 낼 그림이 아니라 다음 계산에 쓸 중간 결과입니다. 색을 담는 메모리라도 사람이 볼 그림만 담는 것은 아닙니다.
한 장을 그리는 동안
색 버퍼의 값은 한 장을 그리는 사이에 여러 번 바뀝니다. 한 칸을 차지하겠다고 나선 색 하나를 후보라고 부르겠습니다. 한 칸에 후보가 여럿 와도 화면에 남는 색은 하나입니다.
후보를 그래픽스에서 부르는 이름은 프래그먼트입니다. 한 칸을 노리는 색 하나가 프래그먼트 하나입니다.
후보는 삼각형에서 나옵니다. 삼각형 하나를 화면의 칸으로 쪼개는 래스터화가 후보를 만들어 냅니다. 후보마다 어떤 색이 될지는 프래그먼트 셰이더가 계산합니다.
계산된 색이 곧바로 색 버퍼에 적히지는 않습니다. 그 사이에 검사가 둘 있습니다. 앞에 다른 것이 가리고 있지 않은지를 보는 깊이 테스트, 미리 표시해 둔 구역 안인지를 보는 스텐실 테스트입니다. 둘 중 하나에서 걸린 후보는 떨어져 색을 못 씁니다.
검사를 통과한 색을 색 버퍼에 적는 단계가 출력 병합기입니다. 색 버퍼는 이 단계가 읽고 쓰는 메모리입니다. 검사와 섞기의 결과가 여기 쌓입니다.
한 장은 지우기로 시작합니다. 앞 장의 색을 안 지우고 그리면 이번 장에서 아무도 안 칠한 칸에 지난 장의 그림이 남습니다.
지우기에서 넘기기까지, 한 장의 처음부터 끝까지가 아래입니다.
flowchart TD
A["한 장 시작 · 모든 칸을 배경색으로 채운다"] --> B["물체를 하나씩 그린다 · 칸마다 후보가 나온다"]
B --> C{"깊이 테스트"}
C -->|통과| D{"스텐실 테스트"}
C -->|탈락| F["후보를 버린다 · 색을 안 쓴다"]
D -->|통과| E["출력 병합기가 그 칸의 색을 고쳐 쓴다"]
D -->|탈락| F
E --> B
F --> B
B --> G["다 그렸다 · 판을 화면 쪽으로 넘긴다"]
이미 있는 색 위에 겹쳐 쓰기
통과한 후보의 색을 적는 방법은 둘입니다. 이미 있던 색을 덮어 버리거나, 둘을 섞어 새 색을 만들어 적습니다. 섞는 처리의 이름이 블렌딩입니다.
섞을 때 새로 계산한 색을 소스 색, 색 버퍼에 이미 있던 색을 목적 색이라고 부릅니다. 유리창 너머가 비쳐 보이는 화면은 소스 색을 알파 값만큼만 얹고 나머지를 목적 색으로 채운 결과입니다.
섞기는 색 버퍼의 성격을 바꿉니다. 덮어쓰기만 하면 쓰기 한 번으로 끝나지만, 섞으려면 먼저 그 칸을 읽어야 합니다. 읽고 · 계산하고 · 다시 쓰는 세 동작이 한 후보마다 붙습니다.
같은 칸을 노리는 후보 둘이 비슷한 때에 도착하면 이 세 동작이 뒤엉킬 수 있습니다.
sequenceDiagram
participant A as 후보 A
participant M as 색 버퍼의 한 칸
participant B as 후보 B
A->>M: 지금 있는 색을 읽는다
B->>M: 같은 색을 읽는다
A->>M: 섞은 색을 쓴다
B->>M: 섞은 색을 쓴다
Note over A,B: B 는 A 가 쓴 색을 못 보고 읽었다 · A 의 칠이 사라진다
Note over M: 그래서 그리는 쪽이 칸 단위로 차례를 지켜 준다
그래서 그리는 쪽은 칸 단위로 차례를 지켜 줍니다. 그만큼 동시에 처리할 수 있는 양이 줄어듭니다.
섞인 색은 그린 차례에 따라 결과가 달라집니다. 반투명한 물체를 먼 것부터 차례로 그리는 까닭이 이것입니다. 색의 일부만 고쳐 쓰고 싶을 때는 채널마다 쓰기를 막는 색 쓰기 마스크를 씁니다.
그리는 판과 보이는 판
화면에 나가고 있는 판에 그대로 그리면 덜 그린 상태가 그대로 보입니다. 화면 윗부분에는 앞 장이, 아랫부분에는 새 장이 걸린 것처럼 갈라져 보이기도 합니다. 이것을 화면 찢김이라고 부릅니다.
그래서 색 버퍼를 두 장 둡니다. 화면에 나가는 판이 앞 판입니다. 다음 장을 그리는 판이 뒤 판입니다.
뒤 판을 다 그리면 두 판의 역할을 맞바꿉니다. 이 방식이 더블 버퍼링입니다. 역할을 맞바꾸는 동작은 스왑이라고 합니다.
맞바꾸는 때를 화면이 새 장을 시작하는 때에 맞추면 찢김이 안 보입니다. 그렇게 때를 맞추는 것이 수직 동기화입니다. 대신 그리기가 일찍 끝나도 그때까지 기다리게 됩니다.
기다리는 동안 노는 것이 아까우면 판을 셋 둡니다. 앞 판과 뒤 판 말고 한 장을 더 두어 미리 그려 두는 방식이 트리플 버퍼링입니다. 이렇게 돌려 쓰는 판 여러 장을 한 묶음으로 다루는 것을 스왑 체인이라고 부릅니다.
판이 두 장일 때와 세 장일 때 역할이 이렇게 돕니다.
flowchart TD
subgraph T2["두 장으로 돌릴 때"]
A["앞 판 · 화면에 나간다"]
B["뒤 판 · 다음 장을 그린다"]
B -->|"다 그리면 스왑"| A
A -->|"화면에서 내려오면"| B
end
subgraph T3["세 장으로 돌릴 때"]
C["앞 판 · 화면에 나간다"]
D["미리 그려 둔 판 · 차례를 기다린다"]
E["뒤 판 · 다음 장을 그린다"]
E -->|"다 그리면"| D
D -->|"스왑"| C
C -->|"화면에서 내려오면"| E
end
칸에 적힌 숫자와 눈에 보이는 밝기
색 버퍼의 숫자는 밝기에 곧바로 비례하지 않는 경우가 많습니다. 값이 두 배가 되어도 두 배로 밝아 보이지 않습니다.
사람 눈은 어두운 쪽의 작은 차이를 잘 알아채고 밝은 쪽의 같은 차이는 잘 못 알아챕니다. 그래서 한정된 단계를 어두운 쪽에 더 촘촘히 나눠 주고 밝은 쪽은 성기게 두는 방식으로 적습니다. 화면과 이미지가 널리 쓰는 그런 방식이 sRGB(Standard Red Green Blue)입니다. sRGB 처럼 값을 어떤 규칙으로 적어 둔 것인지를 밝히는 약속이 색 공간입니다.
이렇게 적힌 값끼리 그대로 더하거나 섞으면 결과가 눈에 보이는 밝기와 어긋납니다. 그래서 섞기 전에 밝기에 비례하는 값으로 풀었다가 다시 되돌려 적습니다. 이 풀고 되돌리는 계산이 감마 보정입니다.
밝은 부분을 넓게 담아 둔 색 버퍼는 화면이 낼 수 있는 밝기를 넘어섭니다. 그 값을 화면이 낼 수 있는 범위로 누르는 계산을 톤 매핑이라고 합니다.
한 칸의 값이 섞였다가 화면으로 나가기까지 이런 왕복을 거칩니다.
flowchart TD
A["색 버퍼에 적힌 값 · sRGB"] --> B["밝기에 비례하는 값으로 푼다"]
B --> C["섞는다"]
C --> D["되돌려 색 버퍼에 적는다"]
D --> E["화면으로 나가기 전 · 톤 매핑으로 범위를 누른다"]
대가
첫째는 메모리입니다. 가로 1920 · 세로 1080 짜리 화면이면 칸이 200만 개쯤 됩니다. 칸마다 32비트를 쓰면 8메가바이트가 넘습니다. 돌려 쓰는 판이 두세 장이면 그만큼 곱절로 듭니다.
둘째는 오가는 값입니다. 후보 하나마다 쓰기가 붙습니다. 섞으면 읽기가 하나 더 붙습니다. 화면을 가득 덮는 물체가 여럿이면 이 오가는 값이 메모리 대역폭을 상당히 먹습니다.
셋째는 버려지는 칠입니다. 뒤에 가려질 것까지 칠하면 그 색은 다음 후보에게 덮여 사라집니다. 같은 칸을 여러 번 칠하는 이 낭비를 오버드로라고 부릅니다.
줄이는 방법은 대개 두 갈래입니다. 하나는 색을 계산하기 전에 깊이부터 견줘 가려질 후보를 미리 버리는 것입니다. 이렇게 미리 버리는 처리가 얼리 Z입니다.
다른 하나는 오가는 값 자체를 줄이는 것입니다. 칸을 묶어 한 묶음씩 처리하는 방식이 타일 기반 렌더링입니다. 판에 적힌 값을 줄여 담는 것이 프레임버퍼 압축입니다.
관련 항목
색 버퍼와 한 벌로 묶이는 화면 메모리
프레임버퍼 · 깊이 버퍼 · 스텐실 버퍼 · 렌더 타깃 · 프레임버퍼 객체 · 누적 버퍼 · G 버퍼
색 버퍼에 쓸 색을 만들어 내는 처리 단계
그래픽스 파이프라인 · 래스터화 · 프래그먼트 · 프래그먼트 셰이더 · 정점 셰이더 · 정점 색 · 텍스처
색을 쓸지 말지를 가리는 검사
깊이 테스트 · 스텐실 테스트 · 알파 테스트 · 가위 테스트 · 출력 병합기 · 색 쓰기 마스크
이미 있는 색과 새 색을 섞는 처리
블렌딩 · 알파 채널 · 알파 합성 · 프리멀티플라이드 알파 · 반투명
한 칸에 색을 적어 두는 형식
픽셀 · 해상도 · 색 공간 · sRGB · 감마 보정 · 색 깊이 · HDR · 톤 매핑
다 그린 판을 화면으로 넘기는 방법
더블 버퍼링 · 트리플 버퍼링 · 스왑 체인 · 수직 동기화 · 화면 찢김
색 버퍼를 받아 다시 손보는 화면 처리
포스트 프로세싱 · 안티앨리어싱 · MSAA · 블룸 · 모션 블러 · 피사계 심도
색 버퍼가 먹는 메모리와 대역폭을 줄이는 기법
얼리 Z · 오버드로 · 타일 기반 렌더링 · 프레임버퍼 압축 · 메모리 대역폭 · 해상도 스케일링
색 버퍼를 다루는 그래픽 API
다른 이름: color buffer · 컬러 버퍼 · 색상 버퍼 · 컬러버퍼