Здавалка
Главная | Обратная связь

Создание источников света в DirectX11



Итак, вы уже догадываетесь, что свет в DirectX11 не обойдется без шейдеров. На самом деле, дело обстоит еще серьезней. В DirectX11 нельзя просто создать источник света указав его координаты и цвет и забыв об этом, поручив все расчеты света системе DirectX.

Но нас не смущает столь острая постановка вопроса, так как шейдеры не только не помешают нам создать освещение объекту но и помогут добиться в будущем некоторых эффектов.

Итак, разберем обычный случай освещения – единичный источник света. Если вы изучали рисование то вы знаете, что чтобы правильно закрасить объект, надо его часть, обращенную к свету сделать светлой, а ту, куда свет не попадает темной. При этом есть четкая граница где начинается тень. Давайте переведем эту проблему на язык математики. Имеется поверхность X произвольной формы и источник света с координатами L.

Необходимо определить угол между каждой из множества точек поверхности и лучем, проходящим от этой точки поверхности к источнику света. Чем меньше будет угол, тем светлее будет часть поверхности. На самом деле, это упрощенная постановка вопроса. Более точная постановка вопроса такая: для каждой точки поверхности необходимо определить угол между нормалью к поверхности и вектором, проходящим через центр источника света и точкой поверхности.

Определив нужный угол, мы можем задать функцию от этого угла, результатом функции будет цвет точки объекта. Чем угол меньше (острее), тем ярче цвет. При угле 90 градусов объект перестает быть освещен. Если угол больше 90 градусов, то это темные, теневые участки объекта. Решим задачу. Обозначим каждую точку поверхности X символом n, нормаль к поверхности обозначим N. Искомая формула будет следующая:

Треугольными скобками здесь мы обозначим операцию нормализации, то есть приведения вектора к длинне 1. Прямыми скобками обозначается операция определения модуля вектора, то есть величины вектора. Таким образом, мы имеем два вектора – вектор нормали к поверхности N для каждой точки и второй вектор – вектор направления луча от точки поверхности к источнику света – вектор L-n. Кстати, исходные данные для операции L-n является координаты двух точек, результат же операции – не координаты, а вектор. На самом деле, в этой формуле даже arcsin не обязателен, так как мы всего-лишь вычисляем цвет, а не траекторию полета на луну. И дело не в точности вычислений, а в том что просто нет необходимости вычислять каждый раз arcsin, к тому-же это может и замедлить шейдер. Так что формула расчета интенсивности цвета упрощается:

Ну а что делать если источников света несколько? На самом деле, эта формула будет прекрасно работать и в этом случае. Просто просуммируем все данные:

То есть мы просто просуммируем данные, подставляя для каждого источника света новые координаты и затем поделим результат на количество источников света. А вот информация к размышлению. Мы все сделали правильно, однако чем острее угол a, тем меньше результат, а значит и темнее цвет… Стоп. Что-то здесь не так… Исходные данные правильны и формулы правильны, но результат чуть не такой, как хотелось бы. Нам ведь нужно точку в освещенных местах делать светлой, а не темной. Всем известно, что любые формулы хороши тогда, когда их можно еще больше упростить. И если нечего уже упрощать, значит формула имеет окончательный вид и её нужно высечь на глиняной дощечке. Сделаем так:

Но 1-a это как раз то, что нужно, ведь если а малое, то 1-а самое большое, а значит и цвет большой. Так что окончательно имеем:

и

Мы заменили операцию векторного умножения векторов, операцией скалярного умножения (crossproduct на dotproduct). К тому-же при скалярном умножении векторов получается не вектор а число, так что и модуль вектора вычислять не нужно. Вектор и так самостоятельно сократился, потеряв все данные о направлении, и оставив нужную нам длину. Получившаяся формула является хорошей информацией к размышлению, ведь одной небольшой формулой мы просчитали освещение объекта, освещаемого произвольным количеством источников. Неужели и правда всё так просто? Да, именно так просто. Теперь у нас есть данные для того, чтобы написать шейдер для освещения. Можно быть уверенным, что даже хорошо неподготвленный к шейдерам программист с легкостью поймет его с учетом текущего контекста беседы об освещении. Определим тип входящих данных вершинного буфера:

struct VS_INPUT { float4 Pos : POSITION; float3 Normal : NORMAL; float2 Tex: TEXCOORD; }

Обратите внимание, что теперь в пиксельныйшейдер мы должны передавать дополнительую информацию для каждой вершины – нормаль к поверхности объекта. Теперь определим константы шейдера для хранения информации об источниках освещения, предположим у нас имеется максимально пять источников:

float4 LigthPosition[5]; float4 LightColor[5];

Теперь определим фрагмент вершинного шейдера, в котором мы будем рассчитывать освещенность объекта по формуле.

for (int i=0; i<5; i++) output.Color+=dot(normalize(Normal),normalize((float3)vLightPosition[i]-(float3)Pos) )*vLightColor[i];

Если исходные данные нормалей представляют из себя нормализованные вектора, то есть если их модуль равняется единица то вместо normalize(Nomal) можно написать просто Normal.







©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.