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

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

interface Entity {
  draw();
}

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

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

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

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

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