Pourquoi les tutoriels utilisent-ils différentes approches du rendu OpenGL?

43

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

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

Ces deux tutoriels utilisent des approches complètement différentes pour obtenir presque le même résultat. Le premier utilise des choses comme glBegin(GL_QUADS). La seconde utilise des trucs comme des vertexBufferObjectsshaders basés sur GLEW. Mais le résultat est le même: vous obtenez des formes de base.

Pourquoi ces différences existent-elles?

La première approche semble beaucoup plus facile à comprendre. Quel est l'avantage de la seconde approche compliquée?

Reynmar
la source
4
Il n'y a jamais qu'une seule façon de peler un chat.
Philipp
4
@Philipp Oui, mais il existe de bonnes et de mauvaises manières, d'anciennes et de nouvelles manières (et comme le montrent les réponses ci-dessous, les anciennes et les nouvelles méthodes peuvent ne pas être compatibles dans toutes les situations)
Andrew Hill le
3
Il n'y a pas de bonnes et de mauvaises manières, seulement des façons pires et meilleures (dans plusieurs dimensions différentes).
user253751
glBeginet glEndont été déconseillés car ils sont extrêmement inefficaces pour les architectures graphiques actuelles
Alex

Réponses:

77

OpenGL a quatre versions principales différentes, sans compter les versions pour les appareils mobiles et les systèmes intégrés (OpenGL | ES) et le Web via JavaScript (WebGL). Tout comme Direct3D 11 a une manière de faire différente de Direct3D 8, OpenGL 3 a une façon de faire différente de celle d’OpenGL 1. La grande différence est que les versions d’OpenGL ne sont généralement que des add-ons aux anciennes versions (mais pas entièrement).

Outre les différentes éditions et versions d'OpenGL, le principal OpenGL a également ajouté le concept de profils. À savoir le profil de compatibilité (qui active la prise en charge des API d'anciennes versions) et le profil principal (qui désactive ces anciennes API). Des choses comme glBegintout simplement ne fonctionnent pas lorsque vous utilisez le profil principal, mais le seront quand vous utiliserez le profil de compatibilité (qui est le profil par défaut).

Comme autre complication majeure, certaines implémentations d'OpenGL (comme celle d'Apple, entre autres) n'activeront les nouvelles fonctionnalités OpenGL que lorsque vous utilisez le profil principal. Cela signifie que vous devez cesser d'utiliser les anciennes API pour pouvoir utiliser les nouvelles API.

Vous vous retrouvez alors avec plusieurs scénarios très déroutants pour les tutoriels:

  1. Le didacticiel est ancien et utilise uniquement des API obsolètes.
  2. Le didacticiel est nouveau et bien écrit. Il utilise uniquement des API compatibles Core.
  3. Le didacticiel est nouveau, mais commet l'erreur de supposer que vous utilisez un pilote qui active toutes les API en mode de compatibilité et mélange librement les anciennes et les nouvelles API.
  4. Le tutoriel concerne une édition d'OpenGL différente, telle qu'OpenGL | ES, qui ne prend en charge aucune des anciennes API, quelle que soit leur version.

Des choses comme glBeginfont partie de ce qu'on appelle parfois l'API en mode immédiat. C'est également très déroutant, car un mode retenu dans OpenGL et le "mode immédiat" avaient déjà une définition différente dans les graphiques. Il est bien mieux de se référer à celles-ci sous le nom d'API OpenGL 1.x, car elles sont obsolètes depuis OpenGL 2.1.

L'API 1.x d'OpenGL soumettait immédiatement les sommets au pipeline graphique dans le passé. Cela fonctionnait bien lorsque la vitesse du matériel qui rendait les sommets était à peu près égale à celle du processeur générant les données du sommet. OpenGL à l'époque vient de décharger le rasterization du triangle et pas grand-chose d'autre.

De nos jours, le GPU peut parcourir de très nombreux sommets à très haute vitesse tout en effectuant une transformation avancée des vertex et des pixels et le processeur ne peut tout simplement pas suivre à distance. De plus, l'interface entre le processeur et le processeur graphique a été conçue autour de cette différence de vitesse, de sorte qu'il n'est même plus possible de soumettre des sommets au processeur graphique à la fois.

Tous les pilotes GL doivent émuler glBeginen allouant en interne un tampon de vertex, en plaçant les vertices soumis avec glVertexdans ce tampon, puis en soumettant ce tampon entier dans un seul appel de dessin lorsque glEndest appelé. Le surcoût de ces fonctions est bien plus important que si vous venez de mettre à jour vous-même le tampon de vertex, c’est pourquoi certaines documentations qualifient (par erreur!) Les tampons de vertex comme "une optimisation" (ce n’est pas une optimisation; c’est le seul moyen de parler au GPU).

Différentes autres API ont été obsolètes ou obsolètes dans OpenGL au fil des ans. Le pipeline dit à fonction fixe est un autre exemple. Certaines documentations peuvent toujours utiliser ce pipeline ou se mélanger au pipeline programmable. Le pipeline à fonction fixe provient de l'ancien temps où les cartes graphiques codaient en dur tous les calculs utilisés pour le rendu des scènes 3D et que l'API OpenGL était limitée à la définition de valeurs de configuration pour ce calcul. De nos jours, le matériel utilise très peu de maths codés en dur et (tout comme votre CPU) exécute des programmes fournis par l'utilisateur (souvent appelés shaders).

Une fois encore, les pilotes doivent émuler l'ancienne API car les fonctionnalités à fonction fixe ne sont tout simplement plus présentes sur le matériel. Cela signifie que le pilote contient un ensemble de nuanceurs de compatibilité qui exécutent l'ancien calcul des jours à fonction fixe, utilisé lorsque vous ne fournissez pas vos propres nuanceurs. Les anciennes fonctions OpenGL qui modifient cet ancien état à fonction fixe (comme l'ancienne API d'éclairage OpenGL) utilisent en fait des fonctionnalités OpenGL modernes, telles que des tampons uniformes, pour transmettre ces valeurs aux shaders de compatibilité du pilote.

Les pilotes qui prennent en charge la compatibilité doivent faire beaucoup de travail en coulisse pour comprendre quand vous utilisez ces fonctionnalités obsolètes et vous assurer que vous pouvez les combiner avec des fonctionnalités modernes, ce qui augmente les frais généraux et complique grandement les tâches du pilote. C'est l'une des raisons pour lesquelles certains pilotes vous obligent à activer le profil principal pour obtenir de nouvelles fonctionnalités. cela simplifie grandement les internes de leurs pilotes en évitant de prendre en charge les anciennes et les nouvelles API utilisées simultanément.

Beaucoup de documentation peut vous recommander de commencer avec les anciennes API simplement parce qu'elles sont plus faciles à utiliser. Direct3D a résolu ce problème pour les débutants en proposant une bibliothèque complémentaire ( Kit DirectX Tool Kit ) qui fournit des API de dessin plus simples et des shaders pré-écrits pouvant être mélangés librement avec l'utilisation brute de Direct3D 11 à mesure que votre savoir-faire se développe. La communauté OpenGL au sens large s’est heurtée au profil de compatibilité pour les débutants, ce qui est malheureusement problématique, car il existe à nouveau des systèmes qui ne vous permettent pas de mélanger les anciennes API OpenGL avec les plus récentes. Il existe des bibliothèques et des outils non officiels pour un rendu plus simple du nouvel OpenGL avec différents niveaux de fonctionnalités, ainsi que des cas d'utilisation et des langues cibles ( MonoGame pour les utilisateurs .NET par exemple), mais rien n’a été officiellement approuvé ou largement accepté.

La documentation que vous trouvez peut ne même pas être pour OpenGL mais peut-être pour l'une des autres API similaires. OpenGL | ES 1.x avait un rendu à fonction fixe mais ne disposait pas des API OpenGL 1.x pour la soumission de vertex. OpenGL | ES 2.x + et WebGL 1+ ne possèdent aucune fonctionnalité à fonction fixe et il n'existe aucun mode de compatibilité avec les versions antérieures pour ces API.

Ces API ressemblent beaucoup à OpenGL principal; ils ne sont pas tout à fait compatibles, mais il existe des extensions officielles d'OpenGL que certains (pas tous) pilotes prennent en charge pour devenir compatibles avec OpenGL | ES (sur lequel WebGL est basé). Parce que les choses n'étaient pas assez déroutantes auparavant.

Sean Middleditch
la source
4
+1 réponse fantastique! Si vous pouviez mentionner quelques bibliothèques et outils non officiels pour un rendu simple sur le nouvel OpenGL, ce serait formidable :)
Mehrdad le
2
Réponse brillante. J'ai eu le même problème avec DirectX à l'époque - beaucoup plus simple qu'avec OpenGL, mais le saut du mode retenu / immédiat aux shaders était énorme. Heureusement, la documentation a beaucoup aidé (contrairement à OpenGL, du moins pour moi), mais le début de "Comment puis-je même allumer la lumière" était fou: D
Luaan
Je suis l'auteur de opengl-tutorial.org et je suis d'accord avec Sean. L'API a évolué de cette manière principalement pour des raisons de performances.
Calvin1602
Très bonnes informations sur le sujet ..
reynmar
1
@ Mehrdad: Je ne me souviens d'aucune de ces idées; Il existe des bibliothèques telles que SDL2 ou SFML qui ajoutent un rendu 2D simplifié, diverses bibliothèques de graphes de scènes, MonoGame pour C #, etc., mais je ne suis au courant de rien qui soit directement équivalent à Direct TK maintenant que j'y pense. Est-ce que le poste sera édité depuis le mot "plusieurs" peut être un gros mensonge. :)
Sean Middleditch le
9

La principale différence réside dans la mise à jour des stratégies. Le mode immédiat utilisé dans le premier tutoriel:

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();

Est obsolète et non pris en charge sur les versions plus récentes.

L'utilisation de vertex buffers et shaders est la méthode actuelle de rendu avec OpenGL. Cela peut sembler plus compliqué, mais cela donne de bien meilleurs résultats. De plus, une fois que votre code de support encapsule les éléments OpenGL, les différences sont pour la plupart abstraites.

MichaelHouse
la source
2

Juste pour ajouter un peu plus de contexte aux autres excellentes réponses.

Le mode immédiat, tel que décrit dans le premier lien, est, comme d'autres l'ont déjà dit, du code hérité des premières versions d'OpenGL (1.1). Il était utilisé à une époque où les GPU n'étaient guère plus que des rasteriseurs triangulaires et l'idée de pipelines programmables n'existait pas. Si vous examinez le code source de certains des premiers jeux à accélération matérielle tels que GLQuake et Quake 2, par exemple, vous verrez que le mode immédiat est utilisé. En termes simples, la CPU envoie des instructions pour les sommets une par une au GPU pour commencer à dessiner des triangles à l'écran. Pour mémoire, GL_QUADS a le même résultat que GL_TRIANGLES, sauf que le GPU doit transformer ces quads en triangles lui-même à la volée.

Moderne (3.2+) OpenGL adopte une approche différente. Il met en mémoire tampon les données de vertex dans la mémoire du processeur graphique pour un accès rapide. Vous pouvez ensuite envoyer des instructions de dessin à l'aide de glDrawArrays ou de glDrawElements. Vous avez également le pipeline programmable (glUseProgram) qui vous permet de personnaliser la façon dont le GPU positionne et colore les sommets.

Le mode immédiat est déconseillé pour plusieurs raisons, principalement les performances. Comme Sean l'a dit dans sa réponse, les GPU peuvent maintenant traiter les données plus rapidement que le processeur ne peut les télécharger, ce qui constituerait un goulot d'étranglement pour les performances du GPU. Il y a une petite surcharge pour chaque appel à OpenGL que vous effectuez, il est minuscule, mais lorsque vous effectuez des dizaines de milliers d'appels, chaque image commence à s'accumuler. En termes simples, pour dessiner un modèle texturé en mode immédiat, vous avez besoin d’au moins 2 appels par sommet (glTexCoord2f et glVertex3f) par image. Avec OpenGL moderne, vous utilisez deux appels au début pour mettre les données en mémoire tampon. Vous pouvez ensuite dessiner le modèle entier, quel que soit le nombre de sommets qu’il contient, en utilisant seulement quelques appels pour lier l’objet de sommet, activer certains pointeurs d’attributs et puis un seul appel à glDrawElements ou glDrawArrays.

Quelle technique est la bonne? Cela dépend de ce que vous essayez de faire. Un simple jeu en 2D qui ne nécessite aucune technique de post-traitement sophistiquée ou des shaders fonctionnera parfaitement en mode immédiat et il sera probablement plus facile d'écrire le code. Cependant, un jeu 3D plus moderne aurait vraiment de la difficulté, et si vous envisagez d'apprendre le GLSL (langage des shader), alors apprenez définitivement la technique moderne.

Néoptolème
la source