Освещение по Ламберту

Это, видимо, самое сильное упрощение формулы, которое можно придумать.Делаются такие предположения:

- V не сильно зависит от P, т.о. V принимается постоянным для всей грани

- L не сильно зависит от P, т.о. L принимается постоянным для всей грани

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

- нормаль к объекту N равна нормали к грани n в любой точке грани P

В этом случае формула принимает вид

intensity = ambient + amp * cos(n, L),

где n - нормаль к грани.

Причем в этой формуле, по предположениям, нет величин, зависящих от P. Так что освещенность равна константе для всей грани, то есть все точки грани освещены Освещение по Ламберту одинаково. Тогда достаточно посчитать ее лишь один раз на грань. Осталось упомянуть, что освещение по Ламберту обычно принято называть flat shading.

39. Освещение по Гуро

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

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

- освещенность меняется по грани линейно

Ну, а раз освещенность меняется линейно - так же, как и координаты текстуры u, v - можно использовать точно такую же процедуру, как для старого доброго текстурирования, только вместо двух координат текстуры u, v нам надо будет интерполировать одну величину - освещенность. В вершинах она считается по той же самой формуле (с учетом Ks = 0, конечно), intensity = ambient + amp * cos Освещение по Ламберту(N, L).

Здесь за N в вершине берется как раз нормаль к объекту в вершине (vertex normal), посчитанная так, как описано в 5.2.

То есть, для написания процедуры освещения по Гуро достаточно написать три строчки расчета освещенности (обозначать ее будем c) в вершинах грани по приведенной здесь формуле и заменить в своей процедуре текстурирования u на c, выкинув при этом v (оно нам не нужно, нас интересует только c). Чтобы добавить освещение по Гуро в текстурирование, надо, соответственно, добавить полностью скопированный с расчета u расчет c. Что делать с полученным по цвету найденного через (u, v) в текстуре пиксела и Освещение по Ламберту освещенности с, описано в соответствующем пункте.

Заметим, что освещение по Гуро - это не линейная интерполяция освещенности по изображению грани на экране, как это обычно принято и понимать и делать.

Освещенность меняется линейно по трехмерной грани, а вовсе не по ее проекции.

То есть, вообще говоря, здесь мы тоже должны учитывать перспективу. Правда, линии одинаковой освещенности все равно будут прямыми, но выглядеть картинка, освещенная по Гуро с учетом перспективы, будет все же покорректнее.

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

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

- 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

if (r > 1) r = 1; // длина N меньше 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). Т.е. добавится расчет этой матрицы перехода и одно матричное умножение на объект, а это уже мелочь.

41. Алгоритм обратной трассировки лучей


documentagbkkjp.html
documentagbkrtx.html
documentagbkzef.html
documentagblgon.html
documentagblnyv.html
Документ Освещение по Ламберту