Играем в Source SDK, освещение и отражение. Компиляция с освещением

Описание:
Теперь в игре Skyrim SE факелы будут отбрасывать динамические тени, настройка освещения от факела...и многое другое.Для игры Skyrim LE этот мод

Обновление:3.1.1
- Улучшено меню отладки в МСМ.
- Незначительные исправления.
- Скрипт теперь применяется к NPC, если это необходимо.

Обновление:3.1
* Добавлено МСМ меню с настройками мода и более подробными описаниями каждой функции.
* Все модули в МСМ меню можно переключать для повышения совместимости с другими модами.
* Логика Магического света изменена.
* Функциональность для НПС работает без проблем в экстерьерах, то есть если у НПС есть факелы или фонари оснащенные, то у них также будут отбрасываться тени, но лучше не применять эту опцию, могут быть баги. Обходной путь возможен с помощью параметров месторасположения.
* Произведены незначительные исправления.
* Улучшена производительность скрипта.
* Совместимость с ELFX (все через скрипт, без дополнительного esp-файла).
* Совместимость с Wearable Lanterns.
* Улучшена совместимость с заклинанием Свет свечи.
* Мод теперь требует наличие skse64 и SkyUI для настроек.
* ВАЖНО: Если вы обновляетесь с версии 2.2, то удалите мод с помощью книги настроек перед обновлением, в книге есть опция Удалить.

Журнал изменений:
- По доп.ссылке добавлен патч для мода "Smoking Torches and Candles".
- НОВАЯ ОСОБЕННОСТЬ СКАНИРОВАНИЯ. Сканирует область с источниками света с тенями и включает / отключает тени соответственно.
- Удалены некоторые вещи (нет больше бесконечных ям в тестовой комнате qasmoke).
- Теперь мод полу-совместим с модами на изменение моделей факелов. Они будут работать только для NPC, игрок использует уникальную модель факела.
- Опциональная модель факела теперь включена в основной мод.
* Добавлена заметка об освещение внизу в описании мода.

Подробнее:
* Факелы будут отбрасывать динамические тени.
* Заклинание Свет свечи будет отбрасывать динамические тени.
* Заклинание Магический свет будет отбрасывать динамические тени (во время полета).
* Переключение заклинания Свет свечи (использовать его снова, чтобы отключить).
* Настройки яркости / диапазона света прямо в игре через МСМ меню.
* Отключение / включение функций мода в определенных местах или в целом.
* Настройки света осуществляется в игре, такие как яркость, диапазон, тени и автоматическое переключение настроек, чтобы обойти проблемы, связанные с динамическими тенями.
* НОВАЯ ОСОБЕННОСТЬ СКАНИРОВАНИЯ. Сканирует область для источников света с тенями и включает / отключает тени соответственно.
* Патч для мода Wearable Lanterns от Chesko: позволяет фонарю который вы носите отбрасывать тени (только спереди на ремне).
* Все настройки мода через МСМ меню.

Совместимость:

* : совместим (патч прилагается)
* (ELFX): совместим (патч не требуется)
* : совместим (патч не требуется). В данном моде есть вариант ELE с уже встроенным модом "Torches Cast Shadows".
* Моды которые вносят изменения в заклинания Свет свечи и Магический свет могут быть несовместимы.

Заметки:
* Если у вас в игре после установки мода нет теней от факелов, то переоснастите факел или выкиньте и подберите.

Требования:
Skyrim SE 1.5.39.0.8 и выше
2.0.7 и выше
5.2 и выше

При обновлении с 2.2 до 3.1 и выше:
1. Зайдите куда нибудь в помещение где нет факелов горящих, уберите у себя факел из рук в инвентарь. Удалите мод с помощью книги настроек перед обновлением, в книге есть опция Удалить. Сохранитесь в пустой слот, выйдите из игры.
2. Удалите файл Player_Shadows.esp из папки Data в игре.
3. Удалите скрипты _ST_CanFX.pex, _ST_Handler.pex, _ST_Magelight.pex, _ST_Options.pex, _ST_TorchEquip.pex по пути Data/scripts/
4. Удалите скрипты _ST_CanFX.psc, _ST_Handler.psc, _ST_Magelight.psc, _ST_Options.psc, _ST_TorchEquip.psc по пути Data/scripts/source
5. Удалите файл lightspellactorfx.nif по пути Data/meshes/magic/
6. Зайдите в игру, сохранитесь снова но в другой пустой слот сохранений, выйдите из игры и установите новую версию.

Установка: (можно через NMM менеджер или вручную)
1. Берем все содержимое из папки Data в архиве и кидаем в папку Data в игре, подтвердите слияние папок, активировать мод.
2. Если у вас есть мод "Smoking Torches and Candles" (вариант факелы), то скачайте по доп.ссылке патч и установите с заменой файлов, если потребуется.
3. Если у вас есть мод "Wearable Lanterns", то скачайте по доп.ссылке патч и установите с заменой файлов, если потребуется. Работает только с фонарями которые размещены спереди на ремне. Установить патч после Wearable Lanterns и Torches Cast Shadows.
4. Как устанавливать моды

Включает/отключает текстуру окружения карты (т.н. «скайбокс » или попросту небо).
очень малое или нет.

Включает/выключает режим использования текстур с разным разрешением в зависимости от дальности, что позволяет увеличить производительность и избавиться от шума на текстурах объектов, находящихся далеко от игрока.
Влияние на производительность: малое.

Подробнее о технологии мип-маппинга

MIP mapping (от лат. multum in parvo - «много в малом») - метод текстурирования, использующий несколько копий одной текстуры с разной детализацией. Это страшное слово на самом деле означает изменение детализации текстур в зависимости от расстояния от камеры. Методика позволяет избавиться от шума на удалённых объектах и существенно повышает производительность отрисовки.

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

При использовании этого метода вы увидите изображение в высоком разрешении, находясь близко от объекта, и изображение в низком разрешении при удалении от объекта. MIP-mapping снижает мерцание и «зашумлённость» изображения, возникающие при texture mapping.

Мип-маппинг использует некоторые умные методы для упаковки данных о текстурах изображения в памяти. Чтобы использовать MIP mapping, необходимо, взяв все размеры текстур и умножив это число на два, построить одну карту наибольшего размера. Все карты меньшего размера обычно фильтруются и становятся усреднёнными и уменьшенными версиями самой большой карты.

Изображение лучше всего выглядит, когда детализация текстуры близка к разрешению экрана. Если разрешение экрана высокое (текстура слишком маленькая/объект очень близко), получается размытое изображение. Если же разрешение текстуры слишком высокое (текстура слишком велика/объект очень далеко), получаем случайные пиксели - а значит, потерю мелких деталей, мерцание и большой процент промахов в кэш. Получается, что лучше иметь несколько текстур разной детализации и накладывать на объект ту, которая наиболее подходит в данной ситуации.

Чтобы получить доступ к ручной настройке графических эффектов, приведённых ниже, снимите галочку с опции «Автоматическая настройка качества графики» в настройках. Если у вас нет этой опции, ознакомьтесь со статьёй «Отсутствие настроек графики » .

Включает/отключает тени, отбрасываемые объектами на карте, включая танки и дроп .
Влияние на производительность: высокое.

Включает/отключает затенение малоосвещённых участков карты, что позволяет получить более объёмное изображение.
Влияние на производительность: очень высокое.

Включает/отключает дымку над картой.
Влияние на производительность: очень малое или нет.

Тени дают объектам ощущения контакта с поверхностью, тем самым позволяя ощутить глубину и пространство.Статические тени отображаются настолько далеко, насколько идёт рендеринг, но динамические тени могут сильнее сказатся на производительности.Данный документ покажет базовые виды теней которые есть в Unreal Engine 4.

Static Lights

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

Персонаж на картинке выше, тот что слева, стоит под статическим светом, свет и тени никак не взаимодействуют с ним; а тот что справа, стоит под стационарным источником света.

Прямое освещение каскадными картами теней(затенение всей сцены)

Directional Stationary Lights — специальные источники света, т.к. они поддерживают затенение всей сцены посредством Cascaded Shadow Maps , в момент использования статического затенения.Это очень удобно на уровнях с множеством анимированной растительности; вы хотите движущиеся тени вокруг игрока, но не хотите переплачивать за чрезмерное количество каскадов, для покрытия больших дистанций обзора.С увеличением расстояния, динамические тени растворяются среди статических теней настолько, что переход практически незаметен.Чтобы применить данную возможность, просто измените значение Dynamic Shadow Distance StationaryLight в DirectionalLightStationary , чтобы изменить дистанцию растворения.

Тени Стационарных источников света

Динамические объекты (такие как StaticMeshComponents и SkeletalMeshComponents с подвижностью установленной в Movable ) должны быть интегрированны в мировое статическое затенение на дистанции полей карт затенения.Это достигается с помощью теней для каждого объекта.Каждый подвижный объект создаёт 2 динамические тени от стационарного источника света: одну, для управления статической тени проецируемой на объект и вторую, для управления тени проецируемую на остальной мир.С такой настройкой, затенение для стационарных источников света происходит от динамических объектов,которое оно затрагивает.Это означает, что стоимость может варьироваться от очень маленькой, до огромной, в зависимости от того, сколько присутствует динамических объектов.При наличии достаточного количества динамических объектов, более эффективным будет использование Movable освещения. На сцене ниже, сферы — подвижный объект, и все они получают тени от статического мира и проецируют собственные тени, которые соединяются с остальными тенями на отдалении.Фруструм Per Object теней для каждого подвижного объекта также показан.

Per Object тени используются для подвижных компонентов используя теневую карту границ объекта, поэтому границы должны быть точными. Для скелетал мешей это значит, что они должны иметь physics asset . Для частиц — любой фиксированный ограничивающий бокс должен быть настолько велик, чтобы вместить в себя все частицы.

Динамические тени

Подвижные источники света проецируют полностью динамические тени (и освещение) на всём.Информация об освещении не будет запекатся в лайтмапы.Статик меши, Скелетал меши, эффекты, прочее — будут получать и проецировать динамические тени от подвижных источников света.

Динамические тени самые ресурсоёмкие.

Превью теней

Когда редактируете стационарное или статическое освещение, тени могут стать «незапечёнными», Preview Shadowing показывает вам как будут выглядеть ваши тени после запекания.

Такие (имеется ввиду незапечённые) тени показываются в редакторе с наложенным поверх текстом «Preview «, для распознавания их среди других теней.

Если вы протестируете свою игру в редакторе, до того как перезапечёте освещение, то превью тени исчезнут, так как они существуют только лишь в режиме редактирования, но никак не в режиме игры в редакторе.

Для того, чтобы получить тени из превью теней, вам необходимо выбрать опцию Build Lighting из меню Build .

Вы можете отключить превью теней посредством снятия галочки с Preview Shadows Indicator во вьюпорте Show/Visualize меню.

Если вы хотите изменить текст материал функции освещения, которая проецирует этот текст, то вы можете его найти в: Engine/EditorMaterials/PreviewShadowIndicator.

Всё вместе

Когда все тени собраны вместе, каждая из которых привносит свои сильные стороны и компенсирует слабые стороны других — они впечатляют своим видом.

При создании сайта вы наверняка пользуетесь возможностями CSS, для добавления теней к элементам на странице. Этот способ подходит тогда, когда вам требуются лишь стандартные тени CSS.

Shine позволяет придать динамику и получить в результате красивые эффекты, связанные с тенями. Написана она на JavaScript, что может также понравиться и не любителям библиотек типа jQuery. Работает данный скрипт только в тех браузерах, которые поддерживают CSS свойства: text-shadow и box-shadow. Если браузеру потребуются префиксы к этим свойствам, то скрипт добавит их сам.

Теперь давайте подключим shine:

Добавим CSS и HTML (применять тень мы будем к тексту):

Html{ background-color: #eef3f8; } #headline{ margin: 60px auto auto; color: #fff; text-align: center; font-size: 8em; font-family: sans-serif; font-weight: 800; letter-spacing: -0.02em; line-height: 1.2em; } сайт

Осталось только вызвать скрипт:

Var shine = new Shine(document.getElementById("headline"));

На этой строке мы передали в shine элемент с id=headline, в котором находится интересующий нас текст. По моей задумке я хочу, чтобы тень меняла своё положение при движении мыши. Это можно сделать написанием следующих строк кода:

Window.addEventListener("mousemove", function(event){ shine.light.position.x = event.clientX; shine.light.position.y = event.clientY; shine.draw(); }, false);

Результат выполнения вышеуказанного скрипта можно увидеть в материалах «демо». Думаю, получилось весьма интересно. Но это не единственное, что может shine. У неё есть хороший набор настроек, который поможет вам достичь нужного результата.

  • numSteps - количество нарисованных теней (по умолчанию 8);
  • opacity - прозрачность тени (по умолчанию 0.1);
  • opacityPow - степень прозрачности, применяющейся для каждой тени (по умолчанию 1.2);
  • offset – смещение теней (по умолчанию 0.15);
  • offsetPow – степень смещения, применяющегося для каждой из теней (по умолчанию 1.8);
  • blur - размытие тени (по умолчанию 40);
  • blurPow - степень размытия, применяющейся к каждой тени (по умолчанию 1.4);
  • shadowRGB - цвет тени, указывается в формате RGB (по умолчанию new shinejs.Color(0, 0, 0)).

Для того чтобы применить эти настройки требуется оформить их в специальном объекте shinejs.Config и передать вторым параметром при создании экземпляра shine:

Var config = new shinejs.Config({ numSteps: 3, opacity: 0.5, shadowRGB: new shinejs.Color(253, 0, 0) }); var shine = new Shine(document.getElementById("headline"), config);

Осталось только упомянуть, где обитает эта библиотека. Нашёл я её на просторах github .

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

На его создание меня вдохновил этот пост на реддите, где aionskull использовал карты нормалей в Unity для динамического освещения своих спрайтов. А пользователь с ником gpillow запостил в комментах что он сделал что-то похожее в Love2D. Вот тут 8-мб гифка с результатами. За неё спасибо jusksmit’у.

Итак, что такое динамическое освещение? Это техника в 3D графике, где источник света освещает объекты на сцене. Динамическое потому, что обновляется в реальном времени при движении источника. Довольно стандартная штука в 3D мире и легко применимая к 2D, если, конечно, вы можете использовать преимущества шейдеров.

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

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

Ок, это всё очень здорово, но как получить вектора нормали в 2d игре? Здесь, вообще-то, нет никаких объемных объектов… Однако, здесь нам могут помочь дополнительные текстуры (те самые карты нормалей), в которых будет записана необходимая информация. Я создал 2 таких карты для двух домов в видео повыше и использовал их чтобы посчитать освещение, вот пример:

В начале вы видите обычный спрайт домика без затенения. На второй части картинки расположена карта нормалей этого дома, кодирующая нормали этого домика в цвет текстуры. У вектора есть (x,y,z) координаты, а у пикселя текстуры есть r,g и b компоненты, так что закодировать нормаль реально просто: Возьмем фасад дома, который направлен на юг. Его нормаль будет вектором с координатами . (По хорошему, нормаль должна быть равна (0, 1, 0), но так как вектор мы определяем от -1 до +1, а кодировать надо в промежуток от 0 до 1, то, видимо, автор решил не запариваться и сразу считать нормали от -0.5 до +0.5. прим. перев.)

RGB значения не могут быть отрицательными, так что мы подвинем все значения на 0.5: . Ну и ещё RGB обычно представляется в числе от 0 до 255, так что мы домножим на 255 и получим , или, другими словами, вектор «на юг» будет вот этим светло-зеленым на карте нормалей.

Теперь, когда у нас есть нормали, мы можем позволить графической карте сделать её магию.
Я использую ImpactJS , у него неплохая совместимость с WebGL2D . (Он платный, я рекомендую pixi.js или любая другая графическая библиотека с webgl рендерером. Если знаете ещё аналоги - пишите в комменты! прим. перев.) Используя WebGL2D мы можем легко добавить пиксельный шейдер для освещения:

#ifdef GL_ES precision highp float; #endif varying vec2 vTextureCoord; uniform sampler2D uSampler; uniform vec3 lightDirection; uniform vec4 lightColor; void main(void) { // Берем вектор нормали из текстуры vec4 rawNormal = texture2D(uSampler, vTextureCoord); // Если альфа-канал равен нулю, то ничего не делаем: if(rawNormal.a == 0.0) { gl_FragColor = vec4(0, 0, 0, 0); } else { // Транслируем из RGB в вектор, а именно из 0..1 в -0.5..+0.5 rawNormal -= 0.5; // Вычисляем уровень освещенности float lightWeight = dot(normalize(rawNormal.xyz), normalize(lightDirection)); lightWeight = max(lightWeight, 0.0); // И записываем в пиксель gl_FragColor = lightColor * lightWeight; } }

Пара заметок: Это попиксельное освещение, которое немного отличается от вершинного освещения (обычного в 3d). У нас нет выбора, так как вершины в 2d бессмысленны (их всего 4 штуки для отображения плоскости на сцене). Но, вообще, это не проблема, попиксельное освещение гораздо более точное. Также следует отметить, что шейдер рендерит только освещение, без основного спрайта. Придется признать, я немного жульничаю, ведь на самом деле я не освещаю свой спрайт, а, скорее, затеняю его и в lightColor я передаю темно-серый цвет. Настоящее освещение пикселей, а именно повышение яркости, выглядит хуже, пиксели кажутся «вытертыми». У этой проблемы есть решения, но сейчас это непринципиально.

Часть вторая: рисование теней. Отбрасывание теней в 3D - хорошо изученная проблема с известными решениями, типа рейтрейсинга или shadow-mapping’а . Однако, я затруднился найти какое-нибудь приемлимое готовое решение для 2d, пришлось делать самому, думаю получилось нормально, хотя и у него есть пара недостатков.

Вкратце, будем рисовать линию от пикселя на сцене к солнцу и проверять, есть ли какое-нибудь препятствие. Если есть - то пиксель в тени, если нет - на солнце, так что, впринципе, ничего сложного.

Шейдер принимает xyAngle и zAngle , которые отвечают за то, где находится солнце. Так как оно очень далеко, то лучи света будут параллельны, и, соответственно, эти два угла будут одинаковы для всех пикселей. Также, шейдер будет принимать карту высот мира. Она будет показывать высоту всех объектов, зданий, деревьев и т.д. Если пиксель принадлежит зданию, то значение пикселя будет примерно 10, и означать, что высота здания в данной точке - 10 пикселей.

Итак, шейдер начнет в пикселе, который надо осветить и, используя вектор xyAngle , будет продвигаться по направлению к солнцу мелкими шажками. На каждом из них мы будем проверять, если в данном пикселе карты высот что-нибудь.


Как только мы найдем препятствие, мы определим его высоту, и насколько высоким оно должно быть в данной точке чтобы преградить солнце (используя zAngle ).


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

Вот код шейдера в упрощенной/псевдо форме:

Void main(void) { float alpha = 0.0; if(isInShadow()) { alpha = 0.5; } gl_FragColor = vec4(0, 0, 0, alpha); } bool isInShadow() { float height = getHeight(currentPixel); float distance = 0; for(int i = 0; i < 100; ++i) { distance += moveALittle(); vec2 otherPixel = getPixelAt(distance); float otherHeight = getHeight(otherPixel); if(otherHeight > height) { float traceHeight = getTraceHeightAt(distance); if(traceHeight height) { traceHeight = getTraceHeight(height, zAngle, distance); if(traceHeight

Статьи по теме: