Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Алгоритмы / Графика / demodesign 3D programming FAQ /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


Освещение - Программирование от RIN.RU
Освещение



Освещение по Фонгу


Здесь принято делать, как минимум, такие предположения:



  • Ks = 0 (то есть грань не отражает свет, а только рассеивает)




Да-да, здесь нет никакой ошибки. Практически все обычно используемые (в demo
по меньшей мере) методы т.н. "освещения по Фонгу" НЕ учитывают отраженной
компоненты освещенности.


Здесь будет рассказано о самом, наверное, популярном методе освещения по
Фонгу, который сводит освещение к текстурированию по определенной текстуре.


Этот метод базируется на таких добавочных предположениях:



  • L - константа (как бы точечный источник, удаленный бесконечно далеко) длина единичной нормали к объекту при интерполяции между вершинами
    грани НЕ меняется




То есть, утверждается, что если в вершинах нормаль к объекту имеет длину 1, то при интерполяции этой нормали между вершинами по какой-то грани мы будем получать в каждой точке нормаль той же самой длины 1. На самом деле это вовсе даже не так, но для несильно разнящихся углов наклона нормалей приблизительно верно.


Так как Ks = 0, а длина N по предположению равна 1 на всей грани, имеем:


intensity = ambient + amp * (N * L).



Рассмотрим упрощенный случай, когда вектор L = (0,0,1). Общий случай можно без особых вычислительных затрат привести к этому упрощенному, как - будет рассказано чуть позже. Так вот, в этом случае


intensity = ambient + amp * (N.x * L.x + N.y * L.y + N.z * L.z) =
= ambient + amp * (N.x * 0 + N.y * 0 + N.z * 1) =
= ambient + amp * N.z =
= ambient + amp * sqrt(1 - (N.x * N.x + N.y * N.y)).


То есть интенсивность выражается через N.x, N.y, а эти величины меняются линейно. N.x и N.y у нас - числа с плавающей запятой от -1 до 1 (т.к. длина вектора равна 1), интерполировать их - занятие медленное, да корень считать раз в пиксел тоже не хочется. Поэтому вместо интерполяции N.x и N.y обычно интерполируют, например, 128*(N.x+1) и 128*(N.y+1), причем уже в целых числах. Тогда все возможные значения таким образом отмасштабировнных N.x, N.y - это 0, 1, ..., 255. Поэтому можно заранее посчитать табличку значений intensity для каждой пары отмасштабировнных N.x, N.y.


То есть, мы линейно интерполируем 128*(N.x+1) и 128*(N.y+1) (эти значения меняются тоже линейно, раз N.x, N.y меняются линейно) и по ним по таблице определяем интенсивность. Это и есть текстурирование, только в качестве текстуры используется таблица освещенности размером 256x256 (или любым другим), а в качестве координат текстуры u, v для каждой вершины берутся отмасшатбированные координаты нормали в этой вершине.


Таблица, согласно всего вышеупомянутого, считается так:



// ...
for (i = 0; i < 256; i++) {
for (j = 0; j < 256; j++) {
r =
pow((i - 128) / 256.0, 2) + // это N.x*N.x
pow((j - 128) / 256.0, 2); // это N.y*N.y
// длина N меньше 1, поэтому r > 1 быть не может
if (r > 1) r = 1;
phongTable[i][j] = amp * sqrt(1 - r);
}
}
// ...




Правда, обычно используют другую - нелинейную - таблицу, видимо, хоть для какой-то компенсации всяких ошибок линеаризации... Результаты выглядят действительно получше. Считается она так:



// ...
for (i = 0; i < 256; i++)
for (j = 0; j < 256; j++)
phongTable[i][j] =
amp * pow(sin(i * PI / 256) * sin(j * PI / 256), 4);
// ...




Для полного комплекта осталось только привести кусочек кода по вычислению координат в этой таблице:



// ...
len = N.x * N.x + N.y * N.y + N.z * N.z;
N.x /= len; // на случай, если длина N не равна 1
N.y /= len;
N.z /= len;
u = (1 + N.x) * 128; // собственно расчет координат
v = (1 + N.y) * 128;
// ...




Теперь вернемся к вопросу о том, как привести случай с произвольным вектором освещения к только что рассмотренному, где L = (0,0,1). Здесь все вроде бы просто. Просто применим к нормалям в вершинах любой поворот, совмещающий наш произвольный вектор света с вектором (0,0,1). Скалярное произведение при этом не изменяется, поэтому так делать можно. Ну, а после этого поворота уже имеем только что расписанный упрощенный случай.


Этот поворот нормалей в каждой вершине не требует практически никаких затрат по следующей причине. Поворот сам по себе, конечно, достаточно медленная процедура и процессорное время отъедает. Но при движении и вращении камеры и объекта мы все равно должны будем соответственно поворачивать нормали. Так вот, эти два поворота можно совместить в один. Если использовать матрицы, все это делается совсем просто - достаточно перемножить (в нужном порядке!) матрицу собственного поворота объекта, матрицу перехода от произвольной камеры к нашей "стандартной" камере и матрицу перехода от произвольного вектора света к "стандартному" вектору света (0,0,1). Т.е. добавится расчет этой матрицы перехода и одно матричное умножение на объект, а это уже мелочь.


<<<  НазадВперед  >>>
 1  2  3  4 


 8  Комментарии к статье  8 8  Обсудить в чате

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь