Я использую OpenGL некоторое время и прочитал большое количество учебников. Помимо того, что многие из них все еще используют фиксированный конвейер, они обычно отбрасывают всю инициализацию, изменения состояния и рисование в одном исходном файле. Это хорошо для ограниченного объема учебника, но я с трудом решаю, как его масштабировать до полной игры.
Как вы разделяете использование OpenGL между файлами? Концептуально, я вижу преимущества наличия, скажем, класса рендеринга, который просто отображает вещи на экране, но как будут работать такие вещи, как шейдеры и источники света? Должен ли я иметь отдельные классы для таких вещей, как свет и шейдеры?
c++
opengl
architecture
objects
Sullivan
источник
источник
Ответы:
Я думаю, что OO OpenGL не так уж и необходим. Это отличается, когда вы говорите о шейдере, модели и т. Д. Классе.
По сути, вы должны сначала выполнить инициализацию игры / движка (и другие вещи). Затем вы загружаете текстуры, модели и шейдеры в ОЗУ (при необходимости) и объекты буфера, а также загружаете / компилируете шейдеры. После этого у вас в структуре данных или классе шейдера, модели, есть int ID шейдеров, модель и объекты текстурного буфера.
Я думаю, что большинство двигателей имеют компоненты двигателя, и у каждого из них есть определенные интерфейсы. Все движки, на которые я смотрел, имеют какой-то компонент, такой как Renderer или SceneManager или оба (зависит от сложности игры / движка). Чем у вас может быть класс OpenGLRenderer и / или DXRenderer, которые реализуют интерфейс Renderer. Затем, если у вас есть SceneManager и Renderer, вы можете сделать некоторые из следующих вещей:
Renderer, вероятно, будет вызывать функцию рисования объекта, которая будет вызывать функцию рисования каждой сетки, из которой он состоит, и сетка будет связывать объект текстуры, связывать шейдер, вызывать функцию рисования OpenGL, а затем использовать шейдеры, текстуры и объекты буфера данных объекта.
ПРИМЕЧАНИЕ: это всего лишь пример, вы должны изучить SceneManager более подробно и проанализировать ваш вариант использования, чтобы увидеть, что является лучшим вариантом реализации
Конечно, у вас будут другие компоненты движка, такие как MemoryManager , ResourceLoader и т. Д., Которые будут заботиться об использовании как видео, так и оперативной памяти, чтобы они могли загружать / выгружать определенные модели / шейдеры / текстуры по мере необходимости. Концепции для этого включают в себя кэширование памяти, отображение памяти и т. Д. И т. Д. Существует множество деталей и концепций для каждого компонента.
Взгляните на более подробное описание других игровых движков, их очень много, и их документация в значительной степени доступна.
Но да, занятия делают жизнь легче; Вы должны полностью использовать их и помнить об инкапсуляции, наследовании, интерфейсах и более интересных вещах.
источник
В OpenGL уже есть некоторые понятия «Объект».
Например, что-либо с идентификатором может быть объектом в качестве объекта (есть также вещи, специально названные «Объекты»). Буферы, текстуры, объекты буфера вершин, объекты массива вершин, объекты буфера кадров и так далее. С небольшой работой вы можете обернуть классы вокруг них. Это также дает вам простой способ вернуться к старым устаревшим функциям OpenGL, если ваш контекст не поддерживает расширения. Например, VertexBufferObject может вернуться к использованию glBegin (), glVertex3f () и так далее.
Есть несколько способов отойти от традиционных концепций OpenGL, например, вы, вероятно, хотите хранить метаданные о буферах в объектах буфера. Например, если в буфере хранятся вершины. Каков формат вершин (то есть положение, нормали, texcoords и т. Д.). Какие примитивы он использует (GL_TRIANGLES, GL_TRIANGLESTRIP и т. Д.), Информацию о размере (сколько чисел с плавающей запятой, сколько треугольников они представляют и т. Д ...). Просто чтобы было легко подключить их к командам рисования массивов.
Я рекомендую вам взглянуть на OGLplus . Это привязки C ++ для OpenGL.
Также glxx , это только для загрузки расширений.
В дополнение к обёртыванию API OpenGL, вы должны взглянуть на создание немного более высокого уровня одной сборки поверх него.
Например, класс менеджера материалов, который отвечает за все ваши шейдеры, их загрузку и использование. Также он будет нести ответственность за передачу им имущества. Таким образом, вы можете просто позвонить: materials.usePhong (); material.setTexture (sometexture); material.setColor (). Это обеспечивает большую гибкость, поскольку вы можете использовать более новые объекты, такие как объекты общего унифицированного буфера, чтобы иметь только один большой буфер, содержащий все свойства, которые ваши шейдеры используют в 1 блоке, но если он не поддерживается, вы возвращаетесь к загрузке в каждую шейдерную программу. Вы можете иметь 1 большой монолитный шейдер и переключаться между различными моделями шейдеров, используя единообразные процедуры, если это поддерживается, или вы можете использовать кучу разных маленьких шейдеров.
Вы также можете посмотреть на затраты из спецификации GLSL для написания своего шейдерного кода. Например, #include было бы невероятно полезно и очень легко реализовать в вашем коде загрузки шейдера (для него также есть расширение ARB ). Вы также можете сгенерировать свой код на лету на основе поддерживаемых расширений, например, использовать общий унифицированный объект или использовать обычную униформу.
Наконец, вам понадобится API конвейера рендеринга более высокого уровня, который выполняет такие вещи, как графики сцены, специальные эффекты (размытие, свечение), вещи, которые требуют многократных проходов рендеринга, такие как тени, освещение и тому подобное. И, кроме того, игровой API, который не имеет ничего общего с графическим API, а имеет дело только с объектами в мире.
источник
oglplus::Context
класс делает эту зависимость очень заметной - будет ли это проблемой? Я верю, что это поможет новым пользователям OpenGL избежать многих проблем.В современном OpenGL вы можете почти полностью отделить визуализируемый объект друг от друга, используя различные программы vaos и shader. И даже реализация одного объекта может быть разделена на множество уровней абстракции.
Например, если вы хотите реализовать ландшафт, вы можете определить TerrainMesh, конструктор которого создает вершины и индексы для ландшафта, и устанавливает их в буфер массива, и - если вы задаете ему позицию атрибута - он шейдерно отбрасывает ваши данные. Он также должен знать, как его визуализировать, и должен позаботиться о том, чтобы отменить все изменения контекста, которые он сделал для настройки рендеринга. Этот класс сам не должен ничего знать о шейдерной программе, которая будет его отображать, и при этом он не должен ничего знать о любых других объектах в сцене. Выше этого класса вы можете определить Terrain, который знает код шейдера, и его работа заключается в создании связи между шейдером и TerrainMesh. Это должно означать получение атрибутов и одинаковых позиций, загрузку текстур и тому подобное. Этот класс не должен ничего знать о том, как реализован ландшафт, какой алгоритм LoD он использует, он просто отвечает за затенение ландшафта. Кроме того, вы можете определить не-OpenGL-функциональность, такую как behivour, обнаружение столкновений и тому подобное.
В заключение, несмотря на то, что OpenGL предназначен для использования на низком уровне, вы все равно можете создавать независимые уровни абстракции, которые позволяют вам масштабироваться до приложений с размером игры Unreal. Но количество слоев, которые вы хотите / нужно, действительно зависит от размера приложения, которое вы хотите.
Но не лгите себе об этом размере, не пытайтесь имитировать объектную модель Unity в 10-строчном приложении, результатом будет полная катастрофа. Постройте слои постепенно, только увеличивайте количество слоев абстракции, когда это необходимо.
источник
ioDoom3, вероятно, является хорошей отправной точкой, так как вы можете положиться на Carmack, чтобы следовать хорошей практике кодирования. Также я полагаю, что он не использует мегатекстурирование в Doom3, поэтому он относительно прямолинеен как канал рендеринга.
источник