sunLoadingImage
whowedImag
decoration left 1
decoration left 2
transhome
transprojects
transgallery
transarticles
decoration rigth
English
Українська
Show/Hide search bar
black cat logo variable logo
[11 Сер 2011]

Освітлення по моделі Бліна-Фонга в GLSL

У цьому уроці буде розглянуто одну з найпростіших моделей освітлення в комп'ютерній графіці - модель освітлення по Бліну-Фонгу. Ця модель освітлення до недавнього часу використовувалася у фіксованих графічних конвеєрах таких графічний API як OpenGL та DirectX. Тепер, у найновіших версіях OpenGL та DirectX, розробник сам повинен запрограмувати через шейдери те, як освітлюються об'єкти візуалізації.
Також будуть розглянуті моделі затінення трикутників: плоске затінення, затінення по Гуро і затінення по Фонгу.
Модель освітлення Бліна-Фонга
Модель освітлення по Фонгу використовує комбінацію дифузного відбиття (як у шорстких неблискучих матеріалів), дзеркального відбиття (як у гладких блискучих матеріалів) та апроксимації заповнюючого освітлення (у місцях сцени, які неосвітлені прямими променями світла). Це модель локального освітлення точок на поверхні, де результат освітлення не залежить від інших об'єктів у сцені чи від повторно відбитих променів світла.
Заповнююче освітлення
Заповнююче освітлення відповідає освітленню, яке не створене прямими променями світла, тобто це непряме освітлення. В реальності таке освітлення виникає завдяки багаторазовому відбиванню променів світла від різних об'єктів. Найпростішим способом реалізації заповнюючого освітлення є використання константного значення інтенсивності світла для усіх об'єктів, що візуалізуються. Таке наближення заповнюючого освітлення використовується в моделі освітлення Фонга. Розрахунок заповнюючого освітлення є дуже швидким, але і дуже нереалістичним.
Різна інтенсивність заповнюючого освітлення
Головними недоліками такого освітлення є те, що освітленими будуть навіть об'єкти до яких не можуть дійти промені світла і те, що заповнююче освітлення буде однаковим для усієї сцени. Також не враховується те, що відбиті від об'єктів промені світла змінюють колір. Наприклад, середовище навколо червоного об'єкта частково освічується червоним кольом, навіть якщо колір світла не є червоним. Але заповнююче освітлення в моделі Фонга не врахує цей ефект.
Апроксимація заповнюючого освітлення через використання константного значення

Розсіяне освітлення через багаторазове відбиття променів світла

Дифузне відбиття
Дифузне відбиття є наслідком освітлення об'єкта прямими променями світла, які відбившись від поверхні об'єкта розсіюються в усі сторони з однаковою інтенсивністю. Незалежно як дивиться камера на об'єкт, дифузне відбиття вважається однаковим. Таке визначення дифузного відбиття відповідає ламбертовому відбиттю. Ламбертове відбиття визначає як ведуть себе промені світла відбиті від матової поверхні. Видима яскравість такої поверхні не залежить від кута під яким дивиться на неї камера. Для ламбертового відбиття, інтенсивність рахується по закону косинуса ламберта: Рівень відбиття світла прямо пропорційний косинусу кута між нормалю (N) і вектором до світла (L):
Різна інтенсивність заповнюючого освітлення
Розрахунок інтенсивності дифузного відбиття

Залежність дифузного відбиття від нормалі (N) і вектора до світла (L)

Дзеркальне відбиття
Дзеркальне відбиття є наслідком освітлення об'єкта прямими променями світла, які відбившись від поверхні об'єкта розсіюються вздовж відбитого від поверхні об'єкта вектора світла (L). Дзеркальне відбиття створює бліки. Блік - яскрава пляма, яка з'являється на дзеркальних об'єктах, коли дзеркальне відбиття світла від поверхні об'єкта потрапляє у камеру. Світло йде від джерела світла до позиції точки, яка освітлюється, і відбивається по формулі відбитого вектора. Дзеркальне відбиття є видимим тільки якщо напрям камери (V) та напрям відбитих променів світла (R) є достатньо однаковим.
Різні значення дзеркальності для одного і того ж обєкта
Дзеркальне відбиття світла вздовж відбитого вектора R
Тобто дзеркальне відбиття видно тільки тоді, коли промені світла відбиваються від поверхні у камеру. Для розрахунку блікового освітлення використовується нормаль в точці (N), вектор від точки до світла (L), а також вектор від точки до камери (V). Вектор до світла відбивається відносно нормалі і утворює відбитий вектор (R):
Розрахунок відбитого вектора
Далі застосовується принцип ламбертового відбиття для відбитого вектора (R) та вектора до камери (V) (тобто інтенсивність рахується як косинус кута між цими векторами). Результат виходить схожим до дифузного освітлення - освітлення з інтенсивністю, що повільно затухає. Але бліки у реальності менші і яскравіші. Щоб зменшити розмір бліків, розраховану інтенсивність підносять до степення. Чим більший степінь, тим менші і чіткіші бліки. Значення степені називається блиск (shininess). Щоб збільшити інтенсивність бліків, значення інтенсивності домножують на константне значення.
Розрахунок інтенсивності дзеркального відбиття через вектори R і V
Замість розрахунку відбитого ветора (R) можна розрахувати половинний вектор (H), який є середнім нормалізованим вектором між вектором до камери (V) і вектором до світла (L). Тепер дзеркальне відбиття рахується як косинус між нормаллю (L) та половинним ветором (H). Модифікація з половинним вектором називається модель освітлення по Бліну-Фонгу. Такий розрахунок дзеркального відбиття використовувався у фіксованих конвеєрах OpenGL та DirectX.
Розрахунок інтенсивності дзеркального відбиття через вектори N і H
Використання половинного вектора H
Блікове освітлення розраховується тільки для поверхонь орієнтованих нормалями до світла.
Розрахунок дзеркального відбиття тільки коли поверхня орієнтована до джерела світла
Комбінація компонентів освітлення
Джерело світла має інтенсивність і колір, які можуть бути окремими значенями для дифузного і дзеркального відбиття, та для заповнюючого освітлення. Колір і інтенсивність світла можна закодувати в RGB значення, тобто трактувати значення як інтенсивність для кожного окремого канала. Дифузний колір світла і інтенсивність позначається як Kdif, дзеркальний - Kspec, заповнюючий - Kamb. Значення інтенсивності джерела світла домножується на розраховане відповідне значення освітлення.
Комбінація дифузного, дзеркального та заповнюючого компонентів моделі Бліна-Фонга
Також в моделі Бліна-Фонга використовується матеріал об'єкта. Параметрами матеріалу є рівень дифузного відбиття Mdif, рівень дзеркального відбиття Mspec і рівень відбиття заповнюючого світла Mamb. Якщо рівень відбиття рівний 0, то поверхня не відбиває промені світла, а якщо рівень рівний 1, то відбувається повне відбиття променів світла. Аналогічно до інтенсивності світла, рівень відбиття може бути різний для кожного каналу RGB кольору.
Для розрахунку кінцевого кольору кожної точки об'єкта додають усі компоненти освітлення моделі Бліна-Фонга, та перемножуються відповідні інтенсивності світла та рівні відбиття матеріалу:
Розрахунок дзеркального відбиття тільки коли поверхня орієнтована до джерела світла
Моделі затінення
Модель затінення визначає на якому шейдерному рівні і з якою якістю розраховується освітлення трикутників об'єкта. Можна розраховувати освітлення один раз для кожного трикутника, по одному разу для кожної вершини трикутника чи по разу для кожного фрагмента трикутника, що візуалізується. В залежності від цього залежить якість освітлення, швидкість обрахунків, ефект і тд. Далі розглянуті базові моделі затінення.
Плоске затінення, затінення по Гуро і затінення по Фонгу
Плоске затінення
Плоска модель затінення розраховує освітлення так, що усі точки трикутника мають однакове освітлення. Це досягається через використання однакової нормалі для кожної вершини трикутника. Таким чином нормаль є однаковою в усіх точках трикутника, і освітлення, яке залежить від нормалі буде мати однакову інтенсивність на всьому трикутнику. Нормаль можна передати як атрибут вершини, але в такому разі доведеться продублювати усі вершини, які належать більш ніж одному трикутнику, щоб вершини кожного трикутника мали необхідну нормаль. Іншим способом візуалізації з плоским затіненням є розрахунок спільної нормалі у геометричному шейдері. В такому разі нормаль розраховується як перпендикуляр до двох векторів, що формують трикутник.
Затінення по Гуро
В затінені по Гуро береться до уваги, що кожна вершина трикутника може мати різну нормаль. У вершинному шейдері для кожної вершини розраховується освітлення залежно від нормалі. Нормалі можуть бути різні, а отже і освітлення у вершинах буде різне. Розрахований колір і інтенсивність освітлення передаються у фрагментний шейдер. Кожен фрагмент отримує інтерпольований по трьох вершинах колір і інтенсивність освітлення.
Недоліком цієї моделі затінення є те, що освітлення розраховується тільки для вершин, а далі інтерполюється. Якщо модель містить великі трикутник, то буде помітно, що освітлення інтерполюється, а не розраховується для кожного фрагмента трикутника. Найбільше цей недолік є помітним при розрахунку дзеркального освітлення, коли бліки візуалізуються з не гладкою формою, або навіть перестають бути видимими. Це стається через те, що блік може бути розміщений посеред трикутника, але повністю затухати не доходячи до вершин. У вершинах буде розраховано освітлення без бліків, і проінтерпольовано. І блік буде невидимим, так як інтерполяція розраховує проміжні дані, в яких немає інформації про блік. При повороті моделі блік буде з'являтися, коли потраплятиме на вершину, і знову зникати, коли потраплятиме на середину трикутника. Для того, щоб мінімізувати артефакти візуалізації рендерінгу бліків в затінені Гуро можна збільшити кількість трикутників.
Затінення по Фонгу
На відміну від затінення по Гуро, в затіненні по Фонгу освітлення розраховується у фрагментноу шейдрері для кожного фрагмента трикутника. З вершинного шейдера у фрагментний передається не розраховане освітлення, а нормаль вершини. Для кожного фрагмента автоматично розраховується інтерпольована нормаль з трьох нормалей кожної вершини трикутника, і вже з допомогою цієї нормалі розраховується освітлення. Ця модель затінення найкраще підходить для візуалізації бліків.
Навіщо різні моделі затінення?
Кожна модель затінення має свої переваги та недоліки. Плоска модель затінення може використовуватися для стилізованого рендерінгу і для візіалізіції трикутників об'єкта. Модель затінення Фонга найкраще підходить для візуалізації бліків, і є найбільш якісною для реалістичного рендерінгу. Але модель затінення Гуро є кращою по швидкодії за модель Фонга, так як розраховує освітлення тільки у вершинах, а не у фрагментах, і кількість таких обрахунків може бути на порядок нищою.
Шейдера для освітлення по моделі Бліна-Фонга
Далі наведено вершинний і фрагментний GLSL шейдера, які проводять освітлення по моделі Бліна-Фонга з затіненням по Фонгу:
Вершинний шейдер:
#version 330

// атрибути
layout(location = 0) in vec3 i_position; // xyz - position
layout(location = 1) in vec3 i_normal; // xyz - normal
layout(location = 2) in vec2 i_texcoord0; // xy - texture coords

// матриці
uniform mat4 u_modelMat;
uniform mat4 u_viewMat;
uniform mat4 u_projMat;
uniform mat3 u_normalMat;

// позиція світла і камери
uniform vec3 u_lightPosition;
uniform vec3 u_cameraPosition;

// дані для фрагментного шейдера
out vec3 o_normal;
out vec3 o_toLight;
out vec3 o_toCamera;
out vec2 o_texcoords;

///////////////////////////////////////////////////////////////////

void main(void)
{
   // позиція у світові координати
   vec4 worldPosition = u_modelMat * vec4(i_position, 1);

   // нормаль у світові координати
   o_normal = normalize(u_normalMat * i_normal);

   // напрям до світла
   o_toLight = normalize(u_lightPosition - worldPosition.xyz);

   // напрям до камери
   o_toCamera = normalize(u_cameraPosition - worldPosition.xyz);

   // текстурні координати у фрагментний шейдер
   o_texcoords = i_texcoord0;

   // екранні координати
   gl_Position = u_projMat * u_viewMat * worldPosition;
}
Фрагментний шейдер:
#version 330

// дані з вершинного шейдера
in vec3 o_normal;
in vec3 o_toLight;
in vec3 o_toCamera;
in vec2 o_texcoords;

// текстура з дифузним кольором об'єкта
layout(location = 0) uniform sampler2D u_diffuseTexture;

// колір для фреймбуфера
out vec4 resultingColor;

/////////////////////////////////////////////////////////

// параметри світла та можливі параметри
uniform vec3 u_lightAmbientIntensitys; // = vec3(0.6, 0.3, 0);
uniform vec3 u_lightDiffuseIntensitys; // = vec3(1, 0.5, 0);
uniform vec3 u_lightSpecularIntensitys; // = vec3(0, 1, 0);

// параметри матеріалу об'єкта
uniform vec3 u_matAmbientReflectances; // = vec3(1, 1, 1);
uniform vec3 u_matDiffuseReflectances; // = vec3(1, 1, 1);
uniform vec3 u_matSpecularReflectances; // = vec3(1, 1, 1);
uniform float u_matShininess; // = 64;

/////////////////////////////////////////////////////////

// повертає інтенсивність розсіяного освітлення
vec3 ambientLighting()
{
   return u_matAmbientReflectance * u_lightAmbientIntensity;
}

// повертає інтенсивність дифузного відбиття
vec3 diffuseLighting(in vec3 N, in vec3 L)
{
   // розрахунок як для ламбертового відбиття
   float diffuseTerm = clamp(dot(N, L), 0, 1) ;
   return u_matDiffuseReflectance * u_lightDiffuseIntensity * diffuseTerm;
}

// повертає інтенсивність дзеркального відбиття
vec3 specularLighting(in vec3 N, in vec3 L, in vec3 V)
{
   float specularTerm = 0;

   // розрахунок дзеркального відбиття тільки якщо поверхня
   // орієнтована до світла

   if(dot(N, L) > 0)
   {
      // половинний вектор
      vec3 H = normalize(L + V);
      specularTerm = pow(dot(N, H), u_matShininess);
   }
   return u_matSpecularReflectance * u_lightSpecularIntensity * specularTerm;
}

void main(void)
{
   // нормалізувати вектори після інтерполяції
   vec3 L = normalize(o_toLight);
   vec3 V = normalize(o_toCamera);
   vec3 N = normalize(o_normal);

   // дістати компоненти освітлення
   float Iamb = ambientLighting();
   float Idif = diffuseLighting(N, L);
   float Ispe = specularLighting(N, L, V);

   // дифузний колір об'єкта з текстури
   vec3 diffuseColor = texture(u_diffuseTexture, o_texcoords).rgb;

   // комбінація усіх компонентів освітлення і дифузного кольору об'єкта
   resultingColor.xyz = diffuseColor * (Iamb + Idif + Ispe);
   resultingColor.a = 1;
}



Sun and Black Cat- Ігор Дихта (igor dykhta email) © 2007-2014