Во-первых, даже сейчас не беспокойтесь о независимости платформы. подождите, пока у вас не будет лучшего представления о вашей архитектуре
Выполнение большого количества вызовов отрисовки / изменений состояния происходит медленно. То, как вы делаете это в движке, это то, что вы обычно захотите иметь визуализируемый класс, который может рисовать сам себя. Это рендеринг будет связан с любыми необходимыми буферами (например, буферами вершин) и другой информацией (например, формат вершин, топология, индексные буферы и т. Д.). Макеты ввода шейдеров могут быть связаны с форматами вершин.
Вы захотите иметь несколько примитивных гео-классов, но отложите что-либо сложное до некоторого типа класса сетки, который обрабатывает индексированные трис. Для высокопроизводительного приложения вам нужно будет группировать вызовы (и, возможно, данные) для похожих типов ввода в конвейере затенения, чтобы минимизировать ненужные изменения состояния и сбросы конвейера.
Параметры и текстуры шейдеров обычно контролируются с помощью некоторого класса материала, связанного с визуализируемым.
Каждый визуализируемый объект в самой сцене обычно является компонентом узла в иерархическом графе сцены, где каждый узел обычно наследует преобразование своих предков через некоторый механизм. Вы, вероятно, захотите сборщик сцен, который использует схему пространственного разделения, чтобы быстро определять видимость и избегать затрат на вызовы отрисовки для вещей вне поля зрения.
Часть сценариев / поведения большинства интерактивных трехмерных приложений тесно связана или подключена к своей структуре узла графа сцены и системе событий / сообщений.
Все это объединяется в цикл высокого уровня, где вы обновляете каждую подсистему в зависимости от времени и рисуете сцену в текущем кадре.
Очевидно, что есть тонны мелких деталей, но они могут стать очень сложными в зависимости от того, насколько обобщенным и производительным вы хотите быть и к какой визуальной сложности вы стремитесь.
Ваш вопрос draw(renderable)
против renderable.draw()
более или менее не имеет значения, пока вы не определите, как все детали соединяются друг с другом.
[Обновить] Поработав в этом пространстве немного больше, некоторые добавили понимание :
Сказав, что в коммерческих движках это обычно больше похоже на draw(renderBatch)
, где каждый пакет рендеринга представляет собой агрегацию объектов, которые однородны каким-то значимым образом для GPU, начиная с итерации по гетерогенным объектам (в «чистом» ООП Граф сцены через полиморфизм) и вызов obj.draw()
один за другим имеют ужасную локальность кэша и, как правило, неэффективное использование ресурсов графического процессора. Очень полезно использовать ориентированный на данные подход к проектированию того, как движок взаимодействует с базовыми графическими API-интерфейсами наиболее эффективным способом, максимально упорядочивая процессы, не оказывая негативного влияния на структуру кода / читаемость.
Практическое предложение - написать первый движок, использующий наивный / «чистый» подход, чтобы по-настоящему ознакомиться с доменным пространством. Затем, на втором проходе (или, возможно, переписать), сфокусируйтесь на аппаратных средствах: вещах, таких как представление памяти, локальность кэша, состояние конвейера, пропускная способность, пакетная обработка и параллелизм. Как только вы действительно начнете рассматривать эти вещи, вы поймете, что большая часть вашего первоначального дизайна выходит за рамки. Хорошо повеселиться.