01 · 픽셀과 색
이 문서가 답하는 질문: 픽셀이란 무엇이고, RGB / RGBA / 알파 / 감마는 어떻게 얽혀 있는가?
한 줄 답 (Pyramid Top)
픽셀은 3채널 + α(불투명도) 의 숫자 묶음이지만, 그 숫자는 선형 빛이 아니라 감마로 휘어진 값이고, 알파는 프리멀티플라이드냐 스트레이트냐에 따라 합성 결과가 바뀐다. 이 세 가지를 모르고 RGB를 다루면 “왜 같은 이미지가 다른 색으로 보이지” 가 시작된다.
Why — 왜 픽셀이 단순하지 않은가
이미지는 “화면의 작은 점” 이라고 가르치지만, 실제로는 “빛의 측정값을 저장한 숫자” 다. 그리고 이 측정값은 사람 눈의 비선형성 때문에 그대로 저장되지 않는다.
- 사람 눈은 어두운 영역의 차이에 민감하고, 밝은 영역의 차이에 둔하다.
- 8bit(0~255)에서 선형으로 빛을 저장하면 어두운 부분이 거칠게 끊어진다 → 밴딩.
- 그래서 모든 이미지 포맷은 감마 인코딩(어두운 값에 더 많은 비트 할당)으로 저장한다.
알파(α)는 더 미묘하다. “투명도” 라고 부르지만 합성 공식에 따라 두 가지 표현이 공존한다:
- Straight alpha:
RGB와α가 독립 - Premultiplied alpha:
RGB에 이미α가 곱해져 있음
이걸 섞으면 PNG의 가장자리에 검은 테두리가 생기는 고전적 버그가 발생한다.
How — 어떻게 픽셀이 만들어지고 저장되는가
1) 픽셀 = 채널의 묶음
| 모델 | 채널 | 예 |
|---|---|---|
| RGB | Red, Green, Blue | (255, 0, 0) = 빨강 |
| RGBA | + Alpha | (255, 0, 0, 128) = 반투명 빨강 |
| YUV / YCbCr | 휘도 + 2 색차 | JPEG 내부 표현 |
| CMYK | Cyan, Magenta, Yellow, Key(Black) | 인쇄용 |
| Grayscale | Luminance 1채널 | 흑백 사진 |
2) 감마(Gamma) — 빛의 비선형 저장
display_light = pixel_value^γ (γ ≈ 2.2 for sRGB)
pixel_value = display_light^(1/γ) (저장할 때)왜 2.2인가: CRT 모니터의 물리적 특성에서 유래했지만, 사람 눈의 비선형 민감도와 우연히 비슷해서 표준이 되었다. sRGB는 정확히는 x^2.4에 가까운 piecewise 함수.
3) 알파 합성 — Straight vs Premultiplied
같은 반투명 빨강 픽셀:
| 표현 | 저장값 |
|---|---|
| Straight | RGB=(255, 0, 0), α=0.5 |
| Premultiplied | RGB=(127, 0, 0), α=0.5 |
합성 공식 (배경 위에 그릴 때):
Straight: out = src.rgb * src.α + dst.rgb * (1 - src.α)
Premultiplied: out = src.rgb + dst.rgb * (1 - src.α)Premultiplied가 연산이 1번 더 적고, 필터링(blur, scale)에서 정확하다. 그래서:
- PNG 파일: Straight alpha
- GPU 텍스처 / Canvas: Premultiplied alpha
<img>디코딩: 브라우저가 Premultiplied로 변환해서 GPU에 올림
4) 비트심도 — 채널당 비트
| 비트심도 | 채널당 값 | 표현 가능 색 (RGB 합산) |
|---|---|---|
| 8bit | 0~255 | 16.7M (24bit color) |
| 10bit | 0~1023 | 1.07B (30bit color) — HDR 표준 |
| 12bit | 0~4095 | 68.7B — Dolby Vision 마스터 |
| 16bit | 0~65535 | RAW / 사진 편집용 |
| 32bit float | -∞ ~ +∞ | HDR 합성 / EXR |
→ 03-bit-depth-and-hdr.md에서 상세
What — 구체 사양과 수치
sRGB 픽셀의 디코딩 공식 (정확한 piecewise)
if v ≤ 0.04045: linear = v / 12.92
else: linear = ((v + 0.055) / 1.055)^2.4JavaScript에서 sRGB 8bit 값을 선형 빛으로 변환:
function srgbToLinear(v) {
v /= 255;
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
}알파 변환 — Straight ↔ Premultiplied
// Straight → Premultiplied
const r_pm = r * a;
const g_pm = g * a;
const b_pm = b * a;
// Premultiplied → Straight (a == 0 일 때 RGB 정보 손실됨)
const r = a > 0 ? r_pm / a : 0;Canvas 2D의 기본값
| API | 알파 형태 |
|---|---|
getImageData() | Straight (RGBA, 0~255) |
| WebGL 텍스처 (기본) | Premultiplied |
canvas.toDataURL("image/png") | Straight (PNG 표준) |
What-if — 잘못 다루면 어떻게 깨지는가
사례 1) PNG 가장자리에 검은 테두리
증상: 둥근 아이콘 PNG를 흰 배경에 올리면 가장자리가 어둡다.
원인:
- PNG는 Straight alpha
- 디자이너가 PNG를 만들 때 α=0인 픽셀의 RGB를 검정(0,0,0)으로 저장
- 브라우저가 Premultiplied로 변환 → α≠0인 가장자리 픽셀이 검은색과 섞임
해결: α=0 영역의 RGB를 색상에 맞게 채워서 export하거나, premultiplied PNG 사용 (드물다).
사례 2) 그라디언트에 줄무늬 (밴딩)
증상: 노을 사진의 하늘에 계단 모양 줄무늬.
원인:
- 8bit × sRGB 감마 = 어두운 영역에서 인접 값 간격이 너무 큼
- 256단계가 부드러운 그라디언트를 표현하기에 부족
해결:
- 10bit 이상 (HDR) 저장
- Dithering (의도적 노이즈 추가)
- →
03-bit-depth-and-hdr.md
사례 3) box blur가 더러워 보임
증상: blur 필터를 적용했는데 가장자리가 어둡거나 색이 흐려짐.
원인: 감마 공간에서 평균을 냄. 빛은 선형이라 (0 + 255) / 2 = 127이 아니라, 선형 변환 후 평균 → 다시 감마 인코딩이 정확하다.
올바른 blur:
linear = srgbToLinear(pixel)
blurred_linear = average(linear)
out = linearToSrgb(blurred_linear)게임엔진(Unreal, Unity)은 모든 셰이더 연산을 선형 공간에서 함. 웹 Canvas는 기본이 sRGB라서 부정확하지만 빠른 blur를 한다.
Insight — 픽셀의 역사
“24bit color는 어쩌다 표준이 되었나”
1987년 VGA가 256색 (8bit indexed) → 1995년 Windows 95가 16bit High Color → 2000년대 24bit True Color. 사람 눈이 구별할 수 있는 색이 약 1000만 개라서 16.7M color는 충분히 많다고 여겨졌고, 메모리 정렬상 8bit×3이 자연스러웠다. HDR 시대(2015~)부터 비로소 24bit가 부족하다는 것이 드러나기 시작.
“감마 2.2의 우연”
CRT 전자총의 비선형성(γ≈2.5)을 보정하기 위해 카메라가 1/2.5로 미리 인코딩 → 우연히 사람 눈의 비선형 민감도(Stevens 법칙)와 거의 일치. LCD/OLED는 본질적으로 선형이지만, 모든 콘텐츠가 감마 인코딩되어 있어서 디스플레이가 흉내내준다. → “감마는 사라지지 않는다, 표준화될 뿐”
“Premultiplied가 GPU에서 표준인 이유”
Mip-mapping(텍스처 다운스케일) 시 Straight alpha는 가장자리에서 색이 새어나간다. Premultiplied는 색과 α가 함께 줄어들어 정확하게 보간된다. → Renderman, OpenEXR, Apple Core Animation 모두 Premultiplied.
한 단락 요약
픽셀은 단순한 RGB가 아니라
색공간 × 비트심도 × 감마 × 알파 모드의 4중 컨텍스트를 가진 숫자다. 이 컨텍스트 중 하나만 어긋나도 (예: PNG가 Straight인데 GPU가 Premultiplied로 가정) 가장자리가 검어지고 그라디언트에 줄이 가고 blur가 더러워진다. 다음 문서(02-color-spaces.md)는 이 중 색공간 레이어를 본격적으로 다룬다.