Fonctionnement du rendu de jeu 3D: traitement des sommets

image

Dans cet article, nous considérerons la phase de travail avec les sommets. Autrement dit, nous devrons à nouveau obtenir les manuels de mathématiques et rappeler l'algèbre linéaire, les matrices et la trigonométrie. Hourra!

Nous découvrirons comment les modèles 3D sont transformés et les sources lumineuses prises en compte. Nous expliquerons également en détail la différence entre les vertex et les shaders géométriques, et vous découvrirez à quel stade est le lieu de la tessellation. Pour faciliter la compréhension, nous utilisons des diagrammes et des exemples de code qui montrent comment le jeu effectue les calculs et traite les valeurs.

La capture d'écran au début de l'article montre le jeu GTA V en mode d'affichage filaire. Comparez-le avec le wireframe demi-vie beaucoup moins complexe 2. Les images ont été créées par thalixte avec ReShade .


À quoi ça sert?


Dans le monde des mathématiques, un point est simplement une place dans l'espace géométrique. Il n'y a rien de plus petit qu'un point, il n'a pas de taille, donc les points peuvent être utilisés pour spécifier l'emplacement exact du début et de la fin des objets tels que les segments de ligne, les plans et les volumes.

Pour les graphiques 3D, ces informations sont extrêmement importantes, l'apparence de tout en dépend, car tous les objets sont affichés sous forme d'ensembles de segments de ligne, d'avions, etc. L'image ci-dessous montre une capture d'écran de Bethesda 2015 Fallout 4 :


Il peut ne pas être facile pour vous de voir qu'il ne s'agit que d'un énorme tas de points et de lignes, nous allons donc vous montrer à quoi ressemble la même scène en mode filaire. Dans ce mode, le moteur de rendu 3D ignore les textures et les effets effectués à l'étape des pixels et ne dessine que des lignes multicolores reliant les points.


Maintenant, tout semble complètement différent, mais nous voyons comment toutes les lignes sont combinées pour former divers objets, environnements et arrière-plans. Certains ne comportent que des dizaines de lignes, par exemple des pierres au premier plan, tandis que d'autres contiennent tellement de lignes qu'elles semblent solides.

Chaque point au début et à la fin de chaque ligne est traité en effectuant tout un tas de calculs. Certains calculs sont très simples et rapides, d'autres sont beaucoup plus compliqués. En traitant les points en groupes, en particulier sous la forme de triangles, vous pouvez obtenir une augmentation significative de la productivité, alors examinons-les de plus près.

De quoi a-t-on besoin pour un triangle?


Le triangle du nom indique clairement que la figure a trois coins internes; Pour ce faire, elle a besoin de trois points d'angle et de trois segments les reliant. Il est correct d'appeler un point d'angle un sommet (sommet) (au pluriel - sommets); chaque sommet est défini par un point. Puisque nous sommes dans un monde géométrique tridimensionnel, le système de coordonnées cartésiennes est utilisé pour les points. Habituellement, les coordonnées sont écrites sous la forme de trois valeurs, par exemple (1, 8, -3) ou de manière générique ( x, y, z ).


Ensuite, nous pouvons ajouter deux sommets supplémentaires pour former un triangle:


Notez que les lignes affichées sont facultatives - nous pouvons définir les points et dire au système que ces trois sommets forment un triangle. Toutes les données de sommet sont stockées dans un bloc de mémoire contigu appelé tampon de sommet ; les informations sur la figure qu'elles forment sont soit encodées directement dans le programme de rendu, soit stockées dans un autre bloc de mémoire appelé tampon d'index .

Si les informations sont codées dans un programme de rendu, les différentes formes qui peuvent être formées par des sommets sont appelées primitives . Direct3D suggère d'utiliser une liste pour eux, des bandes et des ventilateurs sous forme de points, de lignes et de triangles. Lorsqu'elles sont utilisées correctement, les bandes de triangles utilisent des sommets pour plus d'un triangle, ce qui améliore la productivité. Dans l'exemple ci-dessous, nous voyons que pour créer deux triangles connectés ensemble, seuls quatre sommets sont nécessaires - s'ils sont séparés, nous avons besoin de six sommets.


De gauche à droite: liste des points, liste des lignes et bande de triangles

Si nous devons traiter un plus grand ensemble de sommets, par exemple, dans un modèle de jeu NPC, il est préférable d'utiliser un objet appelé maillage , un autre bloc de mémoire, mais composé de plusieurs tampons (sommets, index, etc.) et de ressources de texture de modèle . La documentation en ligne de Microsoft explique brièvement comment utiliser ces tampons.

Pour l'instant, concentrons-nous sur ce qui arrive à ces sommets dans un jeu 3D lors du rendu de chaque nouvelle image. En bref, ils effectuent l'une des deux opérations:

  • Le sommet se déplace vers une nouvelle position.
  • Changements de couleur de sommet

Prêt pour les mathématiques? Excellent, car nous en avons besoin.

Le vecteur apparaît sur scène.


Imaginez que vous avez un triangle sur l'écran et que vous appuyez sur une touche pour le déplacer vers la gauche. Naturellement, nous nous attendons à ce que les nombres ( x, y, z ) de chaque sommet changent en conséquence; c'est ce qui se passe, mais une façon plutôt inattendue de mettre en œuvre des changements Au lieu de simplement changer les coordonnées, la grande majorité des systèmes de rendu graphique 3D utilisent un outil mathématique spécial: nous voulons dire des vecteurs .

Un vecteur peut être représenté par une flèche pointant vers un point spécifique de l'espace et ayant la longueur souhaitée. Les sommets sont généralement définis à l'aide de vecteurs basés sur des coordonnées cartésiennes:


Notez que la flèche bleue commence à un endroit (dans ce cas, le point d'origine ) et s'étend jusqu'au sommet. Pour définir le vecteur, nous avons utilisé un enregistrement dans une colonne , mais il est tout à fait possible d'utiliser un enregistrement dans une ligne . Vous avez peut-être remarqué qu'il existe une autre quatrième valeur, communément appelée composante w . Il est utilisé pour montrer ce que signifie le vecteur: position du point ( vecteur de position ) ou direction générale (vecteur de direction ). Dans le cas d'un vecteur direction, il ressemblera à ceci:


Ce vecteur pointe dans la même direction et a la même longueur que le vecteur de position précédent, c'est-à-dire que les valeurs ( x, y, z ) seront les mêmes; cependant, la composante w n'est pas 1, mais zéro. Nous expliquerons l'utilisation des vecteurs directeurs plus tard, mais pour l'instant, rappelez-vous que tous les sommets de la scène 3D seront décrits de cette façon. Pourquoi? Parce que dans ce format, il est beaucoup plus facile de les déplacer.

Mathématiques, mathématiques et encore mathématiques


Rappelons que nous avons un triangle simple et que nous voulons le déplacer vers la gauche. Chaque sommet est décrit par un vecteur de position, donc les «mathématiques du mouvement» (appelées transformations ) doivent fonctionner avec ces vecteurs. Un nouvel outil apparaît: les matrices ( matrice au singulier). Il s'agit d'un tableau de valeurs écrites dans un format similaire à une feuille de calcul Excel, avec des lignes et des colonnes.

Pour chaque type de transformation il y a une matrice correspondante, et pour une transformation il suffit de multiplier simplement la matrice de transformation et le vecteur position. Nous n'entrerons pas dans les détails de comment et pourquoi cela se produit, mais voyons simplement à quoi cela ressemble.

Le déplacement d'un sommet dans l'espace 3D est appelé translation, et il nécessite le calcul suivant:


Valeurs x 0 , etc. représentent les coordonnées d'origine du vecteur; les valeurs delta - x représentent la quantité de déplacement du sommet. La multiplication de la matrice et du vecteur conduit au fait qu'ils se résument simplement (notez que la composante w reste inchangée de sorte que la réponse finie reste le vecteur position comme précédemment).

En plus de se déplacer, nous pouvons également avoir besoin de faire pivoter le triangle ou de changer son échelle - pour ces opérations, il y a aussi des transformations.


Cette transformation fait pivoter le sommet autour de l'axe z dans le plan XY


Et cela est utilisé si vous devez changer l'échelle de la figure

Nous pouvons utiliser l'outil graphique basé sur WebGL de rendu en temps réel pour visualiser ces calculs pour la figure entière. Commençons par la case en position standard:


Dans cet outil en ligne, le point de modèle est le vecteur de position, la matrice mondiale est la matrice de transformation et le point espace-monde est le vecteur de position du sommet transformé.

Appliquons diverses transformations à la boîte:


Dans l'image ci-dessus, la figure a été déplacée de 5 unités le long de chaque axe. Ces valeurs peuvent être vues dans la dernière colonne de la grande matrice du milieu. Le vecteur de position d'origine (4, 5, 3, 1) reste le même qu'il devrait, mais le sommet transformé est maintenant déplacé vers (9, 10, 8, 1).


Dans cette transformation, tout a été mis à l'échelle par un facteur 2: maintenant les côtés de la boîte sont devenus deux fois plus longs. Enfin, regardez l'exemple de rotation:



Le parallélépipède a été tourné d'un angle de 45 °, mais le sinus et le cosinus de cet angle sont utilisés dans la matrice. Après avoir vérifié sur une calculatrice scientifique, nous pouvons voir que sin (45 °) = 0,7071 ..., qui est arrondi à la valeur indiquée de 0,71. Nous obtenons la même réponse pour la valeur du cosinus .

Les matrices et les vecteurs sont facultatifs; Une alternative populaire pour eux, en particulier lors de la manipulation de virages complexes, est l'utilisation de nombres complexes et de quaternions . Ces calculs sont très différents des vecteurs, nous ne les considérerons donc pas en continuant à travailler avec les transformations.

Vertex Shader Power


À ce stade, nous devons comprendre que tout cela est fait par des personnes programmant le code de rendu. Si un développeur de jeux utilise un moteur tiers (par exemple, Unity ou Unreal), alors tout cela a déjà été fait pour lui; mais si quelqu'un fait son moteur à partir de zéro, alors il devra effectuer tous ces calculs avec des sommets.

Mais à quoi cela ressemble-t-il en termes de code?

Pour comprendre cela, nous utiliserons des exemples de l'incroyable site Web de Braynzar Soft . Si vous voulez commencer à travailler vous-même avec la programmation 3D, c'est le bon endroit pour apprendre les bases, ainsi que des choses plus complexes ...


Ceci est un exemple de transformation tout-en-un. Il crée les matrices de transformation appropriées en fonction de la saisie au clavier, puis les applique au vecteur de position d'origine en une seule opération. Notez que cela se fait toujours dans l'ordre donné (mise à l'échelle - rotation - transfert), car toute autre manière ruinera complètement le résultat.

De tels blocs de code sont appelés vertex shaders , leur complexité et leur taille peuvent varier énormément. L'exemple ci-dessus est simple, ce n'est qu'un vertex shader qui n'utilise pas la nature entièrement programmable des shaders. Une séquence plus complexe de shaders pourrait transformer des objets dans l'espace 3D, traiter leur apparence du point de vue de la caméra de scène, puis transférer des données à l'étape suivante du processus de rendu. En considérant l'ordre de traitement des sommets, nous étudierons d'autres exemples.

Bien sûr, ils peuvent être utilisés pour beaucoup plus, donc lorsque vous jouez à un jeu 3D, n'oubliez pas que tous les mouvements que vous voyez sont effectués par un GPU exécutant des commandes de vertex shader.

Mais cela n'a pas toujours été le cas. Si vous remontez au milieu des années 90, les cartes graphiques de cette époque n'avaient pas la capacité de traiter indépendamment les sommets et les primitives, le processeur central seul a fait tout cela.


Nvidia GeForce, sorti en 2000 , a été l'un des premiers processeurs avec leur propre accélération matérielle de ce processus, et cette fonctionnalité a été appelée Hardware Transform and Lighting (pour faire court, Hardware TnL). Les processus que cet équipement pouvait gérer étaient très limités en termes d'équipes, mais avec la sortie de nouvelles puces, la situation a changé rapidement. Aujourd'hui, il n'y a pas d'équipement séparé pour le traitement des sommets, et un seul appareil fait tout à la fois: points, primitives, pixels, textures, etc.

Soit dit en passant, à propos de l' éclairage : il convient de noter que nous voyons tout grâce à la lumière, voyons donc comment il peut être traité au stade du sommet. Pour ce faire, nous devons profiter de ce dont nous avons parlé plus tôt.

Lumière, caméra, moteur!


Imaginez cette image: le joueur se tient dans une pièce sombre, éclairé par une source lumineuse à droite. Au milieu de la pièce se trouve une immense bouilloire. Vous aurez peut-être besoin d'aide pour cela, alors utilisons le site Web de rendu en temps réel et voyons à quoi il ressemble:


N'oubliez pas que cet objet est un ensemble de triangles plats reliés entre eux; c'est-à-dire que le plan de chaque triangle sera dirigé dans une certaine direction. Certains d'entre eux sont dirigés vers l'appareil photo, certains - à l'autre, certains seront déformés. La lumière de la source tombe sur chaque plan et en est réfléchie sous un certain angle.

Selon l'endroit où la lumière est réfléchie, la couleur et la luminosité de l'avion peuvent changer, et pour que la couleur de l'objet soit correcte, tout cela doit être calculé et pris en compte.

Pour commencer, nous devons savoir où chaque plan est dirigé, et pour cela, nous avons besoin du vecteur normal du plan. Il s'agit d'une autre flèche, mais contrairement au vecteur de position, sa taille n'est pas importante (en fait, après avoir calculé l'échelle des vecteurs normaux, elle diminue toujours pour qu'ils aient une longueur de 1), et elle est toujours dirigée perpendiculairement (à angle droit) par rapport au plan.


La normale au plan de chaque triangle est calculée en déterminant le produit vectoriel de deux vecteurs de direction (montrés ci-dessus p et q ) qui forment les côtés du triangle. En fait, il vaut mieux les calculer pour chaque sommet, et non pour un triangle, mais comme il y a toujours plus de premiers que de seconds, il sera plus rapide de calculer les normales des triangles.

Après avoir reçu la normale à la surface, vous pouvez commencer à considérer la source de lumière et la caméra. Dans le rendu 3D, les sources lumineuses peuvent être de différents types, mais dans cet article, nous ne considérerons que les sources directionnelles , par exemple les projecteurs. Comme le plan du triangle, le projecteur et la caméra pointeront dans une certaine direction, quelque chose comme ceci:


Le vecteur source lumineuse et le vecteur normal peuvent être utilisés pour calculer l'angle auquel la lumière tombe sur la surface (en utilisant la relation entre le produit scalaire des vecteurs et le produit de leur taille). Les sommets du triangle contiendront des informations supplémentaires sur leur couleur et leur matériau. Le matériau décrit ce qui arrive à la lumière lorsqu'elle frappe la surface.

Une surface métallique lisse réfléchira presque toute la lumière incidente à l'angle auquel elle est tombée et changera à peine la couleur de l'objet. Le matériau mat rugueux disperse la lumière d'une manière moins prévisible et change légèrement de couleur. Pour en tenir compte, vous devez ajouter des valeurs supplémentaires aux sommets:

  • Couleur de base d'origine
  • Attribut de matériau ambiant - une valeur qui détermine la quantité d'éclairage «d'arrière-plan» qui peut absorber et refléter un sommet
  • L'attribut du matériau diffus est une autre valeur, mais cette fois, la détermination de la «rugosité» du sommet, qui, à son tour, affecte la quantité d'absorption et de réflexion de la lumière diffusée
  • Attributs de matériau spéculaire - Deux valeurs qui spécifient le brillant du sommet

Différents modèles d'éclairage utilisent différentes formules mathématiques pour regrouper tous ces attributs, et le résultat du calcul est le vecteur d'éclairage sortant. En combinaison avec le vecteur de la caméra, il vous permet de déterminer l'apparence globale du triangle.


Une source lumineuse directionnelle éclaire de nombreuses démos Nvidia différentes

Nous avons omis de nombreux détails détaillés, et pour cause: ouvrez n'importe quel didacticiel de rendu 3D, et vous verrez que des chapitres entiers sont consacrés à ce processus. Cependant, dans les jeux modernes, la majeure partie de tous les calculs d'éclairage et d'effets de matériaux sont effectués au stade du traitement des pixels, nous y reviendrons donc dans le prochain article.


Exemple de code B. Anguelov montrant comment le modèle de réflexion de la lumière Phong peut être traité dans le vertex shader.

Tout ce que nous avons examiné ci-dessus est fait par des vertex shaders, et il semble que rien ne leur soit impossible; ce n'est malheureusement pas le cas. Les shaders de sommet ne peuvent pas créer de nouveaux sommets, et chaque shader doit traiter chaque sommet individuel. Il serait pratique que vous puissiez utiliser le code pour créer de nouveaux triangles entre ceux que nous avons déjà (pour améliorer la qualité visuelle), et avoir un shader qui traite toute une primitive (pour accélérer le traitement). Eh bien, dans les GPU modernes, nous pouvons le faire!

S'il vous plaît monsieur, je veux plus (triangles)


Les puces graphiques modernes sont extrêmement puissantes et capables d'effectuer des millions de calculs matrice-vecteur chaque seconde; ils font facilement face à un énorme tas de pics à la fois. D'un autre côté, la création de modèles très détaillés pour le rendu est un processus très long, et si le modèle est à une certaine distance de la scène, tous ces détails seront perdus.

Autrement dit, nous devons en quelque sorte ordonner au processeur de diviser une grande primitive, par exemple, un triangle plat en un ensemble de triangles plus petits situés à l'intérieur de celui d'origine. Un tel processus est appelé tesselation, et les puces graphiques ont déjà appris à le réaliser très bien; au cours des années de développement, le degré de contrôle que les programmeurs ont sur ce processus a augmenté.

Pour regarder cela en action, nous utiliserons l' outil de référence Heaven du moteur Unigine , car il nous permet d'appliquer différentes valeurs de pavage aux modèles utilisés dans le test.


Pour commencer, prenons une place dans le benchmark et étudions-le sans utiliser de pavage. Notez que les pavés au sol ne semblent pas très naturels - la texture utilisée est efficace, mais elle semble fausse. Appliquons la tessellation à la scène: le moteur Unigine ne l'applique qu'aux parties individuelles, mais la différence sera significative.


Le terrain, les bords des bâtiments et la porte semblent beaucoup plus réalistes. Nous pouvons voir comment cela a été réalisé en redémarrant le processus, mais cette fois avec la sélection de toutes les primitives (c'est-à-dire en mode filaire):


On voit clairement pourquoi la terre a l'air si étrange - elle est complètement plate! La porte se confond avec les murs et les bords du bâtiment sont de simples parallélépipèdes.

Dans Direct3D, les primitives peuvent être divisées en un groupe de parties plus petites (ce processus est appelé une sous-division) en effectuant un processus en trois étapes. Premièrement, les programmeurs écrivent un shader de surface (shader shader) - en fait, ce code crée une structure appelée patch géométrique . Vous pouvez le considérer comme une carte indiquant au processeur où de nouveaux points et lignes apparaîtront à l'intérieur de la primitive initiale.

Ensuite, le bloc tessellateur à l'intérieur du GPU applique ce patch à la primitive. À la fin, un shader de domaine est exécutécalculer les positions de tous les nouveaux sommets. Si nécessaire, ces données peuvent être retransférées vers le tampon de vertex afin que les calculs d'éclairage puissent être effectués à nouveau, mais cette fois avec de meilleurs résultats.

À quoi ça ressemble? Lançons la version filaire de la scène en mosaïque:


Honnêtement, nous avons défini un degré de pavage assez élevé pour rendre l'explication du processus plus visible. Peu importe la qualité des puces graphiques modernes, cela ne devrait pas être fait dans chaque scène - regardez, par exemple, la lampe à côté de la porte.

Dans les images avec filaire désactivé, vous ne trouveriez guère de différences à cette distance, et nous voyons qu'un tel niveau de pavage a ajouté tellement de triangles qu'il est difficile de les séparer les uns des autres. Cependant, lorsqu'elle est utilisée correctement, cette fonction de traitement des sommets peut créer des effets visuels fantastiques, en particulier lors de la simulation de collisions de corps mous.

Voyons à quoi cela pourrait ressembler en termes de code Direct3D; pour cela, nous utilisons l'exemple d'un autre grand site Web, RasterTek .

Voici un simple triangle vert tessellé en plusieurs petits triangles ...


Le traitement des sommets est effectué par trois shaders distincts (voir l' exemple de code ): un vertex shader qui prépare un triangle pour la pavage, un surface shader qui génère un patch et un domaine shader qui traite les nouveaux sommets. Le résultat est suffisamment compréhensible, mais l'exemple Unigine démontre à la fois les avantages potentiels et les dangers de l'utilisation généralisée de la tessellation.

"Iron" ne peut pas le supporter!


Rappelez-vous, nous avons dit que les vertex shaders traitent toujours chaque sommet d'une scène? Il est facile de comprendre que la pavage peut être un problème grave ici. Et il existe de nombreux effets visuels où vous devez traiter différentes versions d'une primitive, mais sans les créer dès le début, par exemple, les cheveux, la fourrure, l'herbe et les particules d'explosions.

Heureusement, en particulier pour de telles choses, il existe un autre shader - un shader géométrique . Il s'agit d'une version plus limitée du vertex shader, mais elle peut être appliquée à l'ensemble de la primitive. En combinaison avec la tessellation, elle donne aux programmeurs un contrôle accru sur de grands groupes de sommets.


UL Benchmark 3DMark Vantage - les shaders géométriques traitent les particules et

les drapeaux Direct3D, comme toutes les API graphiques modernes, il vous permet d'effectuer de nombreux calculs avec des sommets. Les données finies peuvent être soit transférées à l'étape suivante du processus de rendu ( tramage ), soit retournées au pool de mémoire pour être retraitées ou lues par le processeur central à d'autres fins. Comme le dit la documentation Microsoft sur Direct3D , cela peut être implémenté en tant que flux de données:


L' étape de sortie du flux est facultative, en particulier parce qu'elle ne peut transférer que des primitives entières (plutôt que des sommets individuels) au cycle de rendu, mais elle est utile pour les effets nécessitant un grand nombre de particules. La même astuce peut être effectuée à l'aide d'un tampon de sommet variable ou dynamique , mais il est préférable de conserver les tampons d'entrée inchangés, car lorsque vous les ouvrez pour les modifier, les performances sont réduites.

Le traitement des sommets est un élément essentiel du rendu, car il détermine à quoi ressemblera la scène du point de vue de la caméra. Dans les jeux modernes, des millions de triangles peuvent être utilisés pour construire des mondes, et chacun de ces sommets est en quelque sorte transformé et illuminé.


Triangles. Il y en a des millions.

Le traitement de tous ces calculs et calculs peut sembler un cauchemar logistique, mais les processeurs graphiques (GPU) et les API sont conçus dans cet esprit - imaginez une usine fonctionnant parfaitement et passant un produit à la fois dans le pipeline de production.

Les programmeurs de rendu de jeu 3D expérimentés ont des connaissances fondamentales en mathématiques et en physique; ils utilisent toutes les astuces et outils possibles pour optimiser les opérations, en compressant l'étape de traitement des sommets en quelques millisecondes seulement. Mais ce n'est que le tout début de la construction d'un cadre 3D - la prochaine étape est la pixellisation, puis le traitement extrêmement complexe des pixels et des textures, et alors seulement l'image pénètre sur le moniteur.

Source: https://habr.com/ru/post/fr472688/


All Articles