Добавлен , опубликован

Источники света

Содержание:

Принципы освещения

Что влияет

Начну с самого начала а подробности расскажу чуть ниже.
"Излучателями" света служат источники света (в .mdl это Light):
Light "Light1" {
	ObjectId 1,
	Omnidirectional, // Тип источника
	static Visibility 1, // Видимость источника
	static AttenuationStart 80.0, // Дистанция начала затухания (не работает)
	static AttenuationEnd 200.0, // Дистанция конца затухания (не работает)
	static Intensity 15.0, // Интенсивность направленного света
	static Color { 1.0, 0.5, 0.0 }, // Цвет направленного света
	static AmbIntensity 1.5, // Интенсивность окружающего света
	static AmbColor { 1.0, 0.0, 0.0 }, // Цвет окружающего света
}
"Поглотителями" света служат единичные вектора именуемые нормалями (в .mdl это Normals):
Geoset {
	Vertices 4 { // Вершины
		{ 64.0, 64.0, 0.0 },
		{ -64.0, 64.0, 0.0 },
		{ 64.0, -64.0, 0.0 },
		{ -64.0, -64.0, 0.0 }
	},
	Normals 4 { // Каждая из нормалей ниже соответствует вершине Vertices выше
		{ 0.0, 0.0, 1.0 }, // X Y Z , длина вектора равна 1
		{ 0.0, 0.0, 1.0 },
		{ 0.0, 0.0, 1.0 },
		{ 0.0, 0.0, 1.0 }
	}
	// ...
}
Что такое грань, вершина и ребро. Для справки.
Взаимодействие источников света и нормалей в итоге дает то самое освещение.

Источники света

Выше были вкратце описаны поля источников света, а сейчас подробнее.
Тип источника - то как свет будет распространятся в пространстве, в варике их всего 3:
  • Omnidirectional - Всенаправленный источник. Лучи света исходят из одной конкретной точки сразу во всех направлениях. Имитирует всякие фонари, лампочки, костры и т.д.
  • Directional - Направленный источник. Лучи света исходят в одном направлении под определенным углом, не имеет определенной точки излучения и работает на всю карту. Чтобы указать направление света нужно повернуть сам источник с помощью Rotation, изначально он обращен вниз (0, 0, -1) . Имитирует свет от солнца, луны, звезд. Предполагается использовать только в моделях глобального освещения.
  • Ambient - Окружающий источник. "Лучи" тут вообще как концепт не используются, одинаково освещает объекты вне зависимости от того как они ориентированы в пространстве и работает на всю карту. Предполагается использовать только в моделях глобального освещения.
Вообще в варе окружающее освещение сделано для совместной работы с направленным чтобы те грани моделей которые не попадают под направленное освещение небыли полностью черными. Сам по себе тип Ambient полностью копирует тип Directional, серьезно, в функциональном плане эти ноды не отличаются, это потому что за цвет и интенсивность окружающего и направленного освещения отвечают разные поля - Intensity и Color для направленного, AmbIntensity и AmbColor для окружающего. Поэтому что в Directional, что в Ambient, можно использовать сразу и направленное и окружающее освещение.
Visibility - Видимость источника. Принимает только значения 1 или 0, т.е. видим или не видим.
AttenuationStart и AttenuationEnd - СРАЗУ СКАЖУ ЧТО ЭТИ ПОЛЯ НЕ РАБОТАЮТ. По идее должны были означать дистанцию начала и конца затухания света соответственно для типа Omnidirectional (для остальных типов не имело бы смысла т.к. они не имеют местоположения; отсюда например следует что у этих типов интенсивность обычно стоит ~ от 0,3 до 1,2 - не нужно использовать большие значения т.к. они не затухают), т.е. потерю интенсивности с расстоянием, но по какой-то причине не были реализованы (?). Как предполагалось:
Удаленные объекты становятся тусклее.
По дефолту у всех моделей освещения вара стоят значения 80 и 200, это потому что
Это унаследованные из 3дмакса параметры Attenuation. Значения 80 и 200 взялись не из ниоткуда, это значения по умолчанию, которые вписывает макс в эти поля при добавлении нового источника света в сцену.
(комментарий). Предполагаю что остались по дефолту а дальше на них просто хер забили.
Пытался понять как же работает затухание света для всенаправленного источника...
Как вы можете проверить сами, если увеличивать интенсивность света то и расстояние на которое он освещает тоже увеличивается, допустим указав 1000000 можно всю карту осветить (т.е. как понятно вот эти вот 80 и 200 вообще ничего не решают).
Пришел к выводу что либо там используется Квадратичное затухание (Quadratic Attenuation) либо его производные, для 2002 года это были самые распространенные методы. Использует три коэффициента, которые управляют затуханием света на разных расстояниях от источника. Эти коэффициенты позволяют настроить интенсивность света таким образом, чтобы она уменьшалась более реалистично по мере увеличения расстояния.
I' = I/(a + b*d + c*d^2)
I' — интенсивность света в вершине,
I — интенсивность источника света,
d — расстояние от источника света до вершины,
a b c — константный, линейный и квадратичный коэффициенты соответственно.
Вот эти вот a b c не известны, кажется какие-то глобальные константы. Ясно одно - чем больше интенсивность света тем большую область он будет освещать.
Intensity и Color - Начальная интенсивность и цвет всенаправленного/направленного света. Цвет содержит 3 RGB компоненты диапазоном от 0 до 1.
AmbIntensity и AmbColor - Начальная интенсивность и цвет окружающего света. Цвет содержит 3 RGB компоненты диапазоном от 0 до 1.
Вне зависимости от того какой был выбран Тип источника, значения Intensity и Color будут влиять на всенаправленный/направленный свет а значения AmbIntensity и AmbColor на окружающий свет. При этом у типа Omnidirectional обе интенсивности будут затухать с расстоянием.
На счет анимирования источника света - те поля где указано static перед их именем, могут быть анимированы. Можно сделать так чтобы источник света был видим в одних анимациях и небыл виден в других, плавно/резко менять цвет или интенсивность. Подробнее про способы перехода можно прочесть тут, а про анимацию света тут.

Нормали

Как известно модели состоят из граней-треугольников, каждый из которых в свою очередь состоит из трех вершин соединенных между собой. Каждая такая вершина имеет собственную нормаль (X, Y, Z), эти вектора еще называют нормалями вершин (vertex normals); вообще есть еще нормали граней (face normal) и нормали рёбер (edge normal) но хрен с ними.
Так вот именно нормаль определяет как свет будет ложится на вершину.
Нормаль - это единичный вектор (квадратный корень из суммы квадратов его координат равен 1) определяющий ориентацию лицевой грани поверхности. Зачастую они перпендикулярны поверхности, но если нам нужно показать что модель имеет гладкие края а не ребристые то используют усреднение нормалей у смежных вершин чтобы свет ложился так будто углов там нет.
Пример затенения граней по Фонгу (Phong shading):
Вот эти вот желтые линии это нормали.
У смежных вершин нормали перпендикулярны своим граням, поэтому идет такой резкий переход затенения. Такие нормали еще называют жесткими нормалями.
У смежных вершин нормали усреднены (и потому одинаковы), поэтому идет такой плавный переход затенения. Такие нормали еще называют общими или мягкими нормалями.
Конкретно в варике используется метод затенения граней по Гуро (Gouraud shading) - самый распространенный для 2002 года. В то время так же использовалось Плоское затенение (Flat shading) но это прям вообще древняя тема.
Узнаете красавца по центру?
Освещение рассчитывается для каждой вершины, а затем цвет интерполируется по всей поверхности грани. Если по порядку то суть такова:
Есть вершина грани с координатами P.x P.y P.z и нормалью N.x N.y N.z
Есть источник света с координатами S.x S.y S.z и интенсивностью I
  1. Вычисляем вектор света L (L.x L.y L.z) так чтобы он был направлен от вершины к источнику света
L = (S.x - P.x, S.y - P.y, S.z - P.z)
  1. Нормализуем вектор света - делим его составляющие на его длину
|L| = √(L.x^2 + L.y^2 + L.z^2)
Lnorm = L/|L|
  1. Находим косинус угла между нормалью N и вектором света Lnorm учитывая тот факт что их длины равны 1
cosθ = N⋅Lnorm = N.x*Lnorm.x + N.y*Lnorm.y + N.z*Lnorm.z
cosθ может получится и отрицательным, это означает что угол между векторами больше 90° - в этом случае грань обращена к свету тыльной стороной, тогда cosθ просто зануляем.
  1. Получаем освещенность E умножая cosθ на формулу которую я писал разделом выше
E = I*cosθ/(a + b*|L| + c*|L|^2)

Цвет

Поскольку у света есть цвет излучения, для каждой компоненты {L.r, L.g, L.b} света рассчитаем модификатор цвета умножив его на освещенность в вершине:
M.r = E*L.r
M.g = E*L.g
M.b = E*L.b
Такие M считаются для каждого из источников света воздействующих на вершину. Тогда итоговым модификатором цветового канала вершины является минимум между единицей и суммой каждой из освещенностей:
M.r = min(1, M1.r + M2.r + ... + Mn.r)
M.g = min(1, M1.g + M2.g + ... + Mn.g)
M.b = min(1, M1.b + M2.b + ... + Mn.b)
Это один из методов тонального отображения - в варике как и во многих играх того времени использовали просто метод отсечения (Clamping) как в формуле выше, а так их дохрена. Конкретно в варе сделано так чтобы итоговые значения цветовых каналов вершины после расчета освещения не превышали значений цветовых каналов пикселя текстуры, т.е. M будет содержать значения от 0 до 1.
После чего наконец-то получаем цвет вершины после воздействия света, умножив каждую из ее цветовых компонент {P.r, P.g, P.b} на модификатор M:
P.r = P.r*M.r
P.g = P.g*M.g
P.b = P.b*M.b
Итоговый цвет в вершинах билинейно интерполируется по всей поверхности грани.
Как результат - нереальное говно:
Из-за того что используется метод затенения Гуро а ландшафт состоит из больших треугольников получается вот такое уродство.

Заключение

Тут хотел подытожить и добавить пару замечаний:
  • Используйте тип Omnidirectional для локальных источников света, а Directional и Ambient для глобальных.
  • Для управления направления света Directional источников используйте Rotation.
  • Не трогайте AttenuationStart и AttenuationEnd - оставьте их 80 и 200 (где-то читал что могут быть баги с пропаданием света если будут несколько источников с разными значениями затухания).
  • Совмещайте в одном и том же источнике Intensity/Color и AmbIntensity/AmbColor когда необходимо.
  • Меняйте радиус освещения путем изменения Intensity/AmbIntensity.
  • Еще один способ изменить интенсивность - пропорционально изменить RGB значения цвета света Color/AmbColor.
  • Если используете Omnidirectional источники с большой интенсивностью (т.е. светят далеко) то не забудьте увеличить Bounds Radius модели чтобы свет не пропадал когда модель вылазит за камеру.
  • Слишком большие грани могут привести к неверному отображению затенения (особенно если в карте освещение это ключевая составляющая), поэтому рекомендуется разбивать большие грани на несколько мелких.
  • Старайтесь не использовать много локальных источников в одном месте, чревато багами, замените их меньшим кол-вом источников с большей интенсивностью либо замените направленное освещение на окружающее.
  • (!) При выделении модели с источником освещения, в центре карты создастся копия источника, осветив центр карты, это происходит из-за отрисовки портрета модели. Если модель с источником освещения выделяема в игре, лучше сделать ей отдельную модельку портрета без источников освещения.
  • Возможно (но это не точно) что баги могут вылезать при использовании множества источников с разной интенсивностью.

Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...
16
Совсем забыл упомянуть анимацию цвета и интенсивности, бл...
37
Увидел неплохой туториал на Ютубе освещения для моделей. Там проще объясняется чем статьи расписанные формулы каких-то пифагоров и законы ома и участка цепи. Это как раз автор и пытается в статьи видимо расписать, так как из прочитанного всего текста я понял только 5%
Ответы (2)
16
Anime Chan, Вообще на формулы +- похрен, это просто добавочная инфа какие именно алгоритмы используются и как ты сказал туториалов хватает. Основной инфа в пункте Источники света.
30
так как из прочитанного всего текста я понял только 5%
И почему ты считаешь это проблемой статьи?
28
Если используете Omnidirectional источники с большой интенсивностью (т.е. светят далеко) то не забудьте увеличить Bounds Radius модели чтобы свет не пропадал когда модель вылазит за камеру.
Недавно читал, что это не имеет смысла поскольку если центр модели за камерой, то модель не будет видно. Хотя, возможно, это касается моделей без источников всяких, вроде того же пехотинца
Ответы (1)
16
rsfghd, со светом такое помогает, там только фишка в том что если прям далеко увести камеру от источника света то у него стопнется анимация, но светить он продолжит. Можно например сделать декорацию с источником света и поставить у нее галочку "анимирован в тумане войны" (или как-то так) тогда будет отображаться и анимация света как бы далеко он ни был. По дефолту такая галочка только у водопада стоит.
23
Я так понимаю, Амбиент колор создает цвет освещения для затененной поверхности модели? То есть, можно сделать реалистичные "тени" для глобального освещения? Ну типа как зимой, например, если фонарь на снег светит, то снег жёлтый, а где фонарь не попадает, там снег темно синий - под цвет неба, потому что освещен небом, итд
Ответы (15)
28
EugeAl, глобальные источники света можно крутить даже, но проблема в варкрафтовских тенях, они не будут перемещаться вместе с солнцем. Впрочем, для таких продвинутых вещей есть уже реф и рендерэдж
16
EugeAl, Не для затенённой, а для всей. Т.е. те части модели которые подвергнуться и амбиентному и прямому освещению в результате получат сумму цветов света.
Типо есть Omni с Color 1 0 0 и AmbColor 0 1 0
Тогда та часть модели которая не попадает под прямой свет будет зелёная (0 1 0) а та часть которая попадает будет жёлтой (1 0 0 + 0 1 0 = 1 1 0).
23
rsfghd, посмотрел, там не понятно, где амбиент
Да, можно, но не полностью, из за теней. Но можно сделать восход на западе, как и закат, и тогда будет норм, хоть и нереально.
23
OVOgenez, а, понятно. В общем, разных цветов типы освещения лучше не делать. Жаль(
16
EugeAl, та можно, только они складываться будут аддитивно.
23
OVOgenez, а, вот в этот и проблема как раз, не выйдет сделать цветное затенение, от заката, например, либо костылить, чтобы в сумме с обычным светом получился нужный цвет... я про глобальное освещение сейчас - с обычным то ладно, там можно тот же цвет выставить
16
EugeAl, Ща вот подумал, а можно же сделать 2 источника света в глобальной моделе освещения, без амбиентной составляющей. Они будут направлены друг на друга, т.е. параллельны. Первый будет освещать объект с одной стороны, второй с другой. Интересно чё выйдет по итогу...
23
OVOgenez, попробовал так сделать, фигня вышла, переход между источниками был не освещен, и на модели получилась чёрная полоса
16
EugeAl, не увидел сообщение, чёт уведомления барахлят. Можно тогда добавить небольшую амбиент составляющую чтобы небыло слишком темного перехода. Ну и сделать не строго параллельные источники, а просто отзеркаленные (т.е. они как бы светят сверху, но с разных сторон). Я так в статье про DNC в последнем примере делал.
А можно вообще помаяться и замутить множество направленных источников с разных сторон, но с меньшей интенсивностью.
23
OVOgenez, да, что то с уведомлениями...
Можно и амбиент, но тогда в нижнем источнике лучей смысла нет, ведь снизу у нас второе солнце не светит)
А вообще ещё момент - не пробовал ли делать directional источник, только чтобы он светил на область, маленькую? Чтобы как у фонаря сделать или там такое не возможно?
16
EugeAl, ну снизу то солнце не светит, это да)
Но свет отражается от поверхностей и падает на ту часть объекта которая не попадает под прямое излучение. Вот это вот и можно реализовать низко интенсивным направленным источником.
23
OVOgenez, да, свет то отражается) просто он отражается во все стороны по форме предмета. Плюс на него попадает со всех сторон отраженный свет от неба, плюс отраженный от земли отраженный от неба итд итп. Тут либо делать 1 амбиент, либо несколько директионалов, со всех сторон, причём последнее сильнее будет грузить комп) иначе получится "эффект 2х солнц". имхо
16
EugeAl, ну это только тестить, хз че будет если допустим штук 10 директионалов впихнуть. Делать этого я конечно же не буду)
38
На правах рекламы:
Ответы (6)
23
ScorpioT1000,
у вторых у всех есть недостаток - они используют текстуры дыма или облаков, что смотрится не очень. Думаю, нужно будет с этим покончить и запилить текстуру из огня, тоже для партиклов additive, чтоб можно было наконец делать нормальное пламя
38
EugeAl, я работал над этим не короткое время) там вроде в стоке весь огонь хренового качества
23
ScorpioT1000, да, он отвратительный) одно мыло. Нужно переделывать )
23
ScorpioT1000, В общем, эксперимент показал, что спрайтовый огонь в партиклах выглядит офигенно) Правда, под него пришлось переделывать и модель пожара... А ещё дурацкие баги от света, это пипец. конечно
Если надо, вот текстурка на всякий случай, из гифки переделал)
Загруженные файлы
38
EugeAl, много частиц, может нагружать, но норм в целом. Я бы вариативность поиграл.
Заливай как ресурс
23
ScorpioT1000, не особо нагружает) вариативность есть там, размеры и партиклы разные. Ок, попозже залью
11
Разве Directional вообще работает? Он почти как глобальное освещение ведь.. нельзя осветить небольшой участок, как например в том же старкрафте. или современных движках, Unity, Unreal engine. типо например сделать фонарик точечный не получится, я разве не прав? Из адекватно работающих только Omnidirectional и ambient.
Ответы (1)
16
prizraknadache, Directional это тип всех стандартных глобальных моделей освещения, с чего бы ему не работать. Я ж писал что Directional и Ambient и есть само по себе глобальное освещение и кроме как в DNC моделях его юзать смысла нет, на всю карту ведь светит.
25
По дефолту у всех моделей освещения вара стоят значения 80 и 200, это потому что
На самом деле не у всех, хотя цель этих настроек и не очевидна
список
Abilities\Spells\Demon\DemonBoltImpact\DemonBoltImpact.mdx | Omnidirectional Omni01: Att Start: 40, Att End: 52
Abilities\Spells\Demon\RainOfFire\RainOfFireTarget.mdx | Omnidirectional Omni01: Att Start: 0, Att End: 0
Abilities\Spells\Human\ManaFlare\ManaFlareBoltImpact.mdx | Omnidirectional Omni01: Att Start: 40, Att End: 52
Abilities\Spells\NightElf\ManaBurn\ManaBurnTarget.mdx | Omnidirectional Omni01: Att Start: 40, Att End: 52
Abilities\Weapons\Bolt\BoltImpact.mdx | Omnidirectional Omni01: Att Start: 40, Att End: 52
Abilities\Weapons\DemonHunterMissile\DemonHunterMissile.mdx | Omnidirectional Omni01: Att Start: 0, Att End: 0
Doodads\Ruins\Props\Firepot\Firepot.mdx | Omnidirectional Omni01: Att Start: 200, Att End: 400
UI\Glues\SinglePlayer\HumanCampaign3D\HumanCampaign3D.mdx | Omnidirectional Omni01: Att Start: 964, Att End: 988
UI\Glues\SinglePlayer\HumanCampaign3D\HumanCampaign3D.mdx | Omnidirectional Omni02: Att Start: 964, Att End: 988
UI\Glues\SinglePlayer\HumanCampaign3D\HumanCampaign3D.mdx | Omnidirectional Omni04: Att Start: 964, Att End: 988
UI\Glues\SinglePlayer\HumanCampaign3D\HumanCampaign3D.mdx | Omnidirectional Omni05: Att Start: 964, Att End: 988
UI\Glues\SinglePlayer\HumanCampaign3D\HumanCampaign3D.mdx | Omnidirectional Omni06: Att Start: 964, Att End: 988
UI\Glues\SinglePlayer\NightElfCampaign3D\NightElfCampaign3D.mdx | Omnidirectional Omni02: Att Start: 80, Att End: 6999
UI\Glues\SinglePlayer\NightElfCampaign3D\NightElfCampaign3D.mdx | Omnidirectional Omni01: Att Start: 80, Att End: 6999
UI\Glues\SinglePlayer\NightElfCampaign3D\NightElfCampaign3D.mdx | Omnidirectional Omni04: Att Start: 80, Att End: 6999
UI\Glues\SinglePlayer\NightElfCampaign3D\NightElfCampaign3D.mdx | Omnidirectional Omni06: Att Start: 80, Att End: 6999
UI\Glues\SinglePlayer\NightElfCampaign3D\NightElfCampaign3D.mdx | Omnidirectional Omni07: Att Start: 80, Att End: 6999
Units\NightElf\EvilIllidan\IllidanEvil.mdx | Omnidirectional Omni01: Att Start: 40, Att End: 52
Units\NightElf\HeroDemonHunter\HeroDemonHunter.mdx | Omnidirectional Omni01: Att Start: 40, Att End: 52
Ответы (4)
16
Makeba, как бы ни извращался, не смог добиться хоть каких-то изменений с помощью этих полей(
16
Makeba, кстати, не подскажешь какой прогой чекал? Хочу найти все модельки в которых есть источники света.
25
OVOgenez, использовал древнюю MdxLib.dll от Магоса, которая умеет читать и записывать mdx800 модели.
А для поиска файлов можно написать несложный код, который пробежится по моделям из папки с распакованным варкрафтом.
например
        static void SearchFiles(string folderPath)
        {
            foreach (string file in Directory.EnumerateFiles(folderPath, "*.mdx", SearchOption.AllDirectories))
            {
                try
                {
                    CModel model = new CModel();
                    using (FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read))
                    {
                        new CMdx().Load(file, fileStream, model);

                        if (model.Lights.Count > 0)
                        {
                            foreach (var light in model.Lights)
                            {
                                float attStart = light.AttenuationStart.GetValue();
                                float attEnd = light.AttenuationEnd.GetValue();

                                if (attStart != 80f || attEnd != 200f)
                                {
                                    Console.WriteLine($"{file.Remove(0, folderPath.Length)} | {light.Type} {light.Name}: Att Start: {attStart}, Att End: {attEnd}");
                                }
                            }
                        }
                    }
                }
                catch 
                {
                    continue;
                }
            }

            Console.WriteLine("All files have been processed");
        }
Загруженные файлы
16
Вот ещё полезная статья, там приводится объяснение как освещение влияет на фильтр-маску и почему при выборе юнита с источником света в портрете, в центре карте появляется освещение.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.