Получился очень большой пост. Поэтому разбавлю текст скриншотами новых форм рельефа, которые я недавно добавил в движок. Они основаны на модах Rodrigo, пока что я обновил реки и добавил рифты (разломы) для пустынных планет. Параметры рифтов вынесены в редактор и скрипты. Это конечно же не прямая копия кода Rodrigo, а изменённый, оптимизированный и адаптированный под новый движок код.

Полёт над новым ландшафтом. Я всё ещё не удовлетворён резкими переходами между цветом и материалами.

Продолжаю работу над обновлённым ландшафтным движком. Полностью переделана логика обхода quadtree, генерация геометрии отделена от генерации текстур (в некотором смысле), введена система приоритетов генерации, переделана error-метрика. Всё это привело к более быстрой загрузке ландшафта при меньшем потреблении памяти и более высоком визуальном качестве (детализации).

Ландшафт в SE рисуется с помощью quadtree (квадродерева). Чтобы покрыть сферу, и для удобства работы с текстурами, используется т.н. “кубсфера” – куб, каждая грань которого это quadtree, “надутый” в сферу простым преобразованием координат вершин:

Таким образом, каждая планета в SE состоит из шести отдельных квадродеревьев. Разрешение текстур самого верхнего (нулевого) уровня 256*256 пикселей, разрешение сетки геометрии 33*33 вершины. Если камера достаточно близко к планете, так, что текстура слишком сильно растягивается (см. про error-метрику ниже), то соответствующий нод (узел) quadtree делится на 4 потомка первого уровня: у каждого из них свои текстуры и геометрия, но такого же разрешения. Процесс продолжается, пока делить ноды дальше будет уже не нужно (текстуры всех нодов меньше 256 пикселей на экране). Текстуры нодов генерируются или загружаются с диска по мере надобности, т.е. только тех нодов, которые реально видны в данный момент. Таким образом, теоретически можно получить бесконечную детализацию планет. На практике же она ограничена имеющимися текстурами (для реальных планет), или точностью вещественных чисел (для процедурных). Так, для землеразмерной планеты максимальный уровень quadtree составляет 12: при попытке сгенерировать текстуры для 13 уровня появляются артефакты. Разрешение одной “грани куба” (одного quadtree) получается 256 * 2^12 = 1048576 (1 терапиксель), а в метрах вдоль экватора или меридиана это соответствует 9.5 метра на пиксель. Т.к. разрешение геометрии (33*33 вершины) в 8 раз меньше разрешения текстур (256*256 пикселей), можно создать ещё 3 “виртуальных” уровня, чтобы довести детализацию геометрии до максимальной. Текстуры для этих виртуальных уровней генерируются несколько хитрее: карты высот и нормалей используются от последнего, 12-го уровня, т.е. дополнительных деталей не имеют, а карты цвета генерируются как обычно, т.к. для них ограничение точности вещественных чисел не стоит так остро. Разрешение карт цвета получается в 8 раз выше, т.е. около 1 метра на пиксель, но они не совсем полноценные.

Обновлённые реки

Таким образом, детализация 1 метр на пиксель – это фундаментальное ограничение движка SE. В других движках оно преодолевается по-разному, например генерация текстур для последних уровней осуществляется в числах двойной точности (double), которые, однако, весьма медленно работают на видеокартах. Вместо этого применяется их эмуляция с помощью обычных чисел одинарной точности (float). В SE я поступил по-другому – просто добавлял небольшой процедурный шум при рендере планеты. Теперь он заменён на новую систему детальных текстур, что позволяет достичь разрешения 1 миллиметр на пиксель, и при этом ещё и более реалистичного результата, чем если бы текстуры были полностью процедурные. Всё-таки реалистично сгенерировать текстуры камней, гальки, травы, песка и прочие – довольно сложно и затратно.

Река, вид вблизи

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

  • Геометрия для нодов ландшафта создаётся по карте (текстуре) высот, как и прежде. Но т.к. разрешение геометрии в 8 раз меньше разрешения текстур, незачем генерировать карту высот для каждого уровня quadtree, можно “перепрыгивать” через 3 уровня (2 ^ 3 = 8). Т.к. в движке уже есть возможность рендерить ноды с текстурой предка (используется для трёх последних “виртуальных” уровней), то можно попробовать генерировать текстуры только для каждого 3-го уровня. Это приводит к очень быстрой, буквально секундной загрузке ландшафта, правда местами его текстуры получаются сильно размытыми. Зато геометрия (силуэты гор) сразу получается в полной детализации.
  • Из карт высот сразу же создаются карты нормалей для освещения. Кроме них, нужна текстура цвета (альбедо), и, если на планете есть города или вулканы, текстура эмиссии. Для визуального восприятия нормали оказываются гораздо важнее цвета, поэтому можно ввести приоритет – генерировать сперва текстуры нормалей, и только потом цвет и прочие текстуры.
  • После такой первичной загрузки, можно сгенерировать текстуры для пропущенных нодов. Причём только для нодов, которые на самом деле рендерятся (т.е. если какой-то нод разделился на 4 потомка, то сами он ведь не рисуется, вместо него рисуются его потомки). Это дополнительно сокращает список текстур, которые надо сгенерировать, а значит, ускоряет загрузку. Можно и здесь использовать свою систему приоритетов: первым делом генерировать текстуры более нижних (детальных) нодов – они ближе к камере. Ещё один уровень приоритета – расстояние от нода до камеры (потому что ноды одного уровня могут находиться на разном расстоянии, так что приоритета только по номеру уровня недостаточно). Таким образом, ближайшие области ландшафта загружаются первыми.
  • Кроме обозначенных текстур, для последних уровней quadtree генерируются ещё т.н. splat текстуры, указывающие, какие материалы и как накладывать в данной точке (см. предыдущий блог-пост о ландшафте).
  • В горных местностях реки формируют живописные каньоны

    Error-метрика – это условие, при котором нод ландшафта должен разделиться на 4 потомка. Раньше этим условием был “размер текстуры на экране в пикселях”: если он больше разрешения текстуры (256 пикселей), например 300 пикселей, то текстура начинает “мылиться”, и нод делится на 4 потомка. Но рассчитать размер на экране не так-то просто, кусок ландшафта это не куб и даже не четырёхугольник. Поэтому error метрика была весьма заковыристой, с разными формулами для нодов разного уровня. Теперь она заменена на расстояние до края нода, делённое на размер нода, которые рассчитываются в “выпрямленных” координатах (как если бы сфера планеты была выпрямлена обратно в куб). Это автоматически даёт правильный результат для нодов любого уровня и любого положения, в т.ч. вблизи вершин “куба”, которые примерно вдвое меньше, чем в центре граней “куба”. Осталось лишь добавить коэффициент, масштабирующий расстояния в зависимости от поля зрения камеры и от настройки LOD ландшафта (больше LOD – меньше расстояние деления на потомки, чётче текстуры, но и памяти и производительности видеокарты требуется больше).

    Ещё реки с каньонами

    Также был убран код, который позволял движку рисовать только часть нода-предка. После того, как нод делился на 4 потомка, вычислялась error-метрика для каждого из них, и если какой-то из потомков оказывался меньше 256 пикселей, то он не рисовался, а вместо него рисовался кусок нода-предка. Это уменьшало число полигонов (polycount), что увеличивало производительность на слабом или старом “железе”. Но, как следствие, был виден “мыльный” кусок нода-предка – ведь по его собственной error-метрике ему следовало бы уже разделиться. Это уменьшало визуальное качество ландшафта. Другое неприятное следствие – необходимость сгенерировать/загрузить текстуру предка, что увеличивало требования к видеопамяти и продолжительность загрузки ландшафта. Но на дворе 2017 год, видекарты достаточно быстрые, так что от такой оптимизации можно избавиться. Это улучшает качество ландшафта и позволяет использовать оптимизацию памяти, описанную выше в пункте 3 списка (нет нужды загружать текстуры нода, который разбит на потомки).

    Тектонические разломы по типу Долин Маринера на Марсе

    Для пресетов детальных текстур я реализовал систему, которая упрощает моддинг, устраняя необходимость описания всех текстур для каждого типа поверхности. Для каждого класса планеты (например, безвоздушная терра, пустыня терра и т.д.) есть скрипт пресета “по умолчанию” с тщательно сконфигурированными текстурами для всех биомов/материалов, и их цвета (палитра). Другие «альтернативные» скрипты для тех же классов планет могут содержать только отличия, например, описание другой текстуры для одного биома или другие цвета. Все отсутствующие данные загружаются из скрипта-по-умолчанию, ссылка на который указана в данном альтернативном скрипте. Редактор материалов автоматически сохраняет файл скрипта таким же образом: записывает в него только отличия от скрипта-по-умолчанию. Это должно упростить “моддинг в блокноте”, например, преобразование старого файла палитр планет в новую систему.

    Разломы, вид вблизи

    Вы можете оценить скорость загрузки рельефа на этом видео. Оно было записано с использованием внешнего ПО (GeForce Experience), а не встроенным видеозахватом, поэтому видны все лаги и латентность загрузки. Во второй части я включил отладочный оверлей, показывающий границы узлов octree. Разные цвета соответствуют узлам разных уровней, а белые вспышки указывают узлы, которые генерируют текстуры в данный момент.

    Обсудить пост можно на форуме.