Rise of the Tomb Raider (2015) est la suite de l'excellent redémarrage de Tomb Raider (2013). Personnellement, je trouve les deux parties intéressantes parce qu'elles s'éloignent de la série originale stagnante et racontent à nouveau l'histoire de Lara. Dans ce jeu, comme dans la préquelle, l'intrigue occupe le devant de la scène, elle fournit des mécanismes fascinants d'artisanat, de chasse et d'escalade / recherche.
Tomb Raider a utilisé Crystal Engine, développé par Crystal Dynamics, également utilisé dans
Deus Ex: Human Revolution . La suite a utilisé un nouveau moteur appelé Foundation, précédemment développé pour Lara Croft et le Temple d'Osiris (2014). Son rendu peut généralement être décrit comme un moteur de tuile avec un passage d'éclairage préalable, et nous verrons plus tard ce que cela signifie. Le moteur vous permet de choisir entre les moteurs de rendu DX11 et DX12; J'ai choisi ce dernier, pour les raisons que nous discutons ci-dessous. Pour capturer le cadre,
Renderdoc 1.2 a été utilisé sur la Geforce 980 Ti, le jeu comprend toutes les fonctions et décorations.
Trame analysée
Pour éviter les spoilers, je dirai que dans ce cadre, les méchants poursuivent Lara, car elle cherche un artefact qu'ils recherchent. Ce conflit d'intérêts ne peut être résolu sans armes. Lara s'est faufilée dans la base ennemie la nuit. J'ai choisi un cadre avec un éclairage d'ambiance et de contraste, dans lequel le moteur peut se montrer.
Avance en profondeur
Ici, l'optimisation habituelle pour de nombreux jeux est effectuée - une petite passe préliminaire de la profondeur (environ 100 appels de tirage). Le jeu affiche les plus gros objets (et non ceux qui occupent plus d'espace sur l'écran) pour profiter des fonctionnalités du processeur vidéo Early-Z. En savoir plus à ce sujet dans
un article Intel . En bref, les GPU peuvent éviter d'exécuter un pixel shader s'ils peuvent déterminer qu'il est chevauché par le pixel précédent. Il s'agit d'un passage assez peu coûteux, préremplissant le tampon Z avec des valeurs de profondeur.
À ce stade, j'ai découvert une technique intéressante de niveau de détail (LOD) appelée «fizzle» ou «damier». Il s'agit d'un moyen courant d'afficher ou de masquer progressivement des objets à distance, afin qu'ils puissent ensuite être remplacés par un maillage de qualité inférieure ou les masquer complètement. Regardez ce camion. Il semble qu'il effectue un rendu deux fois, mais en fait, il effectue un rendu avec un LOD élevé et un LOD faible dans la même position. Chacun des niveaux rend les pixels que l'autre n'a pas rendus. Le premier LOD a 182226 sommets, et le second LOD a 47250. À une grande distance, ils sont indiscernables, mais l'un d'eux est trois fois moins cher. Dans ce cadre, le LOD 0 disparaît presque et le LOD 1 est presque entièrement rendu. Après la disparition complète de LOD 0, seul LOD 1 sera rendu.
LOD 0LOD 1La texture pseudo-aléatoire et le coefficient de probabilité nous permettent d'éliminer les pixels qui n'ont pas dépassé la valeur seuil. Cette texture est utilisée dans ROTR. On peut se demander pourquoi le mélange alpha n'est pas utilisé. Le mélange alpha présente de nombreux inconvénients par rapport à la décoloration pétillante.
- Commodité pour le passage préliminaire des profondeurs: grâce au rendu d'un objet opaque avec des trous pratiqués, nous pouvons effectuer le rendu dans le passage préliminaire et utiliser early-z. Les objets avec un mélange alpha à un stade aussi précoce ne seront pas rendus dans le tampon de profondeur en raison de problèmes de tri.
- Le besoin de shaders supplémentaires : si un rendu différé est utilisé, le shader des objets opaques ne contient aucun éclairage. Si vous devez remplacer un objet opaque par un objet transparent, vous avez besoin d'une option distincte dans laquelle il y a de l'éclairage. En plus d'augmenter la quantité de mémoire nécessaire et la complexité due à au moins un shader supplémentaire pour tous les objets opaques, ils doivent être précis afin d'éviter que les objets avancent. C'est compliqué pour de nombreuses raisons, mais tout se résume au fait que le rendu est désormais effectué dans un chemin de code différent.
- Redessins plus importants : le mélange alpha peut créer de grands redessins et, à un certain niveau de complexité des objets, une grande partie de la bande passante peut être nécessaire pour ombrer le LOD.
- Conflits Z : les conflits Z sont un effet de scintillement lorsque deux polygones sont rendus à une profondeur très proche l'un de l'autre. Dans ce cas, l'inexactitude des calculs en virgule flottante les oblige à être rendus tour à tour. Si nous rendons deux LOD consécutifs, en cachant progressivement un et en montrant le second, alors ils peuvent provoquer un conflit z, car ils sont très proches l'un de l'autre. Il existe toujours des moyens de contourner ce problème, par exemple en préférant un polygone à un autre, mais un tel système est complexe.
- Effets Z-Buffer : De nombreux effets comme SSAO n'utilisent que le buffer de profondeur. Si nous rendions des objets transparents à la fin du pipeline alors que l'occlusion ambiante était déjà terminée, nous ne pourrions pas en tenir compte.
L'inconvénient de cette technique est qu'elle a l'air pire que le mélange alpha, mais un bon motif de bruit, un flou après fizzle ou un anti-aliasing temporaire peut le cacher presque complètement. À cet égard, ROTR ne fait rien de particulièrement inhabituel.
Passe normale
Crystal Dynamics utilise un modèle d'éclairage plutôt inhabituel dans ses jeux, que nous couvrirons dans l'allée d'éclairage. Pour l'instant, il suffit de dire que le moteur n'a pas de passe G-buffer; au moins dans la mesure où cela est familier dans d'autres jeux. Sur ce passage, les objets transmettent uniquement des informations sur la profondeur et les normales à la sortie. Les normales sont enregistrées dans la cible de rendu du format RGBA16_SNORM dans l'espace universel. Il est curieux que ce moteur utilise le schéma Z-up, pas le Y-up (l'axe Z est dirigé vers le haut, pas l'axe Y), qui est plus souvent utilisé dans d'autres moteurs / packages de modélisation. Le canal alpha contient de la brillance, qui est ensuite décompressée en
exp2(glossiness * 12 + 1.0)
. La valeur de brillance peut également être négative, car le signe est utilisé comme indicateur indiquant si la surface est métallique. Cela peut être remarqué par lui-même, car toutes les couleurs sombres du canal alpha sont liées à des objets métalliques.
R | G | B | |
Normal.x | Normal.y | Normal.z | Brillance + Metalness |
NormalBrillance / métalAvantages du départRappelez-vous que dans la section «Profondeur préliminaire», nous avons parlé d'économiser les coûts en pixels? Je vais revenir un peu en arrière pour l'illustrer. Prenez l'image suivante. Cela rend la partie détaillée de la montagne dans le tampon normal. Renderdoc a gentiment mis en évidence les pixels qui ont réussi le test de profondeur avec du vert et ceux qui ne l'ont pas passé avec du rouge (ils ne seront pas rendus). Le nombre total de pixels qui seraient rendus sans cette passe préliminaire est approximativement égal à 104518 (calculé dans Photoshop). Le nombre total de pixels réellement rendus est de 23858 (calculé par Renderdoc). Économisez environ 77%! Comme on le voit, avec une utilisation intelligente, cette passe préliminaire peut donner un gros gain, et elle ne nécessite qu'une centaine d'appels de tirage.
Enregistrement de commandes multithread
Il convient de noter un aspect intéressant, à cause duquel j'ai choisi le rendu DX12 - l'enregistrement de commandes multi-thread. Dans les API précédentes, telles que DX11, le rendu est généralement effectué dans un seul thread. Le pilote graphique a reçu des commandes de rendu du jeu et a constamment transmis des demandes de GPU, mais le jeu ne savait pas quand cela se produirait. Cela conduit à l'inefficacité, car le pilote doit en quelque sorte deviner ce que l'application essaie de faire, et ne s'adapte pas à plusieurs threads. Les API plus récentes telles que DX12 confient le contrôle à un développeur qui peut décider comment écrire des commandes et quand les envoyer. Bien que Renderdoc ne puisse pas montrer comment l'enregistrement est effectué, vous verrez qu'il y a sept passes de couleur marquées comme Color Pass N, et chacune d'entre elles est enveloppée dans une paire de ExecuteCommandList: Reset / Close. Il marque le début et la fin de la liste des commandes. La liste représente environ 100-200 appels de tirage. Cela ne signifie pas qu'ils ont été enregistrés à l'aide de plusieurs flux, mais y fait allusion.
Empreintes de pas dans la neigeSi vous regardez Lara, vous pouvez voir qu'en se déplaçant devant la capture d'écran, elle a laissé des traces dans la neige. Dans chaque cadre, un shader de calcul est exécuté, qui enregistre les déformations dans certaines zones et les applique en fonction du type et de la hauteur de la surface. Ici, seule la carte normale est appliquée à la neige (c'est-à-dire que la géométrie ne change pas), mais dans certaines zones où la neige est plus épaisse, la déformation est en fait effectuée! Vous pouvez également voir comment la neige "tombe" en place et remplit les traces laissées par Lara. Cette technique est décrite de manière beaucoup plus détaillée dans le
GPU Pro 7 . La texture de la chaîne de neige est une sorte de carte d'élévation qui suit les mouvements de Lara et collée sur les bords afin que le shader d'échantillonnage puisse profiter de ce pliage.
Atlas des ombres
Lors de la création du mappage des ombres, une approche assez courante est utilisée - regroupant autant de cartes d'ombre que possible dans une texture d'ombre commune. Un tel atlas d'ombres est en fait une énorme texture 16 bits avec une taille de 16384 × 8196. Cela vous permet de réutiliser et de mettre à l'échelle de manière très flexible les cartes d'ombre dans l'atlas. Dans le cadre que nous analysons, 8 cartes d'ombre sont enregistrées dans l'atlas. Quatre d'entre eux concernent la principale source d'éclairage directionnel (la lune, car elle se produit la nuit), car ils utilisent des cartes d'ombres en cascade - une technique assez standard d'ombres à longue distance pour l'éclairage directionnel, que j'ai déjà expliqué un peu
plus tôt . Plus intéressant, plusieurs sources de projection et de projecteur sont également incluses dans la capture de cette image. Le fait que 8 plans d'ombre soient enregistrés dans ce cadre ne signifie pas qu'il n'y a que 8 sources de projection d'éclairage d'ombre. Le jeu peut mettre en cache les calculs d'ombre, c'est-à-dire que l'éclairage qui n'a pas changé la position de la source ou la géométrie dans la portée ne doit pas mettre à jour sa carte d'ombre.
Il semble que le rendu des mappages d'ombre profite également de l'écriture de commandes multithread dans la liste, et dans ce cas, jusqu'à 19 listes de commandes ont été écrites pour le rendu des mappages d'ombre.
Ombres de l'éclairage directionnelLes ombres de l'éclairage directionnel sont calculées avant le passage de l'éclairage et ensuite échantillonnées. Je ne sais pas ce qui se passerait s'il y avait plusieurs sources d'éclairage directionnel dans la scène.
Occlusion ambiante
Pour l'occlusion ambiante, ROTR vous permet d'utiliser soit HBAO soit une variante de HBAO + (cette technique a été initialement publiée par NVIDIA). Il existe plusieurs variantes de cet algorithme, je vais donc considérer celui que j'ai trouvé dans ROTR. Tout d'abord, le tampon de profondeur est divisé en 16 textures, chacune contenant 1/16 de toutes les valeurs de profondeur. La séparation est effectuée de manière à ce que chaque texture contienne une valeur d'un bloc 4 × 4 de la texture d'origine illustrée dans la figure ci-dessous. La première texture contient toutes les valeurs marquées en rouge (1), la seconde contient les valeurs marquées en bleu (2), etc. Si vous souhaitez en savoir plus sur cette technique, voici
un article de Louis Bavoil , qui était également l'un des auteurs de l'article sur HBAO.

L'étape suivante calcule l'occlusion ambiante pour chaque texture, ce qui nous donne 16 textures AO. Une occlusion ambiante est générée comme suit: le tampon de profondeur est échantillonné plusieurs fois, recréant la position et accumulant le résultat du calcul pour chacun des échantillons. Chaque texture d'occlusion ambiante est calculée en utilisant différentes coordonnées d'échantillonnage, c'est-à-dire que dans un bloc de pixels 4x4, chaque pixel raconte sa propre partie de l'histoire. Cette opération est effectuée pour des raisons de performances. Chaque pixel échantillonne déjà 32 fois le tampon de profondeur, et le plein effet nécessitera 16 × 32 = 512 échantillons, ce qui est un buste même pour les GPU les plus puissants. Ensuite, ils se recombinent en une seule texture plein écran, ce qui s'avère assez bruyant, donc pour lisser les résultats juste après cela, une passe de flou plein écran est effectuée. Nous avons vu une solution très similaire dans
Shadow of Mordor .
Pièces HBAOHBAO complet avec bruitFlou horizontal complet HBAOPrêt HBAOPré-passage d'éclairage carrelé
Le Light Prepass est une technique assez inhabituelle. La plupart des équipes de développement utilisent une combinaison de calcul d'éclairage différé + direct (avec des variations, par exemple, avec une tuile, un cluster) ou complètement direct pour certains effets de l'espace d'écran. La technique de pré-éclairage est si inhabituelle qu'elle mérite une explication. Si le concept d'éclairage différé traditionnel consiste à séparer les propriétés des matériaux de l'éclairage, alors l'idée de séparer l'éclairage des propriétés des matériaux est la pierre angulaire du passage préliminaire de l'éclairage. Bien que cette formulation semble un peu idiote, la différence par rapport à l'éclairage différé traditionnel est que nous stockons toutes les propriétés des matériaux (tels que l'albédo, la couleur spéculaire, la rugosité, la métallicité, la micro-occlusion, l'émissif) dans un énorme tampon G, et l'utiliser plus tard comme données d'entrée pour les passages d'éclairage ultérieurs. L'éclairage différé traditionnel peut présenter une charge importante sur le débit; plus les matériaux sont complexes, plus il faut d'informations et d'opérations dans le G-buffer. Cependant, dans la passe d'éclairage préliminaire, nous accumulons d'abord tout l'éclairage séparément, en utilisant le minimum de données, puis les appliquons dans les passes suivantes aux matériaux. Dans ce cas, l'éclairage n'est suffisant que pour les normales, la rugosité et la métallité. Les shaders (deux passes sont utilisées ici) produisent des données dans trois formats de rendu RGBA16F cibles. L'une contient un éclairage diffus, la seconde contient un éclairage spéculaire et la troisième contient un éclairage ambiant. À ce stade, toutes les données fantômes sont prises en compte. Il est curieux que dans la première passe (éclairage diffus + miroir) pour une passe plein écran, un quadrilatère de deux triangles soit utilisé, et dans d'autres effets, un triangle plein écran soit utilisé (pourquoi c'est important, vous pouvez le découvrir
ici ). De ce point de vue, l'ensemble du cadre n'est pas intégral.
Éclairage diffusÉclairage miroirÉclairage ambiantOptimisation des carreauxL'éclairage en mosaïque est une technique d'optimisation conçue pour restituer un grand nombre de sources lumineuses. ROTR divise l'écran en carreaux 16 × 16, puis enregistre des informations sur les sources qui coupent chaque carreau, c'est-à-dire que les calculs d'éclairage ne seront effectués que pour les sources liées aux carreaux. Au début du cadre, une séquence de shaders de calcul est lancée, qui détermine quelles sources se rapportent aux tuiles. Au stade de l'éclairage, chaque pixel détermine dans quelle tuile il se trouve et boucle à travers chaque source de lumière dans la tuile, effectuant tous les calculs d'éclairage. Si les sources sont liées aux tuiles de manière efficace, vous pouvez économiser beaucoup de calculs et la majeure partie de la bande passante, ainsi qu'augmenter la productivité.
Zoom en profondeurLe suréchantillonnage basé sur la profondeur est une technique intéressante utile pour cette passe et les suivantes. Parfois, les algorithmes coûteux en calcul ne peuvent pas être rendus à pleine résolution, ils sont donc rendus à une résolution inférieure, puis agrandis. Dans notre cas, l'éclairage ambiant est calculé en demi-résolution, c'est-à-dire qu'après les calculs, l'éclairage doit être correctement recréé. Dans la forme la plus simple, 4 pixels basse résolution sont pris et interpolés pour obtenir quelque chose ressemblant à l'image d'origine. Cela fonctionne pour des transitions en douceur, mais ne semble pas bon sur les discontinuités, car là, nous mélangeons des valeurs non liées qui peuvent être adjacentes dans l'espace de l'écran, mais éloignées les unes des autres dans l'espace du monde. Pour résoudre ce problème, plusieurs échantillons de tampon de profondeur sont généralement prélevés et comparés à l'échantillon de profondeur que nous voulons recréer. Si l'échantillon est trop éloigné, nous ne le prenons pas en compte lors de la reconstruction. Un tel schéma fonctionne bien, mais cela signifie que le shader de loisirs est très gourmand en bande passante.
ROTR fait un pas délicat avec la suppression précoce du pochoir. Après avoir passé les normales, le tampon de profondeur est complètement plein, le moteur effectue donc une passe plein écran, marquant tous les pixels interrompus dans le tampon de gabarit. Lorsque vient le temps de recréer le tampon d'éclairage ambiant, le moteur utilise deux shaders: l'un est très simple pour les zones sans espaces de profondeur, l'autre est plus complexe pour les pixels avec des espaces. Le premier pochoir supprime les pixels s'ils n'appartiennent pas à la région correspondante, c'est-à-dire qu'il n'y a des coûts que dans les régions nécessaires. Les images suivantes sont beaucoup plus claires:
Éclairage ambiant demi-résolutionÉchelle des profondeurs de l'intérieurÉclairage ambiant pleine résolution, sans nervuresAugmenter la profondeur des côtesÉclairage d'ambiance prêtVue demi-résolutionUne vue rapprochée de l'image recrééeAprès un premier passage d'éclairage, la géométrie est transférée sur le convoyeur, mais cette fois, chaque objet échantillonne les textures d'éclairage, la texture d'occlusion ambiante et d'autres propriétés des matériaux que nous n'avons pas écrites dans le G-buffer depuis le début. C'est bien, car la bande passante est considérablement économisée ici car vous n'avez pas besoin de lire un tas de textures pour les écrire dans un grand G-buffer, puis de les lire / décoder à nouveau. L'inconvénient évident de cette approche est que toute la géométrie doit être retransmise, et la texture du passage préliminaire de l'éclairage représente en soi une charge importante sur le débit. Je me suis demandé pourquoi ne pas utiliser un format plus léger, par exemple R11G11B10F, pour les textures d'éclairage de passage préliminaire, mais il y a des informations supplémentaires dans le canal alpha, donc ce serait impossible. Quoi qu'il en soit, c'est une solution technique intéressante. À ce stade, toute la géométrie opaque est déjà rendue et éclairée. Notez qu'il comprend des objets émettant de la lumière tels que le ciel et l'écran d'un ordinateur portable.

Réflexions
Cette scène n'est pas un bon exemple de démonstration de reflets, j'ai donc choisi une autre. Le shader de réflexion est une combinaison de cycles assez compliquée, qui peut être réduite en deux parties: l'une échantillonne les cartes cubiques, et l'autre effectue la SSR (Réflexion de l'espace d'écran - calcul des réflexions dans l'espace d'écran); tout cela se fait en un seul passage et à la fin il est mélangé en tenant compte du coefficient déterminant si la SSR a détecté une réflexion (probablement le coefficient n'est pas binaire, mais est une valeur dans l'intervalle [0, 1]). La SSR fonctionne de manière standard pour de nombreux jeux - elle trace à plusieurs reprises le tampon de profondeur, essayant de trouver la meilleure intersection entre le rayon réfléchi par la surface ombrée et une autre surface n'importe où sur l'écran. SSR fonctionne avec la chaîne mip de l'échelle précédemment réduite du tampon HDR actuel, et non avec l'ensemble du tampon.
Il existe également des facteurs de correction tels que la luminosité de la réflexion, ainsi que la texture particulière de Fresnel, qui a été calculée avant ce passage, sur la base des normales et de la rugosité. Je ne suis pas complètement sûr, mais après avoir étudié le code d'assemblage, il me semble que ROTR ne peut calculer le SSR que pour les surfaces lisses. Le moteur n'a pas de chaîne mip flou après l'étape SSR, qui existe dans d'autres
moteurs , et il n'y a même rien de tel que le traçage du tampon de profondeur à l'aide de rayons, qui
varie en fonction de la rugosité . En général, les surfaces rugueuses reçoivent des réflexions des cartes cubiques ou ne les reçoivent pas du tout. Néanmoins, là où la SSR fonctionne, sa qualité est très élevée et stable, compte tenu du fait qu'elle ne s'accumule pas dans le temps et qu'un flou spatial n'est pas effectué pour elle. Les données alpha prennent également en charge la SSR (dans certains temples, vous pouvez voir de très beaux reflets dans l'eau) et c'est un bon ajout que vous ne voyez pas souvent.
Réflexions surTampon de réflexionRéflexions aprèsBrouillard éclairé

Dans notre scène, le brouillard est mal représenté car il assombrit l'arrière-plan et est donc créé par des particules, de sorte que nous reprenons l'exemple avec des reflets. Le brouillard est relativement simple, mais assez efficace. Il existe deux modes: global, la couleur générale du brouillard et la couleur de la diffusion vers l'intérieur obtenue à partir de la carte cubique. Peut-être que la carte cubique a été à nouveau tirée des cartes de réflexion cubique, ou peut-être créée à nouveau. Dans les deux modes, la raréfaction du brouillard est tirée de la texture de raréfaction globale, dans laquelle les courbes de raréfaction sont compressées pour plusieurs effets. Dans un tel schéma, il est remarquable qu'il donne un brouillard lumineux très peu coûteux, c'est-à-dire dispersant les changements intérieurs dans l'espace, créant l'illusion de l'interaction du brouillard avec un éclairage distant. Cette approche peut également être utilisée pour la diffusion atmosphérique vers l'intérieur près du ciel.

Brouillard àBrouillard aprèsÉclairage volumétrique
Aux premiers stades du cadre, plusieurs opérations sont effectuées pour préparer l'éclairage volumétrique. Deux tampons sont copiés du CPU vers le GPU: les index de source lumineuse et les données de source lumineuse. Les deux sont lus par un shader de calcul qui produit une texture 3D 40x23x16 de la vue de la caméra contenant le nombre de sources de lumière traversant cette zone. La texture est de 40 × 23 car chaque tuile occupe 32 × 32 pixels (1280/32 = 40, 720/32 = 22,5), et 16 est le nombre de pixels en profondeur. La texture n'inclut pas toutes les sources lumineuses, mais seulement celles qui sont marquées comme volumineuses (il y en a trois dans notre scène). Comme nous le verrons ci-dessous, il existe d'autres faux effets volumétriques créés par des textures plates. La texture affichée a une résolution plus élevée - 160x90x64. Après avoir déterminé le nombre de sources lumineuses par tuile et leur indice, trois shaders de calcul sont exécutés séquentiellement, effectuant les opérations suivantes:
- Le premier passage détermine la quantité de lumière entrant dans la cellule dans le volume sous la forme d'une pyramide de visibilité. Chaque cellule accumule l'influence de toutes les sources de lumière, comme si elles avaient des particules en suspension qui réagissent à la lumière et en renvoient une partie à la caméra.
- La deuxième passe brouille l'éclairage avec un petit rayon. Ceci est probablement nécessaire pour éviter le scintillement lors du déplacement de la caméra, car la résolution est très faible.
- La troisième passe contourne la texture du volume d'avant en arrière, ajoutant progressivement l'influence de chaque source et donnant la texture finale. En fait, il simule la quantité totale d'éclairage entrant le long du faisceau jusqu'à une distance donnée.Puisque chaque cellule contient une partie de la lumière réfléchie par les particules vers la caméra, dans chacune d'elles, nous recevrons une contribution conjointe de toutes les cellules précédemment passées. Ce passage est également flou.
Lorsque tout cela est terminé, nous obtenons une texture 3D qui indique la quantité de lumière reçue par une position particulière par rapport à la caméra. Tout ce qui reste à faire dans le passage plein écran est de déterminer cette position, de trouver le voxel correspondant de la texture et de l'ajouter au buffer HDR. Le shader d'éclairage lui-même est très simple et ne contient qu'environ 16 instructions.Éclairage volumétriqueÉclairage volumétrique aprèsRendu des cheveux
Si la fonction PureHair n'est pas activée, les couches de cheveux standard sont rendues les unes sur les autres. Cette solution a toujours fière allure, mais je voudrais me concentrer sur les dernières technologies. Si la fonction est activée, le cadre commence par une simulation des cheveux de Lara avec une séquence de shaders de calcul. La première partie de Tomb Raider a utilisé une technologie appelée TressFX, et dans la suite Crystal Dynamics a implémenté une technologie améliorée. Après les calculs initiaux, nous obtenons jusqu'à 7 tampons. Tous sont utilisés pour contrôler les cheveux de Lara. Le processus est le suivant:- Lancer un shader de calcul pour calculer les valeurs de mouvement en fonction des positions précédentes et actuelles (pour le flou de mouvement)
- 1×1 ()
- 122 (Triangle Strip) ( — ). , . 7 , . , , . « ».
- / quad , , . , , .
- 4, ( « »)
Si vous souhaitez en savoir plus à ce sujet, AMD dispose de nombreuses ressources et présentations , car il s'agit d'une bibliothèque publique créée par l'entreprise . J'ai été troublé par l'étape précédant l'étape 1, dans laquelle le même appel de tirage est effectué que dans l'étape 3, on dit qu'il ne rend que des valeurs de profondeur, mais en fait le contenu n'est pas rendu, et c'est intéressant; peut-être que Renderdoc ne me dit rien. Je soupçonne qu'il a peut-être tenté d'exécuter une demande de rendu conditionnel, mais je ne vois aucun appel de prédiction.Cheveux en l'airPixels capillaires visiblesCheveux ombragésRendu en mosaïque de données alpha et de particules
Les objets transparents utilisent à nouveau la classification des tuiles des sources lumineuses calculée pour le passage d'éclairage préliminaire des tuiles. Chaque objet transparent calcule son propre éclairage en une seule passe, c'est-à-dire que le nombre d'instructions et de cycles devient assez effrayant (c'est pourquoi la passe préliminaire d'éclairage a été utilisée pour les objets opaques). Les objets transparents peuvent même effectuer des réflexions dans l'espace de l'écran s'ils sont activés! Chaque objet est rendu dans l'ordre de tri de l'arrière vers l'avant directement dans le tampon HDR, y compris le verre, la flamme, l'eau d'ornière, etc. Le passage alpha rend également les bords mis en évidence lorsque Lara se concentre sur un objet (par exemple, une bouteille avec un mélange combustible sur une boîte à gauche).
Cependant, les particules sont rendues dans un tampon à demi-résolution afin de lisser l'énorme charge sur la bande passante créée par leur repeinture, en particulier lorsque beaucoup de grosses particules couvrant l'écran sont utilisées pour créer du brouillard, du brouillard, de la flamme, etc. Par conséquent, le tampon HDR et le tampon de profondeur diminuent de moitié de chaque côté, après quoi le rendu des particules commence. Les particules créent une énorme quantité de redessin, certains pixels sont ombrés environ 40 fois. La carte thermique montre ce que je veux dire. Comme les particules ont été rendues en demi-résolution, la même astuce de zoom intelligent est utilisée ici que dans l'éclairage ambiant (les espaces sont marqués dans le pochoir, la première passe se transforme en pixels internes, la seconde recrée les bords). Vous remarquerez peut-être que les particules se traduisent par certains autres effets alpha, tels que la flamme,briller, etc. Cela est nécessaire pour que l'alpha puisse être correctement trié par rapport, par exemple, à la fumée. Vous pouvez également remarquer que des rayons de lumière «volumétriques» apparaissent ici, provenant de projecteurs de sécurité. Ils sont ajoutés ici, et non créés au stade de l'éclairage volumétrique. Il s'agit d'un moyen peu coûteux mais réaliste de les créer sur de longues distances.

-123
-ROTR effectue la vitesse d'obturation et la correction de tonalité en une seule passe. Cependant, bien que nous croyions généralement que la correction gamma se produit avec la correction de tonalité, ce n'est pas le cas ici. Il existe de nombreuses façons de mettre en œuvre l'exposition, comme nous l'avons vu avec d' autres jeux . Le calcul de la luminance dans ROTR est très intéressant et ne nécessite presque pas de données intermédiaires ou de passes, nous allons donc expliquer ce processus plus en détail. L'écran entier est divisé en 64 × 64 tuiles, après quoi le calcul des groupes (20, 12, 1) de 256 flux dans chacun commence à remplir tout l'écran. Chaque thread effectue essentiellement la tâche suivante (un pseudo-code est présenté ci-dessous): for(int i = 0; i < 16; ++i) { uint2 iCoord = CalculateCoord(threadID, i, j);
Chaque groupe calcule la somme logarithmique des 64 pixels (256 threads, chacun traitant 16 valeurs). Au lieu de stocker la valeur moyenne, il enregistre la somme et le nombre de pixels réellement traités (tous les groupes ne traitent pas exactement 64 × 64 pixels, car, par exemple, ils peuvent dépasser les bords de l'écran). Shader utilise judicieusement le stockage des threads locaux pour diviser la somme; chaque flux fonctionne d'abord avec 16 valeurs horizontales, puis des flux séparés résument toutes ces valeurs verticalement, et enfin le flux de contrôle de ce groupe (flux 0) ajoute le résultat et les enregistre tous dans le tampon. Ce tampon contient 240 éléments, nous donnant essentiellement la luminosité moyenne de nombreuses zones de l'écran. La commande suivante démarre 64 threads qui bouclent autour de toutes ces valeurs et les ajoutent,pour obtenir la luminosité finale de l'écran. Il revient également du logarithme en unités linéaires.Je n'ai pas beaucoup d'expérience avec les techniques d'exposition, mais la lecture de ce post de Krzysztof Narkovic a clarifié certaines choses. L'enregistrement dans un tableau de 64 éléments est nécessaire pour calculer la moyenne mobile, dans laquelle vous pouvez afficher les valeurs calculées précédentes et lisser la courbe pour éviter des changements très brusques de luminosité, créant des changements brusques de vitesse d'obturation. Il s'agit d'un shader très complexe et je n'ai toujours pas compris tous ses détails, mais le résultat final est la valeur de la vitesse d'obturation correspondant à l'image actuelle.Après avoir trouvé des vitesses d'obturation adéquates, un passage effectue la vitesse d'obturation finale plus la correction tonale. ROTR semble utiliser la cartographie photographique, ce qui explique l'utilisation de moyens logarithmiques au lieu des moyens habituels. La formule de correction tonale dans le shader (après exposition) peut être développée comme suit:Une brève explication peut être trouvée ici . Je n'ai pas pu comprendre pourquoi une division supplémentaire par Lm est nécessaire, car elle annule l'influence de la multiplication. Dans tous les cas, whitePoint est 1.0, donc le processus ne fait pas grand chose dans ce cadre, l'image ne fait que changer la vitesse d'obturation. Il n'y a même pas de limite aux valeurs de l'intervalle LDR! Cela se produit pendant l'étalonnage des couleurs, lorsque le cube de couleur limite indirectement les valeurs supérieures à 1,0.Exposition àExposition aprèsÉblouissement de l'objectif
Les reflets de l'objectif sont rendus de manière intéressante. Une petite passe préliminaire calcule une texture 1xN (où N est le nombre total d'éléments d'éblouissement qui seront rendus sous forme de reflets de lentille, dans notre cas il y en a 28). Cette texture contient la valeur alpha de la particule et d'autres informations inutilisées, mais au lieu de la calculer à partir d'une demande de visibilité ou quelque chose de similaire, le moteur la calcule en analysant le tampon de profondeur autour de la particule dans le cercle. Pour ce faire, les informations sur les sommets sont stockées dans un tampon disponible pour le pixel shader.Ensuite, chaque élément est rendu comme de simples plans alignés sur le plan émis par des sources lumineuses. Si la valeur alpha est inférieure à 0,01, alors la valeur NaN est affectée à la position afin que cette particule ne pixellise pas. Ils sont un peu comme l'effet de floraison et ajoutent de l'éclat, mais cet effet lui-même est créé plus tard.La lentille éclateÉléments de lumière parasiteÉblouissement de l'objectif aprèsBloom
Bloom utilise une approche standard: sous-échantillonner le tampon HDR, les pixels lumineux sont isolés, puis leur échelle est augmentée séquentiellement avec du flou pour étendre leur zone d'influence. Le résultat est agrandi à la résolution de l'écran et le compositing est superposé dessus. Il y a quelques points intéressants à explorer. L'ensemble du processus est effectué à l'aide de 7 shaders de calcul: 2 pour le sous-échantillonnage, 1 pour le flou simple, 4 pour le zoom avant.- target (mip 1). . , mip- , 0.02.
- mip mip 2, 3, 4 5.
- mip 5. , . , .
- — . 3 , mip N mip N + 1, , . bloom , .
- mip 1 HDR-, bloom.
BloomMIP 1 BloomMIP 2 BloomMIP 3 BloomMIP 4 BloomMIP 5 BloomMIP 5 BloomMIP 4 BloomMIP 3 BloomMIP 2 BloomMIP 1 BloomFloraison après L'aspect curieux est que les textures à échelle réduite modifient le rapport d'aspect. Par souci de visualisation, je les ai corrigées, et je ne peux que deviner les raisons de cela; peut-être que cela est fait pour que les tailles de texture soient des multiples de 16. Un autre point intéressant: comme ces shaders sont généralement très limités en bande passante, les valeurs stockées dans la mémoire partagée de groupe sont converties de float32 à float16! Cela permet au shader d'échanger des opérations mathématiques pour doubler la mémoire libre et la bande passante. Pour que cela devienne un problème, la plage de valeurs doit devenir assez large.Fxaa
ROTR prend en charge un large éventail de différentes techniques d'anticrénelage, telles que FXAA (Fast Approximate AA) et SSAA (Super Sampling AA). Il est à noter que l'option d'activation de l'AA temporel est absente, car pour la plupart des jeux AAA modernes, elle devient standard. Quoi qu'il en soit, FXAA fait face à sa tâche de manière remarquable, SSAA fonctionne également bien, c'est une option plutôt "lourde" si le jeu manque de performances.Flou de mouvement
Il semble que Motion blur utilise une approche très similaire à la solution dans Shadows of Mordor. Après avoir rendu l'éclairage volumétrique, une passe de rendu distincte renvoie les vecteurs de mouvement des objets animés vers le tampon de mouvement. Ensuite, ce tampon est combiné avec le mouvement provoqué par la caméra, et le tampon de mouvement final devient entré dans la passe de flou, qui effectue le flou dans la direction indiquée par les vecteurs de mouvement de l'espace d'écran. Pour estimer le rayon de flou en quelques passes, la texture des vecteurs de mouvement à une échelle réduite est calculée de sorte que chaque pixel ait une idée approximative du type de mouvement à proximité. Le flou est effectué en plusieurs passes à demi-résolution et, comme nous l'avons vu, plus tard, son échelle à l'aide du pochoir augmente en deux passes. Plusieurs passes sont effectuées pour deux raisons: premièrement,pour réduire le nombre de lectures de texture nécessaires pour créer un flou avec un rayon potentiellement très grand, et deuxièmement parce que différents types de flou sont effectués. Cela dépend si le personnage animé était sur les pixels actuels.
Flou de mouvementVitesse de flou de mouvementMotion Blur Pass 1Motion Blur Pass 2Motion Blur Pass 3Motion Blur Pass 4Motion Blur Pass 5Motion Blur Pass 6Flou de mouvement, zoom avant et arrièreFlou de mouvement, zoom des bordsCaractéristiques et détails supplémentaires
Il y a quelques autres choses qui méritent d'être mentionnées sans trop de détails.- Gel de l'appareil photo: par temps froid, ajoute des flocons de neige et du givre à l'appareil photo
- Caméra sale: ajoute de la saleté à la caméra.
- Correction des couleurs: à la fin du cadre, une petite correction des couleurs est effectuée, en utilisant un cube de couleurs assez standard pour effectuer la correction des couleurs, comme décrit ci-dessus, et ajoute également du bruit pour rendre certaines scènes plus graves
UI
L'interface utilisateur est implémentée un peu inhabituelle - elle rend tous les éléments dans l'espace linéaire. Habituellement, au moment du rendu, l'interface utilisateur avait déjà effectué une correction de tonalité et une correction gamma. Cependant, ROTR utilise un espace linéaire jusqu'à la fin du cadre. Cela a du sens, car le jeu rappelle une interface utilisateur 3D; cependant, avant d'enregistrer des images sRGB dans le tampon HDR, elles doivent être converties en espace linéaire afin que l'opération la plus récente (correction gamma) ne déforme pas les couleurs.Pour résumer
J'espère que vous avez apprécié la lecture de cette analyse de la même manière que je l'ai fait. Personnellement, j'en ai certainement beaucoup appris. Félicitations aux talentueux développeurs de Crystal Dynamics pour le travail fantastique accompli pour créer ce moteur. Je tiens également à remercier Baldur Karlsson pour son travail fantastique sur Renderdoc. Son travail a fait du débogage des graphiques sur un PC un processus beaucoup plus pratique. Je pense que la seule chose un peu compliquée dans cette analyse était le suivi des lancements de shader eux-mêmes, car au moment de la rédaction de cette fonctionnalité, elle n'était pas disponible pour DX12. J'espère qu'avec le temps cela apparaîtra et nous serons tous très heureux.