Апгрейд ландшафтного движка #3

Получился очень большой пост. Поэтому разбавлю текст скриншотами новых форм рельефа, которые я недавно добавил в движок. Они основаны на модах 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-метровые треугольники - слишком большие, это особенно бросается в глаза в виртуальной реальности. Я планирую сделать это с помощью тесселляции, но это уже в следующей версии. А пока о недавних изменениях в ландшафтном движке:
Error-метрика - это условие, при котором нод ландшафта должен разделиться на 4 потомка. Раньше этим условием был "размер текстуры на экране в пикселях": если он больше разрешения текстуры (256 пикселей), например 300 пикселей, то текстура начинает "мылиться", и нод делится на 4 потомка. Но рассчитать размер на экране не так-то просто, кусок ландшафта это не куб и даже не четырёхугольник. Поэтому error метрика была весьма заковыристой, с разными формулами для нодов разного уровня. Теперь она заменена на расстояние до края нода, делённое на размер нода, которые рассчитываются в "выпрямленных" координатах (как если бы сфера планеты была выпрямлена обратно в куб). Это автоматически даёт правильный результат для нодов любого уровня и любого положения, в т.ч. вблизи вершин "куба", которые примерно вдвое меньше, чем в центре граней "куба". Осталось лишь добавить коэффициент, масштабирующий расстояния в зависимости от поля зрения камеры и от настройки LOD ландшафта (больше LOD - меньше расстояние деления на потомки, чётче текстуры, но и памяти и производительности видеокарты требуется больше).
Также был убран код, который позволял движку рисовать только часть нода-предка. После того, как нод делился на 4 потомка, вычислялась error-метрика для каждого из них, и если какой-то из потомков оказывался меньше 256 пикселей, то он не рисовался, а вместо него рисовался кусок нода-предка. Это уменьшало число полигонов (polycount), что увеличивало производительность на слабом или старом "железе". Но, как следствие, был виден "мыльный" кусок нода-предка - ведь по его собственной error-метрике ему следовало бы уже разделиться. Это уменьшало визуальное качество ландшафта. Другое неприятное следствие - необходимость сгенерировать/загрузить текстуру предка, что увеличивало требования к видеопамяти и продолжительность загрузки ландшафта. Но на дворе 2017 год, видекарты достаточно быстрые, так что от такой оптимизации можно избавиться. Это улучшает качество ландшафта и позволяет использовать оптимизацию памяти, описанную выше в пункте 3 списка (нет нужды загружать текстуры нода, который разбит на потомки).
Для пресетов детальных текстур я реализовал систему, которая упрощает моддинг, устраняя необходимость описания всех текстур для каждого типа поверхности. Для каждого класса планеты (например, безвоздушная терра, пустыня терра и т.д.) есть скрипт пресета "по умолчанию" с тщательно сконфигурированными текстурами для всех биомов/материалов, и их цвета (палитра). Другие «альтернативные» скрипты для тех же классов планет могут содержать только отличия, например, описание другой текстуры для одного биома или другие цвета. Все отсутствующие данные загружаются из скрипта-по-умолчанию, ссылка на который указана в данном альтернативном скрипте. Редактор материалов автоматически сохраняет файл скрипта таким же образом: записывает в него только отличия от скрипта-по-умолчанию. Это должно упростить "моддинг в блокноте", например, преобразование старого файла палитр планет в новую систему.
Вы можете оценить скорость загрузки рельефа на этом видео. Оно было записано с использованием внешнего ПО (GeForce Experience), а не встроенным видеозахватом, поэтому видны все лаги и латентность загрузки. Во второй части я включил отладочный оверлей, показывающий границы узлов octree. Разные цвета соответствуют узлам разных уровней, а белые вспышки указывают узлы, которые генерируют текстуры в данный момент.
Обсудить пост можно на форуме.








