J'ai décidé d'écrire un peu sur l'aspect de la programmation et sur la manière dont les composants se parlent. Peut-être que cela va nous éclairer sur certains domaines.
La présentation
Que faut-il pour avoir cette seule image, que vous avez postée dans votre question, dessinée à l'écran?
Il y a plusieurs façons de dessiner un triangle à l'écran. Pour simplifier, supposons qu'aucun tampon de vertex n'a été utilisé. (Un vertex buffer est une zone de mémoire dans laquelle vous stockez des coordonnées.) Supposons que le programme communique simplement le pipeline de traitement graphique à chaque sommet (un sommet est simplement une coordonnée dans l'espace) d'une ligne.
Mais avant de pouvoir dessiner quoi que ce soit, nous devons d'abord exécuter un échafaudage. Nous verrons pourquoi plus tard:
// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Drawing Using Triangles
glBegin(GL_TRIANGLES);
// Red
glColor3f(1.0f,0.0f,0.0f);
// Top Of Triangle (Front)
glVertex3f( 0.0f, 1.0f, 0.0f);
// Green
glColor3f(0.0f,1.0f,0.0f);
// Left Of Triangle (Front)
glVertex3f(-1.0f,-1.0f, 1.0f);
// Blue
glColor3f(0.0f,0.0f,1.0f);
// Right Of Triangle (Front)
glVertex3f( 1.0f,-1.0f, 1.0f);
// Done Drawing
glEnd();
Alors qu'est-ce que ça a fait?
Lorsque vous écrivez un programme qui veut utiliser la carte graphique, vous choisissez généralement une sorte d'interface avec le pilote. Certaines interfaces bien connues du pilote sont:
Pour cet exemple, nous allons rester avec OpenGL. Maintenant, votre interface avec le pilote est ce qui vous donne tous les outils dont vous avez besoin pour que votre programme parle à la carte graphique (ou au pilote, qui parle ensuite à la carte).
Cette interface est destinée à vous donner certains outils . Ces outils prennent la forme d'une API que vous pouvez appeler depuis votre programme.
Cette API est ce que nous voyons être utilisé dans l'exemple ci-dessus. Regardons de plus près.
L'échafaudage
Avant de pouvoir réellement réaliser un dessin, vous devez effectuer une configuration . Vous devez définir votre fenêtre d'affichage (la zone qui sera réellement rendue), votre perspective (la caméra dans votre monde), quel anti-aliasing vous allez utiliser (pour aplanir les contours de votre triangle) ...
Mais nous ne regarderons rien de tout cela. Nous allons juste jeter un coup d'oeil sur les trucs que vous devrez faire à chaque image . Comme:
Effacement de l'écran
Le pipeline graphique ne va pas effacer l'écran pour chaque image. Vous devrez le dire. Pourquoi? C'est pourquoi:
Si vous n'effacez pas l'écran, vous dessinez simplement chaque image. C'est pourquoi nous appelons glClear
avec l' GL_COLOR_BUFFER_BIT
ensemble. L'autre bit ( GL_DEPTH_BUFFER_BIT
) indique à OpenGL de vider le tampon de profondeur . Ce tampon est utilisé pour déterminer quels pixels sont devant (ou derrière) d'autres pixels.
Transformation
Source de l'image
La transformation est la partie où nous prenons toutes les coordonnées en entrée (les sommets de notre triangle) et appliquons notre matrice ModelView. C'est la matrice qui explique comment notre modèle (les sommets) est pivoté, mis à l'échelle et traduit (déplacé).
Ensuite, nous appliquons notre matrice de projection. Cela déplace toutes les coordonnées afin qu'elles soient correctement orientées vers notre caméra.
Maintenant, nous transformons encore une fois, avec notre matrice Viewport. Nous faisons cela pour adapter notre modèle à la taille de notre moniteur. Nous avons maintenant un ensemble de sommets prêts à être rendus!
Nous reviendrons sur la transformation un peu plus tard.
Dessin
Pour dessiner un triangle, nous pouvons simplement dire à OpenGL de commencer une nouvelle liste de triangles en appelant glBegin
avec la GL_TRIANGLES
constante.
Il y a aussi d'autres formes que vous pouvez dessiner. Comme une bande de triangle ou un fan de triangle . Ce sont principalement des optimisations, car elles nécessitent moins de communication entre le processeur et le GPU pour dessiner le même nombre de triangles.
Après cela, nous pouvons fournir une liste d’ensembles de 3 sommets qui devraient constituer chaque triangle. Chaque triangle utilise 3 coordonnées (comme nous sommes dans l'espace 3D). De plus, je fournis également une couleur pour chaque sommet, en appelant glColor3f
avant d' appeler glVertex3f
.
La nuance entre les 3 sommets (les 3 coins du triangle) est calculée automatiquement par OpenGL . Il interpolera la couleur sur toute la surface du polygone.
Interaction
Maintenant, lorsque vous cliquez sur la fenêtre. L'application doit uniquement capturer le message de la fenêtre signalant le clic. Ensuite, vous pouvez exécuter n’importe quelle action de votre programme.
Cela devient beaucoup plus difficile une fois que vous souhaitez commencer à interagir avec votre scène 3D.
Vous devez d’abord savoir clairement à quel pixel l’utilisateur a cliqué dans la fenêtre. Ensuite, en tenant compte de votre perspective , vous pouvez calculer la direction d’un rayon, du point du clic de la souris dans votre scène. Vous pouvez ensuite calculer si un objet de votre scène intersecte ce rayon . Vous savez maintenant si l'utilisateur a cliqué sur un objet.
Alors, comment faites-vous la rotation?
Transformation
Je connais deux types de transformations généralement appliqués:
- Transformation basée sur la matrice
- Transformation à base d'os
La différence est que les os affectent les sommets simples . Les matrices affectent toujours tous les sommets dessinés de la même manière. Regardons un exemple.
Exemple
Plus tôt, nous avions chargé notre matrice d’identité avant de tracer notre triangle. La matrice d'identité est une matrice qui ne fournit simplement aucune transformation . Donc, tout ce que je dessine, n’est affecté que par mon point de vue. Ainsi, le triangle ne sera pas tourné du tout.
Si je veux le faire pivoter maintenant, je pourrais soit faire le calcul moi-même (sur le processeur) et simplement appeler glVertex3f
avec d' autres coordonnées (qui sont pivotées). Ou je pourrais laisser le GPU faire tout le travail, en appelant glRotatef
avant de dessiner:
// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);
amount
est, bien sûr, juste une valeur fixe. Si vous voulez animer , vous devrez suivre amount
et augmenter chaque image.
Alors, attendez, qu'est-il arrivé à toutes les discussions sur la matrice plus tôt?
Dans cet exemple simple, nous n’avons pas à nous soucier des matrices. Nous appelons simplement glRotatef
et cela prend soin de tout cela pour nous.
glRotate
produit une rotation de angle
degrés autour du vecteur xyz. La matrice actuelle (voir glMatrixMode ) est multipliée par une matrice de rotation, le produit remplaçant la matrice actuelle, comme si glMultMatrix était appelée avec la matrice suivante comme argument:
x 2 1 - c + cx y 1 - c - z sx z 1 - c + y s 0 y x 1 - c + z sy 2 1 - c + cy z 1 - c - x s 0 x z 1 - c - y sy z 1 - c + x sz 2 1 - c + c 0 0 0 0 1
Eh bien, merci pour ça!
Conclusion
Ce qui devient évident, c'est qu'il y a beaucoup de discussions avec OpenGL. Mais ça ne nous dit rien. Où est la communication?
La seule chose que OpenGL nous dit dans cet exemple, c'est quand c'est fait . Chaque opération prendra un certain temps. Certaines opérations prennent incroyablement longtemps, d’autres sont incroyablement rapides.
Envoyer un sommet au GPU sera si rapide que je ne saurais même pas comment l'exprimer. L'envoi de milliers de sommets de la CPU au GPU, chaque image, n'est probablement pas un problème.
L'effacement de l'écran peut prendre une milliseconde ou moins (gardez à l'esprit que vous ne disposez généralement que d'environ 16 millisecondes pour dessiner chaque image), en fonction de la taille de votre fenêtre d'affichage. Pour le supprimer, OpenGL doit dessiner chaque pixel de la couleur souhaitée, ce qui peut représenter des millions de pixels.
En dehors de cela, nous ne pouvons quasiment que demander à OpenGL les capacités de notre adaptateur graphique (résolution maximale, anti-aliasing maximum, profondeur de couleur maximale, ...).
Mais nous pouvons également remplir une texture avec des pixels ayant chacun une couleur spécifique. Chaque pixel contient donc une valeur et la texture est un "fichier" géant rempli de données. Nous pouvons charger cela dans la carte graphique (en créant un tampon de texture), puis charger un shader , demander à ce shader d'utiliser notre texture en tant qu'entrée et exécuter des calculs extrêmement lourds sur notre "fichier".
Nous pouvons alors "rendre" le résultat de notre calcul (sous la forme de nouvelles couleurs) dans une nouvelle texture.
C'est ainsi que vous pouvez faire en sorte que le processeur graphique fonctionne pour vous d'une autre manière. Je suppose que CUDA fonctionne de manière similaire à cet aspect, mais je n’ai jamais eu l’occasion de travailler avec elle.
Nous n’avons vraiment que légèrement touché le sujet. La programmation graphique 3D est un enfer d'une bête.
Source de l'image