Новое в этой серии? Следите за моим проектом воксельного движка.

Наивный рендеринг вокселей очень быстро становится некрасивым. Я знаю, потому что начал свой проект с простой, наивной реализации. Я загружал геометрию (и рисовал каждую грань) каждого воксельного куба, который существовал в моей сцене. Это включало лица, которые касались других лиц вокселей (то есть те, которые никогда не были видны и, следовательно, не нуждались в рендеринге). При таком подходе блок вокселей 50 x 50 x 50 будет состоять из 125 000 вызовов отрисовки, каждый из которых содержит 12 треугольников. Однозначно не устойчивый.

После некоторых исследований я решил попробовать два метода оптимизации: Instancing и Chunking.

создание экземпляров

Создание экземпляров — хорошо известная и часто используемая техника 3D-рендеринга. Он позволяет загружать данные вершин для одного объекта (в нашем случае куба), но при этом отображать многочисленные его версии, каждая со своими собственными свойствами (например, положением). Это значительно сокращает количество вызовов отрисовки, которые GPU должен выполнять в каждом кадре, тем самым повышая производительность.

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

Чанкинг

Чанкинг — это процесс группировки вокселей в блок и объединения их всех в одну сетку. Это позволяет оптимизировать большую сетку и убрать лица, которые никогда не будут видны пользователю. Вот пример:

В этом примере мы удалили 3 грани на куб, чтобы сэкономить 50% количества граней, которые будут визуализированы. В полной сетке вокселей 16 x 16 фрагментированный подход будет содержать 1536 граней, что составляет всего 6,25% от 24 576, требуемых наивным подходом.

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

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

Какой подход лучше?

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

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

Что дальше?

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