Как стать автором
Обновить

Комментарии 54

Отличная статья.
Вы не пытались вынести расчеты на GPU? GPU должно быть намного быстрее чем потоки CPU. Если пытались какие проблемы возникли и почему отказались от такого решения?
Спасибо.
Планирую перенести на CUDA (пока что нахожусь на этапе изучения данной технологии).
Насколько я понял, придётся отказаться от указателей на функции (а ведь на этом сейчас базируется архитектура рендера).
Присоединяюсь. Это очень круто, но месье всё же знает толк) Можно даже не на куду, а просто шейдерами, например. Правда, это уже совсем другая история…
Мьсе правильно мыслит, многие коммерческие рендеры используют GPU
Видимо, мы про разных месье) Я про автора топика, видимо, неясно выразился… По поводу GPU-то я согласен, естественно…
НЛО прилетело и опубликовало эту надпись здесь
Сглаживания границ не хватает на получившихся картинках.
антиалиасинг вы имели ввиду. А как же глобальное освещение с просчетом до 3-его отскока фотона, угловые засветы по закону Френеля, подповерхностное свечение, bump меппинг?)
Bump mapping можно добавить в текущую архитектуру без особых проблем — по сути это просто отдельная текстура, которая используется для отклонения вектора нормали (а текстуры уже реализованы).

В плане глобального освещения — мне нравится photon mapping. Для реализации данной технологии можно переиспользовать уже написанное kd-дерево (используется для быстрого поиска фотонов). Хотя, в последнее время всё больше обращаю внимание на path tracing.

Много чего хочется реализовать, но т.к. это pet project — иногда просто не хватает времени…
А методом рендера maxwell render или fry render не интересовались? Они конечно дольше но…
свои преимущества имеют несомненно в потрясающей реалистичности
www.youtube.com/watch?v=gXbKFVD-rvo
Честно говоря, особо не интересовался.
Видео красивое, подозреваю что там используется какой-нибудь алгоритм глобального освещения.
Я бы с удовольствием занялся реализацией данной фичи :-) но, чувствую что в одиночку это будет довольно долго…
А уж сколько оно рендерилось и представить страшно)
А вот кстати… и статья на эту тему habrahabr.ru/post/142003/
Кстати, у меня была идея написать механизм, который позволял бы распределять на разные компьютеры рендеринг разных кадров (или частей изображения). Хотел написать для этого прослойку на Эрланге. Но, пока что это так и осталось в планах)
Полагаю, имелось ввиду как раз сглаживание границ (зубчатых граней), которое выполняется путём усреднения группы лучей с небольшими отклонениями (например в рамках 2х2 точки с распределением Гаусса). Наверное, эффект антиалиасинга в этом случаетоже может появиться.
Я пробовал увеличить количество лучей (несколько лучей на пиксель) — но это приводило к замедлению рендеринга.
Наверное, целесообразно увеличивать количество лучей только в тех участках изображения, где наблюдаются границы объектов.
Но в данном случае нужно каким-нибудь эффективным образом детектить границы объектов.
В общем, так это и осталось в мыслях )
Надо не просто увеличивать, но немного смещать их, как я описал выше. Уже при использовании четырех лучей вместо одного картинка улучшается, так что увеличение времени расчета оправдано.

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



В кадре только зеркальная сфера, но в ней отражается вся сцена.
)) и все-таки даже в вашем примере пикселы несут информацию об отображении отраженных объектов, чуть хитрее но все так же просто. Отражения и тени имеют такую же сквозную нумерацию, как и предметы.
А если надо будет сгладить границу черного и белого пятен на текстуре? Или резкую тень? И зачем сглаживать, когда объекты имеют одинаковый цвет?
А FXAA не быстрее?
Хотя это не realtime, как я понимаю.
А еще лучше — завести буфер глубины, и использовать его подобным образом.
Можно.
Еще можно завести буфер нормалей, чтоб отслеживать острые ребра, как у кубика например.
Но использование контраста проще, универсальнее и покрывает гораздо больше ситуаций, таких как резкие границы на текстурах, края теней, тонкие блики, отражения и преломления и т.д.
Спасибо за идеи. Реализовал антиаласинг :)

Для поиска границ, в качестве чернового варианта, используется Оператор Собеля
Для каждой точки, которая находится на границе — запускаю 4 луча.

Стало красивее:

Коммит: github.com/lagodiuk/raytracing-render/commit/b6f2d3d49bc13488c7c560c4b54ccbe9cb6d5c0d
Спасибо большое, за наводку на «Head first C».
1. Как определяли терминальный критерий для SAH? Оптимальное разбиение — это понятно, но нужно же определять когда в разделении нет необходимости.
2. Как решали проблему «дробления» примитива? При рекурсивном делении мы наталкиваемся на момент, когда разделяющая плоскость может разбить притимив.
3. И почему именно kd? :)
1. Относительно того, когда прекращать построение дерева:

Допустим, мы пытаемся разбить сцену на две части.
Начальная площадь поверхности сцены = S_0 и содержит она N_0 полигонов
Допустим — площадь левой части S_L, и содержит она N_L полигонов
Площадь правой части S_R, и содержит она N_R полигонов

Тогда делаем следующее:

1) нормируем площади:
S_0_norm = S_0 / S_0 = 1
S_L_norm = S_L / S_0
S_R_norm = S_R / S_0

2) рассчитываем SAH
SAH_0 = S_0_norm * N_0
SAH_L = S_L_norm * N_L
SAH_R = S_R_norm * N_R

3) введём «штраф» за разбиение (просто константа, которую я подобрал эмпирически) — SPLIT_PENALTY

4) рассчитаем целесообразность разбиения:
delta_SAH = (SAH_L + SAH_R + SPLIT_PENALTY) — SAH_0

если (delta_SAH < 0) — значит разбиение проводит целесообразно

5) ещё, «на всякий случай», я контролирую максимальную глубину дерева (у меня есть константа MAX_TREE_DEPTH)

2. «Дробление» примитива
Если примитив попадает и в «левую» и в «правую» половину — значит считаем его и там и там увеличиваем значение N_L и N_R)

3. Я читал про различные структуры данных для ускорения рендеринга, но kd-дерево для меня показалось наиболее понятным и гибким (хотя, возможно я ошибаюсь)
github.com/lagodiuk/raytracing-render/blob/master/render/src/kdtree.c
Здесь, начиная со строки 267, функция для поиска наилучшего разбиения. Там реализация того что я описал (немножко оптимизированная).
Видимо, по константе, я проглядел в коде :)
Не знаю, как Вы обрабатываете пересечения, но могу посоветовать для треугольников использовать барицентрический тест, а для пересечения вида луч-плоскость Robust Ray–Box Intersection Algorithm. Должно помочь в ускорении :)

И есть над чем поработать, не пробовали использовать VTune для анализа производительности? Несколько минут для 100к элементов — это медлено. Хотя, это уже придирки.
Интересная статья, спасибо.

Если Вы еще будете работать по этой теме, поищите по таким людям как Havran, Wald. можно найти интересную информацию по GPU рендерингу :)

И вместо MAX_SPLITS_OF_VOXEL, можно использовать границы примитивов в текущем voxel.
Спасибо за наводки :)

Я тоже думал об ускорении построения дерева. У меня сейчас используется довольно прямолинейный алгоритм:

1) Допустим, что мы рассекаем сцену плоскостью XY
2) Значит, нужно найти соответствующую координату разбиения по оси Z
3) У нас есть крайние точки сцены: Zmin и Zmax
4) Zdist = Zmax — Zmin
5) dZ = Zdist / MAX_SPLITS_OF_VOXEL
6)
for(z = Zmin; z < Zmax; z += dZ) {
         Внутри цикла - разбиваем сцену на 2 части
         считаем количество полигонов попавших в разные части сцены
         считаем площадь каждой части
         считаем SAH
     }

Шаг 6 можно оптимизировать — для каждой новой итерации не нужно пересчитывать заново какие полигоны куда попали. Но, к сожалению, руки ещё до этого не дошли
Только что получилось ускорить построение дерева :-)
Подробности описал в этом комментарии: habrahabr.ru/post/187720/#comment_6580780
Несколько мыслей:
1) Обратите внимение на структуры данных BVH2, BVH4;
2) Хорошим источником знаний может стать Embree — оптимизированный рендер от Intel с открытым кодом (там включено Global illumination);
3) И, конечно, настольная книга по рендерингу Physically Based Rendering;
4) Мне понравилось, как написано словосочетание трёх английских слов «Ray tracing рендер» :)
5) И «bump меппинг» :)
Еще, для чуть большей реалистичности, можно отдельно трассировать красный / зеленый / синий / и промежуточные между ними цвета, предполагая что для них будут разние коэффициенты преломнения на различных стеклянных поверхностях. (Но «разделять» лучи стоит только при прохождении через полупрозрачные обьекты, естественно).
Спасибо за идею. Тоже думал над этим — должно получится довольно красиво.
При аккуратной реализации можно будет даже смоделировать опыт Ньютона по разложению белого света, при прохождении сквозь призму :-)
«Сцена содержит порядка 100000 полигонов (kd-дерево строилось несколько минут)»
Что-то долго у Вас строится дерево. У меня сцена Стэндфорского дракона из ровно 100000 треугольников рендерилась ~0,9 секунды включая время построения дерева. Но у меня было тупое деление пополам, без SAH.
К сожалению да. У меня не очень оптимально реализована процедура построения дерева. Более подробно описал в этом комментарии: habrahabr.ru/post/187720/#comment_6580078
Подумал над вашим комментарием, а вы правы!

Я пытался найти очень хорошее разбиение сцены, и поэтому анализировал возможность разбиения сцены в 100 различных точках (разбивал сцену, считал SAH и т.д.) — на каждом шаге построения дерева.

Теперь я пытаюсь отыскать оптимальное разбиение только в 5 точках: для сцены с автомобилем (107364 полигонов) — дерево строится за несколько секунд.

Вот этот коммит: github.com/lagodiuk/raytracing-render/commit/3a373e3bbaba25e3d777260e326768b2a4d0ec62

Спасибо :-)

UPD: Упс, комментарий должен быть на уровень выше
Я уже писал чуть выше, что чуть более эффективный способ — не брать какое-то значение (5 или 100), а использовать границы примитивов. Т.е. если в узле 1000 примитивов, ищим SAH в 1000 различных точках, если их 10, то ищим в 10 точках. А опытным путем выбирать количество разбиений — это все же слишком грубо :) При разбиении по центру, конечно, дерево строиться быстрее, всего-то нужно одно действие O(1).

Тут нужен баланс. Дерево строиться будет медленнее, но трассировка лучей в нем будет быстрее.
Теперь понял что имеется ввиду :-) Попробую реализовать.

У меня, кстати, ещё была идея — содеражть дополнительный массив объектов, которые не эффективно хранить в kd-дереве (например скайбокс, или объект, который передвигается по сцене).
Содержимое статьи уже не актуально лет эдак 10 (ибо алгоритм легко расспаралеливается, а соответсвенно переносится на гпу: на GLSL шейдер простенького рейтрейса будет занимать около 20 строк). Да и сам рейтрейсинг не является особо интересным так как физически правильного изображения он не дает, а мощностей требует, и не мало. То ли дело — path tracing — вот это другой разговор.
Он дает физически корректное изображение. В данном плане он не уступает ни одному из path-tracing алгоритмов, т.к. path-tracing суть частный случай рэйтрейсинга (в рэйтрейсинге вторичных лучей может быть сколько угодно, а в path-tracing строго один). Единственный критерий, по которому их следует сравнивать — это скорость сходимости.
Вы, наверное, под термином ray-tracing понимаете только его самый примитивный вариант какой-то.
Ray-tracing не аппроксимирует уравнение рендеринга, path-tracing это делает за счет Monte-Carlo сэмплинга случайных направлений с весом BRDF зависящей от материала.
А чем это отличается от ray-tracing? Еще раз повторяю: path-tracing — это raytracing, где вторичный луч только один, за счет чего и получается путь, а не дерево. Что мешает пустить несколько вторичных лучей и взвесить их согласно BSDF (кстати, именно BSDF, если уж мы говорим о реалистичности)?
Если Вы все равно не согласны, то, пожалуйста, объясните тогда, что вы понимаете под термином ray-tracing (огласите кратко алгоритм). Кстати говоря, Вы же выше ссылаетесь на книгу «Physically Based Rendering: From Theory to Implementation», где рассказывается о визуализаторе pbrt (PHYSICALLY BASED RAY-TRACER — обратите внимание на название). И этот самый ray-tracer, гад такой, использует path-tracing на пару с Metropolis light transport. Что же, Matt Pharr и Greg Humphreys не знают, что такое ray-tracing?

Извините, я объединю ответы в один комментарий, т.к. могу отвечать не чаще, чем раз в час.
Господину risenow:
«Даже тени приходится делать отчасти фейковые, что-бы смотрелись более-менее нормально»
Ну ничего себе, это что Вы имеете в виду, можете примеры изоражений привести что ли?

Господа, давайте так: объясните для начала, что вы понимаете под термином ray-tracing, а потом уже будем спорить. А то я вижу, что у нас с вами эти понятия отличаются. Еще лучше, если вы приведете ссылки, где говорится, что path-tracing это не ray-tracing и что ray-tracing не может дать физически-корректного изображения.
Для того что-бы получить гладкие не-резкие тени нужно хрен-пойми сколько вторичных лучей(их количество порой может достигать 50 и даже больше) через каждый пиксель пропускать.

Нет, лучше вы скажите с чего вы взяли то path tracing как-то относится к ray-tracing(оно конечно относится, но только самыми базывыми принципами). Да и определение которое Вы дали path trace'у сверху — бред, почитайте мат часть, что-ли.
Я матчасть читал, Вы об этом не перживайте. Всё та же книга, например, лежит у меня сейчас на столе.
«Для того что-бы получить гладкие не-резкие тени нужно хрен-пойми сколько вторичных лучей(их количество порой может достигать 50 и даже больше) через каждый пиксель пропускать.»
Я всё об этот и талдычу: ray-tracing допускает множество вторичных лучей. Где тут принципиальная невозможность сделать area light?
Причем опять же, как работает path-tracing? Для того, чтобы изображение не было шумным, надо выпустить некоторое количество путей из каждого пикселя. Точно такой же трюк можно провернуть и с ray-tracing'ом, чтобы не воротить большое число вторичных лучей.
Кстати говоря, вторичные лучи — это лучи для BSDF, а не для прямого освещения (secondary lighting != direct [area] lighting). Прямое освещение тут вообще самая незначительная проблема; если отбросить глобалку, то ray-tracing с множеством direct-lighting лучей будет намного быстрее сходиться, чем path-tracing. А для глобалки — да, множество вторичных лучей неоправданно, т.к. вторичка вносит меньший вклад, чем первичка, причем чем больше номер отскока, тем меньше, а число лучей растет с геометрической прогрессией. В данном случае path-tracing работает намного более рационально. Именно поэтому ray-tracing с множественными отскоками практически не используется. Но ложность изначального утверждения «физически правильного изображения он не дает» это не отменяет.
В природе «гладкие не-резкие тени» наблюдаются по двум причинам:
1) существование не точечных источников света
2) в результате освещения диффузно рассеянным светом — областей геометрической тени

А ещё в природе есть:
1) всяческие каустики
2) свет может рассеиваться в результате проникновения в материал на небольшую глубину с последующим излучением (subsurface scattering)
3) есть частички тумана (или дыма), проходя сквозь которые возникает причудливая игра света (в данном случае используется алгоритм — Ray Marching)

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

Ray tracing является алгоритмом для моделирования базовых явлений геометрической оптики.

Нужна колориметрия? — используйте Path tracing (его можно можно реализовать поверх Ray tracing рендера, т.к. оперирует он теми же абстракциями).

И, кстати, а почему именно Path tracing? Ведь для каждого нового положения камеры — приходится заново рендерить всё изображение.

Как по мне — более интересным вариантом является Ray tracing + Photon mapping. В данном случае, фотоны можно сохранять в kd-дереве — и их не нужно заново трасировать при изменении положения камеры.
Не буду спорить, от этого алгоритмы не изменятся, мы просто используем немного разное понимание ray-tracing, и разное толкование терминов. По-сути да, ray-tracing'ом можно назвать все методы где используется трассировка лучей. Я привык понимать этот термин очень узко.
Не дает он корректного изображения. Даже тени приходится делать отчасти фейковые, что-бы смотрелись более-менее нормально. Path-tracing — совершенно иной способ рендеринга, имеющий схожесть с рейтрейсом только в в том, что и там и там используется лучи.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории