2008-10-03 3 views
47

В этом семестре я взял курс на компьютерную графику в своем университете. На данный момент мы начинаем входить в некоторые более продвинутые вещи, такие как карты высот, усреднение нормалей, тесселяция и т. Д.Каковы некоторые примеры использования OpenGL-кодирования (например, ориентация объекта w.r.t.)?

Я исхожу из объектно-ориентированного фона, поэтому я стараюсь положить все, что мы делаем многоразовые классы. У меня был хороший успех, создав класс камеры, поскольку он зависит в основном от одного вызова gluLookAt(), который практически не зависит от остальной части конечного автомата OpenGL.

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

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

Что представляет собой просмотр сообщества переполнения стека? Каковы ваши лучшие методы для кодирования OpenGL?

ответ

66

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

ООП или нет, чтобы сделать некоторые сцены тех, различные типы и объекты, которые вы обычно имеют:

Геометрия (сетки). Чаще всего это массив вершин и массив индексов (т. Е. Три индекса на треугольник, ака «список треугольников»). Вершина может быть в каком-то произвольном формате (например, только float3 position, float3 position + float3 normal, float3 position + float3 normal + float2 texcoord и т. Д. И т. Д.). Таким образом, чтобы определить часть геометрии вам нужно:

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

Если вы находитесь в ООП-земле, вы можете назвать этот класс Сетка.

Материалов - вещи, которые определяют, как некоторая часть геометрии оказываются. В простейшем случае, например, это может быть цвет объекта. Или следует применять освещение. Или нужно ли альфа-смешивать объект. Или текстуру (или список текстур) для использования. Или использовать шейдер вершин/фрагментов. И так далее, возможности бесконечны. Начните с того, что вещи вам нужны в материалы. В области ООП этот класс можно назвать (сюрприз!) A Материал.

Сцена - у вас есть кусочки геометрии, коллекция материалов, время, чтобы определить, что находится на сцене. В простом случае каждый объект в сцене может быть определен: - Какую геометрию он использует (указатель на Mesh), - Как это должно быть визуализировано (указатель на материал), - Где он находится. Это может быть матрица преобразования 4x4 или матрица преобразования 4x3 или вектор (положение), кватернион (ориентация) и другой вектор (масштаб). Назовем это Узел в стране ООП.

Камера. Ну, камера - это не что иное, как «где она размещена» (опять же, матрица 4x4 или 4x3, позиция и ориентация), а также некоторые параметры проекции (поле зрения, соотношение сторон, ...).

Так что в принципе, это все! У вас есть сцена, которая представляет собой кучу узлов, которые ссылаются на Meshes and Materials, и у вас есть камера, определяющая, где находится зритель.

Теперь, когда нужно разместить фактические вызовы OpenGL, это вопрос дизайна. Я бы сказал, не ставьте вызовы OpenGL в классы Node или Mesh или Material. Вместо этого сделайте что-то вроде OpenGLRenderer, который может пересекать сцену и вызывать все вызовы. Или, что еще лучше, сделать что-то, что пересекает сцену, не зависящее от OpenGL, и поместить вызовы более низкого уровня в класс, зависящий от OpenGL.

Так что да, все вышеперечисленное в значительной степени независимо от платформы. Идя таким образом, вы обнаружите, что glRotate, glTranslate, gluLookAt и друзья совершенно бесполезны. У вас уже есть все матрицы, просто передайте их OpenGL. Вот как большинство из реального реального кода в реальных играх/приложениях работают в любом случае.

Конечно, вышеуказанное может быть осложнено более сложными требованиями. В частности, материалы могут быть довольно сложными. Мешам обычно необходимо поддерживать множество разных вершинных форматов (например, упакованные нормали для эффективности). Узлы сцен, возможно, должны быть организованы в иерархии (это может быть легко - просто добавьте указатели родителя/детей в узел). Охваченные сетки и анимации в целом добавляют сложность. И так далее.

Но основная идея проста: есть Геометрия, есть Материалы, есть объекты в сцене. Затем некоторые небольшие фрагменты кода могут их отображать.

В случае с OpenGL настройка сетки, скорее всего, создаст/активирует/изменяет объекты VBO. Прежде чем какой-либо узел будет визуализирован, необходимо будет установить матрицы. И настройка материала коснется большинства остальных состояний OpenGL (смешение, текстурирование, освещение, комбинаторы, шейдеры и т. Д.).

+0

Ваша идея для класса mesh кажется мне очевидной :) То, что я пытался сделать, это использовать объекты для примитивов, таких как треугольники. Использование объектов для управления сетками дает больше смысла, поскольку они, как правило, достаточно самодостаточны, правильные? – fluffels 2008-10-04 20:20:44

+0

Кроме того, большое спасибо за понимание материалов независимости платформы и деревьев рендеринга! Это очень помогает! – fluffels 2008-10-04 20:22:10

+0

Я думаю, что это отличный ответ! ... Любой, кто начинает писать 3D-движок, должен основывать его на этих принципах. (Или еще лучше: используйте движок, который сделает все это для вас и сохранит значение времени) – fho 2010-11-03 15:33:56

0

Обычно у меня есть функция drawOpenGl() для каждого класса, который может быть отображен, который содержит вызовы opengl. Эта функция вызывается из renderloop. Класс содержит всю информацию, необходимую для вызовов функции opengl, например. о положении и ориентации, чтобы он мог сделать свое собственное преобразование.

Когда объекты зависят от друг друга, например. они составляют часть более крупного объекта, а затем составляют эти классы в другом классе, который представляет этот объект. У которого есть собственная функция drawOpenGL(), которая вызывает все функции drawOpenGL() своих дочерних элементов, поэтому вы можете иметь окружение/ориентацию, используя push-и popmatrix.

Прошло некоторое время, но я думаю, что что-то подобное возможно с текстурами.

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

+0

Это подход, который я использую сейчас (w.r.t. 2 частные функции). Для меня это не совсем подходит, так как каждый класс Треугольника зависит от 6 других треугольников для нормального усреднения. Вы бы также рекомендовали моделировать сетку вместо примитивов в качестве основного класса? – fluffels 2008-10-04 20:24:12

+0

sry для позднего ответа. да, im мышление: класс сетки, состоящий из списка классов треугольников и, возможно, функции для нормальных нормалей; класс треугольника может генерировать свой собственный нормальный. Сетка может рисовать треугольники. Примитив может быть сеткой определенной формы. – 2008-10-19 23:14:42

2

Стандартная методика заключается в том, чтобы изолировать эффект объектов от состояния визуализации друг от друга, выполняя все изменения из некоторого состояния OpenGL по умолчанию в пределах glPushAttrib/glPopAttrib. В C++ определить класс с конструктором, содержащим

glPushAttrib(GL_ALL_ATTRIB_BITS); 
    glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); 

и деструктор, содержащий

glPopClientAttrib(); 
    glPopAttrib(); 

и использовать RAII-стиль класса обернуть любой код, который столовых с состоянием OpenGL. При условии, что вы следуете шаблону, метод визуализации каждого объекта получает «чистый сланец» и не нужно беспокоиться о том, чтобы каждый возможный модифицированный бит состояния openGL был тем, что ему нужно.

Как оптимизация, как правило, вы устанавливали состояние OpenGL один раз при запуске приложения в какое-то состояние, которое максимально приближено к тому, что все хочет; это сводит к минимуму количество вызовов, которые необходимо сделать в пределах толкаемых областей.

Плохая новость - это не дешевые звонки. Я никогда не исследовал, сколько раз в секунду вы можете уйти; конечно, достаточно, чтобы быть полезным в сложных сценах. Главное, чтобы попытаться сделать большинство штатов, как только вы их установили. Если у вас есть армия орков для рендеринга, с разными шейдерами, текстурами и т. Д. Для брони и кожи, не перебирайте все орки, рендеринга брони/скин/броню/кожу/...; убедитесь, что вы однажды установили состояние брони и визуализировали все орки, а затем настроили визуализацию всего скина.

3

Объект преобразований

Избегайте в зависимости от OpenGL, чтобы сделать ваши преобразования. Часто учебные пособия учат вас, как играть с матрицей матриц преобразования. Я бы не рекомендовал использовать этот подход, так как вам может понадобиться какая-то матрица позже, которая будет доступна только через этот стек, и использовать его очень долго, поскольку шина GPU спроектирована так, чтобы быть быстрой с CPU на GPU, но не наоборот.

Мастер объекта

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

Советую использовать мастер-объект. Хотя он не имеет графического представления, он будет проще, потому что вы сможете более эффективно использовать рекурсию.

менеджер разъединить сцены и визуализации

Я не согласен с @ejac, что вы должны иметь метод на каждом объекте делает OpenGL вызовов. Наличие отдельного класса Renderer, просматривающего вашу сцену и выполняющего все вызовы OpenGL, поможет вам разделить вашу логику сцены и код OpenGL.

Это добавляет сложности дизайна, но даст вам больше гибкости, если вам когда-либо придется перейти от OpenGL к DirectX или к чему-либо еще связанному с API.

1

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

- одно приложение с открытым исходным кодом, которое дает вам большую (возможно, слишком большую) библиотеку инструментов для выполнения 3D-графики OO, там много других.

Смежные вопросы