Deltadev-math.ru
поддержать игру
// ГЛОБАЛЬНОЕ ОСВЕЩЕНИЕ

Откуда свет там, куда лампа не смотрит: ambient, AO, отражения и глобальное освещение

Почему ambient константой врёт и чем его заменяют: уравнение рендеринга и отскоки света, коробка Корнелла и перенос цвета, ambient occlusion из буфера глубины и его ореолы, отражения — кубические карты с параллакс-коррекцией и screen-space reflections, запечённые лайтмапы и пробы освещения, и почему всё это — приближения одного интеграла.

6 сентября 2026·40 мин чтения·уравнение рендерингаSSAOотражениялайтмапы и пробы
Дейв заглядывает в коробку с красной и зелёной стенами, Дельта светит в тёмный угол

Прошлая статья оставила кадр в состоянии данных: буферы цвета, глубины и нормалей — текстуры, которые можно читать и считать. Мы гоняли по ним сглаживание, bloom и tone mapping. Но есть вопрос, на который ни один из этих буферов не ответит: откуда берётся свет в тех местах, куда ни одна лампа не смотрит напрямую?

Ситуация знакомая. Вы поставили источник, посчитали N·L, нарисовали тени — и в тенях черно. Совсем черно, как в космосе. Первое, что делается в такой ситуации, — прибавить ко всему константу: ambient. В тенях перестаёт быть черно, сцена принята. И ровно с этого момента картинка начинает выглядеть плоской: угол под столом и открытая стена освещены одинаково, потолок над лампой светится так же, как пол под ней, а красная стена никак не подкрашивает белый пол рядом с собой.

Реальный свет так себя не ведёт. Он отскакивает: от пола — на потолок, от красной стены — на всё, что рядом, в угол — меньше, чем на открытую поверхность. Весь свет, который дошёл до точки не прямо от лампы, а через другие поверхности, называется непрямым, а его расчёт — глобальным освещением (global illumination, GI).

Разберём:

  • почему ambient константой врёт и что именно он выбрасывает: уравнение рендеринга в двух словах, отскоки света и коробка Корнелла;
  • тень в углу: ambient occlusion как поправка к ambient, SSAO из буфера глубины и его артефакты — серые стены, ореолы, исчезающие тени;
  • отражения: кубические карты и их параллакс, коррекция коробкой-заместителем, screen-space reflections и то, чего экран отразить не может;
  • запечённый свет: лайтмапы для статики и пробы освещения для всего, что движется, — и почему запечённое не знает, что сцена изменилась;
  • и общую мысль: все эти техники — приближения одного и того же интеграла, каждая режет его по-своему.
// @easy_dev_math

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

Ambient врёт

Начнём с того, что ambient делает правильно. Он признаёт, что свет приходит не только от лампы. В комнате с одной лампой освещено всё, включая потолок над ней и пол под столом, — потому что свет отражается от поверхностей и летит дальше. Константа говорит: «непрямого света в сцене вот столько», и прибавляет это число, умноженное на альбедо, к каждой точке.

И тут резонный вопрос: а почему одно число? Потому что дешевле не бывает. Ноль лучей, ноль текстур, одно умножение в шейдере. У этой цены есть обратная сторона: непрямой свет в сцене не одинаковый. В углу его меньше — оттуда видна меньшая часть комнаты. Под столом его почти нет. Рядом с красной стеной он красный. Ambient константой обнуляет все три различия сразу, и глаз это читает как «плоско», даже если не может объяснить, что не так.

Интеграл, внутри которого стоит он сам

По сути, вопрос «откуда свет в этой точке» имеет точный ответ, и его записали в 1986 году — Джим Каджия в статье «The Rendering Equation» и, в том же году и на той же конференции, Иммел, Коэн и Гринберг. Ответ такой: свет, уходящий из точки в сторону камеры, — это сумма её собственного излучения и всего света, который в неё пришёл со всех направлений полусферы, умноженного на то, какую долю этого света поверхность отражает в сторону камеры (BRDF), и на косинус угла падения.

Но есть нюанс, и он делает задачу тяжёлой: свет, пришедший в точку с какого-то направления, — это свет, ушедший из другой точки сцены, той, что в этом направлении видна. А для неё справедливо то же самое уравнение. Интеграл стоит внутри самого себя, и так до бесконечности — или до тех пор, пока свет не поглотится. Лампа освещает пол, пол освещает потолок, потолок подсвечивает стены, стены — пол. Каждый отскок слабее предыдущего, потому что альбедо меньше единицы, но их бесконечно много.

Уравнение рендерингаОсторожно! Математика!

Уходящая из точки pp в направлении ωo\omega_o яркость:

Lo(p,ωo)=Le(p,ωo)+Ωf(p,ωi,ωo)Li(p,ωi)cosθidωiL_o(p, \omega_o) = L_e(p, \omega_o) + \int_{\Omega} f(p, \omega_i, \omega_o)\, L_i(p, \omega_i)\, \cos\theta_i \, d\omega_i

Здесь LeL_e — собственное излучение (у лампы оно есть, у стены — нет), ff — BRDF, LiL_i — яркость, приходящая с направления ωi\omega_i, интеграл — по полусфере над точкой. Рекурсия спрятана в LiL_i: это LoL_o той точки pp', которую луч из pp в направлении ωi\omega_i встречает первой:

Li(p,ωi)=Lo(p,ωi)L_i(p, \omega_i) = L_o(p', -\omega_i)

Для матовой поверхности f=ρ/πf = \rho / \pi, где ρ\rho — альбедо. Ambient константой — это подстановка LiAL_i \equiv A для всех направлений, кроме лампы: интеграл от константы по полусфере с косинусом равен πA\pi A, и слагаемое сводится к ρA\rho A. Одно умножение, которое мы и видели в шейдере.

Коробка, с которой всё началось

Чтоб понять, сколько теряется, нужен эталон. В 1984 году Горал, Торренс, Гринберг и Баттейл из Корнелла показали метод расчёта обмена светом между матовыми поверхностями — радиосити — и для проверки построили физическую коробку: белые стены, одна красная, одна зелёная, лампа на потолке, пара предметов внутри. Рендер сравнивали с фотографией настоящей коробки. С тех пор коробка Корнелла — стандартная сцена для проверки любого расчёта непрямого света, и цветные стены в ней не для красоты: по белому полу и потолку сразу видно, докатился ли до них красный и зелёный свет со стен. Этот перенос цвета (color bleeding) — самый заметный признак того, что непрямой свет посчитан, а не подставлен константой.

Радиосити считало обмен между кусочками поверхностей системой уравнений — дорого, но для матовых сцен точно. Второй путь предложил тот же Каджия: не решать уравнение, а оценивать интеграл случайными лучами. Из точки выпускается луч в случайном направлении, в месте попадания — ещё один, и так несколько отскоков; средним по многим таким путям и оценивается свет. Это трассировка путей (path tracing). Ответ получается шумным, и шум убывает медленно — вчетверо больше путей дают вдвое меньше шума. Зато метод ничего не упрощает: любая геометрия, любые материалы, сколько угодно отскоков.

[ DEMO 01 ]//

Комната без глобального света и с ним

В режиме прямого света потолок и всё, что под ящиком, чёрные — лампа смотрит вниз. Ambient поднимает их одним числом, и тень под ящиком светлеет ровно так же, как открытый пол рядом. Один отскок освещает потолок полом и подкрашивает белый пол у красной и зелёной стен. Все отскоки — это картинка, к которой стремятся остальные техники статьи: следите за яркостью потолка в счётчике и за тем, как углы становятся темнее середины стен без всякого ambient occlusion — он там получается сам.

Тень в углу: ambient occlusion

Первое, что можно уточнить в константе, — геометрию. Если непрямой свет приходит «со всех сторон», то в углу сторон меньше: половина полусферы над точкой заслонена соседней стеной. Под столом — почти вся. Доля открытой полусферы и называется Ambient occlusion — доля направлений над точкой, в которых на заданном расстоянии нет препятствий; 1 — открыто, 0 — закрыто со всех сторон.: единица на открытой стене, около половины на стыке двух стен, меньше в нишах. Ambient умножается на это число — и углы темнеют, предметы «прилипают» к полу, ниши уходят в тень.

Заметьте, что это тень не от лампы. Она не зависит ни от положения источника, ни от его цвета — только от геометрии вокруг точки. Хоберок и Цзя в GPU Gems 3 называют ambient occlusion производственным приёмом для приближения освещения окружением, и это точное определение: AO — про свет от неба и от стен, а не про лампу. Умножать на него прямой свет — распространённая ошибка: тени от лампы становятся грязнее, а физического смысла в этом нет.

Считать AO лучами

Чтож, эталонный способ очевиден из определения: выпустить из точки несколько десятков лучей по полусфере и посчитать, какая доля не встретила ничего на расстоянии, скажем, полуметра. Именно так AO считали в офлайн-рендере — и так его запекают в текстуры для статичных объектов до сих пор. В реальном времени лучей в сцену тогда не было, и в 2007 году Crytek показала обходной путь, который стал стандартом на следующие десять лет.

SSAO: посмотреть в буфер глубины

Идея из доклада Мартина Миттринга про CryEngine 2 (SIGGRAPH 2007) в его же тезисах: вокруг каждого пикселя разбрасываются точки-сэмплы в трёхмерной окрестности, масштаб которой зависит от расстояния до камеры; каждая точка проецируется на экран и сравнивается с буфером глубины; AO — функция числа сэмплов, оказавшихся не заслонёнными; сэмплы далеко впереди учитываются слабее; на каждый пиксель ядро сэмплов поворачивается по текстуре 4×4, а полученный шум снимается размытием, которое учитывает глубину. Метод полностью динамический, и, как прямо отмечает сам автор, результат зависит от детализации геометрии и от подкрутки параметров.

Что здесь важно — SSAO ничего не знает о сцене. Он читает буфер глубины как текстуру, восстанавливает из него позиции и сравнивает числа. Поэтому он дёшев и работает с чем угодно, и поэтому же у него есть встроенные артефакты, которые не лечатся параметрами.

Серые стены. Сфера сэмплов вокруг точки на ровной стене наполовину уходит под саму стену. Эти сэмплы «заслонены» — самой стеной — и ровная поверхность темнеет примерно вдвое сама по себе. Лечение известно: брать не сферу, а полусферу вдоль нормали (у LearnOpenGL в главе про SSAO — ядро из 64 сэмплов в полусфере, ориентированной по нормали поверхности, поворот по текстуре 4×4 и размытие). Для этого нужна нормаль — ещё один буфер.

Ореолы. Предмет на переднем плане заслоняет сэмплы всего, что за ним, даже если между ними метры. Без проверки дальности вокруг любого близкого объекта появляется тёмная кайма на далёкой стене. Проверка дальности ослабляет вклад сэмплов, заслонённых чем-то далёким — и заодно убирает часть настоящей окклюзии.

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

Шум и полосы. Сэмплов мало — иначе дорого; мало сэмплов — либо полосы (одинаковое ядро для всех пикселей), либо шум (ядро повёрнуто на каждый пиксель), который нужно размывать. Размытие по глубине мажет только внутри одной поверхности, но мелкие детали AO всё равно теряет.

SSAO в псевдокодеОсторожно! Код!
float occlusion = 0.0;
for (int i = 0; i < KERNEL; i++) {
    vec3 samplePos = P + TBN * kernel[i] * radius;      // точка в полусфере вокруг P
    vec2 uv        = project(samplePos);                 // её место на экране
    float sceneZ   = texture(depthBuffer, uv).r;         // что в буфере на этом месте
    float rangeChk = smoothstep(0.0, 1.0, radius / abs(P.z - sceneZ));
    occlusion += (sceneZ <= samplePos.z - bias ? 1.0 : 0.0) * rangeChk;
}
float ao = 1.0 - occlusion / KERNEL;

TBN — базис вокруг нормали, повёрнутый по шумовой текстуре; bias — сдвиг против самозатенения; rangeChk — проверка дальности против ореолов. Дальше — размытие по соседям с той же глубиной.

[ DEMO 02 ]//

Тень в углу: AO из буфера глубины

Сфера сэмплов — вариант, с которого начинали: смотрите на счётчик AO ровной стены, он около половины при любом радиусе. Полусфера по нормали возвращает стене единицу, а ошибка относительно лучей падает вдвое-втрое. Сдвиньте камеру так, чтобы ящик ушёл за край, — его тень на полу растает в обоих экранных вариантах и останется у лучей. Режим «прямой + ambient × AO» показывает, для чего это всё: AO умножает только непрямую часть.

Отражения: что видно в направлении луча

Чтож, у матовых поверхностей непрямой свет приходит со всей полусферы, и его можно усреднять — константой, AO, чем угодно. У зеркальных всё наоборот: почти весь свет приходит с одного направления, отражённого. Вопрос «откуда свет» для зеркала звучит как «что находится в той стороне». И ответить на него растеризатору нечем: он проецирует сцену на экран, а в отражении видно то, чего на экране нет, — потолок над камерой, стену за спиной, низ предмета.

Кубическая карта

Первый ответ появился раньше самих игр. В 1976 году Блинн и Ньюэлл предложили environment mapping: окружение сцены рисуется один раз в текстуру, и зеркальная поверхность читает из неё по направлению отражённого луча. В 1986-м Грин предложил хранить это окружение шестью гранями куба — кубическая карта, cube map. В движках это reflection probe: в нужной точке уровня рисуются шесть видов, и все зеркальные поверхности вокруг читают из них.

Но есть нюанс, и он в самом слове «направление». Карта отвечает на вопрос «что видно отсюда в ту сторону» — из точки, где её сняли. Поверхность пола в трёх метрах от этой точки спрашивает про то же направление и получает тот же ответ, хотя видит другое. Отражение не сдвигается вместе с точкой, у него нет параллакса: стены в зеркальном полу стоят не там, а при движении камеры отражение плывёт отдельно от комнаты. Себастьян Лагард, разбирая этот эффект в 2012 году, формулирует прямо: кубическая карта по определению изображает бесконечно далёкую коробку, отсюда ошибка положения отражённых объектов, и её приходится компенсировать.

Его же компенсация — параллакс-коррекция: вокруг пробы задаётся коробка-заместитель размером с комнату; отражённый луч из точки поверхности продлевается до пересечения с этой коробкой, и в карту читают не исходное направление, а направление из центра пробы на точку пересечения. Для стен, совпадающих с коробкой, отражение встаёт на место. Для всего, что внутри комнаты — ящик, шар, — нет: у заместителя одна форма, и предметы в ней не учтены.

Отражения по экрану

Второй ответ пришёл с буферами глубины. Если кадр — это данные, то отражённый луч можно провести не по сцене, а по буферу: шагать вдоль луча в мире, каждую точку проецировать на экран и смотреть, не оказалась ли она за поверхностью, записанной в буфер глубины. Оказалась — вот и отражение, цвет берётся из буфера цвета в этом пикселе. Это screen-space reflections, SSR; Макгвайр и Мара в 2014 году описали, как делать этот марш эффективно, шагая сразу в экранных пикселях, а не в мировых единицах.

SSR точен ровно в той мере, в какой то, что отражается, есть в кадре. Отражение стены, которая видна на экране, совпадёт с трассированным. А дальше начинаются промахи трёх сортов. Луч ушёл за край экрана — про потолок над камерой и стену за спиной буфер не знает ничего. Луч должен отразить обратную сторону предмета — в буфере только лицевая, и отражение шара показывает его перед, а не низ. И толщина: у поверхности в буфере нет задней стороны, поэтому «за поверхностью» решается допуском, и тонкие предметы либо пропускают луч сквозь себя, либо ловят его там, где не надо.

На практике оба ответа складывают: SSR даёт точное отражение там, где может, а в промахах подставляется кубическая карта с параллакс-коррекцией. Шов между ними маскируют размытием и плавным затуханием у краёв экрана.

[ DEMO 03 ]//

Зеркальный пол: четыре способа отразить

Зеркальный пол отражает стены, ящик и шар. У кубической карты стены и предметы стоят не там; параллакс-коррекция возвращает стены и оставляет предметы; SSR отражает всё, что в кадре, а промахи — потолок над камерой и низ шара — подсвечены ржавым, и их можно закрыть картой. Луч в сцену — эталон, к которому считается ошибка.

Запечь свет заранее

Эталон у нас есть — трассировка путей, но она шумная и медленная. AO чинит углы, но не знает, откуда свет. Отражения закрывают зеркала. А непрямой свет на матовых стенах — тот самый перенос цвета, освещённый потолок, мягкая тень под ящиком — по-прежнему константа. Если считать его в реальном времени нечем, остаётся посчитать заранее.

Лайтмапы

Идея старше многих читателей. Quake в 1996 году освещал уровни лайтмапами: для каждой поверхности заранее считалась освещённость и хранилась в маленькой текстуре — по описанию Абраша, одно значение на каждые 16 текселей по горизонтали и вертикали, — а при рендере текстура поверхности умножалась на интерполяцию четырёх ближайших точек лайтмапа. Освещение было статичным по построению; динамическую лампу подмешивали, правя лайтмап перед сборкой поверхности.

Чем считать лайтмап — вопрос инструмента. Радиосити, потом трассировка путей: в текселе стены запускаются пути, как в эталоне, только не на кадр, а один раз, на этапе сборки уровня, хоть час, хоть ночь. Результат — непрямая освещённость на тексель. Прямой свет и тени от лампы обычно оставляют живыми: их и так умеет конвейер, а запечённые они не смогут двигаться. Half-Life 2 пошла дальше и запекла не одно значение на тексель, а три, по трём направлениям базиса, — radiosity normal mapping из доклада Митчелла, Мактаггарта и Грина (SIGGRAPH 2006): так запечённый свет реагирует на карту нормалей.

Где это ломается — видно из принципа. Разрешение: тексель лайтмапа накрывает кусок стены, и мягкая тень под ящиком расплывается пятном за его края, а на стыке стен свет одной перетекает на другую. Плотнее тексели — точнее, но память и время запекания растут квадратично. Швы развёртки: стена разрезана на куски, и на стыке кусков соседние тексели принадлежат разным местам сцены. И главное — статика. Запечённое не знает, что сцена изменилась: сдвинули ящик, а его тень в лайтмапе осталась на старом месте; открыли дверь, а свет из неё не пришёл.

Пробы для того, что движется

Для персонажа лайтмапа нет — у него нет постоянного места в сцене. Ему нужен непрямой свет в точке, где он сейчас стоит, для любой нормали. Решение — пробы освещения: в объёме уровня расставляются точки, в каждой заранее записывается, сколько непрямого света приходит с каждой стороны, а объект читает ближайшие пробы и смешивает.

Как хранить «сколько света с каждой стороны» компактно? Грегер с соавторами в 1998 году описали irradiance volumes — сетку таких точек. Half-Life 2 хранила в пробе шесть значений, по осям куба. А Рамамурти и Ханрахан в 2001-м показали, что для матовой поверхности освещённость как функция нормали настолько гладкая, что её хватает описать девятью коэффициентами сферических гармоник — со средней ошибкой около одного процента. Девять чисел на канал, и освещённость для любой нормали считается несколькими умножениями. Так пробы устроены в большинстве движков до сих пор.

Освещённость из девяти коэффициентовОсторожно! Математика!

Приходящая яркость L(ω)L(\omega) раскладывается по сферическим гармоникам YlmY_{lm}:

Llm=S2L(ω)Ylm(ω)dωL_{lm} = \int_{S^2} L(\omega)\, Y_{lm}(\omega)\, d\omega

Освещённость — интеграл яркости с косинусом по полусфере вокруг нормали nn. Косинус тоже раскладывается по гармоникам, и свёртка сводится к умножению коэффициентов на множители, зависящие только от полосы ll:

E(n)=l=02m=llAlLlmYlm(n),A0=π,A1=2π3,A2=π4E(n) = \sum_{l=0}^{2}\sum_{m=-l}^{l} A_l\, L_{lm}\, Y_{lm}(n), \qquad A_0 = \pi,\quad A_1 = \tfrac{2\pi}{3},\quad A_2 = \tfrac{\pi}{4}

Полосы выше второй Рамамурти и Ханрахан отбрасывают: их множители малы, косинус — гладкая функция, и высокие частоты яркости на освещённость почти не влияют. Отсюда девять коэффициентов: один для l=0l = 0, три для l=1l = 1, пять для l=2l = 2. Проверка: равномерная яркость 1 со всех сторон даёт E=πE = \pi, и матовая поверхность с альбедо ρ\rho отражает ρE/π=ρ\rho E / \pi = \rho — ровно как ambient константой, равной единице.

Слабое место проб — то же, что у лайтмапов, плюс своё. Они статичны: передвинули шкаф — пробы посчитаны без него. И они не знают о стенах между собой: объект между пробой на свету и пробой в тени получает среднее, и свет протекает сквозь перегородку. Расстановка проб — ремесло левел-артиста: гуще там, где свет меняется быстро, реже в открытом пространстве, и ни одной внутри стены.

Что изменилось недавно

Всё, что выше, — приближения, вычисленные заранее, потому что считать интеграл в кадре было нечем. Это меняется. Lumen в Unreal Engine 5 — система полностью динамического глобального освещения и отражений, и она там стоит по умолчанию: при включённом Lumen предрассчитанное освещение выключается и лайтмапы скрываются, его отражения заменяют экранные, а его затенение — прежнее затенение полями расстояний. Считает он трассировкой — программной, по полям расстояний вроде тех, что мы разбирали в статье про SDF, или аппаратной, если она есть. То есть ровно тем способом, который в этой статье был эталоном для сравнения. Об этом — следующий сезон.

[ DEMO 04 ]//

Запечь свет: лайтмапы и пробы

Стены освещены лайтмапом, ящик и шар — пробами, прямой свет считается живьём. Четыре текселя на стену дают пятна и утечку тени за края ящика; тридцать два — чистую картинку за 64-кратную цену. Одна проба на комнату светит объектам одинаково с любой стороны. Запечь и прямой свет — потерять тень от лампы на объектах. А главное — сдвиньте шар: его тень в лайтмапе останется на старом месте, и ошибка к эталону поползёт вверх.

Выводы

Если оставить от статьи одну мысль, пусть будет такая: непрямой свет — один интеграл, и все техники этой статьи режут его по-разному. Ambient константой считает его одним числом на сцену. AO уточняет по геометрии — сколько полусферы открыто, — но не знает, откуда свет. Кубическая карта отвечает за одно направление из одной точки, SSR — за то, что в кадре. Лайтмапы и пробы считают интеграл заранее и держат ответ в памяти, пока сцена не изменилась. Эталон — трассировка путей — не режет ничего и платит шумом и временем.

Отсюда разбор проблем:

  • картинка плоская, угол освещён как открытая стена — ambient константой без поправки по геометрии;
  • тени от лампы стали грязными после включения AO — AO умножен на прямой свет, а его место — только у непрямого;
  • ровные стены посерели с включённым SSAO — сфера сэмплов вместо полусферы по нормали;
  • тёмная кайма вокруг персонажа на далёкой стене — SSAO без проверки дальности: ореол;
  • тень под предметом тает, когда он уходит за край экрана — экранный эффект не видит того, чего нет в буфере;
  • отражение плывёт отдельно от комнаты при движении камеры — кубическая карта без параллакс-коррекции;
  • стены в зеркале на месте, а предметы нет — коробка-заместитель не знает о предметах внутри;
  • в отражении дыра или чернота у верхнего края — промах SSR: отражается то, чего в кадре нет;
  • в зеркале виден перед предмета вместо его низа — в буфере глубины только лицевые поверхности;
  • непрямой свет пятнами, тень под ящиком шире ящика — низкая плотность текселей лайтмапа;
  • сдвинули предмет, а его тень осталась на полу — запечённое не знает, что сцена изменилась;
  • персонаж светится в тени, как на свету — интерполяция между пробами по обе стороны перегородки.

Чего мы не разобрали

Фотонные карты и кэш освещённости. Офлайн-подходы, из которых выросли многие идеи реального времени: свет раскладывается по сцене заранее и ищется рядом с точкой.

Объёмные методы. Свет, разложенный по вокселям или по объёмной сетке (light propagation volumes, voxel cone tracing), — динамический непрямой свет до аппаратной трассировки, со своими утечками сквозь тонкие стены.

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

Экранный непрямой свет. SSGI — тот же марш по буферу, что у SSR, но для матового отскока: перенос цвета от того, что в кадре, и ровно с теми же промахами.

И общая закономерность, сквозная для всей дуги. Растеризация отвечает на вопрос «что видно из камеры» и ни на какой другой. Непрямой свет — вопрос «что видно из точки на стене», и его пришлось собирать из подручного: из константы, из буфера глубины, из карт, снятых заранее, из текстур, запечённых ночью. Каждая техника — след того, что задать вопрос напрямую было нельзя. Разница между «включил GI и стало красиво» и «знаю, что чиню» — в том, чтобы понимать, какой из вопросов каждая техника на самом деле задаёт.

Дальше — последняя часть дуги. Всю статью эталоном был луч, пущенный в сцену: он отвечал на вопрос «что видно отсюда» из любой точки, а не только из камеры, и все приближения мы сравнивали с ним. В следующей статье этот луч перестаёт быть эталоном для сравнения и становится способом рисовать: трассировка лучей против растеризации, пересечения и ускоряющие структуры, трассировка путей в реальном времени и шум, который приходится давить.

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

// @easy_dev_math

Разборы графики с кодом — каждую неделю в канале.