Почему в учебниках используются разные подходы к рендерингу OpenGL?


43

http://www.sdltutorials.com/sdl-opengl-tutorial-basics

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/

Эти два руководства используют совершенно разные подходы, чтобы получить почти одинаковый результат. Первый использует такие вещи, как glBegin(GL_QUADS). Второй использует такие вещи, как vertexBufferObjectsшейдеры на основе GLEW. Но результат тот же: вы получаете базовые формы.

Почему существуют эти различия?

Первый подход кажется гораздо проще для понимания. В чем преимущество сложного второго подхода?


4
Никогда не бывает только одного способа снять шкуру с кошки.
Филипп

4
@ Филипп Да, но есть правильные и неправильные пути, старые и новые способы (и, как показывают ответы ниже, старые и новые способы могут быть несовместимы во всех ситуациях)
Эндрю Хилл,

3
Нет Есть нет правильных пути и неправильные способы, только хуже пути и лучшие способы (в нескольких различных размерах).
user253751

glBeginи glEndустарели, потому что они крайне неэффективны для современных графических архитектур
Алекс

Ответы:


77

OpenGL имеет четыре основные версии, не считая версий для мобильных устройств и встраиваемых систем (OpenGL | ES) и Интернета через JavaScript (WebGL). Так же, как Direct3D 11 работает по-другому, чем Direct3D 8, так и OpenGL 3 отличается от OpenGL 1. Большим отличием является то, что версии OpenGL в основном являются дополнениями к старым версиям (но не целиком).

Помимо различных выпусков и версий OpenGL, основной OpenGL также добавил концепцию профилей. А именно Профиль совместимости (который обеспечивает поддержку API из более старых версий) и Основной профиль (который отключает эти старые API). Такие вещи glBeginпросто не работают, когда вы используете профиль Core, но будут работать, когда вы используете профиль совместимости (который используется по умолчанию).

Еще одним серьезным осложнением является то, что некоторые реализации OpenGL (как, например, Apple) будут включать новые функции OpenGL только при использовании Core Profile. Это означает, что вы должны прекратить использование старых API, чтобы использовать более новые API.

Затем вы получите несколько очень запутанных сценариев для уроков:

  1. Учебник старый и использует только устаревшие API.
  2. Учебное пособие является новым и хорошо написанным и использует только Core-совместимые API.
  3. Учебное пособие является новым, но допускает ошибку, предполагая, что вы работаете с драйвером, который включает все API в режиме совместимости и свободно смешивает как новые, так и старые API.
  4. Учебное пособие предназначено для другой редакции OpenGL, такой как OpenGL | ES, которая вообще не поддерживает ни один из старых API, ни в какой версии.

Подобные вещи glBeginявляются частью того, что иногда называют API непосредственного режима. Это также очень запутанно, потому что в OpenGL не существует такого понятия, как режим сохранения, а в «немедленном режиме» уже есть другое определение в графике. Гораздо лучше называть их API-интерфейсами OpenGL 1.x, поскольку они устарели с OpenGL 2.1.

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

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

Все драйверы GL должны эмулировать glBegin, внутренне выделяя буфер вершин, помещая вершины, отправленные с, glVertexв этот буфер, а затем отправляя весь этот буфер в одном вызове отрисовки при glEndвызове. Затраты на эти функции намного больше, чем если бы вы только что обновили буфер вершин самостоятельно, поэтому в некоторой документации (очень ошибочно!) Ссылки на буферы вершин называются «оптимизацией» (это не оптимизация; это единственный способ на самом деле поговорить с ГПУ).

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

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

Драйверы, поддерживающие совместимость, должны выполнять большую часть закулисной работы, чтобы выяснить, когда вы используете эти устаревшие функции, и убедиться, что вы можете плавно сочетать их с современными функциями, что увеличивает издержки и значительно усложняет драйвер. Это одна из причин того, что некоторые драйверы заставляют вас активировать Core Profile для получения новых функций; это значительно упрощает внутреннюю структуру их драйверов, поскольку не требует одновременной поддержки старого и нового API.

Во многих документах может быть рекомендовано начинать со старых API просто потому, что с ними проще начать работу. Direct3D решил эту проблему для новичков, предложив сопутствующую библиотеку ( DirectX Tool Kit ), которая предоставляет более простые API рисования и предварительно написанные шейдеры, которые можно свободно смешивать с использованием Direct3D 11 по мере роста вашего опыта. Более широкое сообщество OpenGL в основном придерживалось профиля совместимости для начинающих, к сожалению, это проблематично, поскольку опять-таки существуют системы, которые не позволяют смешивать старые API OpenGL с более новыми. На новом OpenGL есть неофициальные библиотеки и инструменты для более простого рендеринга с различными уровнями функций и целевыми вариантами использования и языками ( MonoGame для пользователей .NET, например), но ничего официально не одобрено или не согласовано.

Документация, которую вы находите, может даже не относиться к OpenGL, но может относиться к одному из других подобных API. OpenGL | ES 1.x имел рендеринг с фиксированной функцией, но не имел API OpenGL 1.x для представления вершин. OpenGL | ES 2.x + и WebGL 1+ вообще не имеют функций с фиксированными функциями, и для этих API нет режимов обратной совместимости.

Эти API выглядят очень похоже на основной OpenGL; они не совсем совместимы, но существуют официальные расширения для OpenGL, которые поддерживаются некоторыми (не всеми) драйверами, чтобы стать совместимыми с OpenGL | ES (на котором основан WebGL). Потому что вещи не были достаточно запутанными раньше.


4
+1 Фантастический ответ! Если бы вы могли упомянуть пару этих неофициальных библиотек и инструментов для простого рендеринга на новом OpenGL, это было бы здорово :)
Mehrdad

2
Блестящий ответ. У меня были те же проблемы с DirectX в те времена - гораздо проще, чем с OpenGL, но переход от режима сохранения / немедленного перехода к шейдерам был огромным. К счастью, документация очень помогла (в отличие от OpenGL, по крайней мере, для меня), но начало «как я вообще
зажегся

Я автор opengl-tutorial.org, и я согласен с Шоном. API развивался таким образом, прежде всего, из соображений производительности.
Calvin1602

Очень хорошая информация о теме ..
Рейнмар

1
@ Mehrdad: я не припоминаю ничего из головы; Существуют библиотеки, такие как SDL2 или SFML, которые добавляют упрощенный 2D-рендеринг, различные библиотеки графов сцен, MonoGame для C # и т. д., но я на самом деле не знаю ничего, прямо эквивалентного Direct TK, когда я об этом думаю. Буду редактировать пост, так как высказывание «многие» может быть большой жирной ложью. :)
Шон Миддледич

9

Основное отличие заключается в том, насколько современными являются стратегии. Непосредственный режим, используемый в первом уроке:

glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(1, 1, 0); glVertex3f(100, 0, 0);
    glColor3f(1, 0, 1); glVertex3f(100, 100, 0);
    glColor3f(1, 1, 1); glVertex3f(0, 100, 0);
glEnd();

Устаревший и не поддерживается в новых версиях.

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


2

Просто чтобы добавить немного контекста к другим отличным ответам.

Непосредственный режим, описанный в первой ссылке, - это, как уже говорили, устаревший код из ранних версий OpenGL (1.1). Он использовался еще тогда, когда графические процессоры были немногим больше, чем растровые треугольники, и идея программируемых конвейеров не существовала. Если вы посмотрите на исходный код для некоторых ранних игр с аппаратным ускорением, таких как, например, GLQuake и Quake 2, вы увидите использование немедленного режима. Проще говоря, ЦП посылает инструкции для вершин по одному в графический процессор, чтобы начать рисовать треугольники на экране. Напомним, что GL_QUADS имеет тот же результат, что и GL_TRIANGLES, за исключением того, что графический процессор должен сам на ходу превращать эти четырехугольники в треугольники.

Современный (3.2+) OpenGL использует другой подход. Он буферизует данные вершин в память GPU для быстрого доступа, а затем вы можете отправлять инструкции рисования, используя glDrawArrays или glDrawElements. У вас также есть программируемый конвейер (glUseProgram), который позволяет вам настраивать, как GPU позиционирует и окрашивает вершины.

Есть несколько причин, по которым немедленный режим не рекомендуется, основной причиной является производительность. Как сказал Шон в своем ответе, в настоящее время графические процессоры могут обрабатывать данные быстрее, чем процессор может их выгружать, поэтому вы будете испытывать трудности с производительностью графического процессора. При каждом обращении к OpenGL, которое вы делаете, требуются небольшие накладные расходы, это незначительно, но когда вы делаете десятки тысяч вызовов в каждом кадре, он начинает складываться. Проще говоря, чтобы нарисовать текстурированную модель с использованием непосредственного режима, вам нужно как минимум 2 вызова на вершину (glTexCoord2f и glVertex3f) на кадр. В современном OpenGL вы используете пару вызовов в начале для буферизации данных, затем вы можете нарисовать всю модель, независимо от того, сколько вершин она содержит, используя всего несколько вызовов, чтобы связать объект массива вершин, включить некоторые указатели атрибутов и затем один вызов glDrawElements или glDrawArrays.

Какая техника подходит? Ну, это зависит от того, что вы пытаетесь сделать. Простая 2D-игра, которая не требует каких-либо изощренных методов пост-обработки или шейдеров, будет отлично работать в непосредственном режиме, и, вероятно, будет проще написать код. Тем не менее, более современная 3D-игра действительно будет бороться, и если вы планируете изучать GLSL (язык шейдеров), то определенно изучите современную технику.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.