PROJET AUTOBLOG


blog.fevrierdorian.com

source: blog.fevrierdorian.com

⇐ retour index

Guerilla, le shutter et Yeti

lundi 19 août 2019 à 22:55

Dans ce billet on va encore parler de poils et tout particulièrement du motion blur de Yeti dans Guerilla.

Rien de révolutionnaire, on va juste faire le tour des choses à savoir quand on doit gérer cette combinaison. :reflechi:

À l’époque, calculer le motion blur était très coûteux pour un ray tracer. Muni de nos passes de motion vector (en .tiff…), on bricolait au compo pour éviter de provoquer des crises d’épilepsie chez le spectateur (car à par les animateurs qui trouvent que l’effet stroboscopique d’un playplast est trop cool, tout le monde à mal au crâne).

C’était l’époque ou une roue ou une sphère tournant rapidement sur un plan n’augurait rien de bon… :triste:

S’il est intéressant de voir que les techniques à base de passes de motion vector au compo n’ont jamais complètement disparues, on ne peut, en revanche, pas ignorer que le motion blur 3D (c.à.d, rendu) arrive avec son lot de complications.

Ce billet part du principe que vous savez manipuler Yeti et Guerilla. :cayMal:

Guerilla et le motion blur

Il faut bien comprendre que Guerilla n’est pas un logiciel comme Maya qui va « sampler » la scène à des valeurs temporelles données. Il n’assume rien du sampling interne des éléments de la scène (Alembic, Realflow, etc.). Il récupère tout ce qui peut pour un shutter donné.

Ce mécanisme permet de faire coexister, dans une même scène, des Alembic exportés avec différentes valeurs de step (e.g. la majorité en 2 samples : 0.0-0.2 et les personnages rapides en 5 samples : 0.0-0.05-0.1-0.15-0.2, etc.).

Les procédurales RenderMan

Guerilla supporte les procédurales au format RenderMan. Une procédurale est un objet (une primitive plutôt) de votre scène de rendu qui déploie son contenu quand le moteur le demande (souvent, quand un rayon touche sa bounding box). À ce moment, le moteur appelle la procédurale avec ses paramètres. Chaque procédurale dispose de ses propres arguments, un peu comme une ligne de commande.

La procédurale Yeti

Pour que Yeti crache ses poils au moteur, il nécessite un fichier .fur généré par vous au moment de l’export Maya. Ce fichier contient tout ce qu’il faut à Yeti pour générer des poils :

La documentation du format de cache (.fur) est ici si vous voulez un peu plus d’information.

Au moment de l’appel de la procédurale, on lui donne ce fichier, ainsi que certain paramètres (density, width, thread, etc.) et elle fait ensuite son travail. :mechantCrash:

La documentation des paramètres de la procédurale Yeti se trouve ici. Si vous la regardez, vous remarquerez la présence de l’argument --samples. Comme expliqué, Yeti va cracher les samples de motion blur aux valeurs données dans cet argument.

Yeti dans Guerilla

Avant qu’un nœud Yeti d’interfaçage n’existe dans Guerilla, il fallait faire une RIB box contenant l’appel de la procédurale. Ce n’était pas super pratique, mais on avait un contrôle absolu sur l’appel (c’est nous qui donnions les arguments).

Le manque d’icône devrait vous donner une idée du côté user friendly…

Et toto.rib est le fichier ouvert par Guerilla.

Pour vous donner une idée de ce qu’on écrivait dans ce fichier :

Procedural "DynamicLoad" ["pgYetiPrmanRender" --density 1 --length 1 --width 1 --frame 1 --samples 0.0 0.2 --file "/path/to/my.fur"]

Du bonheur en boite ! :petrus:

Comme c’était un brin casse-pieds, un nœud Yeti a vu le jour :

Documentation du nœud ici.

Ce nœud encapsule la procédurale, ce qui lui permet de bénéficier du workflow de Guerilla (overrides notamment). Dès lors, il faut se demander quelles sont les valeurs que Guerilla passe à --samples ?

Et bien, par défaut, Guérilla demande deux samples, un au shutter open et l’autre au shutter close du projet. Rappelez-vous :

Pour pallier à ça vous pouvez passer une liste explicite de samples dans l’attribut Motion Samples du nœud Yeti de Guerilla. À vous, toutefois, de vous assurer que ces valeurs sont cohérentes avec ce qu’il y a dans votre .fur ainsi qu’avec le shutter fourni à Guerilla.

J’espère avoir rendu tout ça moins opaque. :pasClasse:

Un tout petit écart pour vous dire que vous pouvez baker la procédurale en .ghostdata. Guerilla va générer le contenu de la procédurale et l’enregistrer, vous pouvez ensuite l’importer dans et Yeti n’est plus nécessaire. Ce sera lourd, mais dans le cas d’éléments statiques c’est très pratique.

Et bien entendu, vous pouvez scripter ça :

import guerilla
guerilla.Procedural.baketoarchive("/path/to/my.ghostdata")

Pensez-y si vous êtes ric-rac coté licences (et que vous avez vraiment beaucoup de stockage).

Fin de la parenthèse. :hehe:

Jouer avec les paramètres Yeti

Cette seconde partie s’apparente plus à du partage d’expérience sur le comportement de la procédurale Yeti elle-même.

Détail comme ça : Assurez-vous toujours que les valeurs de FPS sont identiques entre Maya et Guerilla. Évidant, mais comme un TD est amené à faire du support sur plusieurs projets, ce n’est pas inutile de le rappeler. Je me suis fait avoir lors d’un débogage sur un projet parallèle qui n’avait pas les mêmes FPS que le projet principal. :ideenoire:

Passons… :slow_clap:

Envisageons le scénario suivant : Le .fur Yeti est exporté sur 2 samples (0.0, 0.2). La trajectoire est donc linéaire :

Si Guerilla demande 0.0, 0.1, Yeti interpole correctement :

En revanche, si Guerilla demande 0.0, 0.3, Yeti « stop » le mouvement :

D’un côté, c’est logique, Yeti ne peut vraiment fournir l’information. Gardez ça à l’esprit si vous augmentez vos shutters sur un plan pour ajouter un peu de motion blur. et que vos poils sont en décalage de vos scalps.

Générer des bounding box pour ses .fur

pgYetiCacheInfo est une ligne de commande qui permet d’extraire quelques informations d’un .fur. La plus intéressante étant la bounding box.

En effet, comme le dit clairement la doc, Guerilla lui-même utilise pgYetiCacheInfo pour extraire la bounding box, mais de mécanisme par défaut est tellement lourd qu’un autre système est proposé.

Si un fichier .bbox existe a coté d’un .fur, Guerilla lira ce dernier pour en extraire la bounding box. L’idée est donc de générer ces fichiers au moment (enfin juste après) de la génération des .fur. Vous pouvez faire ça en farm. Gardez tout de mème à l’esprit que, sous le capot, pgYetiCacheInfo va générer le système de fur au complet. Donc les machines qui s’occupent de ça doivent en avoir dans le pantalon. :banaeyouhou:

Vous pouvez aussi bricoler cette bounding box vous-même depuis Maya en prenant celles des scalps que vous grossissez un peu, mais sur des systèmes de fur un peu complexe, vous risquez d’en faire une trop petite et payer la note au rendu.

Sachez également que l’argument –samples permet, comme pour la procédurale de prendre en compte les samples donnés pour la génération de la bounding box.

Conclusion

J’espère que ce billet vous aura plus, et que vous allez, etc. etc.

:marioCours:

LOD, suite et fin v2 (les poils)

samedi 17 août 2019 à 17:52

J’espère qu’avec ce billet je vais enfin terminer cette longue et indigeste série sur les LOD. :vomit:

Attaquons enfin la densité des poils au plan. Ce dernier billet va être un peu technique. On va faire un peu de trigonométrie (espace caméra, toussa) et parler de farm.

Si vous avez suivi un des liens de mon premier billet, vous avez peut-être remarqué que la méthode stochastique est utilisée pour diminuer la densité des poils sur Ratatouille à la volée, au moment du rendu, en prenant en compte la distance, le motion blur et le DOF. Je ne reviendrais pas sur les raisons pour lesquelles ce n’est plus facilement faisable de nos jours (il faut lire le billet :IFuckTheWorld: ), mais l’idée générale reste inspirante et on peut tenter de se l’approprier pour bricoler quelque chose. :dentcasse:

Cette méthode est un peu brouillonne et les habitués de trigonométrie auront sûrement une approche plus fine du problème.

Sur un plan, on cherche à calculer la densité et l’épaisseur optimale des poils d’un personnage sur ce plan.

C’est ça qu’on cherche à calculer.

Un même système de poil, différentes densités et épaisseurs.

Ces valeurs ne peuvent pas être animées au risque d’engendrer des inconsistances de nombre de poils entre deux échantillons temporels, ce qui mettrait le moteur dans une bien mauvaise passe au moment où ce dernier calcule les vecteurs de mouvement des poils. D’après mes tests (sous Guerilla et Arnold, je suppose que Renderman fait pareil) les moteurs désactivent simplement les vecteurs de mouvement du système de poil incriminé et lèvent un warning.

Il va donc falloir déterminer ces deux valeurs pour chaque système de poil sur l’ensemble du plan.

Il nous faut deux choses (suivant son pipeline, on peut utiliser de l’Alembic ou la combinaison Rig + ATOM) :

Une fois ces deux éléments dans notre scène, le bonheur est à notre porte. En effet, les mathématiques commencent. :baffed:

Pour chaque image du plan (et pour chaque step de motion blur), nous allons identifier le sommet géométrique qui soit à la fois le plus proche de la caméra et présents en son tronc (frustrum).

Finalement, nous calculons le diamètre, en espace caméra, du disque projeté d’une sphère de 1 unité de diamètre.

Nous récupérons ainsi le plus grand diamètre du plan.

Notez que quand, comme moi, on ne sait pas gérer ce mécanisme de façon totalement mathématique ( :seSentCon: ), on peut fabriquer une sphère et projeter chaque vertex via ce bout de code (honteusement inspiré de ce billet) :

import maya.cmds as cmds
import maya.OpenMaya as om

# retrieve MObject from node
sel = om.MSelectionList()

sel.add("myCamera")
sel.add("myGeo")

cam_dag_path = om.MDagPath()
geo_dag_path = om.MDagPath()

sel.getDagPath(0, cam_dag_path)
sel.getDagPath(1, geo_dag_path)

# generate projection matrix
cam_inv_world_mtx = cam_dag_path.inclusiveMatrixInverse()

fn_cam = om.MFnCamera(cam_dag_path)

proj_mtx = fn_cam.projectionMatrix()
proj_mtx = om.MMatrix(proj_mtx.matrix)  # convert MFloatMatrix te MMatrix

post_proj_mtx = fn_cam.postProjectionMatrix()
post_proj_mtx = om.MMatrix(post_proj_mtx.matrix)

view_proj_mtx = cam_inv_world_mtx * proj_mtx * post_proj_mtx

# iterate over each geo vertex
vtx_iter = om.MItGeometry(geo_dag_path)

# init 2D bounding box values
proj_x_min = 999999.0
proj_x_max = -999999.0
proj_y_min = 999999.0
proj_y_max = -999999.0

while not vtx_iter.isDone():

    # get vertex position in world space
    geo_pos = vtx_iter.position(om.MSpace.kWorld)

    # compute projected position on cam
    proj_pos = geo_pos * view_proj_mtx

    proj_pos_x = (proj_pos.x / proj_pos.w) / 2.0 + 0.5
    proj_pos_y = (proj_pos.y / proj_pos.w) / 2.0 + 0.5

    proj_x_min = min(proj_pos_x, proj_x_min)
    proj_x_max = max(proj_pos_x, proj_x_max)

    proj_y_min = min(proj_pos_y, proj_y_min)
    proj_y_max = max(proj_pos_y, proj_y_max)

    vtx_iter.next()

À ce stade, on a, pour un plan et un personnage, un diamètre de la partie la plus proche. :sourit:

Mais vous vous doutez qu’il va falloir la comparer avec une autre valeur. Cette valeur, c’est le diamètre du disque projeté de la sphère sur une caméra donnée quand l’asset est a une densité de 1 (densité nominale : 100 % de poils).

Ce diamètre se récupère au niveau de la modé/lookdev.

On se retrouve donc avec deux diamètres. On fait ensuite un simple ratio qui peut être utilisé pour calculer la valeur de densité.

Super : Reste l’épaisseur : En admettant que facteur soit la valeur de densité (1.0 pour 100 %, 0.5 pour 50 %, etc.), j’applique la formule suivante :

Épaisseur nominale * (1 + (−ln(facteur)÷ln(2)))^2

Neuh, que, nan, mais, de quoi ? Mais… Mais… Mais… Dorian ! Ça n’a aucun putain d’sens !!!

T’es mauvais ! Je savais bien qu’il n’y avait pas que dans les yeux que tu avais de la merde !!!

Je ne vais pas risquer de me vautrer en démonstrations. Les plus matheux d’entre vous auront peut-être remarqué la présence de X = ln(A)/ln(2) pour résoudre l’équation 2x = A. L’idée est de calculer une puissance, plus d’information ici.

En gros j’avais remarqué que la formule de calcul d’épaisseur suivante fonctionnait bien :

Pour info, à chaque fois qu’on baisse l’Exponent de un, on divise la densité par deux.

Il fallait donc que je réussisse à trouver les valeurs en gras pour chaque facteur de densité. En gros, il faut trouver une formule pour convertir 0.03125 en 6… J’avoue ne pas avoir trop le niveau et je ne me fais aucun doute sur le fait qu’une méthode plus sérieuse existe. Le graph est pour ceux qui veulent faire mumuse.

Il n’empêche que je voudrais quand même vous montrer les résultats en rendu. :redface:

On part sur ces valeurs :

Et le rendu de référence :

Density 50, Exponent 7, Width 0.002

Et en commence à descendre :

Density 50, Exponent 6, Width 0.008

Density 50, Exponent 5, Width 0.018

Density 50, Exponent 4, Width 0.032

Density 50, Exponent 3, Width 0.05

Density 50, Exponent 2, Width 0.072

Density 50, Exponent 1, Width 0.098

« Le monsieur fait mine que ça marche, mais il dit qu’il sait pas comment… » :neutral:

Mouaih…

Comme vous vous en rendez compte, cette méthode est limitée (pas de prise en compte du DOF, ni du motion blur, ni de la réfraction), mais avant de crier au scandale, il faut bien comprendre qu’on ne peut, de toutes façons, pas arriver à un résultat parfait sans que ce soit le moteur qui nous donne lui-même les informations. C’est sûrement faisable en calculant un mini rendu d’une AOV de l’aire de la surface au pixel combiné à une AOV de motion vector, mais je n’ai jamais mis cela en place. :seSentCon:

Le but est d’arriver à proposer une valeur au graphiste qu’il pourra, de toutes façons, modifier si les besoins du plan l’impose. Il y a fort à parier que la plupart des plans sur lesquels vous travaillerez n’auront rien d’exotique et avoir une valeur cohérente sous le coude vous évite de perdre du temps en essais/erreurs. :laClasse:

Toujours plus loin ?

On doit pouvoir améliorer ce mécanisme. Par exemple, si votre logiciel de lookdev supporte les valeurs animées, vous pourriez extraire les valeurs par frame (en regroupant les steps)et animer la densité. Je n’ai jamais testé, je ne peux donc pas vous dire si la « saute » de densité à chaque frame est visible à l’écran. :reflechi:

Conclusion

Les LOD en début de prod :

Vous, à deux semaines de la livraison :

J’espère que cette suite de billets, bien trop verbeux, j’en ai conscience, vous auront tout de même intéressés et élargie vos horizons techniques sur le sujet.

:marioCours:

 
 
<link href="moz-extension://b97715ad-75a4-41d9-bd2e-488cca6b1ecd/skin/s3gt_tooltip_mini.css" rel="stylesheet" type="text/css" /> <style media="print" type="text/css">#s3gt_translate_tooltip_mini { display: none !important; }

LOD, suite et fin

samedi 10 août 2019 à 21:25

Je me suis rendu compte que j’ai oublié de parler de deux-trois choses dans mon billet précédent et je suis parfois passé trop rapidement sur certains points.

Je vous propose un billet d’appoint pour corriger le tire, ce sujet aux branchements infinis le mérite bien. :sourit:

Aujourd’hui, je comparerai l’approche automatique et semi-manuel pour gérer les LOD géométriques, j’aborderai la relation entre le département de modeling et de lookdev puis on finira par les poils.

Système automatique

Avant propos

Si c’est la production ou la supervision qui met le sujet sur le tapis, il est probable que ce soit avec l’idée d’en faire un truc « automatique et transparent pour l’utilisateur », ce qui, ironiquement, correspond à la définition d’un truc « magique et opaque ».

Il va de soi que plus le projet est ambitieux, plus l’automatisation est importante, personne ne veut voir ses graphistes passer leurs journées à faire des opérations rébarbatives à la main. Et pourtant…

En pratique l’ajout de LOD géométriques dans vos projets veut dire que vous faites intervenir, dans vos tuyaux/scènes, une « particularité » de vos assets. Ainsi, même si vous automatisez la gestion des LOD, ces derniers ne disparaissent pas, vous ne les voyez simplement plus, jusqu’à ce qu’ils posent problèmes. Vous me répondriez : Pourquoi diable voulez-vous qu’ils posent problèmes, car leur gestion est « automatique et transparent pour l’utilisateur » ? :bete:

Cette perception nie la nuance. En effet, on ne fabrique pas des plans comme on fabrique des objets dont l’essence est d’être identique (agroalimentaire, informatique, jouets, etc.). La « nature » des plans varient. Par nature, j’entends ce qui en détermine sa fabrication ; un plan de visage a ses propres problématiques de fabrication (SSS des yeux/cou/vêtements, cheveux, Rim light, arrière-plan, etc.), de même qu’un plan de décor (nombre d’objets, lookdev d’ensemble, matte painting, etc.). Les deux ne se fabriquent pas de la même manière et la pertinence d’utiliser des LOD ou non en est très dépendante. D’une manière générale, plus on pousse la qualité vers le haut, plus des contrastes assez net s’opèrent dans la façon dont les plans sont fabriqués.

On commence à comprendre pourquoi les LOD, prétendument automatiques, reviennent toujours sur le tapis. Les graphistes travaillent avec un ensemble de choses simples (Alembic d’animation, assignation de lookdev, surcharge des positions/attributs, etc.) qui s’entremêlent, formant un tout complexe. Le rendu est le plus touché car c’est le département qui rassemble tout. Si la gestion des LOD est automatique et qu’ils ne sont donc pas explicitement « choisis » par les graphistes, ces derniers devront les ajouter aux potentielles raisons des problèmes qu’ils ont sous les yeux.

La production (et dans une certaine mesure, la supervision) a parfois du mal à juger de cet effet indirect.

Après ce (trop) long aparté, rentrons dans le vif du sujet : À quoi faut-il penser quand on automatise la gestion des LOD ? :reflechi:

Concrètement

Je pars du principe que vous êtes un TD à qui on a refilé la patate chaude et que ni le modeling, ni le lookdev ne veut intervenir (« transparent pour l’utilisateur » toussa).

L’approche naïve consiste à prendre toutes les géométries de sa scène et de leur appliquer un gros Reduce. Le résultat est très moche, mais a priori, c’est pas le sujet. :vomit:

Comme dit dans mon billet précédent, il faut s’assurer que les UV de la géométrie réduite ne soit pas détruits et correspondent à la géométrie originale.

Le plus gros problème c’est taille de la hiérarchie. Si elle est trop grosse, vous pourriez vous retrouver avec des milliers d’objets qui ne font chacun que deux ou trois polygones. Suivant vos méthodes de travail il est fort probable qu’une fois instancié des milliers de fois, vos logiciels de lookdev pédalent à traverser la hiérarchique de vos LOD.

On pourrait être tenté de supprimer les petits objets (petites bounding boxes) mais si c’est ce qui compose l’ensemble de votre asset (e.g. un arbre, une plante), vous risqueriez d’en supprimer une partie visible importante et vous retrouver avec un asset tout nu ce qui peut mettre le modeleur mal à l’aise devant son superviseur :

Si vos superviseurs modeling et lookdev sont de connards qui ne veulent rien entendre, il vaut mieux éviter les LOD. Si vous ne pouvez pas intervenir sur cette décision, vous êtes foutu. Vous pouvez mettre à jour votre Linked’In et commencer à postuler ailleurs, car les départements rendus vont pleurer du sang.

Mais on est dans le vrai monde, celui des Bisounours et des glaces à la vanille, celui ou les superviseurs comprennent les intérêts des solutions que vous leur proposez, pour peu que vous leur parliez lentement.

Ce qui m’amène au second point : La méthode semi-manuel. :sauteJoie:

Système semi-manuel

Aider les graphistes par « contrat »

Au passage, appelez-la semi-automatique, c’est plus facile à vendre à la production… :trollface:

Cette approche vise à résoudre les problèmes précédents par « contrat » avec les graphistes. Dans le cas de la hiérarchie trop dense, vous pouvez proposer la chose suivante à votre graphiste :

« Si ton groupe de nœuds porte le suffixe « _combined », le contenu sera combiné à l’export et le suffixe sera supprimé. »

En gros, vous garantissez que la hiérarchie suivante :

Sera converti en ça :

La confiance n’excluant pas le contrôle, vous n’aurez qu’à ajouter un sanity check s’assurant que chaque groupe suffixé « _combined » ne contient que des géométries et sans attributs, ces derniers étant perdu lors du combine.

Cette approche à l’avantage de laisser le graphiste contrôler la livraison de son travail par simple renommage des nœuds, directement dans Maya et c’est le code qui s’occupe de la partie rébarbative, le sanity check s’étant assuré qu’il ne puisse pas y avoir de surprise.

Il y a fort à parier que les personnes au lookdev y trouveront un intérêt en proposant des modifications au modeling, voir, en les faisant elles-mêmes. :laClasse:

Cette approche par contrat implique de rester aussi simple que possible. La raison est que le graphiste peut se débrouiller tout seul en cas de problème sans que les outils d’automatisation ne soient un obstacle à la compréhension. Un réflexe que peuvent avoir les TD qui ne sont pas graphistes de formation est de trouver dommage de limiter le graphiste à ses propres compétences, là ou des supers outils, tout d’héritage de classes vêtus, pourraient l’aider à une échelle supra luminique. C’est vrai, jusqu’à ce que les TD deviennent les seules personnes à comprendre le travail des graphistes. Si vous ne laissez qu’un contrôle partiel à ce genre de contrat, vous « prenez » l’autonomie du graphiste sur des taches qui devraient rester sous sa responsabilité et que, par conséquent, il doit parfaitement comprendre, en vu de l’assimiler en tant que méthode. Vous voyez que vous êtes sur la bonne voie quand les graphistes développent de nouvelle façon de travailler tout seul, en s’appuyant sur ces « contrats ». Pour cela, il faut que ces derniers soient simples. :petrus:

Dans un pipeline, certaines choses sont humainement infaisables, c’est là-dessus que votre cerveau doit être créatif. Accélérer le travail du graphiste implique de se mettre sous sa perspective. Ce sont des erreurs d’estimation qui peuvent coûter cher. :neutral:

Il est plus facile de faire tout ça via des jobs d’export Alembic, car vous n’avez pas à mettre une logique de « retour à l’état précédent » une fois la scène modifiée ; vous ouvrez, vous modifiez, vous exportez, vous quittez.

Mais ne nous arrêtons pas en si bon chemin…

Les close up

Au même titre qu’une version basse définition, il peut arriver que des projets nécessitent des versions « high » (ou close up). Les toits des bâtiments par exemple. Vous pouvez « résoudre » la hiérarchie du graphiste suivante :

Vous pouvez procéder de plusieurs façons. La méthode bourrine consiste à tout exporter dans un seul Alembic. C’est l’assembleur (ou le graphiste au rendu) qui s’occupera de cacher l’un ou l’autre au besoin (via une petite expression par exemple), la version close up est caché par défaut.

Disons que c’est mieux que rien, mais ce n’est pas comme ça que l’on brille en société.

En effet, il est dommage de se trimbaler de la géométrie close up dans tout le pipeline, même si cette dernière est cachée. Il nous faut donc procéder à deux exports. :reflechi:

Vous pouvez proposer un contrat en deux clauses :

Le résultât d’un export standard sera :

En celui d’un export close up sera :

Pourquoi ne pas avoir renommé « toiture_closeUp » en « toiture » ? Ce serait, en effet, plus logique, mais, dépendant de votre façon de gérer les assignations, cela risquerait de couper l’herbe sous les pieds du lookdev qui peut avoir besoin de faire la distinction entre l’objet « toiture » et le contenu de « toiture_closeUp ».

Mais ça, c’est quand votre logiciel de lookdev c’est du caca (ou que vous ne savez pas l’utiliser :gniarkgniark: ).

Vous pouvez contourner ce problème en s’appuyant sur les rangées des UDIM :

Ainsi, vous gardez le contrôle via textures, indépendamment de  la hiérarchie. Gardez toutefois à l’esprit que compenser l’accès à la géométrie par des UDIM implique que la moindre retake de lookdev nécessitera un export de texture, ce qui amène à des situations qui défient l’entendement : Là ou diminuer le spec ou la diffuse pourrait se faire en un override sur l’objet en question, vous devez ouvrir votre projet Mari/Designer/Toshop, faire la modification et exporter avant de pouvoir tester. La limite entre une gestion par override et une gestion 100 % texture est un sujet à part qui mériterait son propre billet, mais je vais fermer la parenthèse. :dentcasse:

Si votre logiciel de lookdev vous permet de faire la distinction d’assignation entre « toiture » et « toiture|poutre17 », alors il ne faut pas se priver et faire en sorte que la version close up prenne la place de la hiérarchie originale :

Comme ça, si le rendu fait une expression pour modifier « toiture », cela fonctionnera, que le modèle soit en close up ou non.

Mais, bordel de m* :injures: , on peut aller encore plus loin !!! :grenadelauncher:

Le merge de hiérarchie

Si vous avez Katana, vous pouvez opérer un merge exclusif entre deux hiérarchies. Si A est la hiérarchie standard et B la hiérarchie close up, vous pouvez demander à Katana de « superposer » les hiérarchies en choisissant la hiérarchie B en cas de conflit.

Ça veut dire que votre Alembic de close up ne contient plus que les nœuds ayant précédemment le suffixe « _closeUp », sans les géométries standards, car la hiérarchie de close up est tout le temps combiné (mergé) à la hiérarchie standard.

Je ne sais plus du tout comment on fait ça par contre, va falloir me croire sur parole. :slow_clap:

Autre point important qui rejoint le sujet des combines,

Sans aller jusque-là, je pense avoir pas mal poussé le truc pour vous donner matière à réflexion. L’objectif étant toujours de rendre les graphistes autonomes tout en leur proposant des « outils » (ce n’est peut-être pas le bon mot) qui correspondent à ce qu’ils font.

Dans tous les cas

Quelle que soit la méthode utilisée, tout ceci demande un travail supplémentaire, et avec les moteurs de rendu actuel, ce n’est pas toujours justifié.

Faire l’économie de la vérification de ce qui sort des tuyaux peut se payer cher plus tard. Il vaut mieux vérifier en amont (via tournettes) si les données générées sont propres et « rendent » bien, mais c’est encore un temps à passer à regarder des assets. Les LOD sont des objets internes à la fabrication. Jouez pas au con et ne présentez pas ça au réal…

Note sur le transfert d’UV

Là, on passe en manuel ! Donc nous allons parler de techniques à ajuster au cas par cas.

C’est une technique a priori un peu bancale mais c’est parce que je l’ai vu fonctionner que je vous la partage.

Des studios font faire les UV directement au lookdev au motif, tout à fait justifié, qu’il est le premier concerné par le résultat. Dès lors, vous avez un croisement entre les données générées par vos graphistes et l’ordre dans lesquels vous les produisez : Si le lookdev est censé faire les UV, mais que les LOD sont déjà fait par le modeling, comment assurer que les UV des LOD sont cohérents ?

Par le transfert d’UV pardi ! :banaeyouhou:

Je suis sûr qu’il y a des cas où ça foire (les arbres), mais les résultats sont convaincants si la géométrie des modèles basses définitions correspond à leur homologue en hautes définitions.

Ça nécessite quelques nuances dans l’organisation : La modé ne fait aucun UV, le lookdev qui les prends tous à sa charge. Il est important que la production ait cette information car ça nécessite un glissement des ressources vers le lookdev, ces derniers auront plus de travail par asset et mettront plus de temps à travailler. En revanche vous pouvez séparer le travail intelligemment en laissant le lookdev faire les UV des assets compliqués (personnages, bâtiments de premier plan, etc.) et laisser le modeling faire les UV d’asset de second plan (props, etc.).

Vous en voulez encore ? :gniarkgniark:

Le TriPlanar

Celle-là je la sors de Guerilla, mais je suis sûr qu’on peut la déporter sur d’autres moteurs. Le principe est de faire plusieurs rendus de trois caméras (face, côté, dessus). Pour chaque rendu dont on exporte l’albédo, le masque le spéculaire et surtout, la normale. Guerilla permet de faire ça assez simplement (doc ici).

On fait ensuite un shader très simple (Couleur de diffuse + une couche de spéculaire grossière + la normale), une géométrie très simple (des cubes + un toit = 6 faces) et on a un asset très léger qui a un peu de gueule quand il y en a (vraiment) énormément. On est très proche du jeu vidéo (peu de poly, tout dans la texture).

Cette technique marche assez bien sur les bâtiments lointains (genre l’horizon d’un Paris…). Vous devriez pouvoir étendre le principe aux arbres (la vielle technique des plans superposées), mais je n’ai jamais testé.

Les transitions

Si vous utilisez un système qui permet de permuter un LOD suivant la distance et la caméra, et que vous faites ça sur une foule ou un amas (une forêt), il est probable que ce passage d’un LOD à l’autre entraîne un « saut » inesthétique entre les changements de LOD. :perplex:

Si vous avez la main sur le mécanisme de distribution, vous pouvez définir une zone, assez large dans laquelle les LOD sont mélangés au prorata de la distance. Admettons que vous voulez que la transition se fasse entre 100 m et 200 m. Pour un arbre à X m, vous calculez un random (entre zéro et un) multiplié par le ratio de X par rapport à 100 m et 200 m :

ratio = (X-100)/(200-100)
if ratio <= 0.0:
    # high resolution
elif ratio > 1.0:
    # low resolution
else:
    # pseudo random choice
    if random * ratio <= 0.5:
        # high resolution
    else:
        # low resolution

La documentation du nœud le setRange. Je la regarde à chaque fois que j’ai un doute sur l’équation.

Faire une transition de LOD implique tellement de choses qu’il vaut mieux s’en passer autant que possible. Si votre plan est animé ? Tentez de gérer le tout en haute définition. En effet, le grain que vous souhaitez chasser via vos LOD (c’est le but après tout) risque de ne jamais être visible du fait du mouvement de caméra.

Si vous pouvez les éviter, alors faites-le. :papi:

Subdivision et displacement

C’est tellement basique, mais je soupçonne que les logiciels permettant de gérer des lookdev en masse et le fait que les path tracer mangent du poly nous pousse à ne trop penser à la subdivision est au displacement. Pourtant, ça ne coûte pas grand-chose de couper sa subdivision et son displacement sur tout ce qui est éloigné ou peu visible de la caméra. Vous pouvez chercher toutes les méthodes de LOD du monde, si vous ne faites pas ça, ça ne sert à rien. :redface:

Les poils

Et nous arrivons à la raison pour laquelle je voulais vous faire ce second billet : J’avais oublié de vous parler des poils (fur/hair/Yeti/XGen, j’utiliserais le mot poil pour désigner cet ensemble). J’y vois deux gros cas de figures : Les poils des personnages principaux et les poils « statiques » (par arbre, par buisson, etc.).

Les poils « statiques »

Ils sont moins fréquents, mais le plus simple à gérer, car vous les considérez comme de la modé : Vous les générez une seule fois pour votre asset et vous l’instanciez comme une géométrie. Comme sortir un LOD de poil est assez facile, vous pouvez générer plusieurs versions et laisser les graphistes afficher l’une ou l’autre, l’idée étant d’éviter de générer ces poils par plan et par asset.

J’ai vu des graphistes utiliser cette méthode pour des buissons et arbustes d’un décor visible sur peu de plans. Plutôt que de s’embêter à faire une modélisation dédiée (quand on sait à quel point placer des feuilles est casse-pied), le superviseur a proposé de faire une géo de branche, instancié de gros poils dessus, cuire ça (via les ghostdata de Guerilla) puis l’instancier. C’est malin et terriblement efficace. Car ils ont pu faire plusieurs variations de formes de buisson assez différentes (rendu totalement différente par les couleurs du lookdev), mettre ça des milliers de fois dans un décor et ça tournait sans trop de difficultés.

Je vais aussi vous parler d’un problème difficile à déboguer. Il est peu probable que cela vous arrive, mais ce dernier ne sautant pas aux yeux, je trouve intéressant de vous en parler : Suivant le système de poil que vous utilisez (ici, Yeti, mais je pense que d’autres pourraient avoir le même problème), il est possible de l’utiliser pour générer la végétation de tout un décor. Votre moteur peut se faire avoir en assignant une bounding box unique à tout ce que la procédurale génère. En pratique, l’impact est négligeable, car les moteurs sont rapides. Mais il est toujours difficile de gérer tout un décor et les graphistes peuvent être tenté de faire leur dispersion d’éléments en couches (cailloux au sol, vielles branches, buissons, arbustes et enfin, arbres), voir par section du décor. Si vous avez six couches, vous avez six bounding box de la taille du décor qui vont être questionné inutilement pour chaque rayon. Et là ça commence à se sentir sur les performances.

Encore une fois, il est peu probable que vous n’ayez jamais à gérer un tel problème, mais c’est aussi un rappel à garder ses bounding box cohérentes.

Les poils « dynamiques »

Voila pour les poils statiques. Les poils dynamiques sont plus compliqués à gérer. Compliqués parce qu’ils sont souvent sur les personnages principaux, ce qui en nécessite beaucoup, à cela s’ajoute un shading complexe. Ces plans rentrent dans la farm comme un barbare dans un couvent de nonnes.

Je pense qu’il n’y a pas de solutions qui marchent à tous les coups, mais on peut s’éviter du sang et des larmes en gardant quelques réflexes dont je vais essayer de faire le tour.

Ce qui prend de la mémoire, c’est la géométrie, mais il est important de ne pas s’arrêter au nombre de poil, car la complexité de chacun peut drastiquement peser dans la balance.

Pour chaque poil:
    Pour chaque point par poil (3 minimums pour une courbe):
      la position (3 float) * step de motion blur (2 minimums)
      la couleur (3 float)

Il est difficile de donner une équation parfaite, car les moteurs ont différentes stratégies que vous pouvez, plus ou moins, contrôler. Par exemple, vous pouvez (ou non) avoir des step de motion blur pour la couleur, voir ne pas avoir de couleur du tout, ou une seule couleur par poil, etc.

De plus, vous pouvez activer la compression ce qui influe encore sur le résultat. Pas de secret, il faut tester.

Pour ce qui est du temps de rendu, c’est le shading qui est le plus influant et autant on dispose de marge de manœuvre avec la géométrie (couleur, step de motion blur), autant le shading est plus capricieux car visible et toute modification visuelle risque d’entraîner des discussions difficiles. Si vous êtes superviseur, vous avez un rôle à jouer. Ne laissez pas vos graphistes les gérer seul, car ils peuvent baisser les bras et vous prenez le risque de partir en production avec des choses inrendables.

En vrac :

D’une manière générale, vous devez maîtriser les shaders que vous utilisez en production, c’est encore plus vrai en ce qui concerne les shader de poil pour éviter de s’arracher les cheveux.

Croyez-le ou non : J’ai créé ce second billet avec l’ambition de vous parler de l’automatisation de la densité des poils au plan, mais le fil de mes développements lui ont donné une taille conséquente et ce sujet me semble suffisamment complexe pour mériter son propre billet, je vais donc m’arrêter là et je vous dis à bientôt !

Aperçu du concept de LOD

dimanche 4 août 2019 à 16:44

lod_jeu_de_motBonjours, dans ce billet je vous propose de faire le tour de ce qu’on entend par LOD (Level Of Detail), en quoi ça consiste, à quoi ça sert, quand faut-il l’utiliser et quand vaut-il mieux s’en éloigner.

Vous vous rendrez compte que derrière ce concept simple se cache des réalités techniques assez nuancées. :reflechi:

La question des LOD est récurrente dans les différentes disciplines liées à l’informatique graphique. Le principe est de décomposer la complexité de ce qu’on souhaite visualiser (souvent, de la géométrie) dans le but d’accélérer son calcul/affichage.

Et comme ce concept peu sembler simple à comprendre, il peut apparaître comme une solution séduisante quand tes rendus n’avancent pas et que ta prod prend l’eau :

Fais des LOD

Les différents types de LOD

Le type de LOD le plus connu est la diminution de la géométrie de l’objet.

Ce mécanisme est très utilisé en jeu vidéo où l’on privilégiera plutôt des grosses textures de normal maps pour l’ajout de détails plutôt que des triangles. En effet, moins il y a de triangles, plus la rastérisation est rapide. Les performances des GPU tombent également quand la taille du triangle à calculer est plus petite que la taille du pixel.

Cette méthode oblige le stockage d’une seconde géométrie (voir plus) en mémoire, là ou stoker des milliers d’instances d’arbres en haute définition ne pose plus de problèmes à nos moteurs. C’est un énorme désavantage, car le switch entre la version haute et basse géométrie doit se faire avant le rendu pour pouvoir être efficace.

Il y a des dizaines de façons de gérer ce type de LOD la « bonne » va grandement dépendre du degré de flexibilité requise par la production et il est difficile de rester générique sur ce problème.

Le shading

Une autre façon de faire du LOD consiste à diminuer le shading en fonction de la distance et/ou du nombre l’objet à rendre. Cela peut être une diminution des spéculaires afin d’éviter que des samples de hautes lumières ne sois récupérés par le raytracer pour des petits objets au fond le l’image (des cheminées/antennes métalliques sur des plans large de villes) :

Voici un plan magnifique de villes. Quand on regarde ce qu’il y a dans un pixel, on peut se rendre compte que de nombreux détails apparaissent, ici, neuf samples sont lancés (3 × 3) mais l’un d’entre eux vient taper l’antenne qui a un haut spéculaire et une très forte valeur. Même une fois moyenné, la valeur du pixel restera très importante. Si vous avez un problème de la sorte, il y a fort à parier que le problème vient de votre shading, mais quand on a passé 3 jours à éclairer son plan et que quelques mouches cassent les pieds, on prend la solution la plus simple : Désactiver le spéculaire sur l’antenne à partir d’une certaine distance. :grenadelauncher:

Ce type de LOD est vraiment mis à contribution dans le rendu en lancé de rayon où quelques samples peuvent récupérer des valeurs très disparates comparé à leurs voisins du fait d’objets denses en petites géométries et lointains, ce qui occasionne du grain. Idem pour le SSS/bump/normal map qui peut-être volontairement désactivé sur les objets lointains.

C’est au cas par cas, suivant ce qu’on cherche à rendre, sachant que les moteurs gèrent parfois les optimisations de shading en interne, la logique étant toujours de diminuer la variation (variance) des samples en vu de diminuer le grain :

Sur le graphique de droite, la moyenne sera beaucoup moins « stable » suivant les pixels.

Les mipmaps

L’autre mécanisme utilisé à la fois en jeu vidéo et en rendu est l’utilisation de mipmaps. Ce mécanisme est tellement répandu qu’il est souvent géré directement par le moteur de rendu sans manipulations explicite de l’utilisateur. En pratique, vous utiliser l’outil maketx (render --buildtex sous Guerilla) pour convertir les textures dans un format prévu pour le moteur (souvent en .tex). L’outil se charge de générer les mipmaps et le moteur ira piocher à la profondeur nécessaire aux vues de la distance et des surfaces (flous ou nets) parcourues par le rayon. Plus d’informations ici et ici.

Une dernière méthode assez impressionnante mais très compliqué à mettre en place : Le Stochastic Pruning. Je vous invite à regarder le PDF, il est très bien illustré (comme tous les PDF de Pixar :siffle:).

Le principe ne peut fonctionner que sur des objets composé de petits objets assez similaires. Les arbres sont un parfait candidat et c’est une (la ?) technique de simplification par excellence des arbres dans le jeu-vidéo. Le fonctionnement est très bien expliqué dans ce billet. Je vous le traduis ici :

  1. Construisez votre objet de N éléments (les feuilles dans le cas d’un arbre, habituellement représenté par des quads).
  2. Ranger les éléments dans un ordre aléatoire (Une manière robuste de le faire consiste à utiliser le mélange de Fisher-Yates).
  3. Calculez U, la portion d’éléments à rendre en se basant sur la distance de l’objet.
  4. Rendez N*U éléments « non-taillé » (unpruned) avec une aire mise à l’échelle par 1/U.

Une petite vidéo du résultat (Rendu rapproché d’un buisson alors que la camera se recule). Et si vous voulez encore plus d’images, c’est par ici.

Comme je le disais, ce système est assez difficile à mettre en place, car il nécessite une relation forte entre le moteur et ce qu’il rend. Je soupçonne qu’il tende à résoudre un problème apparaissant principalement sur un REYES (Vieux Renderman) qui, lui aussi, pédale quand le nombre de triangles par pixel augmente, et à ma connaissance, seul Pixar et Weta l’ont utilisé (sur Cars, Ratatouille et Avatar). Pour être honnête, je pense que cette technique est désuète pour les path tracers (j’explique plus loin pourquoi) mais elle m’a toujours très impressionné alors je partage. :joue:

L’env ball

Celle-là, vous l’avez peut-être déjà utilisée. C’est la bonne vielle technique qui consiste à calculer une env ball à l’endroit ou se trouve le personnage et d’exclure ce dernier de l’illumination du décor pour ne l’illuminer qu’avec l’env ball (ce qui diminue la variance des samples, et donc, le grain) :

À gauche, le plan. En haut, à droite, on calcule l'env ball (en bleu) à la place de la tête du personnage. En bas, à droite, on illumine uniquement le personnage (en rouge) avec cette env ball.

Cette technique est très efficace sur les plans où le centre d’intérêt est complexe à sampler (SSS dans le cou, cheveux, etc), où il ne bouge pas trop et ou la zone de contact entre l’objet à illuminer et le reste est caché (personnage qui parle au premier plan). Ça fait un certain nombre de contraintes, mais si vous cochez toutes les cases, vous êtes bon ! :banaeyouhou:

Dernière contrainte : Comme le personnage est illuminé par une seul light, l'env ball, certaines AOV seront difficile à obtenir.

Les harmoniques sphériques

J’hésitais à vous en parler, car elles ne sont plus vraiment utilisées en lancé de rayon, mais le principe s’apparente un peu à celui expliqué précédemment. L’idée est de calculer des minis env balls (un terme plus adapté serait plutôt light probes) un peu partout dans un décor. De cette façon, on stocke l’illumination à plusieurs endroits et on interpole en fonction de la distance entre chaque env ball.

Sauf qu’on n’appelle pas ça harmoniques sphériques pour rien… La compréhension du mécanisme nécessitent de bonnes bases en mathématique, mais le principe, plutôt qu’une texture, est d’utiliser des coefficients désignant, grosso modo, la couleur suivant des directions. Voici une image honteusement tirée de ce très instructif billet :

Comme vous vous en doutez, c’est très utilisé en jeu-vidéo, mais, comme les env balls, la gestion des zones de contact nécessite son propre mécanisme.

Les LOD et la production

Comme tout mécanisme que vous souhaitez utiliser dans vos productions, son fonctionnement doit avoir une réalité technique ou vos graphistes risquent de se battre contre leurs outils pour faire ce que vous avez décidé.

Le truc qu’un responsable (production et supervision) doit savoir, c’est que soit l’usage des LOD est exceptionnel (c-à-d. au cas par cas) et c’est la compétence du graphiste qui prend le relais. Dans ce cas, l’utilisation de LOD est justifié et son usage est adapté, car la portée de sa mise en place est connu ; eg. un simple plan. Les graphistes qui maîtrisent différentes méthodes et savent gérer leurs LOD au plan sont du pain béni pour la fabrication, surtout quand le projet est complexe et que le support est congestionné par des demandes générales. Soit le projet implique une utilisation des LOD plus fréquente/constante, ce qui implique de l’automatisation (le mot magique des incantations occultes) et les discussions sur comment ils doivent être gérés (les « choix techniques » au fond) doivent être mené entre la production, la supervision et la technique. Le but étant d’anticiper les besoins et d’éviter de se retrouver, plus tard, dos au mur ; « Mais on ne peut pas faire ça ? », « Non, tu ne l’as jamais demandé… », « Mais enfin, c’est évidant ! ».

Bref, c’est un sujet complexe alors soyez pro. :hehe:

Conclusion

Voici pour ce billet qui ne répond, finalement, pas à grand-chose, mais que j’espère instructif. :smileFou:

:marioCours:

mrViewer, MP4 8 bit, OpenColorIO et Rec. 709

samedi 27 juillet 2019 à 12:17

Si vous utilisez mrViewer, vous avez peut-être déjà eux des problèmes de lectures de fichiers MP4 en Rec. 709. En effet, mrViewer lit les MP4 8 bits en sRGB par défaut.

Il existe plusieurs façons de corriger le problème. On va essayer de faire le tour dans ce billet. :reflechi:

Générer un fichier MP4

Le logiciel couramment utilisé dans les pipelines pour générer des fichiers vidéos est FFmpeg. Derrière ces quelques lettres peu sexys se cache un monstre du logiciel libre.

Se lancer dans l’écriture d’une ligne de commande FFmpeg s’apparente à calligraphier un Flaubert; le doute vous assaillie rapidement. :petrus:

Voici une ligne de commande qui convertit une séquence EXR en MP4 :

ffmpeg -colorspace bt709 -color_primaries bt709 -apply_trc bt709 -i ma_sequence.$04d.exr -f mp4 -pix_fmt yuv420p -colorspace bt709 -color_primaries bt709 -color_trc bt709 ma_sequence.mp4

Les arguments avant la séquence d’entrée (avant « -i ») spécifient les paramètre de la séquence EXR :

Le dernier vient demander à FFmpeg d’appliquer une transformation de couleur vers bt709. Votre EXR, en linéaire, est donc convertis en Rec. 709.

Notez que FFmpeg utilise le nom des standards. Si cela ne pose pas de problèmes pour BT/Rec/ITU 709 (il y a 709 dedans…), sachez que pour du sRGB, il faut utiliser la valeur « iec61966_2_1 », IEC 61966-2-1 étant le nom du standard définissant sRGB. Plus d’informations ici.

Dans le cas des paramètres de sortis (après « -i »), c’est presque identique :

Le dernier explicitant, ce coup-ci, la transformation de couleur utilisé dans l’image.

Si vous utilisez ffprobe, vous devriez obtenir quelque chose qui ressemble à ça :

$ ffprobe ma_sequence.mp4
...
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'ma_sequence.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf57.83.100
  Duration: 00:00:03.28, start: 0.000000, bitrate: 3995 kb/s
    Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, bt709), 1024x540 [SAR 1:1 DAR 512:270], 3992 kb/s, 24 fps, 24 tbr, 12800 tbn, 48 tbc (default)
...

On voit donc que le flux vidéo est en bt709.

Au passage, durant l’écriture de ce billet, je suis tombé sur le site de John Van Sickle qui compile des builds de FFmpeg pour Linux, merci à lui. :sourit:

Comportement de mrViewer

mrViewer s’appuie sur OpenColorIO avec le profile nuke-default qui contient les informations de la plupart des espaces colorimétriques de notre industrie.

Pourtant, quand on ouvre notre MP4 8 bits, mrViewer ne semble pas s’intéresser à ce qu’il y a dedans.

[img] ma_sequence.mp4 - Got colorspace 'sRGB' from bitdepth 8 bits as default

Mais d’où sort-il son sRGB ? :bete:

Quand on lit les préférences utilisateurs de mrViewer (Dans .filmaura/mrViewer.prefs sous Linux), on trouve ceci :

[./ui/view/ocio/ICS]

8bits:sRGB
16bits:
32bits:
float:

Une première méthode consiste donc à remplacer « sRGB » par « rec709 » directement dans ce fichier.

Bien que faisable, ce n’est pas forcément optimal : Pouvoir déterminer le profile colorimétrique suivant ce qu’on cherche à afficher (playblast, séquence d’images EXR/JPEG, texture) nécessite de devoir modifier ce paramètre à chaque fois qu’on lance mrViewer. :pasClasse:

La question reste donc en suspend : Où mrViewer définit-il sRGB comme profile colorimétrique des fichiers 8 bits par défaut ? :perplex:

On pourrait envoyer un mail à Gonzalo Garramuño (qui maintient ce logiciel et le fait évoluer depuis presque dix ans !!!), mais quand on a un égo disproportionné comme le mien, on prend le code, on cherche le message d’erreur dedans (« Got colorspace ») et on le lit. :grenadelauncher:

Après quelques minutes de lecture, je me retrouvais avec pas mal de noms de variables que le code semblait utiliser comme si elles étaient initialisés, sans que je ne retrouve jamais l’endroit ou elles étaient définies. :gne2:

Je ne vais pas vous faire un passage en revue du code, mais le morceau qui va nous intéresser est celui-là :

    fltk::Preferences ics( ocio, "ICS" );
    {
#define OCIO_ICS(x, d)							\
        ok = ics.get( #x, tmpS, d, 2048 );                              \
        CMedia::ocio_##x##_ics = environmentSetting( "MRV_OCIO_" #x "_ICS" , \
                                                     tmpS, ok );	\
        uiPrefs->uiOCIO_##x##_ics->text( tmpS );

          OCIO_ICS( 8bits,  "sRGB" );
          OCIO_ICS( 16bits, "" );
          OCIO_ICS( 32bits, "" );
          OCIO_ICS( float,  "" );

    }

Fichtre ! Une macro ! :mechantCrash:

Ce bout de code initialise les préférences; pour chaque appel à la macro OCIO_ICS(), il « exécute » (« duplique » serait plus approprié) un bout de code. Par exemple, le premier appel (« OCIO_ICS(8bits, "sRGB") ») donnera :

ok = ics.get("8bits", tmpS, "sRGB", 2048);
CMedia::ocio_8bits_ics = environmentSetting("MRV_OCIO_8bits_ICS", tmpS, ok);
uiPrefs->uiOCIO_8bits_ics->text(tmpS);

Pour expliquer brièvement, ics est une section des préférences, enfant de la variable ocio, portant de nom "ICS" (Input Color Space je suppose). Cette section se retrouve dans les préférences (« [./ui/view/ocio/ICS] », voir plus haut).

La première ligne prend, dans cette section ics, la variable défini à la clef « 8bits » qu’il met dans tmpS (une variable défini plus haut « char  tmpS[2048]; » pour stocker temporairement une valeur). Si cette clef n’existe pas (dans le cas d’un premier lancement et que les préférences n’existent pas ou sont vides), il prend la valeur « sRGB ». Le 2048 étant juste la limite de le nombre de lettres maximum que la méthode get() peut stocker dans tmpS. Les codeurs C sont habitués à ce genre de choses (et un xkcd pour la route !).

On comprend donc d’où sort la valeur par défaut. :sauteJoie:

La seconde ligne est la plus intéressante, c’est elle qui défini le contenu des variables dont je parlais plus haut. Si la variable d’environnement existe, il renvoie sa valeur. Si elle n’existe pas, il renvoie la valeur de tmpS.

Et ça c’est un peu le cadeau bonus ! À savoir qu’on peut outrepasser la variable par défaut via une variable d’environnement. :smileFou:

Ainsi, votre code de lancement peut définir le contenu de la variable MRV_OCIO_8bits_ICS à la volée au moment du lancement de mrViewer. :laClasse:

Si vous définissez un environnement d’application en Python il suffit de faire ça :

my_env['MRV_OCIO_8bits_ICS'] = 'rec709'

Et, gratification suprême, la confirmation:

[img] ma_sequence.mp4 - Got colorspace 'rec709' from bitdepth 8 bits as default

Conclusion

Comme je ne sais pas comment conclure ce billet, je vous propose un poème de haute qualité :

De Rec 709 et sRGB
Dépendent les remarques inspirées
D’un réal toutefois blasé
Face à l’art pixelisé
De graphistes fatigués.

Il insista, faussement serein :
« Que ce bleu soit plus myrtille… »
Des déjections dans leurs pupilles
D’autres raisons, il n’en voyait point
Comment personne de goût pouvait trouver reluisant
L’exposition d’un tel néant.

D’un seul homme, une graphiste objecta
« Tu ne parles plus à des incultes aisés
Retire donc tes lunettes teintées
Du soleil, s’il y en a,
C’est le fruit de notre labeur que tu vois
Car de tes choix peu avisés
En faire de l’or est notre métier ».

Un TD qui passait nonchalamment
Remarqua la querelle chimérique
Alors calme et rassurant
Il prit contrôle des périphériques
Puis tût la discorde en sélectionnant
Le bon profil colorimétrique.

:marioCours: