Raymarching — очень полезная техника. Он обычно используется для визуализации неявных поверхностей, однородной или неоднородной участвующей среды и вообще любой поверхности, где функцию пересечения найти не так просто. Хотя чистый raymarching не так широко используется в графике и играх в реальном времени, как в демонстрационной сцене, он по-прежнему широко используется вместе с растеризацией для создания потрясающих эффектов. Я собираюсь поэкспериментировать с raymarching в Unity и, как обычно, опубликую здесь код и некоторые пояснения более высокого уровня.

Вы можете получить мой проект Unity здесь, на Github: https://github.com/IRCSS/UnityRaymarching.

Raymarching уже был объяснен на нескольких ресурсах, намного лучше, чем любое объяснение, которое я мог бы дать. Именно поэтому я бы отослал заинтересованного читателя к этой статье от Inigo на тему как визуализировать рельеф поля высот. Причина, по которой я рекомендую именно эту статью, заключается в том, что карты высот очень легко отображать и визуализировать. Это простой случай: выберите точку в пространстве и возьмите ее значение xz, передайте его через функцию карты высот и проверьте, превышает ли высота точки вашу карту высот. Если да, то вы над поверхностью, если нет — под ней.

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

Моя цель на этот раз состояла в том, чтобы объединить растеризацию вместе с raymarching для создания объема в сцене Unity. Эта настройка позволила бы нормально отображать большую часть сцены, но добавляя в микс raymarching, сосуществующий с остальными.

Для этого я сначала визуализирую обратные стороны мешей, на которые я буду выполнять raymarch, в текстуру рендеринга. Это дает мне внутреннюю сторону сетки, если сетка водонепроницаема. Затем идет основной проход, сначала я вычисляю глубину передних граней и с помощью фрагментного шейдера моего основного прохода начинаю маршировать. Идея состоит в том, что передняя грань является начальной точкой raymarch, а задняя — конечной точкой. Рендеринг обратных сторон выполняется с помощью командных буферов.

Для поверхностной функции я использую Волны Герстнера, которые я написал для другого эффекта.

Освещение — это модель PBR, которую я взял отсюда. Это немного перебор, но у меня в любом случае есть другие планы на поверхности. Это первоначальная настройка.

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

Есть некоторые известные проблемы с настройкой. Марширование лучей происходит во фрагментном шейдере полигонов, поэтому, если фрагмент по какой-то причине отбракован, эффект также исчезнет. Это может произойти из-за ZTest или, например, почти отсечения. Вы увидите это, если подойдете слишком близко к объемам или выровняете несколько объемов по оси камеры. Чтобы обойти это, вы можете захотеть выполнить raymarch на пространстве экрана и отобразить глубину передних граней также в цели рендеринга.

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

Дальнейшие чтения

Надеюсь, вам понравилось читать. Вы можете следить за мной в моем твиттере IRCSS

  1. Статья Иниго: https://www.iquilezles.org/www/articles/terrainmarching/terrainmarching.htm
  2. Шейдер PBR: https://github.com/Nadrin/PBR/blob/master/data/shaders/hlsl/pbr.hlsl
  3. пост о том, как использовать буферы команд в Unity: http://colourmath.com/2018/tutorials/adventures-in-commandbuffers-an-epic-pt-1/
  4. Глава GPU Gems о воде и волнах: https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch01.html