Чому підручники використовують різні підходи до візуалізації 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
@Philipp Так, але є правильні шляхи та неправильні способи, старі та нові способи (і як показують відповіді нижче, старий і новий способи можуть бути не сумісні у всіх ситуаціях)
Andrew Hill

3
Немає Є немає правильних шляху і неправильні способи, тільки гірше шляху і кращі способи (в кількох різних розмірах).
користувач253751

glBeginі glEndвони застаріли, тому що вони вкрай неефективні для нинішньої графічної архітектури
Алекс

Відповіді:


77

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

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

Як ще одне важливе ускладнення, деякі реалізації OpenGL (як, наприклад, Apple) дозволять включати новіші функції OpenGL лише тоді, коли ви використовуєте Core Profile. Це означає, що ви повинні припинити використання старих API, щоб використовувати новіші API.

Потім у вас виходить кілька дуже заплутаних сценаріїв навчальних посібників:

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

Такі речі glBeginє частиною того, що іноді називають API безпосереднього режиму. Це також дуже заплутано, оскільки в OpenGL немає такого поняття, як утримуваний режим, а «негайний режим» вже не відрізнявся від графіки. Набагато краще посилатися на такі, як API OpenGL 1.x, оскільки вони застаріли з часу OpenGL 2.1.

API API 1.GL негайно надсилає вершини до графічного конвеєра ще за старих часів. Це добре спрацювало, коли швидкість апаратури, яка надає вершини, була приблизно на рівні зі швидкістю ЦП, що генерує дані вершини. OpenGL тоді просто вивантажив растерізацію трикутника і не багато іншого.

У наші дні GPU може пережовувати величезну кількість вершин з дуже високою швидкістю, виконуючи передові вершини та пікселі, а процесор просто навіть не може віддалено йти в ногу. На додаток до цього, інтерфейс між процесором та графічним процесором був розроблений приблизно на цій різниці швидкості, що означає, що більше навіть неможливо надсилати вершини до GPU.

Усі драйвери GL повинні емулювати glBegin, внутрішньо виділивши вершинний буфер, помістивши вершини, подані разом з glVertexцим буфером, а потім подавши цілий буфер в один дзвінок виклику, коли glEndвикликається. Накладні витрати цих функцій набагато більше, ніж якби ви щойно оновлювали вершинний буфер самостійно, саме тому деяка документація (дуже помилково!) Відноситься до вершинних буферів як "оптимізація" (це не оптимізація; це єдиний спосіб насправді поговорити з ГПУ).

Існують різні інші API, які протягом багатьох років були застаріли або застаріли в OpenGL. Так званий трубопровід з фіксованою функцією - ще одна така деталь. Деяка документація все ще може використовувати цей конвеєр або змішуватися з програмованим трубопроводом. Конвеєр з фіксованою функцією походить від старих часів, коли відеокарти жорстко кодували всю математику, яка використовується для візуалізації 3D-сцен, а API OpenGL обмежувався встановленням певних значень конфігурації для цієї математики. У наші дні в апаратному забезпеченні дуже мало жорсткої математики і (як і ваш процесор) замість цього запускаються програми, що надаються користувачем (часто їх називають шейдерами).

Ще раз драйвери повинні наслідувати старий API, оскільки функції фіксованої функції просто відсутні в апаратному забезпеченні. Це означає, що у драйвері є вбудований шейдер сумісності, який виконує стару математику з днів фіксованої функції, яка використовується, коли ви не постачаєте власні шейдери. Старі функції OpenGL, які змінюють цей старий стан з фіксованою функцією (наприклад, старий API освітлення OpenGL), насправді використовують сучасні функції OpenGL, такі як рівномірні буфери, щоб подавати ці значення в шейдери сумісності драйвера.

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

Багато документації може рекомендувати починати зі старих API лише для того, щоб їх було легше розпочати. Direct3D вирішив цю проблему для початківців, запропонувавши супровідну бібліотеку ( DirectX Tool Kit ), яка надає більш прості API для малювання та заздалегідь написані шейдери, які можна вільно змішувати із сировинним використанням Direct3D 11 у міру зростання ваших знань. Ширша спільнота OpenGL в основному застрягла з профілем сумісності для початківців, на жаль, що проблематично, оскільки знову є системи, які не дозволяють змішувати старі API 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, це було б чудово :)
Мехрдад,

2
Блискуча відповідь. У мене були ті ж проблеми з DirectX ще в той день - набагато простіше, ніж з OpenGL, але стрибок із збереженого / негайного режиму до шейдерів був величезним. На щастя, документація дуже допомогла (на відміну від OpenGL, принаймні для мене), але початок "як я навіть світла" був шаленим: D
Луаан,

Я автор opengl-tutorial.org, і я згоден з Шоном. API розвивався таким чином насамперед з міркувань продуктивності.
Calvin1602

Дуже хороша інформація по темі ..
reynmar

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, наприклад, ви побачите, що використовується негайний режим. Простіше кажучи, процесор надсилає інструкції для вершин по черзі в GPU, щоб почати малювати трикутники на екрані. Для запису, GL_QUADS має той самий результат, що і GL_TRIANGLES, за винятком того, що GPU повинен перетворити ці квадратики на трикутники самостійно.

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

Є кілька причин, через які негайний режим застарілий, головна причина - продуктивність. Як зазначив Шон у своїй відповіді, сьогодні GPU можуть стискати дані швидше, ніж процесор може завантажувати їх, тож ви будете обмежувати продуктивність GPU. Для кожного дзвінка до OpenGL, який ви здійснюєте, є невеликий накладний набір, він незначний, але коли ви здійснюєте десятки тисяч дзвінків, кожен кадр починає складатись. Простіше кажучи, щоб намалювати текстуровану модель в режимі негайного режиму, вам потрібно щонайменше 2 дзвінки на вершину (glTexCoord2f та glVertex3f) на кадр. У сучасному OpenGL ви використовуєте пару дзвінків на початку для буферизації даних, тоді ви можете намалювати всю модель, незалежно від того, скільки вершин вона містить, використовуючи лише декілька викликів для прив’язки об’єкта вершинного масиву, включення деяких покажчиків атрибутів та то один виклик до glDrawElements або glDrawArrays.

Яка методика правильна? Ну, це залежить від того, що ви намагаєтеся зробити. Проста 2D гра, яка не вимагає будь-яких фантазійних методів післяобробки та шейдерів, буде працювати чудово, використовуючи режим негайного використання, і, можливо, буде простіше написати код. Однак, більш сучасна 3D-гра справді б боролася, і якщо ви плануєте вивчати GLSL (шейдерську мову), то обов'язково вивчіть сучасну техніку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.