Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimize lighting: check shadow ray after brdf shading #756

Open
wants to merge 3 commits into
base: vulkan
Choose a base branch
from

Conversation

LifeKILLED
Copy link

@LifeKILLED LifeKILLED commented Dec 26, 2024

Увеличило производительность полигональных источников света

В комнате ученых пасс light_direct_poly занимал 19 миллисекунд. После изменений занимает 13 миллисекунд. Общая длительность кадра уменьшилась с 42-45 миллисекунд до 32-35 миллисекунд.

Главным изменением стала проверка яркости brdf затенения перед пусканием луча (до этого сначала пускался луч и только потом считался шейдинг). В результате лишние лучи пускались даже тогда, когда свет не падал на плоскость (этот расчет и происходит в brdf)

Порядок проверки не играл роли при PROJECTED варианте сэмплинга. Но при выборе SIMPLE_SOLID производительность выросла.

Точно так же без изменения порядка проверок SIMPLE_SOLID не увеличивала производительность относительно PROJECTED. Но вместе с изменением порядка проверок сэмплинг SIMPLE_SOLID увеличивает производительность.

UPDATE: Второй (небольшой) оптимизацией стало вынесение вычисления рандома вне функции сэмплинга источников света. Теперь для всех источников света в текселе считается один набор значений vec3 rnd. Это никак не влияет на визуал, т.к. эти значения варьируются в соседних текселях, что важнее

P.S.: в названии одного из коммитов указана ишью, но он к ней отношения не имеет

До изменения порядка и выбора SIMPLE_SOLID:
image

После изменения порядка и выбора SIMPLE_SOLID:
image

После оптимизации рандомных значений для источников света:
image

Комната ученых (после изменений):
image

@w23
Copy link
Owner

w23 commented Dec 26, 2024

Главным изменением стала проверка яркости brdf затенения перед пусканием луча (до этого сначала пускался луч и только потом считался шейдинг). В результате лишние лучи пускались даже тогда, когда свет не падал на плоскость (этот расчет и происходит в brdf)

Это интересное изменение, и результаты интересные. Я про это как-то не думал вообще, спасибо.

Порядок проверки не играл роли при PROJECTED варианте сэмплинга. Но при выборе SIMPLE_SOLID производительность выросла.

А вот это некорректное изменение. Дело в том, что SIMPLE_SOLID довольно заметно проигрывает качеством картинки PROJECTED'у. Я там много недель страдал со всякими вариантами того, как считать вклад площадного источника, и пришёл к выводу, что без физически-PROJECTED-корректного никак. Можно, наверное, по git blame найти коммиты-пулл-реквесты-ишьи с картинками и сравнениями.

Для прямого освещения, увы, придётся оставить PROJECTED пока. В планах есть его ушатать -- там сложный алгоритм, но он написан математиками, а не инженерами, и на перформанс не оптимизировался в достаточной мере. Его ещё можно сильно упростить, если зафорсить все источники света в квады или треугольники -- там пропадёт под капотом цикл, сразу пачка регистров освободится, VGPR pressure ослабнет, occupancy пробъёт членом потолок.

А ещё можно попробовать finish your derivations применить, наверняка по красоте многое схлопнется ещё, как обычно.

UPDATE: Второй (небольшой) оптимизацией стало вынесение вычисления рандома вне функции сэмплинга источников света. Теперь для всех источников света в текселе считается один набор значений vec3 rnd. Это никак не влияет на визуал, т.к. эти значения варьируются в соседних текселях, что важнее

Это интересно, и может быть валидным изменением, надо смотреть, не появляются ли какие когерентные артефакты.

@LifeKILLED
Copy link
Author

Если PROJECTED сэмплинг станет более легким, будет отлично. Остальные два изменения помогут выиграть дополнительные ресурсы. То, что SIMPLE_SOLID все облегчает, говорит о том, что если сэмплинг будет более легким, то производительность повысится. Я даже пробовал заменять их на spotlight, чтобы было еще меньше расчетов. И производительность повышалась еще сильнее.

Что касается объединения рандомных значений всех источников света, артефактов просто не может быть.

Причина в том, что рандомное распределение значений влияет на light direction. Например, луч тени будет указывать на левый-верхний угол полигона для всех источников света в одном текселе. В соседнем тексле лучи будут указывать уже на в правый-нижний у угол всех источников света. И так далее.

Ровное рандомное распределение важно для создания мягкой тени источника света. И важно именно то, чтобы значения в соседних текселях были ровно распределены для одного источника света. Результат этого источника света не зависит от того, что получилось в других источниках света. Все ведь друг с другом складывается, а от перемены слагаемых сумма не меняется. Поскольку источники света друг от друга не зависят, то нам не важно, совпадают ли их рандомные значения или не совпадают

@LifeKILLED
Copy link
Author

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

А если в цепочке вычислений будет два раза использован синий шум, получится, что во всех пущенных вправо лучах отражения будет один источник света, а в пущенных влево - другой. И это уже действительно приведет к артефактам. Поэтому синий шум лучше использовать только один раз, а для менее важного использовать обычный рандом

@w23
Copy link
Owner

w23 commented Dec 26, 2024

Вот, кстати, почему SIMPLE_SOLID не алё: #361

@0x4E69676874466F78
Copy link
Collaborator

0x4E69676874466F78 commented Dec 26, 2024

Вот, кстати, почему SIMPLE_SOLID не алё: #361

Но если удастся убрать мусорную нарезку bsp (а это вроде как было в планах чтобы жижу исправить #654) то вероятно проблема уйдёт и SIMPLE_SOLID будет пригоден, нет? Там ещё был случай с нормалями насколько помню, но это в игре едва ли заметно.
Помимо test_gradient есть серия карт test_light_sampling* разработанных специально под тестирование сэмплирования.

@@ -10,9 +10,9 @@
#define DO_ALL_IN_CLUSTER 1

#ifndef RAY_BOUNCE
#define PROJECTED
//#define PROJECTED
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это в любом случае пока рановато, много где будет выглядеть некорректно без решения #654.

@0x4E69676874466F78
Copy link
Collaborator

Это можно было бы принять как улучшение SIMPLE_SOLID на будущее, но без форса его как основного сейчас.

@LifeKILLED
Copy link
Author

LifeKILLED commented Dec 26, 2024

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

@LifeKILLED
Copy link
Author

Мне очень не нравится, что в projected туда-сюда передаются массивы точек, их в структуре несколько штук. И какая-то стена кода непонятная.

Из семплинга можно убрать спекуляр вообще, потому что плоскости видны в отражениях (сейчас не видны, я выключил, надо бы включить). Отражение плоскости и есть спекуляр. Более того, когда мы аппроксимируем спекуляр, у нас там видны квадраты, залитые цветом. Таким образом даже такая стена кода, как Projected, оказывается просто аппроксимацией ненужной, лишней и очень неточной.

Тени, которые используют light direction, можно рандомить с помощью барицентрик координаты на квадратике. там даже матрицы не нужны, просто 2 стороны куба умножаем на 2 рандома, складываем. Получается кончик для тени.

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

@LifeKILLED
Copy link
Author

То, как сделано сейчас, вызывает дублирование спекуляра на отражении и в функции прямого освещения. Спекуляр получается в два раза ярче. Но лучом и отражением считать легче и правильнее в любом случае.

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

@LifeKILLED
Copy link
Author

LifeKILLED commented Dec 26, 2024

Резюмирую:

  1. Дублирование спекуляра и отражения - очень плохо. Надо выбрать что-то одно, желательно более быстрое, иначе это объективно некорректно. Бесплатный сэмпл эмиссива в пассе отражения против огромных стен кода на каждый источник света
  2. Диффуз огромных поверхностей неправильной формы (лужи токсинов) можно считать через indirect diffuse. Бесплатный сэмпл одной эмиссив текстуры на тексель (который и так будет делаться) против тяжелых циклов, аппроксимаций, теней на каждую плоскость токсинов и так далее. И то и другое одновременно - некорректное дублирование, лучше выбрать бесплатный indirect diffuse
  3. Диффуз квадратных лампочек можно считать гораздо проще без циклов, используя две стороны квадрата и проверять рассечение лучом тени, который в любом случае будет пускаться. Нужен будет solid angle целого квадрата (не рассеченного), а это наверняка можно сделать как-то просто. Грубо говоря квадрат расстояния до сэмпла умноженный на dot product от плоскости источника света (это соответствует уменьшению solid angle при повороте плоскости источника света)

@LifeKILLED
Copy link
Author

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

Может быть даже как-то автоматом это детектить. Если плоскость источника света не соответствует квадрату, то обрабатывать через баунсы. А если соответствует, то через прямой свет

@w23
Copy link
Owner

w23 commented Dec 26, 2024

Вот, кстати, почему SIMPLE_SOLID не алё: #361

Но если удастся убрать мусорную нарезку bsp (а это вроде как было в планах чтобы жижу исправить #654) то вероятно проблема уйдёт и SIMPLE_SOLID будет пригоден, нет?

Увы. SIMPLE_SOLID это вообще хак тренировочный, чтобы понять, что в принципе происходит.
Там смешаны в одну кучу две вещи: выбор треугольника из полигона для семлирования по его телесному углу в точку семплирования, а затем семплирование барицентрически случайной точки на его поверхности.

Именно барицентрически случайная точка (а не спроецированно, PROJECTED, случайная) даёт основные артефакты. Даже на исправленной BSP нарезке (что само по себе уже не очень просто) оно будет артефачить, особенно там, где мы считаем "примыкающий свет", т.е. там, где лампочка перпендикулярно касается стены. То есть там, где свет даёт самые пространственно-высокочастотно заметные эффекты. Где это будет наиболее бить в глаза.

Там ещё был случай с нормалями насколько помню, но это в игре едва ли заметно. Помимо test_gradient есть серия карт test_light_sampling* разработанных специально под тестирование сэмплирования.

Про нормали не помню, напомни.

@w23
Copy link
Owner

w23 commented Dec 26, 2024

Резюмирую:

1. Дублирование спекуляра и отражения - очень плохо. Надо выбрать что-то одно, желательно более быстрое, иначе это объективно некорректно. Бесплатный сэмпл эмиссива в пассе отражения против огромных стен кода на каждый источник света

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

2. Диффуз огромных поверхностей неправильной формы (лужи токсинов) можно считать через indirect diffuse. Бесплатный сэмпл одной эмиссив текстуры на тексель (который и так будет делаться) против тяжелых циклов, аппроксимаций, теней на каждую плоскость токсинов и так далее. И то и другое одновременно - некорректное дублирование, лучше выбрать бесплатный indirect diffuse

Это интересная мысль, надо ставить эксперименты. Но есть заметная сложность в том, что в коде крайне тяжело понять, какие лапшинки из этой bsp-нарезанной лапши являются частью огромных поверхностей. Руками их тоже не проставишь -- их там сотни, если не тысячи.

3. Диффуз квадратных лампочек можно считать гораздо проще без циклов, используя две стороны квадрата и проверять рассечение лучом тени, который в любом случае будет пускаться. Нужен будет solid angle целого квадрата (не рассеченного), а это наверняка можно сделать как-то просто. Грубо говоря квадрат расстояния до сэмпла умноженный на dot product от плоскости источника света (это соответствует уменьшению solid angle при повороте плоскости источника света)

Циклы там и передача массивов потому, что писали математики (и оно компилятором как-то оптимизируется, но фундаментально с таким количеством working set данных оно ничего не может сделать, конечно, поэтому жрёт регистры, как не в себя).
Ну и нельзя так просто взять и заменить корректное семплирование телесного угла на тупо расстояние-угол. Потому что всякие случаи вытянутых прямоугольников (много), близких одним углом (много -- всё, что касается стен), и пр.

Мы об это всё уже разбивали лоб ранее.

@w23
Copy link
Owner

w23 commented Dec 26, 2024

Тут во всём это проклёвывается другая мысль ещё:
а что если вернуться к корням, и попробовать взад самый тупой в лоб brute force path tracing без никто. Когда я начинал этот проект, это было первое, что я попробовал, и работало оно из рук вон плохо. Но тогда у меня не было ни понимания, что я делаю, ни нормальной модели данных, ни оптимизации, ни шумодава.
С учётом того, что прямые лучи считаются на моём условном железе сотни микросекунд, мы заочно можем позволить себе много десятков лучей на кадр/пиксель.

Даже если оно не заработает достаточно хорошо, было бы всё равно полезно иметь рядом лежаший unbiased ground-truth рендер, который можно по команде позвать отрисовываться на несколько секунд, чтобы сравнивать со всеми нашими сложными семплирующими утехами.

@LifeKILLED
Copy link
Author

В маленькие источники света маловероятно попадать лучом случайно, поэтому приходится сэмплить их как источники света. Вряд ли получится от этого уйти. Но есть еще пара мыслей.

  1. Весово рандомить источники света. Я это уже пробовал в старой другой ветке. ФПС прибавлялось так, что получалось даже рисовать баунсы и отражения в полном разрешении. Но прибавлялся и небольшой шум

  2. Использовать преимущества compute shader'ов: сохранять промежуточные результаты в shared memory и записывать результат в произвольные тексели экранного буфера. Например, чтобы поток (который pix) не обрабатывал свой тексель, как во фрагментных шейдерах. А к примеру брал один источник света, предрассчитывал для него значения. И освещал бы им все пиксели клетки. А можно даже и не все, а несколько рандомных в зависимости от веса. Вот это сумасшествие я бы попробовал.

@LifeKILLED
Copy link
Author

LifeKILLED commented Dec 26, 2024

То есть я хочу попробовать сохранить position_t, нормали и material_rmxx всех пикселей клетки 8х8 в shared_memory, чтобы можно было прочитать их один раз (так оптимизируются блюры)

А потом в одном потоке (pix) брать один источник света и с ним ходить по всем этим пикселям. можно сделать несколько рандомных сэмплов, и если они есть, осветить еще и остальные пиксели. или посчитать сэмплинг эмиссив кусочка в половинном разрешении (w компонента дает мягкие переходы).

А соседние потоки (pix) будут считать другие источники света.

Это изменение порядка позволит прочитать в одном текселе всего-лишь 1 источник света на всю плитку 8х8 и, возможно, сэкономить на промежуточных значениях. А не читать сотни этих источников света в каждом потоке.

@0x4E69676874466F78
Copy link
Collaborator

0x4E69676874466F78 commented Dec 26, 2024

Про нормали не помню, напомни.

Сравни на test_material где "синтетический" тест нормалей с разными сэмплерами, по идее на PROJECTED оно там выглядит визуально корректнее чем на SIMPLE_SOLID.

@LifeKILLED
Copy link
Author

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

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

К сожалению, это не решит проблемой с комнатой ученых. Там очень много плоскостных источников света в одном очень маленьком месте

@0x4E69676874466F78
Copy link
Collaborator

0x4E69676874466F78 commented Dec 28, 2024

К сожалению, это не решит проблемой с комнатой ученых. Там очень много плоскостных источников света в одном очень маленьком месте

В комнате не много, хотя там есть лишние 5 источников из-за сраной bsp нарезки, там проблема отсечения видимости из-за неудачных кластеров (попадает парадная комната с её лампами) и кривого BSP-отсечения (попадает коридор с лампочками по бокам, но парадной не видно, но это не мешает кластерам как-то заафектить если подойти к стене).

@LifeKILLED
Copy link
Author

... кривого BSP-отсечения

Когда я добавлял в старых ветках рандом источников света, рядом со стенками очень сильно шумело как раз там, где было много лишних источников света. Но и в самой комнате тоже шум был выше нормы, потому что в ней стоит несколько шкафов, на каждом из которых по 4 плоскостных лампочки. И еще на потолке источники света. В сумме их получается очень много

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

@0x4E69676874466F78
Copy link
Collaborator

В сумме их получается очень много

18 всего логических + 5 паразитных из-за нарезки, итого 23, это очень много?

Например в раздевалке 15 источников полигональных + 3 спотлайта

@LifeKILLED
Copy link
Author

LifeKILLED commented Dec 29, 2024

18 всего логических + 5 паразитных из-за нарезки, итого 23, это очень много?
Например в раздевалке 15 источников полигональных + 3 спотлайта

Тогда странно, что такая маленькая разница в числе источников света и такая большая во времени кадра

UPD: у нас ведь не BSP видимость. У нас кубические кластеры, в которые собраны источники света соседних BSP участков. Надо сделать вывод разного количества источников света разными цветами и узнать фактическое число у нас. У меня наибольшую проблему вызывает стенка, контачащая с синим холлом, в который ты заходишь от вагончика.

@0x4E69676874466F78
Copy link
Collaborator

0x4E69676874466F78 commented Dec 29, 2024

r_speeds 15
r_speeds_graph add light.polygons
r_speeds_graph add light.polygons_dynamic
r_speeds_graph add light.points
r_speeds_graph add light.points_dynamic

Почему-то число источников не обновляется, показывает что их дохера, только в одном месте у меня обновилось число поинтов и всё, в других ничего.

@w23
Copy link
Owner

w23 commented Dec 29, 2024

r_speeds 15
r_speeds_graph add light.polygons
r_speeds_graph add light.polygons_dynamic
r_speeds_graph add light.points
r_speeds_graph add light.points_dynamic

Почему-то число источников не обновляется, показывает что их дохера, только в одном месте у меня обновилось число поинтов и всё, в других ничего.

Это на vulkan ветке, или на моих поделках свежих?
Если не свежих -- там может быть сломано в теории, т.к. я переделывал трекинг некоторых вещей.

@0x4E69676874466F78
Copy link
Collaborator

0x4E69676874466F78 commented Dec 29, 2024

Это на vulkan ветке, или на моих поделках свежих?

Да On branch vulkan Your branch is up to date with 'origin/vulkan'.

Меняется оно только при заходе на другую карту. Или так и должно быть? (показывается на всю карту и всё?)
Хотя почему тогда число поинтлайтов менялось без перехода а от того где я в сцене?
Я перепутал, там менялось число динамических полигонов:
image
Либо это какая-то плавающая бага.

@0x4E69676874466F78
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants