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

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

http://www.opengl-tutorial.org/beginners- учебники /учебник-2-заместитель первого треугольника /

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

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

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

44 голоса | спросил reynmar 25 Jpm1000000pmSun, 25 Jan 2015 19:17:56 +030015 2015, 19:17:56

3 ответа


78

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

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

В качестве еще одного серьезного осложнения некоторые реализации OpenGL (например, Apple, среди прочих) позволят использовать только новые функции OpenGL, когда вы используете основной профиль. Это означает, что вы должны прекратить использование старых 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.

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

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

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

Существуют различные другие API, которые устарели или устарели в OpenGL на протяжении многих лет. Так называемый конвейер с фиксированной функцией - это еще одна такая штука. В некоторых документах может использоваться этот конвейер или смешиваться с программируемым конвейером. Конвейер с фиксированной функцией исходит из старых дней, когда графические карты с жестким кодированием всех математических характеристик, используемых для рендеринга 3D-сцен и 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). Потому что раньше вещи не путали.

ответил Sean Middleditch 26 Jam1000000amMon, 26 Jan 2015 04:15:51 +030015 2015, 04:15:51
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, различия отвлекаются большей частью.

ответил MichaelHouse 25 Jpm1000000pmSun, 25 Jan 2015 20:55:32 +030015 2015, 20:55:32
2

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

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

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

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

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

ответил Neoptolemus 27 Jpm1000000pmTue, 27 Jan 2015 17:57:51 +030015 2015, 17:57:51

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132