Стаття Алгоритми рендерингу · ≈ 12 хв читання

PBR: BRDF Кука-Торренса пояснено

Майже кожен фізично коректний рендерер — від Unreal Engine до трирядкового MeshStandardMaterial у Three.js — обчислює свій спекулярний блиск за тією самою формулою 1982 року. Ось що саме означають D, F і G, чому їх обрали саме такими, і як написати їх на GLSL (мова шейдерів OpenGL — код, що виконується на відеокарті для обчислення кольору кожного пікселя).

Коротко: Усі сучасні ігри та 3D-рендери розраховують блиск на глянцевих поверхнях за однією формулою Кука-Торренса 1982 року, яка перемножує три члени — D (скільки мікроскопічних граней дивляться у потрібному напрямку), G (скільки з них затінені чи заблоковані) і F (яка частка світла відбивається під цим кутом) — і разом вони дають фізично коректний відблиск; стандартними будівельними блоками для цього є GGX, модель Сміта та наближення Фреснеля-Шліка.

1. Навіщо мікрофасеточна модель?

Жодна реальна поверхня не є ідеально гладкою на мікроскопічному рівні. Під мікроскопом навіть відполірований метал — це ландшафт з крихітних граней, кожна з яких діє як ідеальне дзеркало, спрямоване трохи по-своєму. Теорія мікрофасетів розглядає точку поверхні як статистичний розподіл цих граней, а не моделює кожну окремо — саме це робить її придатною для рендерингу в реальному часі у фрагментному шейдері.

Лише грані, чия нормаль h (напіввектор між напрямком до камери v та напрямком до світла l) збігається з реальною геометричною нормаллю, можуть відбити світло прямо в камеру. Шорстка поверхня має нормалі граней, розсіяні широко → широкий, тьмяний відблиск. Гладка поверхня має їх щільно згрупованими → маленький, яскравий відблиск. Ця одна ідея пояснює, чому шорсткість керує одночасно і розміром, і яскравістю спекулярного відблиску.

Звідки це походить

Роберт Кук і Кеннет Торренс опублікували «A Reflectance Model for Computer Graphics» у 1982 році, адаптувавши раніше роботу Торренса і Сперроу (1967) з оптичної інженерії. Метод залишався нішевою технікою офлайн-рендерингу 25 років, доки «принципова BRDF» Disney 2012 року та розподіл GGX не зробили його стандартним вибором для рушіїв реального часу (Unreal 4, Unity HDRP, Filament, Three.js).

2. Формула Кука-Торренса

Спекулярний член BRDF Кука-Торренса — це дріб із трьома множниками у чисельнику та нормалізуючим членом у знаменнику:

Спекулярна BRDF Кука-Торренса fspec(l, v) = (D(h) · G(l, v, h) · F(v, h)) / (4 · (n·l) · (n·v))
  • Dфункція розподілу нормалей: яка частка мікрофасетів орієнтована саме вздовж напіввектора h?
  • Gгеометричний член: яка частка цих граней не затінена сусідніми гранями на шляху до світла і не замаскована на шляху до камери?
  • Fчлен Фреснеля: яка частка світла відбивається (а не заломлюється/поглинається) під цим кутом огляду?
  • 4·(n·l)·(n·v) — нормалізуючий множник, що коригує зміну міри між простором нормалей мікрофасетів і простором вихідних напрямків.

Кінцевий колір також потребує дифузного (ламбертівського) члена для світла, що заломлюється в поверхню, розсіюється і виходить назад — див. розділ 7.

3. D — функція розподілу нормалей (GGX)

Кук і Торренс спочатку використовували розподіл Бекмана; сучасні рушії практично завжди використовують GGX (також відомий як Трoubridge-Reitz), тому що він має довші «хвости» — повільніше згасає під кутами ковзання, значно краще відповідаючи виміряним реальним матеріалам і створюючи м'які, світлі краї відблиску, характерні для фізично коректного рендерингу.

GGX / Trowbridge-Reitz NDF D(h) = α² / (π · ((n·h)² · (α² − 1) + 1)²)

де α = шорсткість² (перцептивне маппування — піднесення шорсткості до квадрата дає інтуїтивніший контроль повзунка для художників, популяризоване принциповою BRDF Disney)
float distributionGGX(vec3 N, vec3 H, float roughness) {
  float a      = roughness * roughness;
  float a2     = a * a;
  float NdotH  = max(dot(N, H), 0.0);
  float denom  = (NdotH * NdotH * (a2 - 1.0) + 1.0);
  denom = PI * denom * denom;
  return a2 / max(denom, 0.0000001);
}

4. G — геометрія (затінення-маскування Сміта)

Навіть грань, зорієнтована для відбиття світла у камеру, може бути заблокована: затінення зупиняє світло від джерела, а маскування зупиняє відбите світло на шляху до камери. Модель Сміта розкладає це на два незалежні члени, що перемножуються, кожен з яких використовує той самий будівельний блок «Schlick-GGX»:

Геометричний член Сміта G(n, v, l, k) = G1(n, v, k) · G1(n, l, k)

G1(n, x, k) = (n·x) / ((n·x) · (1 − k) + k)

де k = (шорсткість + 1)² / 8   (пряме освітлення, за Karis/Epic 2013)
float geometrySchlickGGX(float NdotX, float k) {
  return NdotX / (NdotX * (1.0 - k) + k);
}

float geometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
  float r  = (roughness + 1.0);
  float k  = (r * r) / 8.0;
  float ggx1 = geometrySchlickGGX(max(dot(N, V), 0.0), k);
  float ggx2 = geometrySchlickGGX(max(dot(N, L), 0.0), k);
  return ggx1 * ggx2;
}

Під кутами ковзання обидва скалярні добутки прямують до нуля, тому G коректно затемнює відблиск — саме це не дає шорстким сферам виглядати як світні диски на своєму силуеті.

5. F — Фреснель (наближення Шліка)

Ефект Фреснеля стверджує, що будь-який матеріал стає більш відбивним під кутами ковзання — саме тому нерухоме озеро виглядає як дзеркало біля горизонту, але прозоре, якщо дивитися прямо вниз. Точні рівняння Фреснеля залежать від показника заломлення матеріалу і дорогі; наближення Крістофа Шліка 1994 року точне до частки відсотка і коштує один pow():

Fresnel-Schlick F(v, h) = F₀ + (1 − F₀) · (1 − (v·h))⁵

F₀ — це відбивна здатність при нормальному падінні (погляд прямо на поверхню). Саме тут підключається робочий процес metalness: діелектрики (пластик, дерево, шкіра) мають низьке, ахроматичне F₀ близько 0.04, тоді як метали мають високе, забарвлене F₀, що дорівнює кольору їхнього альбедо (золото ≈ (1.0, 0.71, 0.29)).

F₀ = mix(vec3(0.04), albedo, metalness)
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
  return F0 + (vec3(1.0) - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}

6. Складаємо разом у GLSL

Мінімальний фрагментний шейдер Кука-Торренса з одним джерелом світла, що поєднує усі три члени плюс ламбертівську дифузну складову:

vec3 cookTorrance(vec3 N, vec3 V, vec3 L,
                     vec3 albedo, float roughness, float metalness,
                     vec3 radiance) {
  vec3  H  = normalize(V + L);
  vec3  F0 = mix(vec3(0.04), albedo, metalness);

  float D  = distributionGGX(N, H, roughness);
  float G  = geometrySmith(N, V, L, roughness);
  vec3  F  = fresnelSchlick(max(dot(H, V), 0.0), F0);

  vec3  kS = F;                       // частка спекулярного внеску
  vec3  kD = (vec3(1.0) - kS) * (1.0 - metalness); // у металів немає дифузії

  float NdotL = max(dot(N, L), 0.0);
  float NdotV = max(dot(N, V), 0.0);
  vec3  numerator   = D * G * F;
  float denominator = 4.0 * NdotV * NdotL + 0.0001;
  vec3  specular = numerator / denominator;

  return (kD * albedo / PI + specular) * radiance * NdotL;
}
Кілька джерел світла та IBL

Для повної сцени ви накопичуєте результат цієї функції для кожного прямого джерела світла, а потім додаєте член image-based lighting (IBL) з попередньо відфільтрованої кубічної карти оточення для фонового внеску — карта дифузного опромінення, попередньо відфільтрований ланцюг mip-рівнів для спекулярності та LUT-таблиця інтеграції BRDF (наближення split-sum, Karis 2013).

7. Збереження енергії та дифузний член

Фізично правдоподібний матеріал не повинен відбивати більше світла, ніж отримав. Наведений вище код забезпечує це через kD = (1 - kS) * (1 - metalness): та частка світла, що не відбилася спекулярно (kS = F), доступна для дифузного розсіювання, а метали отримують нульову дифузію, бо все світло, що потрапляє у провідник, майже миттєво поглинається вільними електронами — у металів немає підповерхневого розсіювання.

Ламбертівський дифузний член albedo / π сам по собі є наближенням — реальна дифузна відбивна здатність не є ідеально рівномірною в усіх напрямках (Oren-Nayar — альтернатива для шорсткіших поверхонь) — але для більшості випадків використання PBR ця похибка непомітна на тлі внеску спекулярного члена.

🔺 Побачити рендеринг поверхонь наживо

Симуляція Ray Marching дозволяє налаштовувати освітлення на SDF-поверхнях у реальному часі — гарний майданчик для експериментів із шорсткістю та Фресnelем на око.

Читати статтю про Ray Marching →

🔗 Схожі статті

🎨PBR — теорія 🌈Path Tracing 🔺Ray Marching 🎞️TAA