Perception de la technique

Ceux qui me connaissent savent que je ne suis pas super dogmatique vis-à-vis de la technologie et que chaque paradigme contenant le mot « moderne » oublies souvent de lister les inconvénients et marges de manœuvre perdues du paradigme qu’il cherche à remplacer. De plus, le fait que ces paradigmes soient poussés par des intérêts économiques rend difficile les discussions et la recherche de ce qui est perdu (c’est un peu comme si, dans une discussion sur la colorimétrie, une personne parle de « smart color », parce que c’est ce qui est écrit sur la boite de son nouveau moniteur…) :

  • « Le temps réel, le GPU path-tracing, le GPU machin computing, c’est l’avenir. » − Nvidia
  • « Le ray-tracing, c’est l’avenir. » − SolidAngle
  • « Les UDIMS, c’est l’avenir. » − Mari
  • « Le deep compo, c’est l’avenir. » − Weta
  • « Les UVs, c’est le passé. » − Ptex
  • « Les NURBS, c’est le futur. » − Une publication Siggraph de 98
  • « La soupe aux épinards, c’est l’avenir » − Tes nobles parents qui essaient de te refourguer des légumes à toi, le couillon·ne ingrat·e qui leur sert de môme… :gne2:

Vous voyez l’idée…

Bien souvent ces propositions ne sortent pas de nulle-part. Elles sont cohérentes et résolvent beaucoup des problèmes des paradigmes précédents. C’est la raison pour laquelle il faut les prendre au sérieux. :nannan:

Mais comme n’importe quel paradigme, ils viennent avec leur lot de situations difficiles à gérer. Comme personne ne vous en parlera, il faut souvent aller les chercher sous le capot, dans la documentation des logiciels, dans des interviews, des VFX breakdown, par la pratique, dans les bars (quand vous avez la chance de tomber sur une personne qui s’en est servi), parfois en regardant les images.

Vous pourriez découvrir que :

  • Pour être efficace, le GPU path-tracing a son lot de contraintes auxquelles il faut faire attention (instancing et taille de textures).
  • Le deep compo est tellement lourd (en poids et en traitement) que même des studios de taille importante ne peuvent pas généraliser son utilisation.
  • Les UVs sont toujours là et le filtrage qu’implique le Ptex n’est pas efficace sur des accès incohérents.
  • Les NURBS, c’est d’la merde. :grenadelauncher:
  • La soupe aux épinards, c’est loin d’être dégueulasse, surtout si on met un peu d’ail et du beurre. :petrus:

En gros, à l’exception de quelques-uns qui arrivent à s’imposer, les autres ne deviennent que des options dans la boite à outil dont on dispose pour faire nos productions, et certains disparaissent tout simplement. C’est très bien d’avoir plus d’options pour résoudre un problème, mais ce n’est pas la même chose que sauter aveuglément dedans. Si on est incapable de lister clairement ce qu’on gagne et ce qu’on perd quand on amène une solution pour en remplacer une autre, c’est risqué. Et tout le discourt dogmatique qui peut accompagner ses solutions vise à faire sauter la question du risque, à ne pas répondre à la question. Quand tu pars sur 1 an et demi de prod comme ça, ça peut mal se passer…

Et la fameuse, répétée à outrance :

« Le temps humain, c’est plus cher que le temps machine. »

Waow… Je n’y avais vraiment pas pensé, merci d’éclairer mon chemin, je m’en vais méditer là-dessus et penser à mon avenir. :bete:

Après ce énième vomit de tonton grincheux et condescendant, vous pouvez rebrancher votre cerveau, on peut y aller ! :papi:

Le plan

Le papier tourne autour d’un plan précis du film, le voici :

Regardez bien les personnages de la foule. On pourrait faire quelques remarques artistiques, mais ce n’est pas le sujet. Mettez cette image de côté, et gardez-la sous les yeux durant la lecture de ce billet.

Avant-propos

J’aimerais préciser que le terme « pose » utilisé dans le papier est assez difficile à traduire. Littéralement, il veut dire « prendre la pose ». Et dans le cas du rendu, il s’approche d’une idée de pré-calcul, un « bake d’information » qui semble être un process important de Pixar. J’ai pensé à utiliser le terme bake, mais il aurait prêté à confusion. Dans le cas du papier, il s’agit de baker les choses au moment du rendu (pose at render-time) de sorte que le moteur puisse s’éviter de lourds calculs au moment de tracer les rayons ; c’est très courant en temps-réel et pas du tout path-tracing, dans l’idée. Je vais conserver l’utilisation du terme pose, en italique, mais je voudrais que vous gardiez cette idée de « bake au moment du rendu » en tête. :sourit:

Autre point que je dois expliquer. Le terme « hero » fait référence à tout ce qui à trait aux personnages principaux. « Hero shader » et « Hero volume » sont respectivement des shader et des volumétriques de personnage principaux, souvent lourd et prévu pour les gros plans. Sur les grosses productions, il n’est pas rare d’avoir des morceaux de pipeline dédiée à des personnages dans le but d’augmenter sa qualité finale, mais qui ne sont pas appliqués au reste des personnages pour des raisons de coût.

Si vous n’aimez ni le mot « rasterize », ni les anglicismes, vous allez vomir en lisant ce billet, et j’en suis le premier désolé. J’ai vraiment fait ce que j’ai pu… :vomit:

Autre chose : J’alternerai entre ma traduction des paragraphes du papier (qui sera préfixée d’un gros « Traduction : ») et mes remarques.

Pour finir, gardez à l’esprit que je ne suis pas un expert de RenderMan, en particulier sur certaines notions pointues, comme les Implicit Field Plugin, les modifications de scène « at render-time », et le mécanisme de moteur embarqué (dont je soupçonne qu’aucune intégration publique n’y fait référence). Bref, je fais au mieux pour expliquer ce que je comprends des notions utilisées, mais il y aura pas mal de spéculations, pas taper… :seSentCon:

Traduction

Et on commence avec la partie abstract :

Traduction : Pour sortir les plans de Soul, contenant une foule de centaines de personnages composés de plusieurs volumes, on ne pouvait pas s’appuyer sur le pipeline de cache de pose utilisé sur les personnages principaux [Coleman et al.2020], trop lourd en IO. Nos équipes rendues et systèmes ont évalué le stockage total nécessaire au « monde des âmes » à plus de 100 To, pour une moyenne de deux personnages principaux par plans. Pour pouvoir gérer les larges foules de personnages ayant le même aspect volumétrique que les personnages principaux, tout en évitant d’atteindre cette limite d’IO, deux nouvelles techniques « at render-time » furent développées. La première s’appuie sur un rasterizer de volumétrique existant pour « poser » les volumes au moment du rendu en utilisant leur déformeur lattice. La seconde technique permettait aux primvars rasterizées de la surface d’être utilisé par le shader de volumétrique.

Voilà pour la partie abstract. :bravo:

En gros, il y a des limites/quota d’IO à respecter, et ce plan partait pour les dépasser éclater, donc il fallait trouver une solution. C’est le genre de problèmes qui doit vous parler. Il est probable que vous n’ayez pas de quotas imposés (« segfault is the limit » :smileFou: ), mais si vous avez un minimum de conscience professionnelle, anticiper des plans complexes est un classique des débuts de productions.

La première partie concerne l’économie de la bande passante des fichiers de volumétrique :

Traduction : En plus des limitations d’IO qu’aurait généré l’utilisation du pipeline de cache de pose, le temps de génération des « hero volumes » dans Houdini appliqué à la foule aurait eut un coût important en farm. Avec notre librairie maison de rasterization de volume REVES (une implémentation de l’algorithme REYES pour les volumétriques) [Wrenninge 2016], nous avons fait un pipeline autour de la gestion des volumes en rest pose et des lattices déformées. Cela nous permit de générer des volumes de façon implicite au moment du rendu pour ne pas atteindre la limite de stockage réseaux.

En gros, seuls les fichiers de rest pose des volumétriques ainsi que les lattices de déformation étaient envoyés au moteur, et c’est ce dernier qui s’occupait de générer le volume final. :petrus:

Un peu comme si, dans le cas de la géométrie, on envoyait le personnage en T-pose avec ses joints et les informations d’influence et que le moteur s’occupait de faire le skinning au moment du rendu (au passage, c’était la technique utilisée par Massive, et il me semble que c’est encore la méthode utilisée par les différents moteurs de foule, mais je ne veux pas m’avancer :jdicajdirien: ).

Notez aussi que REYES (ou du moins, son principe) semble toujours de la partie, ce qui ne me surprend qu’à moitié. :reflechi:

Ce passage sous-entend aussi que la génération des personnages principaux au rendu est faite dans Houdini.

Traduction : Au moment du build de personnage des foules, un lattice est généré pour chaque volume d’un personnage ; body, hair, face, details, et accessoires (La résolution de chaque lattice était de 15 × 15 × 15, permettant d’équilibrer la préservation des détails générés lors de la rastérization, et les performances). Le lattice est déformé par le pipeline de foules UsdSkel [Yen et al.2018], qui génère les motion samples des points de la lattice et les envois à un plugin Implicit Field de RenderMan. Muni de la lattice déformée et du volume en rest pose stoqué sur le disque, le plugin voxelise chaque field au moment du rendu, dont le field de vélocité calculée depuis les vecteurs de vélocité des points. Le field de vélocité est ensuite samplé par le moteur pour générer le motion blur des volumes. À cela s’ajoute la génération de fields min-max afin d’optimiser le tracé de rayon après la rasterization, via les algorithmes de volume de RenderMan [Fong et al. 2017]. Rasterizer la rest pose d’entrée à sa densité de voxel maximum avait un haut coût de calcul pré-pixel. Cela ralentissait le temps d’itération des lighters, nous obligeant à utiliser la technologie de LOD stochastique pour ajuster la résolution du field utilisée [Cook et al. 2007].

Il y a beaucoup de choses là-dedans. Je pense que vous avez compris l’idée. Ce qui m’a vraiment surpris, c’est la présence d’une référence à un papier de 2007 de simplification stochastique. J’ai le souvenir que ce type de mécanisme n’était pas très pratique en path-tracing, car il cassait les instances (chaque objet devient unique), et était utilisé sur Cars, à l’époque ou RenderMan était encore full REYES. On peut donc spéculer… :youplaBoum:

Soit le REYES est encore énormément ancré dans le workflow de Pixar, ce qui encore une fois, ne serait pas étonnant : Quand un pipeline est tourné vers du bake à outrance, il est difficile d’obtenir plus efficace. Comprenez que baker des PTC à la main est un problème de paysan du rendu et qu’un pipeline optimisé fait ça en sorti d’anim sans intervention humaine.

La dernière option est que cette simplification stochastique (et tous les mécanismes « at render-time ») soient fait lors d’une première passe de rendu. À titre personnel, j’ai déjà eux à implémenter un mécanisme pour déterminer la densité optimale de poils. Je passais par Maya, j’importais l’Alembic du perso et de caméra. Ensuite, pour chaque image (et motion step) je déterminais le vertex le plus proche et dans le frustrum, y plaçait une sphère de taille 1 et calculait le ratio de sa projection (sa taille) sur l’axe Y de la caméra. Je suppose qu’à Pixar, ce genre de chose est fait via des plugins du moteur, au moment du rendu. Dans le cas de la simplification stochastique, ça permettrait de simplifier une fois, pour lancer le rendu ensuite (mais ça casse quand même les instances, donc je ne sais pas… :pasClasse: ).

Vient ensuite la partie geometry rasterizer… Mais ! De la géométrie ? N’est-on pas supposé calculer des volumétriques ? Nous aurait-on menti ? :reflechi:

C’est le moment de vous resservir du thé, car on entame la partie qui saigne, celle qui fait qu’à mes yeux, les ingés de Pixar ont bien plus de jugeote que ceux qui affirment sans sourciller qu’ils envoient tout en farm.

Détendez-vous, prenez une inspiration, ça va chier ! :enerve:

Traduction : Bien que les personnages de la foule étaient principalement composés de volumes, les yeux étaient en meshs. L’opacité des faces volumétriques n’étant pas géré de la même manière que les surfaces, on avait l’impression que les yeux « flottaient ». L’occlusion des yeux pouvait facilement se faire via un Z-buffer, mais comme nos personnages n’étaient pas opaques et se chevauchaient en screen space, il fallait générer un buffer unique par personnage. Cette exigence nous empêchait d’utiliser RenderMan pour générer ces signaux, ce dernier n’ayant qu’un unique contexte de rendu par process, et non des centaines. Nous avons développé un rasterizer CPU (en non GPU, car notre farm est principalement en CPU) afin de générer des textures en mémoire (In-Memory Textures). Muni de ce moteur de rendu embarqué (sic), la Z-depth est rasterizé pour ensuite pouvoir tester la présence de la surface des yeux.

Je ne suis pas un expert RenderMan, mais la question de l’occlusion d’une surface à l’intérieur d’un voxel volumétrique est en effet un problème assez pointu si on veut conserver de bonnes performances. Je soupçonne que l’utilisation d’un Implicit Field Plugin empêche le moteur de pouvoir tracer l’occlusion correctement (ou bien ils sont en REYES pûr, ce qui, encore une fois, reste une option). Dans tous les cas, les yeux semblent ne pas s’intégrer correctement dans les volumétriques et ils ont dû coder un rasterizer CPU embarqué à l’intérieur de RenderMan (un moteur dans le moteur) pour générer, par personnage (et at render-time, Monsieur !), une image permettant de savoir s’il faut afficher l’œil ou non.

Le fait qu’ils aient utilisé un rasterizer donne une information intéressante, notamment le fait qu’ils ne rendent pas avec un lens shader. :reflechi:

Vous allez me dire : « Putain, mais Dorian ! Personne rends avec un lens shader, et puis c’est quoi le rapport avec le papier ?!!! :injures:  ». Et bien, comme il m’est arrivé de me retrouver dans une réunion très sérieuse (avec prodex et tout le bordel) sur le fait qu’il fallait aaaabsolument rendre avec un lens shader, je pose ça là… Si un jour ça vous arrive, l’argument Pixar avec une petite démonstration via ce papier peut vous sauver les miches, et celles de votre équipe compo…

Bref, ils ne doivent pas être bien nombreux les ingés qui ont on jour codé un moteur dans un moteur pour générer puis injecter une texture en attribut géométrique pour ensuite la sampler dans le shader… :smileFou:

Traduction : De plus, certains fields du shading des volumes de rest pose ne pouvaient pas être facilement rasterizé par REVES. Les fields indépendants de la caméra (c.à.d la densité ou les fields de mask) ne posaient pas de problèmes, mais certains signaux (comme les gradient fields) étaient alignés sur le frustrum. À cela s’ajoutait des inquiétudes montantes concernant le temps d’attente du premier pixel (time-to-first-pixel) généré par le coût de rastérization REVES de ces fields. Nous avons utilisé le geometry rasterizer pour rendre, en texture, les propriétés de surface de chaque mesh représentant un volume. Ces textures pouvaient ensuite être connectées aux shaders de volume, comme si elles provenaient des fields. Cela a permis à nos shading artists de construire un shading network qui pouvait facilement être utilisé sur les personnages hero et foule, quelle que soit la provenance des signaux. De plus, un shader de surface séparé émulant le look de la volumétrique tirait parti d’une texture d’alpha généré par le rasterizer.

Ici, on aborde les effets sur la production. Leur système de rest pose + lattices permettait de rester sous la barre des quotas d’IO, mais intégrait des fields dont le contenu dépendait de la caméra et que REVES avait du mal à les évaluer (trop lent, je suppose). Ceci allongeait la durée de démarrage du rendu, ce qui gênait les équipes qui devait attendre avant de voir le résultat, chaque fois qu’ils modifiaient un potard. Pour résoudre le problème, ils ont dû baker les informations de volumétrique en se servant d’un mesh qu’ils ont rasterizé. De ce que j’en comprends, les volumétriques avaient une représentation en mesh (un peu comme un « volume to mesh ») et c’est ce mesh qui a été utilisé (c.à.d, rendu via le geometry rasterizer) pour générer un bake d’information (apparemment, les dégradés de couleur) qui ont été baké dans des textures (screen-space ? brickmap ? Ce papier ne le précise pas). Le shader, au lieu d’évaluer le fields, allait directement sampler la texture en question. :neutral:

Traduction : Le fait de rasterizer à la volée évitait les problèmes de synchronisation du pipeline de foule. De plus, ces signaux étaient nécessaires au light shaping, d’où le besoin de rasterizer avant le rendu, plutôt que de laisser cette responsabilité au compo. Ce geometry rasterizer dispose d’un ensemble limité de features et n’a pas pour but de remplacer les possibilités d’un moteur REYES ; C’est un rasterizer à un sample, simpliste et pas cher, conçu pour fournir des entrées à d’autres partis de la scène.

Je crois que tout est dit… :baffed:

Je comptais sauter la section suivante, « In-Memory Textures », car elle était très spécifique, mais il n’aurait manqué que ça pour que tout le papier soit traduit, donc on va essayer de faire ça bien :

Traduction : Le système (In-Texture Memory) devait gérer plusieurs textures en 512 × 512 par personnage, par frame (Il s’agit d’une résolution moyenne pour des personnages à mi-distance. Nous avons utilisé la taille du personnage en screen-space pour déterminer procéduralement la résolution nécessaire, sur un écart partant de 2048 × 2048 à 256 × 256). Afin d’éviter le coût d’écriture et de lecture de plusieurs centaines de textures par plan, les textures rasterizées étaient gardées en mémoire sous la forme d’attributs de géométries, qui pouvaient être passés à l’interface de texture Rtx de RenderMan. Nous avons écrit un plugin Rtx pour répondre aux requêtes de tile fill des buffers. Il implémentait aussi le support de la génération de mipmaps pour les accès aux textures floutées. Le sampling de texture sur les bords du volume nécessitait une dilatation du signal rasterizé. Nous compositions des mips de plus haute résolution au-dessus des mips de plus basses résolutions pour les faire « déborder ». Ce mécanisme avait l’avantage d’être rapide et temporellement plus cohérent que l’algorithme de push-pull d’OpenImageIO.

Comme vous pouvez le voir, c’est très spécifique, si vous êtes arrivé jusqu’ici, je ne vois pas trop quoi développer. :hehe:

Et arrive la conclusion du papier ! :banaeyouhou:

Conclusion

Traduction : À travers l’intégration des techniques de rasterization à l’initiation de scène de notre moteur de rendu path-tracer, nous avons été en mesure de gérer une grande quantité de personnages de foule en volumétrique dans Soul. REVES nous a permis de poser des foules de volumes at render-time, tandis que le geometry rasterizer générait les entrées de shader permettant de matcher le look d’un hero, à une fraction de son coût.

Conclusion 2, le retour du fils de la première conclusion que tu croyais qu’il y en avait qu’une seule alors qu’en fait non

On y est arrivé ! Si vous êtes encore à lire ces lignes, félicitation ! :bravo:

J’espère avoir réussi à rendre ça le plus clair possible et vous avoir motivé à jeter un œil par vous-même dans leurs publications, certaines devraient forcément vous intéresser ! :joue:

Un gros merci à Pixar de sortir ce genre de papier qui a le mérite d’attaquer un problème précis et de donner pas mal de détails en deux pages. Ça peut aussi rassurer pas mal de personnes de voir qu’il faut parfois contorsionner nos outils pour en sortir ce qu’on veut. :sourit:

Bon, je sais pas pour vous, mais quand je lis ça, je me dis qu’il est loin le path-tracing qui résout tous les problèmes de rendu et la faim dans le monde… :nannan:

On voit à quel point la réalité de Pixar est très loin de ce qu’on pourrait appeler « l’idéal RenderMan », tout de XPU path-tracing vêtu, qui ne fait pourtant que suivre et tenter de dépasser la tendance. D’un côté on a un RenderMan qui suit les tendances de ses concurrents (car c’est sur ce marché qu’il évolue) et de l’autre, un RenderMan qui sort des longs. :reflexionIntense:

C’est à la lecture de ce genre de papiers que je me dis qu’il serait vraiment intéressant que les moteurs de rendu commerciaux proposent un paradigme pré-rendu (ou préanalyse). En pratique, on le fait déjà avec les moyens du bord :

  • Évaluation de ce qui est visible où non afin de lui appliquer des paramètres précis (subdivision, désactivation du spéculaire, etc).
  • Évaluation de densité de poils.
  • etc.

Ces mécanismes sont souvent lourds à mettre en place à la main. :reflexionIntense:

Un moteur capable de lancer un sample (ou plus) par pixel et me sortir un JSON (dixit le mec qui écrit la moitié d’un billet sur l’inefficacité des technologies modernes) de ce qui est le plus, et le moins samplé (objet ET composants de shaders), avec un ray differential moyen (ou médian), serait déjà énorme en termes d’optimisation possible. :hehe:

On arrive dans un monde sous contrainte. La question de la sur-optimisation (et son automatisation) va revenir à grands pas. On a une industrie de l’animation, on est capable de profiter de ces paradigmes, pour peu qu’ils soient exposés par les moteurs. Un tel papier ne fait que prouver qu’en pratique, le contrôle reste la mère des belles images. (Putain, c’est beau ! :neutral: )

Je reste persuadé que l’avenir du path-tracing passe par l’analyse de scène par le moteur lui-même. L’analyse de scène est le pilier du temps-réel : Plus il dispose d’informations, plus il peut optimiser et baker de choses. Si le temps réel est l’avenir, les moteurs path-tracing devraient s’en inspirer, au moins pour résoudre les problèmes les plus récurrents et évidents. :redface:

Oui, bon, à essayer de faire une conclusion un peu sérieuse je commence à dire vraiment n’importe quoi, je m’arrête là… :slow_clap:

À bientôt !

:marioCours: