10 — 래스터화 (Rasterization)
출처: SSU 컴퓨터 그래픽스 슬라이드 —
SSU_Intro2CG_10_Rendering_rasterization.pdf주차: 9주차 — 기말 시험범위 시작 선행: 07 — 3D 기하변환·뷰잉, 09 — 은면의 제거
3차원 삼각형을 화면의 픽셀로 바꾸는 과정. "이 픽셀이 삼각형 안에 있나?" 와 "그 픽셀의 색/깊이는 얼마인가?" 두 질문을 edge function 과 무게중심좌표(barycentric coordinates) 로 푸는 것이 이 장의 핵심입니다.
1. 렌더링의 두 갈래
출처: 슬라이드 — Two Popular Methods
3D 장면을 2D 이미지로 만드는 대표적인 두 방법:
| 방법 | 중심 | 순회 단위 | 특징 |
|---|---|---|---|
| Rasterization | 객체 중심 (Object-centric) | 삼각형마다 → 그 안의 픽셀 | 빠름, 실시간 GPU |
| Ray Tracing | 이미지 중심 (Image-centric) | 픽셀마다 → 광선을 쏨 | 사실적, 느림 |
이 장은 래스터화. 즉 "삼각형을 하나씩 꺼내서, 그 삼각형이 덮는 픽셀들을 칠한다" 는 객체 중심 루프입니다.
1.1 폴리곤 메시 (Polygon Mesh)
렌더링의 입력 형상은 정점(vertex)·에지(edge)·면(face) 으로 표면을 정의한 폴리곤 메시입니다.
- Face-vertex mesh: 면이 자신을 이루는 정점 인덱스를 참조하는 가장 흔한 표현.
- Blender 등에서 모델링 후
.obj같은 포맷으로 내보냄.
2. 래스터화의 큰 그림
출처: 슬라이드 — Rasterization Overview / Naïve Algorithm
두 단계로 요약됩니다:
- 투영(Projection): 삼각형의 정점들을 원근투영으로 화면에 투영.
- 포함 판정(Coverage): 각 픽셀이 투영된 2D 삼각형 안에 들어가는지 판정 → 들어가면 색을 칠함.
2.1 Naïve 알고리즘
for (each triangle in scene) {
// STEP 1: 정점을 원근투영
Vec2f v0 = perspectiveProject(triangle[i].v0);
Vec2f v1 = perspectiveProject(triangle[i].v1);
Vec2f v2 = perspectiveProject(triangle[i].v2);
for (each pixel in image) { // ← 모든 픽셀을 도는 게 낭비
if (pixelContainedIn2DTriangle(v0, v1, v2, x, y)) {
image(x, y) = triangle[i].color;
}
}
}2.2 최적화: 바운딩 박스 (Bounding Box) ⭐️
삼각형마다 이미지 전체 픽셀을 도는 것은 낭비. 투영된 삼각형의 바운딩 박스 안 픽셀만 순회합니다.
세 정점의 최소/최대 좌표로 박스를 잡고, ymin..ymax × xmin..xmax 만 검사 → 큰 화면에서 대부분의 빈 픽셀 검사를 건너뜁니다.
정점을 드래그해 바운딩박스가 어떻게 잡히고, 박스 안에서 edge function 으로 어떤 픽셀이 내부 판정되는지(그리고 검사했지만 바깥인 픽셀과, 아예 검사조차 안 하는 픽셀의 차이) 직접 보세요.
래스터화 — 바운딩박스 + Edge Function 내부 판별
drag삼각형 꼭지점을 드래그하세요. 바운딩박스 안 픽셀만 edge function 검사를 수행합니다. 세 edge 부호가 모두 같은 픽셀만 채워집니다.
2.3 한 픽셀에 두 삼각형이 겹칠 때 — Z-Buffering ⭐️
같은 픽셀을 여러 삼각형이 덮으면 카메라에 더 가까운 것이 이겨야 합니다. 픽셀마다 깊이값(거리) 을 저장하는 z-buffer(depth-buffer) 를 둡니다.
float buffer[W*H];
for (i in pixels) buffer[i] = INFINITY; // 무한대로 초기화
for (each triangle) {
// 투영 + 바운딩박스 계산 ...
for (y...) for (x...) {
float z;
if (pixelContainedIn2DTriangle(v0,v1,v2, x,y, z)) {
if (z < zbuffer(x,y)) { // 더 가까우면 갱신
zbuffer(x,y) = z;
image(x,y) = triangle.color;
}
}
}
}은면 제거의 이미지공간법 (09장)이 래스터화 루프 안에 자연스럽게 들어가는 지점.
2.4 한 장의 그림에 필요한 것들
- 이미지 버퍼 (색의 2D 배열)
- 깊이 버퍼 (float 의 2D 배열)
- 삼각형들 (장면 형상)
- 정점을 캔버스로 투영하는 함수, 투영된 삼각형을 래스터화하는 함수
- 이미지 버퍼를 디스크로 저장하는 코드
3. 투영 단계 (The Projection Stage)
출처: 슬라이드 — Projecting Vertices / Screen → NDC → Raster
3.1 화면 공간으로의 원근분할 (Perspective divide)
카메라 공간 3D 점을 z 좌표로 나눠 2D 화면 좌표를 얻습니다.
3.2 z 좌표를 버리지 마라 ⭐️
화면 좌표만 남기면 가시성(visibility) 문제를 풀 수 없습니다. 투영 후에도 정점의 카메라 공간 z 를 별도로 보관해야 합니다 (보통 양수 -z_camera 로 저장해 이후 계산을 단순화).
"Screen-space is also three-dimensional" — 화면 공간도 사실 3차원이며, x·y 만 원근 왜곡된 채로 깊이 z 가 함께 살아 있습니다 (Ed Catmull, 1974).
3.3 좌표계 사슬: Screen → NDC → Raster
GPU 관례로 화면 좌표를 정규화 장치좌표(NDC, 각 축 [-1, 1])로 재매핑한 뒤, 픽셀 격자(raster space)로 옮깁니다.
raster space 는 y 축이 아래로 향하므로
(1 - y_NDC)로 뒤집습니다.
4. 래스터화 단계 — Edge Function ⭐️
출처: 슬라이드 — The Edge Function (Juan Pineda, 1988)
"이 픽셀이 삼각형 안에 있나?" 를 푸는 고전적이고 지금도 쓰이는 방법.
4.1 정의
에지(V0→V1)에 대해 점 P 의 위치를 부호로 판정:
→ P 가 에지의 오른쪽 → P 가 직선 위 → P 가 왼쪽
세 에지에 대해 부호가 모두 같으면 점은 삼각형 안에 있습니다.
4.2 기하학적 의미 — 외적 (Cross Product)
edge function 은 두 벡터의 외적과 닮았습니다. 그 크기는 두 벡터가 만드는 평행사변형의 넓이이고, 따라서 삼각형 넓이의 2배가 됩니다.
수식만 보면 와닿지 않습니다. 아래에서 변 A→B 와 점 P 를 드래그해 보세요. edge function이 곧 두 벡터가 만드는 평행사변형 넓이(= 밑변 × 높이) 이고, 부호가 직선의 어느 쪽인지 알려준다는 걸 눈으로 확인할 수 있습니다.
Edge Function = 부호 있는 넓이 (외적)
drag방향이 있는 변 A→B 와 점 P 를 드래그하세요. edge function 값은 두 벡터 (B−A), (P−A) 가 만드는 평행사변형의 넓이(= 외적의 크기)이고, 부호는 P가 직선 A→B 의 어느 쪽에 있는지를 말합니다. P가 직선을 넘어가면 부호가 뒤집히고 넓이는 0을 지납니다.
bool edgeFunction(const Vec2f &a, const Vec2f &b, const Vec2f &c) {
return ((c.x-a.x)*(b.y-a.y) - (c.y-a.y)*(b.x-a.x) >= 0);
}
bool inside = true;
inside &= edgeFunction(V0, V1, p);
inside &= edgeFunction(V1, V2, p);
inside &= edgeFunction(V2, V0, p); // 세 부호가 모두 같으면 내부4.3 와인딩 순서 (Winding Order) 가 중요
정점을 시계/반시계 어느 방향으로 도느냐에 따라 edge function 의 부호 기준이 통째로 뒤집힙니다. 일관된 와인딩 순서를 전제로 "모두 양수면 내부" 같은 규칙을 세워야 합니다.
5. 무게중심좌표 (Barycentric Coordinates) ⭐️⭐️
출처: 슬라이드 — Barycentric Coordinates / Vertex Attribute Interpolation
"픽셀의 색·노멀·UV 는 얼마인가?" 를 푸는 정점 속성 보간의 도구. 정점 사이를 선형 보간하는 가중치입니다.
5.1 정의
삼각형 위 임의의 점 P 를 세 정점의 가중합으로 표현:
세 가중치의 합은 항상 1. 같은 가중치로 색·노멀·UV 도 보간합니다.
5.2 어떻게 구하나 — 부분삼각형 넓이의 비
λ 는 P 가 만드는 부분삼각형 넓이 ÷ 전체 삼각형 넓이. edge function 이 곧 (넓이 × 2)이므로 그대로 재사용됩니다.
float area = edgeFunction(v0, v1, v2); // 전체 넓이 × 2
float w0 = edgeFunction(v1, v2, p); // V0 맞은편 부분삼각형
float w1 = edgeFunction(v2, v0, p); // V1 맞은편
float w2 = edgeFunction(v0, v1, p); // V2 맞은편
if (w0>=0 && w1>=0 && w2>=0) { // 내부 판정과 보간을 동시에!
w0 /= area; w1 /= area; w2 /= area; // λ0, λ1, λ2
// color = w0*C0 + w1*C1 + w2*C2 ...
}핵심: edge function 하나로 내부 판정 과 무게중심좌표 를 동시에 얻습니다. 아래에서 직접 확인하세요 — P 를 옮기면 세 부호와 λ, 그리고 보간된 색이 실시간으로 바뀝니다.
Edge Function & Barycentric Coordinates
drag삼각형의 세 꼭지점(V0, V1, V2)과 점 P 를 드래그하세요. 세 edge function 부호가 모두 같으면 P 는 삼각형 내부이고, 세 부분삼각형 넓이의 비가 곧 무게중심좌표 (λ0, λ1, λ2) 입니다.
λ1 = 0.32
λ2 = 0.36
Σ = 1.00
그런데 λ 가 "왜" 그 값인지 기하학적으로 보려면, 부분삼각형 넓이의 비로 이해하는 게 가장 직관적입니다. 아래에서 각 부분삼각형의 실제 넓이 숫자와 λ 비율 막대, 그리고 로 재구성한 점(점선 고리)이 항상 P에 겹치는 것을 확인하세요.
무게중심좌표 = 부분삼각형 넓이의 비
drag점 P 가 삼각형을 세 개의 부분삼각형으로 나눕니다. 각 꼭지점의 무게 λᵢ 는 그 꼭지점 맞은편 부분삼각형 넓이 ÷ 전체 넓이 입니다. P를 꼭지점으로 끌면 그 λ→1, 변 위로 끌면 맞은편 λ→0, 밖으로 끌면 λ가 음수가 됩니다.
P 내부
5.3 선형 보간의 기초
두 점 사이의 값은 거리에 반비례하는 가중평균. 예를 들어 C0 와 C1 을 7등분한 위치에서 C0 쪽으로 4칸 떨어졌다면:
무게중심좌표는 이것을 삼각형(2D)으로 확장한 것입니다.
6. 깊이 보간과 원근 보정 ⭐️
출처: 슬라이드 — Depth Interpolation / Perspective Correct Interpolation
6.1 z 를 그냥 보간하면 틀린다
P 의 깊이를 무게중심좌표로 z 를 직접 보간하면 틀립니다. 원근투영은 직선은 보존하지만 거리(비율)는 보존하지 않기 때문입니다.
6.2 해법: z 의 역수를 보간
정점 z 의 역수를 무게중심좌표로 선형 보간합니다:
6.3 원근 보정 보간 (Perspective-Correct Interpolation)
색·노멀·UV 같은 정점 속성도 같은 이유로 그냥 보간하면 왜곡됩니다 (무게중심좌표는 2D 화면에서 계산했으므로). 올바른 절차:
- 각 정점 속성을 그 정점의 z 로 나눈다.
- 무게중심좌표로 선형 보간한다.
- 결과에 픽셀의 깊이 Z 를 곱한다.
아래에서 깊이비(zFar/zNear) 슬라이더를 키워 보세요. 왼쪽(화면 공간 선형 보간)은 체커가 일그러지고, 오른쪽(원근 보정)은 멀어질수록 칸이 올바르게 작아집니다.
원근 보정 보간 — Affine vs Perspective-Correct
live체커보드 바닥면이 깊이 방향으로 멀어질 때, 화면 공간에서 선형 보간(Affine)하면 칸 간격이 균일하게 보이지만 실제로는 틀린 원근입니다. 원근 보정은 u/z·1/z를 보간한 뒤 나눠서 복원합니다. 깊이 비율을 크게 할수록 두 방식의 차이가 뚜렷해집니다.
u/z와 1/z를 보간한 뒤 z를 곱해 복원하는 것입니다. 이렇게 하면 비스듬한 면에서도 텍스처가 찌그러지지 않습니다.텍스처가 비스듬한 면에서 찌그러지지 않게 하는 바로 그 보정. 텍스처 매핑(13장)에서 다시 만납니다.
📝 Knowledge Check: 래스터화
Rasterization
래스터화와 레이트레이싱의 가장 본질적인 차이는?
🔗 다음 학습
- 이전 (중간 범위): 09 — 은면의 제거 — z-buffer/이미지공간법의 출발점.
- 다음: 11 — 파라메트릭 곡선 — 직선 보간을 곡선으로 확장.
- 시험 대비: 기말 대비 — Numerical Hard Mode