Роллинг мой собственный граф сцены

23

Привет Разработка игр SE!

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

До сих пор я использовал GLFW для получения некоторого базового ввода-вывода, окна (с очень красивым полноэкранным ключом F11) и, конечно, контекста OpenGL. Я также использовал GLEW для демонстрации остальных расширений OpenGL, потому что я использую Windows и хочу использовать все OpenGL 3.0+.

Что подводит меня к графу сцены. Короче говоря, я хотел бы кататься самостоятельно. Это решение было принято после просмотра OSG и прочтения нескольких статей о том, как концепция графа сцены стала искаженной, изогнутой и сломанной. Одна такая статья описала, как графы сцены развивались как ...

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

Следуя аналогии, я бы хотел стейк, суть того, каким должен быть граф сцены, без необходимости набивать лишний код или любые целые коровы.

Поэтому, учитывая это, я задаюсь вопросом, каким именно должен быть граф сцены и как должен быть реализован простой граф сцены? Вот что у меня так далеко ...

Одно родительское, n-дочернее дерево или DAG, которые ...

  • Должен отслеживать трансформации игровых объектов (положение, вращение, масштаб)
  • Должен содержать состояния рендеринга для оптимизации
  • Следует предусмотреть средства выбраковки предметов, не входящих в зону видимости

Со следующими свойствами ...

  • Все узлы должны рассматриваться как визуализируемые (даже если они не отображаются). Это означает, что они ...

    • Должны ли все иметь методы cull (), state () и draw () (возвращать 0, если они не видны)
    • cull () рекурсивно вызывает cull () для всех дочерних элементов, создавая тем самым полную сетку cull для всего узла и всех дочерних элементов. Другой метод, hasChanged (), позволяет так называемым статическим сеткам не вычислять геометрию отбраковки каждого кадра. Это будет работать так, что если какой-либо узел в поддереве изменился, то вся геометрия вплоть до корня перестраивается.
  • Состояния рендеринга будут храниться в простом перечислении, каждый узел будет выбирать из этого перечисления набор состояний OpenGL, который ему требуется, и это состояние будет настроено до вызова draw () на этом узле. Это позволяет выполнять пакетную обработку, все узлы данного набора состояний будут визуализироваться вместе, затем устанавливается следующий набор состояний и так далее.

  • Ни один узел не должен напрямую содержать данные геометрии / шейдера / текстуры, вместо этого узлы должны указывать на общие объекты (возможно, управляемые каким-то одноэлементным объектом, например, менеджером ресурсов).

  • Графы сцен должны иметь возможность ссылаться на другие сцены графиков (возможно с использованием прокси - узла) , чтобы разрешить ситуации , как это , таким образом , позволяя комплекс мульти-сетку модели / объекты должны быть скопировано вокруг графа сцены без добавления тонны данных.

Я надеюсь получить ценные отзывы о моем текущем дизайне. Не хватает функциональности? Есть ли намного лучший способ / шаблон дизайна? Я скучаю по какой-то более крупной концепции, которую необходимо будет включить в этот дизайн для несколько простой 3D-игры? И т.п.

Спасибо, Коди

Коди Смит
источник

Ответы:

15

Концепция

По сути, граф сцены - это не что иное, как двунаправленный ациклический граф, который служит для представления иерархически структурированного набора пространственных отношений.

Как отмечалось, в дикой природе двигатели, как отмечалось, имеют тенденцию включать другие полезности в граф сцены. Видите ли вы это как мясо или корова, вероятно, зависит от вашего опыта работы с двигателями и библиотеками.

Сохраняя это Легким

Я предпочитаю стиль Unity3D, в котором ваш узел графа сцены (который по своей сути является топологической, а не пространственно-топографической структурой) по своей сути включает пространственные параметры и функциональность. В моем движке мои узлы даже легче, чем Unity3D, где они наследуют много ненужных ненужных членов от суперклассов / реализованных интерфейсов: Вот что у меня есть - настолько легкое, насколько вы можете получить:

  • члены родительского / дочернего указателя.
  • Предварительно преобразовать элементы пространственных параметров: положение xyz, шаг, рыскание и крен.
  • матрица преобразования; матрицы в иерархической цепочке могут очень быстро и легко размножаться, рекурсивно перемещаясь вверх / вниз по дереву, давая вам иерархические пространственные преобразования, которые являются основной особенностью графа сцены;
  • updateLocal()метод , который обновляет только этот узел матрицы преобразования это
  • updateAll()метод , который обновляет это и все дочерние узлы преобразования матриц

... Я также включил в свой класс узлов логику уравнений движения и, следовательно, элементы скорости / ускорения (линейные и угловые). Вы можете отказаться от этого и обрабатывать его в своем главном контроллере, если хотите. Но это все - действительно очень легкий. Помните, вы могли бы иметь это на тысячах сущностей. Так что, как вы и предлагали, держите это в руках.

Построение Иерархий

Что вы скажете о графе сцены, ссылающемся на другие графы сцены ... Я жду изюминку? Конечно, они делают. Это их основное использование. Вы можете добавить любой узел к любому другому узлу, и преобразования должны автоматически происходить в локальном пространстве нового преобразования. Все, что вы делаете, это меняете указатель, это не значит, что вы копируете данные! Изменяя указатель, вы получаете более глубокий граф сцены. Если использование Proxies делает вещи более эффективными, то конечно, но я никогда не видел в этом необходимости.

Избегайте рендеринга логики

Забудьте о рендеринге, когда будете писать класс узлов графа сцены, иначе вы все перепутаете сами. Все, что имеет значение, это то, что у вас есть модель данных - будь то граф сцены или нет, не имеет значения - и что какой-то рендер будет проверять эту модель данных и соответственно отображать объекты в мире, будь то в 1, 2 3 или 7 размеров. Идея, которую я делаю, заключается в следующем: не загрязняйте свой граф сцены логикой рендеринга. Граф сцены - это топология и топография, то есть связность и пространственные характеристики. Это истинное состояние симуляции и существует даже при отсутствии рендеринга (который может принимать любую форму под солнцем от вида от первого лица до статистического графика и текстового описания). Узлы не указывают на объекты, связанные с рендерингом - однако обратное вполне может быть верным. Также учтите это: Не каждый узел графа сцены во всем вашем дереве будет отображаться. Многие будут просто контейнерами. Так зачем вообще выделять память для указателя на объект рендера? Даже элемент указателя, который никогда не используется, все еще занимает память. Так что поменяйте направление указателя: связанный с рендерингом экземпляр ссылается на модель данных (которая может быть или включает ваш граф графа сцены), а НЕ наоборот. И если вам нужен простой способ пробежаться по списку контроллеров, но получить доступ к связанному представлению, используйте словарь / хеш-таблицу, которая приближается к O (1) времени доступа для чтения. Таким образом, нет никакого загрязнения, и вашей логике симуляции все равно, какие рендеры находятся на месте, что делает ваши дни и ночи кодирования Так зачем вообще выделять память для указателя на объект рендера? Даже элемент указателя, который никогда не используется, все еще занимает память. Так что поменяйте направление указателя: связанный с рендерингом экземпляр ссылается на модель данных (которая может быть или включает ваш граф графа сцены), а НЕ наоборот. И если вам нужен простой способ пробежаться по списку контроллеров, но получить доступ к связанному представлению, используйте словарь / хеш-таблицу, которая приближается к O (1) времени доступа для чтения. Таким образом, нет никакого загрязнения, и вашей логике симуляции все равно, какие рендеры находятся на месте, что делает ваши дни и ночи кодирования Так зачем вообще выделять память для указателя на объект рендера? Даже элемент указателя, который никогда не используется, все еще занимает память. Так что поменяйте направление указателя: связанный с рендерингом экземпляр ссылается на модель данных (которая может быть или включает ваш граф графа сцены), а НЕ наоборот. И если вам нужен простой способ пробежаться по списку контроллеров, но получить доступ к связанному представлению, используйте словарь / хеш-таблицу, которая приближается к O (1) времени доступа для чтения. Таким образом, нет никакого загрязнения, и вашей логике симуляции все равно, какие рендеры находятся на месте, что делает ваши дни и ночи кодирования И если вам нужен простой способ пробежаться по списку контроллеров, но получить доступ к связанному представлению, используйте словарь / хеш-таблицу, которая приближается к O (1) времени доступа для чтения. Таким образом, нет никакого загрязнения, и вашей логике симуляции все равно, какие рендеры находятся на месте, что делает ваши дни и ночи кодирования И если вам нужен простой способ пробежаться по списку контроллеров, но получить доступ к связанному представлению, используйте словарь / хеш-таблицу, которая приближается к O (1) времени доступа для чтения. Таким образом, нет никакого загрязнения, и вашей логике симуляции все равно, какие рендеры находятся на месте, что делает ваши дни и ночи кодированияМиры проще.

Что касается отбраковки, вернитесь к вышесказанному. Отбор интересующей области - это логическая концепция симуляции. То есть вы не обрабатываете мир за пределами этой (обычно коробочной, круглой или сферической) области. Это происходит в главном контроллере / игровом цикле, прежде чем происходит рендеринг. С другой стороны, отбраковка усеченного конуса чисто связана с визуализацией. Так что забудьте об отбраковке прямо сейчас. Это не имеет ничего общего с графами сцен, и, сосредоточившись на нем, вы будете скрывать истинную цель того, чего пытаетесь достичь.

Заключительная записка ...

Я чувствую, что вы пришли из Flash (особенно AS3), учитывая все детали рендеринга, включенные здесь. Да, парадигма Flash Stage / DisplayObject включает в себя всю логику рендеринга как часть графа сцены. Но Flash делает много предположений, которые вы не обязательно хотите делать. Для полноценного игрового движка лучше не смешивать их по причинам производительности, удобства и управления сложностью кода с помощью надлежащей SoC .

инженер
источник
1
Спасибо Ник. Я на самом деле 3D-аниматор (настоящий 3D, а не flash), ставший программистом, поэтому я склонен думать с точки зрения графики. Если это не так уж и плохо, я начинал на Java и мучился от менталитета «все должно быть объектом», привитого этим языком. Вы убедили меня в том, что граф сцены должен быть отделен от кода рендеринга и отбраковки, теперь мои механизмы вращаются вокруг того, как именно это должно быть достигнуто. Я думаю о том, чтобы относиться к визуализатору как к его собственной отдельной системе, которая ссылается на граф сцены для данных преобразования и т. Д.
Коди Смит,
1
@CodySmith, рад, что это помогло. Бесстыдный плагин, но я поддерживаю фреймворк, который все о SoC / MVC. При этом я подрался с более традиционным в отрасли лагерем, которые настаивают на том, что все должно быть в центральном монолитном объекте. Но даже они обычно говорят вам - держите рендеринг отдельно от графика сцены. SoC / SRP - это то, что я не могу особо подчеркнуть - никогда не смешивайте больше логики в одном классе, чем вам нужно. Я бы даже защищал сложные цепочки наследования ОО по смешанной логике в одном классе, если бы ты приставил пистолет к моей голове!
инженер
Нет, мне нравится концепция. И ваше право, это первое упоминание о SoC, которое я когда-либо видел за годы чтения об игровом дизайне. Еще раз спасибо.
Коди Смит
@CodySmith Quick подумал, просматривая это снова. В общем, хорошо, чтобы вещи были развязаны. Для различных типов объектов модели-контроллера в вашем коде , которые подвергаются визуализации, однако, это нормально для вас , чтобы сохранить коллекции Renderableс (который представляет собой интерфейс или абстрактный класс) внутренне этих основных объектов модели-контроллера. Хорошими примерами этого являются объекты или элементы пользовательского интерфейса. Таким образом, вы можете быстро получить доступ только к тем средствам визуализации, которые относятся к этому конкретному базовому объекту - без особенностей реализации, которые могли бы загрязнить класс сущности, а следовательно, и использование интерфейсов.
Инженер
@CodySmith Преимущество ясно с сущностями, которые могут, например,. иметь представления как в окне просмотра мира, так и на мини-карте. Отсюда и коллекция. В качестве альтернативы вы можете разрешить только один слот рендерера для каждого объекта модели-контроллера, внутри этого объекта. Но держите интерфейс общим! Никаких подробностей - просто Renderer.
Инженер