Архитектура OO для рендеринга в играх на основе шейдеров

Я продолжаю сталкиваться с этой проблемой при создании игровых движков, где мои классы хотят выглядеть так:

interface Entity {
  draw();
}

class World {
  draw() {
    for (e in entities)
      e.draw();
  }
}

Это просто псевдокод, чтобы показать, как происходит рисование. Каждый подкласс субъекта реализует собственный чертеж. Мир проходит через все сущности, не имеющие особого порядка, и говорит им, чтобы они рисовали друг за другом.

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

То, что я обычно получаю, - это своего рода рендеринг singleton для каждого класса сущности, который хранит список всех экземпляров и выводит их все сразу. Это не так уж плохо, поскольку он отделяет рисунок от логики игры. Но рендерингу необходимо выяснить, какое подмножество объектов нужно рисовать, и ему нужен доступ к нескольким частям графического конвейера. Здесь моя объектная модель имеет тенденцию становиться беспорядочной, с большим количеством дублированного кода, плотной связью и другими плохими вещами.

Итак, мой вопрос: что такое хорошая архитектура для такого игрового рисунка, который эффективен, универсален и модулен?

Ответ 1

Использовать двухэтапный подход: первый цикл через все сущности, но вместо рисования пусть они вставляют ссылки на себя в список партий чертежа. Затем отсортируйте список по состоянию OpenGL и использованию шейдеров; после сортировки объектов смены состояния в каждом транзите состояния.

Наконец, итерации по списку, выполняющему процедуру рисования каждого объекта, указанного в списке.

Ответ 2

Ответ на этот вопрос непросто, так как существует множество способов решения этой проблемы. Хорошая идея - изучить некоторые механизмы Game/Rendering и посмотреть, как там обрабатывается. Хорошей отправной точкой было бы Ogre, так как его хорошо документированный и открытый исходный код.

Насколько я знаю, он отделяет данные вершин от материальных компонентов (шейдеров) через встроенные материальные скрипты. Сам рендерер знает, какая сетка должна быть нарисована в каком порядке и с каким шейдером (и его проходами).

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