Довідник Оновлено у липні 2026

Довідник кольорових просторів

RGB, sRGB, лінійне світло, HSL, HSV, CIE XYZ, CIELAB та ACEScg — формули перетворення, гамма-корекція та точні місця, де конвеєри WebGL і Three.js найчастіше помиляються з кольором.

🎨 RGB і sRGB

RGB — це не один кольоровий простір, а ціла родина: три числа набувають сенсу лише тоді, коли зафіксовано набір первинних кольорів (хроматичність червоного, зеленого, синього) і передавальну функцію (як числові значення відображаються на інтенсивність світла). Веб, файли PNG/JPEG і більшість текстур використовують первинні кольори та передавальну функцію sRGB, визначені стандартом IEC 61966-2-1.

Простір Первинні кольори Типове застосування
sRGB (кодований) Гама Rec. 709 Текстури, кольори UI, PNG/JPEG, canvas 2D
Лінійний sRGB Гама Rec. 709 Обчислення освітлення, блендинг у фреймбуфері
Display-P3 Гама DCI-P3 Дисплеї з розширеною гамою, HDR canvas
Rec. 2020 Надширока гама Мовлення 4K/8K, деякі HDR-конвеєри

Пакування у 8 біт

Текселя, збережений як #RRGGBB, пакує кожен канал у діапазон $[0,255]$; перед будь-якими обчисленнями його треба нормалізувати до $[0,1]$:

Байт → нормалізоване число з рухомою комою $$c_{norm} = \frac{c_{byte}}{255}$$

💡 Гамма-корекція та лінійне світло

Інтенсивність світла складається лінійно — два фотони несуть удвічі більше енергії, ніж один, — але значення пікселів, закодовані у sRGB, не складаються так само. sRGB застосовує криву, наближену до степеня 2.2, щоб 8-бітні значення виділяли більше точності темним тонам, до яких око найчутливіше. Будь-яке освітлення, блендинг або генерація міп-мапів, виконані безпосередньо на закодованих sRGB-байтах, фізично некоректні й дають вимитий або каламутний результат.

sRGB → лінійний (декодування)

Кусково-задана EOTF, для кожного каналу c ∈ [0,1] $$c_{linear} = \begin{cases} \dfrac{c_{srgb}}{12.92} & c_{srgb} \le 0.04045 \\[6pt] \left(\dfrac{c_{srgb} + 0.055}{1.055}\right)^{2.4} & c_{srgb} > 0.04045 \end{cases}$$

Лінійний → sRGB (кодування)

Обернена OETF, для кожного каналу c ∈ [0,1] $$c_{srgb} = \begin{cases} 12.92\,c_{linear} & c_{linear} \le 0.0031308 \\[6pt] 1.055\,c_{linear}^{1/2.4} - 0.055 & c_{linear} > 0.0031308 \end{cases}$$

Дешеве наближення, яке часто зустрічається в шейдерах — з похибкою до 1% — замінює кусково-задану криву простим степенем 2.2:

Просте наближення гамою 2.2 $$c_{linear} \approx c_{srgb}^{2.2} \qquad c_{srgb} \approx c_{linear}^{1/2.2}$$
Етап Має виконуватись у
Обчислення освітлення / затінення Лінійному просторі
Альфа-блендинг, MSAA-резолв Лінійному просторі
Генерація міп-мапів / фільтрація текстур Лінійному просторі
Фінальний фреймбуфер / вивід у swap-chain Кодуванні sRGB
Зберігання дифузних / альбедо-текстур на диску Кодуванні sRGB
Карти нормалей, шорсткості/металевості, дата-текстури Лінійному просторі (без прапорця sRGB!)
// GLSL: перетворення sRGB <-> лінійний для одного каналу
float srgbToLinear(float c) {
  return c <= 0.04045
    ? c / 12.92
    : pow((c + 0.055) / 1.055, 2.4);
}

float linearToSrgb(float c) {
  return c <= 0.0031308
    ? c * 12.92
    : 1.055 * pow(c, 1.0 / 2.4) - 0.055;
}

vec3 srgbToLinear(vec3 c) {
  return vec3(srgbToLinear(c.r), srgbToLinear(c.g), srgbToLinear(c.b));
}

🌈 HSL і HSV

HSL (відтінок, насиченість, світлота) і HSV/HSB (відтінок, насиченість, яскравість) — це циліндричні перепараметризації RGB, створені для зручних людині палітр вибору кольору, а не для обчислень освітлення. Відтінок $H \in [0°,360°)$ спільний для обох; насиченість і вісь світлоти/яскравості відрізняються.

RGB → HSV

c_max, c_min з R,G,B ∈ [0,1]; Δ = c_max − c_min $$H = \begin{cases} 0 & \Delta = 0 \\ 60°\left(\dfrac{G-B}{\Delta} \bmod 6\right) & c_{max}=R \\ 60°\left(\dfrac{B-R}{\Delta}+2\right) & c_{max}=G \\ 60°\left(\dfrac{R-G}{\Delta}+4\right) & c_{max}=B \end{cases}$$ $$S = \begin{cases} 0 & c_{max}=0 \\ \Delta / c_{max} & \text{інакше} \end{cases} \qquad V = c_{max}$$

Світлота HSL проти яскравості HSV

Співвідношення для однакових R,G,B $$L = \frac{c_{max}+c_{min}}{2} \qquad S_{HSL} = \frac{\Delta}{1 - |2L-1|}\;(\Delta \ne 0)$$

Чистий відтінок при $V=1, S=1$ у HSV повністю насичений і яскравий — але той самий відтінок у HSL досягає максимальної насиченості лише при $L=0.5$; при $L=1$ HSL завжди сходиться до білого незалежно від відтінку. Саме тому палітри HSL відчуваються «збалансованішими» для роботи з UI, а HSV — природнішим для художників, які змішують відтінки й тіні наче фарби.

// GLSL: відтінок [0,1] -> RGB (повна насиченість, повна яскравість)
vec3 hue2rgb(float h) {
  vec3 p = abs(fract(vec3(h) + vec3(1.0, 2.0/3.0, 1.0/3.0)) * 6.0 - 3.0);
  return clamp(p - 1.0, 0.0, 1.0);
}

vec3 hsv2rgb(vec3 hsv) {
  vec3 rgb = hue2rgb(hsv.x);
  return mix(vec3(1.0), rgb, hsv.y) * hsv.z;
}

👁 CIE XYZ і CIELAB

CIE 1931 XYZ — це незалежний від пристрою кольоровий простір, виведений безпосередньо з кривих відгуку колбочок ока людини — будь-який інший кольоровий простір визначається через перетворення до або з XYZ. $Y$ несе яскравість (люмінансу); $X$ і $Z$ несуть хроматичну інформацію, яка сама по собі не має перцептивного сенсу.

Лінійний sRGB → XYZ (точка білого D65)

Матриця первинних кольорів Rec. 709 $$\begin{pmatrix}X\\Y\\Z\end{pmatrix} = \begin{pmatrix} 0.4124564 & 0.3575761 & 0.1804375 \\ 0.2126729 & 0.7151522 & 0.0721750 \\ 0.0193339 & 0.1191920 & 0.9503041 \end{pmatrix} \begin{pmatrix}R_{linear}\\G_{linear}\\B_{linear}\end{pmatrix}$$

XYZ все ще не є перцептивно рівномірним — числова відстань 0.01 в одній ділянці може виглядати однаково, а та сама відстань в іншій — разюче по-іншому. CIELAB (L*a*b*) було створено спеціально, щоб виправити це: рівні числові відстані приблизно відповідають рівним сприйнятим відмінностям кольору, тому саме цей простір використовують для метрик відмінності кольору ($\Delta E$) і мапінгу гами.

XYZ → LAB, відносно точки білого (Xn, Yn, Zn) $$L^* = 116\,f(Y/Y_n) - 16$$ $$a^* = 500\left(f(X/X_n) - f(Y/Y_n)\right)$$ $$b^* = 200\left(f(Y/Y_n) - f(Z/Z_n)\right)$$ $$f(t) = \begin{cases} t^{1/3} & t > (6/29)^3 \\ \dfrac{1}{3}(29/6)^2 t + \dfrac{4}{29} & \text{інакше} \end{cases}$$
Простір Перцептивно рівномірний? Типове застосування
CIE XYZ Ні Проміжний формат між кольоровими просторами
CIELAB Приблизно Відмінність кольору ΔE, мапінг друкованої гами
OKLab / OKLCH Краще за LAB Сучасні градієнти CSS, перцептивне обертання відтінку

🔥 Колірна температура та точки білого

Корельована колірна температура (CCT), виміряна в кельвінах, описує колір ідеального випромінювача абсолютно чорного тіла при цій температурі. Нижчі значення кельвінів виглядають теплими/оранжевими (полум'я свічки ≈ 1900K), вищі — холодними/ синіми (похмуре небо ≈ 7000K і вище). «Точка білого» — це просто CCT, яку рендерер вважає нейтрально білою — D65 (~6504K) є стандартом для sRGB і Rec. 709.

Джерело Приблизна CCT
Полум'я свічки 1900 K
Лампа розжарювання 2700 K
Схід / захід сонця 3000–4000 K
D50 (точка білого для друку / графічного дизайну) 5003 K
D65 (точка білого sRGB / Rec. 709) 6504 K
Похмуре денне світло 7000–10 000 K

У рендерері CCT зазвичай перетворюється на лінійний RGB- множник (наближення планківського локусу) і застосовується як тонування балансу білого до фінального освітленого зображення, або передається в колір спрямованого джерела світла для симуляції конкретного джерела освітлення.

🌅 HDR, ACEScg і тон-мапінг

Фізично коректні рендерери накопичують необмежену лінійну радіанс — яскраве джерело світла чи відблиск на дзеркальній поверхні може багаторазово перевищувати 1.0. Перш ніж таке HDR-зображення можна показати на SDR-дисплеї (стандартний динамічний діапазон), його потрібно тон-мапити у придатний для відображення діапазон $[0,1]$, а потім повторно закодувати в sRGB.

Тон-мапінг Reinhard (простий, по каналах)

Стискає [0,∞) → [0,1) $$c_{mapped} = \frac{c_{hdr}}{1 + c_{hdr}}$$

Наближення ACES filmic (Narkowicz, 2015)

Швидка апроксимована крива, широко застосовна в рушіях реального часу $$a=2.51,\; b=0.03,\; c=2.43,\; d=0.59,\; e=0.14$$ $$c_{mapped} = \text{clamp}\!\left(\frac{c_{hdr}(a\,c_{hdr}+b)} {c_{hdr}(c\,c_{hdr}+d)+e},\; 0,\; 1\right)$$

ACEScg — це лінійний робочий простір широкої гами, що використовується всередині конвеєра Academy Color Encoding System для кіно та високоякісного рендерингу реального часу — він має первинні кольори ширші за Rec. 2020 саме для того, щоб проміжні обчислення освітлення не обрізалися до фінального етапу тон-мапінгу. Налаштування рендерера Three.js ACESFilmicToneMapping реалізує наведене вище наближення Narkowicz.

// GLSL: наближення ACES filmic
vec3 acesFilmic(vec3 x) {
  const float a = 2.51;
  const float b = 0.03;
  const float c = 2.43;
  const float d = 0.59;
  const float e = 0.14;
  return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);
}

⚙️ Керування кольором у Three.js

Починаючи з r152 Three.js за замовчуванням вмикає THREE.ColorManagement: кольори, задані в JavaScript (hex, CSS-рядки), трактуються як sRGB і автоматично перетворюються на лінійні внутрішньо, а текстури потребують явного тегу кольорового простору, щоб рендерер знав, чи їх треба декодувати.

import * as THREE from 'three';

// Альбедо / дифузні текстури несуть колір, заданий людиною -> sRGB
const albedoMap = new THREE.TextureLoader().load('brick-albedo.jpg');
albedoMap.colorSpace = THREE.SRGBColorSpace;

// Карти нормалей / шорсткості / металевості / AO зберігають сирі дані -> лінійний (без декодування)
const normalMap = new THREE.TextureLoader().load('brick-normal.jpg');
normalMap.colorSpace = THREE.NoColorSpace;

// Кодування виводу рендерера + фільмовий тон-мапінг
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;

// Hex/CSS-кольори задаються в sRGB і автоматично перетворюються на лінійні
const mat = new THREE.MeshStandardMaterial({
  map: albedoMap,
  normalMap: normalMap,
  color: 0xff6b35, // трактується ColorManagement як sRGB
});
Типова помилка

Позначення текстури альбедо як NoColorSpace (або застарілого encoding = LinearEncoding) пропускає декодування sRGB, тож освітлення обчислюється на гамма-кодованих значеннях — поверхні виглядають надто темними й знебарвленими в тінях, надто пласкими в середніх тонах. Позначення карти нормалей як SRGBColorSpace спотворює векторні дані небажаною гамма-кривою, що дає явно неправильне затінення та артефакти освітлення.

⚠️ Типові помилки

sRGB #ff6b35
лінійний ≈ 0.897, 0.056, 0.010
sRGB #60a5fa
лінійний ≈ 0.121, 0.373, 0.925
sRGB #34d399
лінійний ≈ 0.023, 0.677, 0.336
sRGB #c084fc
лінійний ≈ 0.510, 0.216, 0.955