[CS300 #281] 컴퓨터 그래픽스 기초 — 래스터와 벡터
컴퓨터공학 300 주제 시리즈의 281번째 글이다. 전체 지도는 여기.
한 줄 요약
래스터는 “어느 칸에 무슨 색이 있는가”를 저장하고, 벡터는 “무엇을 어떻게 그리는가”를 저장한다. 화면에 보이는 순간에는 결국 둘 다 픽셀이 된다.
왜 필요한가
웹 페이지의 로고가 흐릿하게 깨져 보이는 경우가 있다. 대개 작은 PNG 를 고해상도 화면에서 늘려 그렸기 때문이다. 반대로 사진을 SVG 로 바꾸려 하면 파일이 터무니없이 커진다. 두 현상은 같은 질문에서 나온다. 그림을 픽셀 격자로 저장할 것인가, 도형의 기술(description)로 저장할 것인가.
이 구분을 알면 다음 판단이 쉬워진다.
- 아이콘·로고·도표는 어떤 형식으로 내보낼지
- 고해상도(HiDPI) 화면 대응을 어떻게 할지
- 차트 라이브러리가 Canvas 를 쓰는지 SVG 를 쓰는지에 따라 성능이 어떻게 달라지는지
- 그래픽스 파이프라인의 “래스터화” 단계가 무엇을 하는지
핵심 개념
래스터(raster) 이미지
래스터 이미지는 픽셀의 2차원 배열이다. 각 픽셀은 색 값을 가진다. 흔한 형태는 채널당 8비트, RGB 세 채널이다. 투명도(알파)를 더하면 네 채널이 된다.
너비 W, 높이 H, 채널 C, 채널당 b 비트
압축 전 크기 = W x H x C x b / 8 바이트
예) 1920 x 1080 x 4 x 8 / 8 = 8,294,400 바이트 (약 7.9 MiB)
PNG·JPEG·WebP·GIF 가 대표적인 래스터 형식이다. 해상도가 저장 시점에 고정된다. 확대하면 새 정보가 생기지 않는다. 확대 알고리즘은 없는 픽셀 값을 이웃에서 추정할 뿐이다.
- 최근접 이웃(nearest neighbor): 가장 가까운 픽셀을 복제한다. 계단이 커진다.
- 쌍선형(bilinear)·쌍삼차(bicubic): 주변 픽셀을 가중 평균한다. 계단 대신 흐림이 생긴다.
사진처럼 색이 연속적으로 변하고 경계가 복잡한 그림은 래스터가 자연스럽다.
벡터(vector) 그래픽
벡터 그래픽은 도형을 수학적으로 기술한다. 선분, 원, 베지어 곡선, 채우기 규칙, 선 굵기, 변환 행렬 같은 것이다. 웹에서 가장 흔한 벡터 형식은 W3C 표준인 SVG 다.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="steelblue"/>
<path d="M10 90 L90 10" stroke="black" stroke-width="2"/>
</svg>
viewBox 는 사용자 좌표계를 정한다. 실제로 몇 픽셀로 그려질지는 표시 시점에 결정된다. 그래서 같은 SVG 가 16픽셀 아이콘으로도, 인쇄용 포스터로도 선명하게 나온다. 확대할 때마다 수식에서 다시 픽셀을 계산하기 때문이다.
래스터화(rasterization)
모니터는 픽셀 격자다. 벡터 그림도 화면에 나오려면 어느 순간 픽셀로 바뀌어야 한다. 이 변환을 래스터화라고 한다. 기본 질문은 단순하다. “이 픽셀의 중심(또는 표본점)이 도형 안에 있는가?”
픽셀 중심이 원 안이면 칠한다
. . . . . . . .
. . # # # # . .
. # # # # # # .
. # # # # # # . <- 경계에서 계단(aliasing)이 생긴다
. # # # # # # .
. # # # # # # .
. . # # # # . .
. . . . . . . .
픽셀당 한 점만 검사하면 경계가 계단처럼 보인다. 이것을 앨리어싱(aliasing)이라 한다. 해결책은 픽셀당 여러 점을 검사해 덮인 비율만큼 색을 섞는 것이다. 이를 안티앨리어싱이라 하고, 다중 표본(MSAA)이나 덮임 비율(coverage) 계산이 대표적이다.
둘을 비교하면
| 항목 | 래스터 | 벡터 |
|---|---|---|
| 저장 내용 | 픽셀 값 배열 | 도형·경로·속성 |
| 확대 | 정보가 늘지 않는다 | 다시 그려서 선명하다 |
| 파일 크기 결정 요인 | 해상도와 압축 | 도형의 수와 복잡도 |
| 잘 맞는 대상 | 사진, 스캔, 텍스처 | 로고, 아이콘, 도표, 글꼴 |
| 그리기 비용 | 복사·합성 위주 | 매번 경로 계산 필요 |
| 대표 형식 | PNG, JPEG, WebP | SVG, PDF 의 경로, 폰트 외곽선 |
글꼴도 벡터다. TrueType·OpenType 글리프는 곡선 외곽선으로 저장되고, 화면 크기에 맞춰 그때그때 래스터화된다.
브라우저의 두 그리기 모델: Canvas 와 SVG
웹에는 두 API 가 공존한다.
<canvas>는 즉시 모드(immediate mode)다.fillRect를 호출하면 픽셀 버퍼에 그려지고 끝이다. 무엇을 그렸는지 브라우저는 기억하지 않는다. 다시 그리려면 코드가 전부 다시 호출해야 한다.- SVG 는 유지 모드(retained mode)다. 도형이 DOM 노드로 남는다. CSS 로 꾸미고, 이벤트를 붙이고, 속성만 바꿔도 브라우저가 알아서 다시 그린다.
도형이 수천 개를 넘어가면 DOM 노드 비용 때문에 SVG 가 느려지고, Canvas 가 유리해진다. 도형이 적고 상호작용이 많으면 SVG 가 편하다. 차트 라이브러리가 렌더러를 고를 수 있게 해 두는 이유다.
직접 해 보기
원 하나를 수식으로 정의하고 두 해상도로 래스터화해 본다. 그리고 작은 래스터를 확대한 결과와 큰 해상도로 다시 래스터화한 결과를 비교한다. 표준 라이브러리만 쓴다.
# 벡터(수식)로 정의한 원을 서로 다른 해상도로 래스터화한다.
def rasterize_circle(size, cx=0.5, cy=0.5, r=0.4):
rows = []
for y in range(size):
row = ""
for x in range(size):
# 픽셀 중심 좌표를 0~1 범위로 정규화
px, py = (x + 0.5) / size, (y + 0.5) / size
inside = (px - cx) ** 2 + (py - cy) ** 2 <= r * r
row += "#" if inside else "."
rows.append(row)
return rows
def upscale_nearest(rows, k):
# 이미 만든 래스터를 k배로 확대: 각 픽셀을 k x k 블록으로 복제
out = []
for row in rows:
wide = "".join(ch * k for ch in row)
out.extend([wide] * k)
return out
small = rasterize_circle(8)
print("\n".join(upscale_nearest(small, 2)))
print()
print("\n".join(rasterize_circle(16)))
실행 결과의 일부다. 왼쪽은 8x8 을 2배로 늘린 것, 오른쪽은 수식에서 16x16 으로 다시 계산한 것이다.
8x8 을 2배 확대 16x16 재래스터화
................ ................
................ ................
....########.... .....######.....
....########.... ...##########...
..############.. ...##########...
..############.. ..############..
..############.. ..############..
확대본은 2픽셀 단위 계단이 그대로 남는다. 재래스터화한 쪽은 1픽셀 단위로 윤곽이 다듬어진다. 같은 크기의 결과물이지만 담긴 정보량이 다르다. 이것이 “벡터는 확대해도 깨지지 않는다”의 실제 의미다.
연습으로 inside 를 픽셀당 4개 표본(예: 0.25, 0.75 오프셋)의 평균으로 바꾸고, 덮인 비율에 따라 " .:#" 중 하나를 고르게 해 보라. 간단한 안티앨리어싱이 된다.
현업에서는
- 프런트엔드 아이콘은 SVG 로 관리하는 경우가 많다. 색을
currentColor로 두면 CSS 글자색을 따라간다. 사진·썸네일은 WebP·JPEG 같은 래스터를 쓰고,srcset으로 화면 밀도별 해상도를 따로 준다. - 모니터링 대시보드(예: Grafana)는 시계열 점이 수만 개가 될 수 있다. 이런 차트는 Canvas 계열 렌더링이 흔하다. 노드 몇 개의 상태를 그리는 작은 홈랩 대시보드라면 SVG 로도 충분하다.
- PDF 보고서를 만들 때 차트를 PNG 로 넣으면 인쇄 시 흐려진다. 벡터(SVG·PDF)로 넣으면 선명하지만, 점이 수십만 개인 산점도는 PDF 가 무거워진다. 이럴 땐 점 영역만 래스터로 굽고 축·글자는 벡터로 두는 혼합 방식을 쓴다.
- 게임과 3D 는 삼각형(벡터적 기술)을 GPU 가 매 프레임 래스터화한다. 다음 글들에서 다룰 렌더링 파이프라인의 핵심 단계가 바로 이 래스터화다.
확인 문제
- 2560x1440, RGBA 채널당 8비트 이미지의 압축 전 크기는 몇 바이트인가?
- 벡터 로고를 64픽셀로 래스터화한 PNG 를 512픽셀로 늘렸다. 원본 SVG 를 512픽셀로 그린 것과 무엇이 다른가?
- 앨리어싱이 생기는 근본 원인을 한 문장으로 말하라.
- 도형 5만 개를 매 프레임 갱신하는 시각화에 SVG 와 Canvas 중 무엇이 일반적으로 유리한가? 이유는?
풀이
- 2560 x 1440 x 4 = 14,745,600 바이트다.
- 늘린 PNG 는 64픽셀 시절의 정보만 가지고 추정한 값이라 계단이나 흐림이 보인다. SVG 를 512픽셀로 그리면 경계를 512픽셀 해상도로 새로 계산하므로 선명하다.
- 연속적인 도형 경계를 픽셀당 유한한 표본으로만 판정하기 때문에, 표본 간격보다 세밀한 정보가 계단 모양으로 왜곡된다.
- Canvas 다. SVG 는 도형마다 DOM 노드를 유지하므로 노드 생성·스타일 계산·레이아웃 비용이 쌓인다. Canvas 는 픽셀 버퍼에 직접 그리고 상태를 유지하지 않는다.
더 읽을거리 (References)
- W3C, Scalable Vector Graphics (SVG) 2
- MDN, Canvas API
- MDN, SVG: Scalable Vector Graphics
- J. F. Hughes 외, Computer Graphics: Principles and Practice, 3rd ed., Addison-Wesley, 2013.