Объектно-ориентированный OpenGL


12

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

Как вы разделяете использование OpenGL между файлами? Концептуально, я вижу преимущества наличия, скажем, класса рендеринга, который просто отображает вещи на экране, но как будут работать такие вещи, как шейдеры и источники света? Должен ли я иметь отдельные классы для таких вещей, как свет и шейдеры?


1
Существуют игры с открытым исходным кодом, а также более крупные учебники, которые содержат больше кода. Проверьте некоторые из них и посмотрите, как организован код.
MichaelHouse

Вы начинающий, когда дело доходит до рендеринга движков?
Самаурса

На этот вопрос нельзя дать разумный ответ без некоторого сужения области. Что ты пытаешься построить? Что за игра, какая у нее графика и т. Д.?
Никол Болас

1
@Sullivan: «Я думал, что подобные концепции применимы практически к любой игре». Они не Если вы попросите меня сделать тетрис, я не буду беспокоиться о создании большого слоя-обертки или чего-то вроде OpenGL. Просто используйте его напрямую. Если вы попросите меня сделать что-то вроде Mass Effect, то нам понадобится несколько уровней абстракции вокруг нашей системы рендеринга. Бросок двигателя Нереаль в Тетрис использует дробовик для охоты на мух; это делает задачу намного сложнее, чем просто ручное кодирование. Вы должны строить только настолько большую и сложную систему, насколько вам нужно , и не больше.
Николь Болас

1
@Sullivan: Единственная сложность, о которой говорит ваш вопрос, - это когда вы говорите о разделении нескольких файлов. Что бы я сделал даже в тетрисе. И есть много уровней сложности между Tetris и движком Unreal. Итак, еще раз: о чем ты спрашиваешь?
Николь Болас

Ответы:


6

Я думаю, что OO OpenGL не так уж и необходим. Это отличается, когда вы говорите о шейдере, модели и т. Д. Классе.

По сути, вы должны сначала выполнить инициализацию игры / движка (и другие вещи). Затем вы загружаете текстуры, модели и шейдеры в ОЗУ (при необходимости) и объекты буфера, а также загружаете / компилируете шейдеры. После этого у вас в структуре данных или классе шейдера, модели, есть int ID шейдеров, модель и объекты текстурного буфера.

Я думаю, что большинство двигателей имеют компоненты двигателя, и у каждого из них есть определенные интерфейсы. Все движки, на которые я смотрел, имеют какой-то компонент, такой как Renderer или SceneManager или оба (зависит от сложности игры / движка). Чем у вас может быть класс OpenGLRenderer и / или DXRenderer, которые реализуют интерфейс Renderer. Затем, если у вас есть SceneManager и Renderer, вы можете сделать некоторые из следующих вещей:

  • Траверса SceneManager и отправить каждый объект Renderer для оказания
  • Инкапсулируйте верхний регистр в некотором методе SceneManager
  • Отправить SceneManager в Renderer, чтобы он обрабатывал его

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

ПРИМЕЧАНИЕ: это всего лишь пример, вы должны изучить SceneManager более подробно и проанализировать ваш вариант использования, чтобы увидеть, что является лучшим вариантом реализации

Конечно, у вас будут другие компоненты движка, такие как MemoryManager , ResourceLoader и т. Д., Которые будут заботиться об использовании как видео, так и оперативной памяти, чтобы они могли загружать / выгружать определенные модели / шейдеры / текстуры по мере необходимости. Концепции для этого включают в себя кэширование памяти, отображение памяти и т. Д. И т. Д. Существует множество деталей и концепций для каждого компонента.

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

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


Это ответ, который я искал. Однако, когда вы говорите «... загружать текстуры, модели и шейдеры в ОЗУ (при необходимости) и объекты буфера и загружать / компилировать шейдеры ...», я возвращаюсь к моему первоначальному вопросу; я должен использовать объекты для инкапсуляции таких вещей, как модели и шейдеры?
Салливан

объект является экземпляром класса, и «вы должны полностью использовать их».
Един-

Извините, я хотел спросить «как использовать объекты». Виноват.
Салливан

Ну, это зависит от вашего класса дизайна. Некоторый базовый пример был бы интуитивно понятен, что-то вроде: object3d (класс) имеет сетки (класс); каждая сетка имеет материал (класс); каждый материал имеет текстуру (может быть класс или просто идентификатор) и шейдер (класс). Но когда вы прочитаете о каждом компоненте, вы увидите, что существуют разные, но равные подходы к созданию SceneManager, Renderer, MemoryManager (все они могут быть одного класса или нескольких, наследоваться и т. Д. И т. Д.). Десятки книг написаны на эту тему (архитектура игрового движка), и, чтобы ответить на вопрос подробно, можно написать одну.
edin-m

Я думаю, что это лучший ответ, который я собираюсь получить. Спасибо за вашу помощь :)
Салливан

2

В 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, а имеет дело только с объектами в мире.


2
«Я рекомендую вам взглянуть на OGLplus. Это привязки C ++ для OpenGL». Я бы рекомендовал против этого. Мне нравится RAII, но он совершенно не подходит для большинства объектов OpenGL. Объекты OpenGL связаны с глобальной конструкцией: контекстом OpenGL. Таким образом, вы не сможете создавать эти объекты C ++ до тех пор, пока у вас не будет контекста, и вы не сможете удалить эти объекты, если контекст уже был уничтожен. Кроме того, это создает иллюзию того, что вы можете изменять объекты без изменения глобального состояния. Это ложь (если вы не используете EXT_DSA).
Николь Болас

@NicolBolas: Могу я не проверить, есть ли контекст, и только потом инициализировать? Или, возможно, только позволяют менеджерам создавать объекты, которые требуют контекста OpenGL? --- кстати, отличный набор учебников (ссылка в вашем профиле)! Почему-то я никогда не сталкивался с ними при поиске OpenGL / Graphics.
Самаурса

@Samaursa: для чего? Если у вас есть менеджер для объектов OpenGL, тогда ... вам не нужно, чтобы объекты заботились о себе; менеджер может сделать это для них. И если вы инициализируете их только при наличии контекста, что произойдет, если вы попытаетесь использовать объект, который не был должным образом инициализирован? Это просто создает слабость в вашей кодовой базе. Это также не меняет ничего, что я сказал о способности изменять эти объекты, не затрагивая глобальное состояние.
Николь Болас

@NicolBolas: «вы не сможете создавать эти объекты C ++, пока у вас не будет контекста» ... это отличается от использования голых конструкций OpenGL? На мой взгляд, oglplus::Contextкласс делает эту зависимость очень заметной - будет ли это проблемой? Я верю, что это поможет новым пользователям OpenGL избежать многих проблем.
xtofl

1

В современном OpenGL вы можете почти полностью отделить визуализируемый объект друг от друга, используя различные программы vaos и shader. И даже реализация одного объекта может быть разделена на множество уровней абстракции.

Например, если вы хотите реализовать ландшафт, вы можете определить TerrainMesh, конструктор которого создает вершины и индексы для ландшафта, и устанавливает их в буфер массива, и - если вы задаете ему позицию атрибута - он шейдерно отбрасывает ваши данные. Он также должен знать, как его визуализировать, и должен позаботиться о том, чтобы отменить все изменения контекста, которые он сделал для настройки рендеринга. Этот класс сам не должен ничего знать о шейдерной программе, которая будет его отображать, и при этом он не должен ничего знать о любых других объектах в сцене. Выше этого класса вы можете определить Terrain, который знает код шейдера, и его работа заключается в создании связи между шейдером и TerrainMesh. Это должно означать получение атрибутов и одинаковых позиций, загрузку текстур и тому подобное. Этот класс не должен ничего знать о том, как реализован ландшафт, какой алгоритм LoD он использует, он просто отвечает за затенение ландшафта. Кроме того, вы можете определить не-OpenGL-функциональность, такую ​​как behivour, обнаружение столкновений и тому подобное.

В заключение, несмотря на то, что OpenGL предназначен для использования на низком уровне, вы все равно можете создавать независимые уровни абстракции, которые позволяют вам масштабироваться до приложений с размером игры Unreal. Но количество слоев, которые вы хотите / нужно, действительно зависит от размера приложения, которое вы хотите.

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


0

ioDoom3, вероятно, является хорошей отправной точкой, так как вы можете положиться на Carmack, чтобы следовать хорошей практике кодирования. Также я полагаю, что он не использует мегатекстурирование в Doom3, поэтому он относительно прямолинеен как канал рендеринга.


6
«Как вы можете полагаться на Кармака, чтобы следовать большой практике кодирования» О, чувак, это хорошо. Кармак следует большой практике программирования на С ++. Это бунт! Ох ... ты не шутишь. Хм ... вы когда-нибудь смотрели его код? Он программист на Си, и он так думает. Что хорошо для C, но вопрос о C ++ .
Николь Болас

Я родом из C, поэтому его код имеет для меня большой смысл: P и да, я потратил немного времени на разработку игр на основе Quake.
chrisvarnz
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.