En gros, la réponse est "cela dépend". La façon dont on enverrait des mises à jour de particules est assez différente de la façon dont vous enverriez une série de modèles à peau de GPU.
Vertex / Index Buffers
Dans le sens général, cependant, tout se fait de nos jours avec un VBO ( vertex buffer object ). L'ancienne API en mode immédiat ( glBegin
/ glEnd
) est probablement juste implémentée comme une enveloppe de graisse autour du système de tampon de vertex interne du pilote.
Pour les objets statiques, créez un VBO statique et remplissez-le avec vos données de sommet. Si l'un des sommets est partagé (généralement le cas), vous souhaiterez probablement également créer un tampon d'index. Cela réduit à la fois la nécessité d'envoyer les mêmes données plus d'une fois pour changer de maillage (potentiellement économiser sur la bande passante de transfert) et sur le traitement (économiser sur le temps d'ombrage des vertex). Notez que vous dessinez avec différentes fonctions lorsque vous effectuez des tirages indexés vs non indexés.
Pour les objets dynamiques, faites de même, mais avec un ensemble dynamique de tampons.
Notes avancées
Pour les pièces plus grandes comme le terrain, vous ne diviserez probablement pas le maillage en plusieurs pièces. Faire en sorte que le GPU rende cent millions de triangles alors que seulement deux cent mille d'entre eux sont visibles est un énorme gaspillage, surtout s'ils ne sont pas triés et qu'il y a beaucoup d'invocations de shaders de fragments superflus et gaspillés. Décomposez le maillage en gros morceaux et ne restituez que ceux qui se trouvent dans la vue. Il existe également diverses techniques d'abattage plus avancées que vous pouvez utiliser pour éliminer les morceaux qui pourraient être dans le frustrum mais qui sont entièrement derrière une colline ou un bâtiment ou quelque chose. Garder le décompte des appels de tirage est bon, mais il y a un équilibre à trouver (que vous devez trouver pour votre application / matériel spécifique) entre la réduction des appels de tirage et la réduction du dessin de la géométrie cachée.
L'un des éléments clés à garder à l'esprit avec un tampon GPU est que vous ne pouvez pas y écrire pendant que le GPU lit à partir de celui-ci. Vous devez informer le pilote que vous pouvez supprimer l'ancienne copie du tampon (une fois terminé) et vous en fournir une nouvelle (si l'ancienne est occupée). Il y a bien sûr depuis longtemps qu'aucune fonction n'est OpenGL pour faire cela (maintenant il y a InvalidateBufferData pour GL 4.3 et certaines implémentations plus anciennes en tant qu'extension). Il existe plutôt un comportement non standard mais commun que la plupart des pilotes implémentent. Faites ceci pour supprimer le tampon avant de le mettre à jour:
glBindBuffer(GL_ARRAY_BUFFER, my_vbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);
Bien sûr, changez GL_ARRAY_BUFFER
et GL_DYNAMIC_DRAW
aux valeurs appropriées pour le tampon. Les tampons statiques ne seront pas mis à jour (ou ne devraient pas l'être), il est donc peu probable que vous ayez à vous soucier de la suppression d'un tel tampon.
Notez qu'il peut être plus rapide à utiliser glBufferData
ou glBufferSubData
ou il peut être plus rapide avec glMapBuffer
. Cela dépend vraiment du pilote et du matériel. Le matériel PC de génération actuelle sera probablement le plus rapide glBufferData
mais testez pour être sûr.
Une autre technique consiste à utiliser l' instanciation . L'instanciation vous permet de faire un seul appel de dessin qui dessine plusieurs copies des données dans un tampon vertex / index. Si vous aviez, disons, 100 roches identiques, vous voudriez toutes les tirer en une seule fois plutôt que de faire 100 tirages indépendants.
Lors de l'instanciation, vous devez placer les données par instance dans un autre tampon (comme la position de l'objet de chaque individu). Il peut s'agir d'un tampon uniforme ( tampon constant dans la terminologie D3D) ou d'un tampon de texture ou d'un attribut de sommet par instance. Encore une fois, ce qui est plus rapide dépend. Les attributs par instance sont probablement plus rapides et certainement plus faciles à utiliser, mais de nombreuses implémentations GL courantes ne prennent toujours pas en charge glBindingAttribDivisor
, vous devrez donc voir si elles sont disponibles à utiliser et si elles sont vraiment plus rapides (certains pilotes plus anciens ont émulé l'instanciation en ajoutant tampons et il a fini par être plus lent à utiliser des instanciations sur eux que de faire des appels de tirage indépendants et il n'y a pas de moyen standard de découvrir ... les joies de l'utilisation d'OpenGL).
Il existe également des algorithmes pour l' optimisation du cache de vertex , qui consiste à ordonner les sommets / index dans vos tampons pour jouer avec le cache de vertex sur les GPU modernes. Un GPU exécutera uniquement le shader pour un sommet, puis le mettra en cache dans le cache de vertex, mais il devra peut-être être supprimé trop tôt pour faire de la place à d'autres sommets. (Par exemple, deux triangles partagent un sommet, mais il y a 100 autres triangles dessinés entre eux; le sommet partagé finira probablement par être gaspillé deux fois par le vertex shader.)
Certaines de ces fonctionnalités nécessitent une nouvelle version de GL ou GLES. GLES2 ne supportait pas l'instanciation, par exemple.
Toujours profil
Encore une fois, si vous vous souciez des performances, testez chaque méthode possible et voyez laquelle est la plus rapide pour votre application sur votre matériel cible. Non seulement différents matériels / pilotes de différents fabricants seront différents, mais certaines classes entières de matériel sont intrinsèquement différentes. Un GPU mobile typique est une bête très différente d'un GPU de bureau discret typique. Les techniques qui sont "les meilleures" sur l'une ne seront pas nécessairement les meilleures sur l'autre.
En matière de performance, soyez toujours sceptique .