L'utilisation de Nanite dans le Chapitre 4 de Fortnite Battle Royale

26 janvier 2023
Bonjour, je suis Graham Wihlidal, ingénieur associé (graphismes) chez Epic Games. Je vais vous présenter les fonctionnalités et améliorations que nous avons développées cette année afin de lancer Nanite dans le Chapitre 4 de Fortnite Battle Royale. Vous pouvez essayer ces nouvelles fonctionnalités dans l'Unreal Engine 5.1 (dans le cadre de la Bêta).

L'outil Nanite était déjà prêt pour la production dans l'Unreal Engine 5.0. Il était déjà possible de l'utiliser à de nombreuses fins, mais la version initiale n'était pas assez compatible avec la myriade de fonctionnalités disponibles pour les maillages non Nanite. Nous avions en effet axé nos efforts sur le peaufinage de la fonctionnalité de base de Nanite.

À l'avenir, nous souhaitons travailler sur de nombreux aspects que Nanite ne prend pas encore en charge. Les utilisateurs ont notamment beaucoup demandé l'intégration de fonctionnalités telles que World Position Offset, Pixel Depth Offset, les UV personnalisés, les matériaux bifaces et les matériaux masqués. Au début de l'année, l'équipe a entrepris d'implémenter une prise en charge pour certaines de ces fonctionnalités, ce qui a nécessité la résolution de problèmes non négligeables.

Au départ, les processeurs graphiques étaient dotés d'un pipeline à fonction fixe. La méthode de transformation de la géométrie (et l'intégration de la profondeur et de la couleur) était intégrée au matériel et ne pouvait être configurée qu'avec un ensemble de fonctions prédéfinies. Le matériel est ensuite devenu "programmable" grâce à un code de shader, ce qui a offert de nouvelles possibilités en termes de graphismes et a permis l'implémentation de fonctionnalités qui ne pouvaient pas fonctionner avec un pipeline à fonction fixe.

Depuis sa création, Nanite a toujours été compatible avec les shaders des graphiques de matériaux, qui contrôlaient la couleur émise. Le rastériseur en lui-même, qui contrôle la manière dont les sommets sont positionnés à l'écran et quels pixels sont couverts par les triangles, restait un pipeline à "fonction fixe" : il fonctionnait comme un code de shader, mais le créateur de contenu ne pouvait pas contrôler la logique.

Pour que les fonctionnalités susmentionnées puissent être prises en charge dans Nanite, nous devions rendre le rastériseur programmable.

Prototype initial

Nous avons entrepris de créer un prototype de l'architecture nécessaire à la prise en charge d'une logique de graphique de matériaux dans le rastériseur, auquel nous avons donné le nom de "Rastériseur programmable de Nanite".

Les images suivantes montrent le prototype initial d'un matériau masqué qui s'anime sur un maillage Nanite de camion-poubelle.
 
Le prototype nous a permis de constater qu'il était possible d'intégrer un rastériseur programmable, mais il nous reste beaucoup de travail pour le rendre efficace et prêt pour la production.

Nous avons défini des objectifs clairs pour le développement de cet outil :
  • Conserver le profil de performance du chemin rapide à "fonction fixe" existant. L'introduction d'un rastériseur programmable ne doit pas ralentir le contenu existant.
  • Veiller à ce que les chemins du rastériseur à fonction fixe et du rastériseur programmable partagent globalement le même code, pour des raisons de maintenabilité.
  • Garder à l'esprit que le rastériseur programmable risque d'être très utilisé : sa rapidité doit donc rester sensiblement la même que celle du rastériseur à fonction fixe.
  • N'effectuer le culling d'instance et le culling de cluster qu'une seule fois.
  • Minimiser l'impact mémoriel sur le processeur graphique et Nanite.
  • Implémenter cet outil en tant que fonctionnalité de production dans l'Unreal Engine 5.1

Le prototype initial a été codé en dur afin de ne prendre en charge qu'un seul matériau programmable dans cette scène. Il nous a donc ensuite fallu nous attaquer à une passe de "compartimentage" adéquate qui nous permettrait de prendre en charge de vraies scènes de jeux composées de centaines de matériaux. Nous avons ensuite pu expérimenter avec du véritable contenu.
Prise de l'environnement de jeu médiéval presque entièrement convertie vers Nanite !
Visualisation de triangles Nanite
Broches (matériaux) de rastériseur uniques dans cette scène
Une fois que la scène de test de l'environnement de jeu médiéval était fonctionnelle, il nous restait beaucoup à optimiser avant de pouvoir livrer le rastériseur programmable Nanite.

Dès le début, nous avons souhaité utiliser Nanite pour le Chapitre 4 de Fortnite Battle Royale, mais les fonctionnalités nécessaires à la mise à disposition de l'outil n'étaient pas encore prises en charge. Même pour les maillages les plus simples, comme les bâtiments opaques, l'effet de tremblement (survenant en cas de dégâts) devait être animé avec World Position Effect. Fortnite est devenu le premier bénéficiaire du rastériseur programmable.

Applications possibles dans Fortnite

Détails animés

La première application possible concerne les détails en maillage statique opaque qui utilisent World Position Offset pour l'animation secondaire. Bien que ces détails ne nécessitent pas forcément l'utilisation de Nanite, l'outilVirtual Shadow Map est bien plus performant avec des maillages Nanite. Il était donc essentiel de rendre la majeure partie de cette scène avec Nanite.


 

 

Bâtiments

L'un des aspects majeurs de Fortnite est la présence de divers ensembles de bâtiments ; or Nanite a déjà fait ses preuves avec les grandes villes. Il n'est donc pas surprenant que nous ayons choisi Nanite pour tous les maillages des bâtiments afin d'améliorer la fidélité visuelle, d'éviter les changements brutaux du niveau de détail et d'obtenir de meilleures performances.
Construction
Il y a bien trop de maillages de bâtiments dans Fortnite pour que nous puissions tous les reconstruire sur toutes les plateformes. Nous avons donc créé un processus hors ligne nous permettant de prendre les textures et règles de déplacement des artistes et de produire des maillages Nanite déplacés de haute qualité avec un compte de triangles bien plus élevé que dans les versions traditionnelles.

Remarque : ce déplacement n'est pas effectué pendant l'exécution, il s'agit d'un flux de travail hors ligne spécifique à Fortnite pour la création de maillages déplacés.

Outre les améliorations visuelles apportées au rendu de la vue principale, les maillages Nanite améliorent également de manière significative la qualité de Virtual Shadow Map, puisque les détails géométriques comme les briques, qui étaient jusqu'alors des zones peintes en 2D sur la texture, sont désormais en 3D. Cela confère davantage de profondeur ainsi que de détails aux textures et permet d'obtenir un auto-ombrage et des silhouettes adéquats.
 
Dans Fortnite, les bâtiments sont des maillages statiques opaques et pourraient être intégralement rendus à l'aide des fonctionnalités Nanite présentes dans l'Unreal Engine 5.0, à l'exception de l'effet de "tremblement" qui survient lorsqu'une partie d'un bâtiment subit des dégâts (par exemple, lorsqu'un joueur tape dessus avec un piolet).

L'effet de tremblement est une simple piste animée appliquée avec World Position Offset et l'évaluation est toujours en cours d'exécution, même lorsque le tremblement n'apparaît pas (une valeur de zéro est utilisée).

Au vu du grand nombre de maillages de bâtiments dans Fortnite, nous avons implémenté une optimisation pour ce modèle. Un mode spécial peut être activé (r.OptimizedWPO), puis, indépendamment du fait que ce matériau dispose de la logique nécessaire à l'exécution de World Position Offset, Nanite évalue la logique si la fonction "Evaluate World Position Offset" d'un composant primitif donné est activée (il s'agit du réglage par défaut).
Lorsque Nanite effectue le "compartimentage du rastériseur" susmentionné, tous les composants primitifs qui auraient normalement suivi un chemin programmable, mais pour lesquels l'option "Evaluate World Position Offset" est désactivée, suivront désormais le chemin à fonction fixe standard du rastériseur.

Cette optimisation a fait ses preuves à plusieurs reprises (elle permet notamment de désactiver World Position Offset à distance), mais s'est avérée essentielle pour l'effet de tremblement des bâtiments. Nous avons désactivé l'option "Evaluate World Position Offset" par défaut pour tous les maillages de bâtiments et avons ajusté le code de Fortnite pour que la valeur soit définie programmatiquement en fonction des dégâts subis par le bâtiment (et du tremblement).
En plus de cette optimisation, nous avons ajouté un mode débogage pour "Evaluate WPO" dans Nanite (r.Nanite.Visualize EvaluateWPO). La couleur verte apparaît si une évaluation est en cours et la couleur rouge apparaît si ce n'est pas le cas.
Grâce à cette optimisation, presque tous les maillages de bâtiments suivront le chemin à fonction fixe, à l'exception de quelques maillages qui suivront le chemin de l'effet de tremblement programmable si nécessaire.
Exemple
r.OptimizedWPO désactivé ou activé

Arbres

Nous avons beaucoup travaillé sur les arbres. Pour le Chapitre 4, nous souhaitions ajouter des zones boisées très denses, il nous fallait donc trouver une solution efficace avec des performances prévisibles. Les arbres mettent à profit une toute nouvelle fonctionnalité de Nanite que nous n'avions encore jamais utilisée. Il a fallu de nombreuses itérations pour déterminer l'approche qu'il convenait d'adopter.
Construction
Lors de nos premières expériences, nous utilisions des matériaux cachés et des cartes pour les arbres.
Pour Fortnite, nous avons estimé qu'il était plus rapide d'éviter les matériaux masqués. Nous avons préféré augmenter le compte de triangles du maillage et faire en sorte que les matériaux restent opaques, en particulier pour les arbres et l'herbe. Nous avons fait ce choix, car les matériaux masqués dans Nanite impliquent un coût important lors des passes d'ombrage de base, puisqu'il convient de recalculer les triangles barycentriques par pixel. Nous avons également pris en compte le fait que l'espace négatif dans les cartes alpha encourt davantage d'overdraw.

Preserve Area (Préservation de la zone)

Après avoir converti les arbres de Fortnite dans Nanite, nous avons remarqué que les canopées disparaissaient à distance à cause du processus de simplification. Les feuilles se clairsemaient et disparaissaient parfois complètement. À un certain point, les feuilles individuelles ne pouvaient pas être simplifiées davantage, au-delà d'un simple triangle, et devaient être retirées pour réduire la quantité de triangles. La disparition des feuilles avait un impact direct sur l'apparence de la canopée.

Pour pallier ce problème, nous avons ajouté une nouvelle logique dans le constructeur Nanite (une option appelée "Preserve Area" qui peut être activée dans les paramètres du maillage Nanite). Celle-ci redistribue la zone perdue aux triangles restants en dilatant les bords limites ouverts. Dans ce cas, les feuilles restantes deviennent plus grosses. De près, cela peut sembler étrange, mais à la distance à laquelle la préservation de zone s'active, cela permet de conserver une certaine densité.

Cette fonctionnalité ne doit être activée que pour les maillages de végétation affectés par ce problème.
 
Sans la fonctionnalité de préservation de zone

 
Avec la fonctionnalité de préservation de zone
Animation du vent
Il serait étrange d'introduire des arbres haute fidélité, mais que le vent n'anime pas. Traditionnellement, les animations de vent dans Fortnite étaient réalisées à l'aide d'une logique particulièrement complexe de World Position Offset. Puisque les arbres Nanite ont considérablement plus de sommets (environ 300 000 à 500 000) que les arbres traditionnels (10 000 à 20 000) et puisque la logique Word Position est désormais évaluée dans le rastériseur de Nanite, nous avons cherché d'autres moyens d'animer les arbres de manière à réduire le coût d'évaluation par sommet.
Nous avons fini par appliquer une simulation de vent complexe à une texture dont nous avons prélevé un échantillon en exécutant World Position Offset. Cela nous a évité d'avoir à effectuer des calculs complexes pour chaque sommet.
Pour la géométrie des arbres, nous pouvons dégager le point de pivot de chaque branche et la place de la branche sur l'arbre, ainsi que la branche à laquelle elle est reliée. Nous pouvons ensuite créer un squelette à partir de ces informations, puis créer une simulation à l'aide de Vellum dans Houdini.
Le point de pivot et l'orientation de chaque branche peuvent être codés comme une valeur de pixel dans une image, ajoutés à un shader de matériaux, puis indexés à l'aide d'une valeur UV personnalisée codée dans la ressource de maillage. Cela permet ensuite de choisir la rangée de pixels adéquate pour la branche.

Le rastériseur Nanite ne doit alors chercher qu'une seule position et qu'un quaternion pour calculer la compensation et ne s'appuie pas seulement sur l'analyse de textures. Cette approche ne fonctionne pour l'instant qu'avec l'animation de corps rigides, puisque la valeur des UV de chaque branche est la même.
Culling de distance
La simulation du vent dans les arbres est très importante lorsque la caméra en est proche, mais l'est bien moins à distance. Pour optimiser les performances, nous avons ajouté la possibilité de désactiver les calculs de World Position Offset à partir d'une distance définie par l'artiste. Cette optimisation vient s'ajouter au mode "Evaluate World Position Offset".

Herbe

Nous avons fait en sorte que l'outil Landscape Grass puisse générer des maillages Nanite. Le culling de distance est également disponible pour ce système. L'approche est similaire à celle adoptée pour les arbres. Nous avons utilisé des matériaux opaques avec une géométrie pour les brins d'herbe, mais l'animation est liée à des calculs avec World Position Offset.
 

 

Landscape (Nanite)

En raison de la manière dont fonctionne Virtual Shadow Maps, le rendu des ombres dans un grand maillage non Nanite prend bien plus de temps que dans un maillage Nanite de taille équivalente. Cela posait notamment problème pour Landscape, un énorme maillage non Nanite qui impliquait un important temps de calcul du processeur graphique. Nous avons implémenté une fonctionnalité de rendu expérimentale pour Landscape disponible dans l'Unreal Engine 5.1. Cette fonctionnalité convertit le champ de hauteur du paysage en maillage Nanite au moment de la génération. La conversion n'améliore pas la qualité visuelle, mais permet de retrouver les caractéristiques de performance du culling et de la rastérisation Nanite lors du rendu dans la passe de base ou dans Virtual Shadow Maps.
Lumen dispose d'un chemin spécifique pour le tracing par rapport au champ de hauteur dans Landscape. Nanite ne prend pas en charge le rendu dans Runtime Virtual Textures pour le moment. Dans ces cas-là, ce n'est pas la représentation Nanite qui est utilisée, mais le champ de hauteur.
 

À venir

Nous devons encore travailler sur le calcul du volume des clusters et des instances (par image) par rapport à l'animation exécutée par World Position Offset. Cette fonctionnalité est essentielle, car elle permet au culling d'occlusion de Nanite de retirer autant de clusters que possible avant la rastérisation.
 

Pour l'instant, nous utilisons les limites de position de référence (sans tenir compte de World Position Offset), ce qui peut être trop conservateur (trop de clusters sont rastérisés) ou générer des artefacts visuels si World Position Offset anime des clusters en dehors des limites de position de référence (des morceaux de maillage disparaissent). Nous avons implémenté une prise en charge rudimentaire pour Bounds Scale pour les composants primitifs (similaire à la manière dont le problème est géré pour les maillages non Nanite). Il est donc possible d'augmenter les limites d'une valeur arbitraire, mais le culling agit alors de manière encore plus conservatrice et les performances en pâtissent.

Nous souhaitons également continuer à optimiser le système de matériaux dans Nanite afin de réduire le coût des matériaux masqués et des matériaux qui utilisent Pixel Depth Offset.

Nous avons hâte d'implémenter cette nouvelle fonctionnalité pour Nanite dans l'Unreal Engine 5.1 et de découvrir le contenu imaginé par les développeurs !
Pour plus d'informations sur les fonctionnalités mentionnées dans cet article, consultez la documentation Nanite.

    Obtenez l'Unreal Engine dès maintenant !

    Procurez-vous l'outil de création le plus ouvert et le plus avancé au monde.
    L'Unreal Engine est prêt à l'emploi, avec toutes les fonctionnalités et un accès complet au code source.