opengl
Базовое освещение
Поиск…
Модель освещения Phong
ПРИМЕЧАНИЕ. В этом примере используется WIP, он будет обновляться с помощью диаграмм, изображений, других примеров и т. Д.
Что такое Фонг?
Phong - очень простая, но реальная легкая модель для поверхностей, которая состоит из трех частей: окружающего, диффузного и зеркального освещения.
Освещение окружающей среды:
Эмбиентное освещение - это простейшая из трех частей для понимания и расчета. Окружающее освещение - это свет, который наводняет сцену и равномерно освещает объект во всех направлениях.
Две переменные в окружающем освещении - это сила окружающей среды и цвет окружающей среды. В вашем шейдере фрагмента для окружающей среды будет работать следующее:
in vec3 objColor;
out vec3 finalColor;
uniform vec3 lightColor;
void main() {
float ambientStrength = 0.3f;
vec3 ambient = lightColor * ambientStrength;
finalColor = ambient * objColor;
}
Диффузное освещение:
Диффузное освещение немного сложнее, чем окружающее. Диффузное освещение - это направленный свет, по существу означающий, что лица, обращенные к источнику света, будут лучше освещены, а лица, указывающие в сторону, будут темнее из-за того, как свет бьет их.
Примечание: диффузное освещение потребует использования нормалей для каждого лица, которое я не буду показывать, как рассчитать здесь. Если вы хотите узнать, как это сделать, просмотрите 3D-математическую страницу.
Для моделирования отражения света в компьютерной графике используется функция распределения двунаправленного отражения (BRDF). BRDF - это функция, которая дает связь между светом, отраженным вдоль исходящего направления, и светом, падающим из входящего направления.
Идеальная диффузная поверхность имеет BRDF, которая имеет такое же значение для всех инцидентов и исходящих направлений. Это существенно уменьшает вычисления и, следовательно, обычно используется для моделирования диффузных поверхностей, поскольку это физически правдоподобно, хотя в реальном мире нет чистых диффузных материалов. Этот BRDF называется ламбертовским отражением, потому что он подчиняется косинусному закону Ламберта.
Ламбертовское отражение часто используется в качестве модели диффузного отражения. Этот метод заставляет все замкнутые многоугольники (такие как треугольник в трехмерной сетке) отражать свет одинаково во всех направлениях при визуализации. Коэффициент диффузии вычисляется по углу между вектором нормали и вектором света.
f_Lambertian = max( 0.0, dot( N, L )
где N
- нормальный вектор поверхности, а L
- вектор к источнику света.
Как это устроено
В общем случае точечное произведение двух векторов равно косинусу угла между двумя векторами, умноженному на величину (длину) обоих векторов.
dot( A, B ) == length( A ) * length( B ) * cos( angle_A_B )
Отсюда следует, что точечный продукт двух единичных векторов равен косинусу угла между двумя векторами, поскольку длина единичного вектора равна 1.
uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )
Если взглянуть на функцию cos (x) между углами -90 ° и 90 °, то мы увидим, что она имеет максимум 1 под углом 0 ° и она опускается до 0 под углами 90 ° и -90 °.
Такое поведение именно то, что мы хотим для модели отражения. Когда нитроральный веток поверхности и направление к источнику света находятся в одном и том же направлении (угол между 0 °), то мы хотим максимировать отражение. Напротив, если векторы ортонормированы (угол между ними равен 90 °), то мы хотим минимум отражения и хотим, чтобы между двумя границами 0 ° и 90 ° выполнялся гладкий и непрерывный функционал.
Если свет рассчитан на вершину, то отражение рассчитывается для каждого угла примитива. Между примитивами отражения интерполируются в соответствии с его барицентрическими координатами. См. Полученные отражения на сферической поверхности:
Итак, чтобы начать с нашего фрагментарного шейдера, нам понадобятся четыре входа.
- Нормали вершин (должны быть в буфере и заданы указателями атрибутов вершин)
- Положение фрагмента (должно выводиться из вершинного шейдера в frag shader)
- Положение источника света (равномерное)
- Светлый (однородный)
in vec3 normal;
in vec3 fragPos;
out vec3 finalColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 objColor;
Внутри основного - это то, где нам нужно сделать математику. Вся концепция диффузного освещения основана на угле между нормальным и световым направлением. Чем больше угол, тем меньше света до 90 °, где вообще нет света.
Прежде чем мы сможем начать вычислять количество света, нам нужен вектор направления света. Это можно получить, просто вычитая положение света из положения фрагмента, которое возвращает вектор из положения света, указывающего на положение фрагмента.
vec3 lightDir = lightPos-fragPos;
Кроме того, продолжайте и нормализуйте normal
и lightDir
векторы, чтобы они были одинаковой длины для работы.
normal = normalize(normal);
lightDir = normalize(lightDir);
Теперь, когда у нас есть наши векторы, мы можем вычислить разницу между ними. Для этого мы будем использовать функцию точечного произведения. В принципе, это принимает 2 вектора и возвращает cos () сформированного угла. Это прекрасно, потому что при 90 градусах он будет давать 0 и при 0 градусах он даст 1. В результате, когда свет указывает прямо на объект, он будет полностью гореть и наоборот.
float diff = dot(normal, lightDir);
Есть еще одна вещь, которую мы должны сделать с расчетным числом, мы должны убедиться, что она всегда положительная. Если вы думаете об этом, отрицательное число не имеет смысла в контексте, потому что это означает, что свет находится за лицом. Мы могли бы использовать оператор if, или мы можем использовать функцию max()
которая возвращает максимум два входа.
diff = max(diff, 0.0);
Сделав это, мы теперь готовы рассчитать окончательный выходной цвет для фрагмента.
vec3 diffuse = diff * lightColor;
finalColor = diffuse * objColor;
Зеркальное освещение:
Работайте в прогрессах, зайдите позже.
комбинированный
Незавершенная работа, зайдите позже.
В приведенном ниже коде и изображении показаны три концепции освещения в сочетании.