Dorian Fevrier's blog - Mot-clé - frJe m’appelle FEVRIER Dorian, je suis infographiste 3D passionné par mon métier, l’informatique en général, l’internet, la programmation et l’évolution de tout ce petit monde. Vous trouverez sur ce blog des tutoriaux, mes coups de cœurs, avis, etc.2024-01-02T23:48:05+01:00FEVRIER Dorianurn:md5:695d9c73474c33ce3dab043823509c4bDotclearLa ferme de rendu (seconde partie) : Les jobsurn:md5:4087ae2ba44dd0f1c4a520ba193b1c972020-05-16T17:48:00+02:002020-05-21T12:33:43+02:00NarannInfographie 3D - Boulotfrjobrenderfarmversion<p>Dans la <a href="https://www.fevrierdorian.com/blog/post/2018/08/18/La-ferme-de-rendu-premi%C3%A8re-partie-Les-jobs" hreflang="fr">première partie</a>, nous avons abordé les différents jobs qu’on peut retrouver sur une ferme de calcul. Ici nous allons parler de certains aspects tel que la notion de <em>prédiction</em> de données, relation aux versions, gestion du temps de chargement des scènes et des versions des logiciels et plugins.</p> <h3>Prédiction des données</h3>
<p>Si vous ne deviez retenir qu’un point de ce billet ce serait celui-là ! :hehe:</p>
<p>Pas forcément connu, ce concept me semble crucial pour gérer les ressources de sa ferme correctement.</p>
<p>Le principe est de savoir exactement quelles données une chaîne de jobs va produire avant de générer les-dites données. Avant de vous expliquer ce que c’est, on va imaginer une gestion de ferme de calcul sans ce principe.</p>
<p><img alt="dessin_prediction" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2020_05_16_render_farm/prediction.png" style="margin: 0px auto; display: table; width: 490px; height: 400px;" /></p>
<p>Si vous débutez en gestion de ferme, vous aurez tendance à exécuter des gros scripts qui s’occupent de tout faire à la volée, sans vraiment savoir sur quoi ils vont tomber :</p>
<ul>
<li>Ouvrir la scène d’animation Maya.</li>
<li>Choper des trucs dedans (persos, props, etc.).</li>
<li>Créer des versions pour les trucs qu’on va exporter en fichiers Alembic.</li>
<li>Exporter les Alembic.</li>
<li>Créer des versions pour les trucs qu’on va exporter en fichiers Yeti.</li>
<li>Exporter les poils Yeti.</li>
</ul>
<p>Dans le cas de Guerilla :</p>
<ul>
<li>Ouvrir le projet Guerilla.</li>
<li>Choper les trucs dedans (Passe de rendu, AOV, etc.).</li>
<li>Exporter des RIB.</li>
<li>Rendre les RIB.</li>
</ul>
<p>Bim ! Comme ça, d’un coup. :grenadelauncher:</p>
<p>Quand tu as trois personnages légers, c’est cool et plutôt rapide, mais le gros souci de cette méthode c’est que la difficulté à gérer vos jobs augmentera de façon exponentielle au fil de la montée en complexité de vos scènes. Bah oui, si à chaque exécution du job ça recrée des versions, tu vas le sentir passer l’export qui plante et que tu dois relancer dix fois…</p>
<p>Vous l’aurez compris, c’est moisi. Mais qu’est-ce qu’on entend par « gérer vos jobs » ?</p>
<p>Déjà leur utilisation au quotidien : Plus un job est lourd et fait plein de choses, moins il est facile pour les équipes de savoir ce qui est sorti et ce qui ne l’est pas. Sans compter que la moindre chose qui pourrait faire planter le job l’oblige à recommencer entièrement (Et nous savons tous que c’est la <em>dernière</em> partie du job d’export du plan le plus long du projet qui plante, toujours…).</p>
<p>Ensuite le test par les TDs. La facilité à tester un job permet de le développer rapidement (mais ça on s’en fout, parce qu’on sait tous qu’un <em>vrai</em> TD ça fait des cubes et des sphères) et surtout, de le déboguer <s>rapidement</s> moins lentement (Et ça, on s’en fout pas…).</p>
<p>« C’est plus facile de faire un gros script monolithique » diront certains, et ils auront raison, mais je répondrais que « c’est plus facile à déboguer » d’en faire plusieurs petits. Le temps économisé à l’écriture du code se paie par la lourdeur des jobs. En gros, la question n’est pas d’aller vite, mais d’aller loin (C’est beau… :petrus: ).</p>
<p>Le pire, c’est si vos jobs créent des jobs à la volée (création dynamique). Encore une fois, tout ceci est pratique (et parfois nécessaire), mais il n’est pas toujours simple de déboguer de tels jobs.</p>
<p>Je le crie sur tous les toits mais l’argument « c’est plus facile à déboguer » est vraiment fondamental dans un pipeline. En particulier sur les jobs de fermes qu’il est difficile de garder rigides tant ils tendent à évoluer au fil des projets en ouvrant tout et n’importe quoi ; scènes d’animateurs™, scène de rendu, versions des logiciels différentes suivant les projets, etc. Sans compter qu’il faut, pour tester, que la ferme puisse exécuter votre code à vous (La méthode la plus simple consistant à faire passer sa machine pour un <em>worker</em> de la ferme).</p>
<p>Bref, pour pouvoir développer, tester et déboguer des jobs efficacement, il faut qu’ils soient le plus légers et rapide possible (à démarrer et à s’exécuter) et ne fasse qu’une seule et unique chose. La capacité de mettre votre ferme à l’échelle avec des projets de plus en plus gros dépends de ça.</p>
<p>Si vous gardez ça à l’esprit quand vous structurez vos jobs, vous allez vite vous rendre compte que le meilleur (seul ?) moyen d’avoir des jobs précis et granulaires, c’est de savoir à l’avance ce qu’il y a dans vos scènes, c’est-a-dire au moment où vous générez le graphe de dépendance de jobs.</p>
<p>En effet, si vous savez <em>avant</em> d’ouvrir une scène d’animation ce qu’il y a dedans, vous allez pouvoir faire autant de job d’export qu’il y a de personnages (ou <em>prop</em>) et ainsi gagner en temps.</p>
<p>Si vous avez trois personnages avec, pour chaque personnage, un export Alembic et une simulation automatique de <em>cloth</em>, vous aurez une chaîne de six jobs :</p>
<pre>
toto_001_abc -> toto_001_auto_cloth
tata_001_abc -> tata_001_auto_cloth
tutu_001_abc -> tutu_001_auto_cloth</pre>
<p>Le petit malin du fond me fera remarquer qu’il suffit d’ouvrir la scène Maya et de générer la chaîne de job depuis cette dernière pour savoir ce qu’il y a à générer. Après lui avoir rappelé que les petits malins du fond dans son genre finissent seul, alcoolique et au chômage, je lui expliquerai qu’on a pas que ça à faire d’ouvrir des scènes Maya et que même si on le fait dans un job qui se charge d’ouvrir les scènes Maya pour générer les jobs, on ne sait pas, au moment où on génère ce fameux job ce qu’il va générer. En faisant ça, vous avez simplement décalé le problème. :reflechi:</p>
<p>Bref, pas le choix, il faut connaître à l’avance, au moment où on fait toute la chaîne de job.</p>
<p>Il y a plusieurs méthodes pour y arriver suivant votre pipeline. Voici quelques pistes :</p>
<ul>
<li>Écrire un fichier à côté de votre scène (<em>side car</em>) contenant les instances présentes dedans ; toto001, tata001, etc.</li>
<li>Sauvegarder les informations en métadonnées de votre version dans la base de donnée que vous utilisez.</li>
<li>Si vous utilisez un pipeline s’appuyant sur des notions de connexions et de nœud, vous pouvez faire un nouveau type de connexion.</li>
<li>etc.</li>
</ul>
<p>Ces méthodes ont en commun de s’exécuter au moment de la publication afin d’avoir les informations nécessaires à disposition.</p>
<p>Cela implique aussi que ces informations doivent être accessible et lu au moment de la création du graph de job.</p>
<h3>Le <em>versionning</em></h3>
<p><img alt="dessin_versioning" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2020_05_16_render_farm/versioning.png" style="margin: 0px auto; display: table; width: 215px; height: 292px;" /></p>
<p>Comme je le disais, un des risques qu’il y a à relancer une chaîne de job non prédictible est de se retrouver avec autant de versions créées que de fois où la chaîne est relancé. Une manière d’éviter ça consiste à créer les versions au moment de la soumission du job. Ceci permet d’avoir une gestion <em>synchrone</em> des versions, mais c’est la ferme, <em>asynchrone</em> par nature, qui va les remplir, puis les « fermer ».</p>
<p>Un problème se pose toutefois rapidement : En effet, quand on crée une version, elle est souvent accessible immédiatement. Ainsi, si vous assemblez un plan dans la minute qui suit la publication d’un asset qui le compose (e.g. l’export d’une animation d’un personnage), le plan assemblé utilisera cette version, mais si la sauvegarde du fichier se fait en décalé (de façon asynchrone, par la ferme), vous risquez d’avoir des problèmes, car l’assembleur de scène ne comprendra pas pourquoi une version existe mais le fichier n’est pas présent et/ou invalide.</p>
<p>Une façon de résoudre ce problème passe par la possibilité d’activer ou de désactiver d’une version. Ainsi, une version crée mais non « fini » (le fichier n’est pas encore là), sera « désactivée » aux yeux du pipeline, et ne sera activé (et ses fichiers mis en lecture seule) qu’une fois la génération du fichier (export Alembic, rendu, playblast, etc.) terminé.</p>
<p>Toute version visant à être « remplis » sur la ferme est donc désactivée à sa création, et un job dédié s’occupe de l’activer quand la chaîne de job est terminé.</p>
<p>Dernier problème (promis) : Si on relance une chaîne de job qui s’est totalement terminé, et dont, implicitement, les fichiers sont « fermés » à la modification, il est donc nécessaire de les rouvrir. Une étape (un job) est donc également nécessaire <em>avant</em> la modification d’une version.</p>
<p>On a donc :</p>
<ul>
<li>Créations des versions : Localement, au moment de la soumission de la chaîne de job (synchrone).</li>
<li>Ouverture/modification/fermeture des versions : Sur la ferme, par chaque worker (asynchrone).</li>
</ul>
<p>Il n’est pas nécessaire de passer par un job dédié pour l’ouverture et la fermeture des versions, cela peut se faire directement dans le code qui s’exécute sur la ferme. À titre personnel, je préfère des jobs dédiés, car la modification d’une version peut nécessiter que deux jobs (ou plus) lui passe dessus.</p>
<p>J’appelle l’ensemble de cette approche « l’allocation de versions » : À un moment <em>t</em>, vous demandez au pipeline des « espaces » (ici, des versions) qui vont être modifié, plus tard, par différentes machines de la ferme qui s’occuperont ensuite de les libérer. Si aucune erreur ne s’est produite lors de l’exécution de votre chaîne de job, les versions créées par la ferme sont remplis, disponibles pour le reste du pipeline et les permissions y sont correctement appliquées.</p>
<h3>Durée d’ouverture des scènes d’animation</h3>
<p>Je vous invite à garder à l’esprit que la durée d’ouverture des scènes d’animation peut être importante. Certains plans de <em>certains</em> projets sur lesquelles j’ai pu travailler mettaient quasiment 30 minutes à s’ouvrir. C’est souvent un problème à résoudre en amont (car c’est l’animateur qui perd du temps), mais comme souvent en production, ce n’est pas possible pour le projet en cours. Si vous exportez vos Alembics directement depuis la scène d’animation, ça veut dire qu’une mise à jour du <em>rig</em> d’un seul personnage nécessite la réouverture complète de la scène que ce soit par l’animateur ou un job de ferme, c’est du gâchis.</p>
<p><img alt="dessin_toutessoukontrolle" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2020_05_16_render_farm/toutessoukontrolle.png" style="margin: 0px auto; display: table; width: 640px; height: 400px;" /></p>
<p>Une méthode pour éviter ça consiste à n’exporter que les courbes d’animation et de reconstruire l’ensemble en partant d’une scène vide.</p>
<p>Il y a plusieurs façons d’exporter des courbes d’animation, je vous en donne deux, en vrac :</p>
<ul>
<li>Exporter les nœuds (principalement les courbes d’animations) seuls en format .mb/.ma ainsi qu’un fichier texte avec les informations de reconnexions de ces nœuds. Cette méthode permet de conserver l’animation originale de Maya, mais nécessite pas mal de développement et ne résout pas le problème des contraintes.</li>
<li>Exporter un fichier ATOM, ce dernier comprenant une version baké des courbes d’animations. C’est ma méthode préférée, car elle permet de n’importer qu’un range précis d’une animation sur un autre range, permettant ainsi de reconstruire une animation en y ajoutant un <em>preroll</em> automatique pour nos amis du cloth. En passant par le bake, cette méthode résout le problème des contraintes qui ne sont plus utiles.</li>
</ul>
<p>Une fois les courbes exportées, vous n’avez plus qu’à ouvrir une scène vide, importer la dernière version du <em>rig</em> et y coller vos courbes pour exporter l’Alembic.</p>
<p>Ces mécanismes, qui, mine de rien, ajoutent des étapes d’export, permettent de gérer des exports complexes car chaque asset est exporté individuellement.</p>
<h3>Un dossier temporaire pour votre ferme</h3>
<p>Il peut être pratique de disposer la ferme d’un système de fichier temporaire partagé par tous les <em>worker</em>. L’avantage immédiat étant de ne plus s’embêter avec des problématiques de pipeline et de version. Vous créez un dossier /farm/tmp/mon_id/ et vous y mettez ce que vous voulez. Les différents jobs liront et écriront à l’intérieur, avec, éventuellement, un job de nettoyage en fin de chaîne.</p>
<p>Un exemple d’utilisation que j’ai souvent observé tourne autour de l’assemblage de scènes :</p>
<ul>
<li>On exporte/assemble les scènes dans leur format de rendu (RIB, ass).</li>
<li>On les rend.</li>
<li>On sépare, parfois, les layers des images.</li>
<li>On supprime les fichiers de rendu ; RIB, ass, voir les images brutes qui ne sont plus utiles (sauf pour du débogage).</li>
</ul>
<p>On pourrait grossir cette liste d’étapes avec du précalcul de fichier (bake d’illumination, simulation et génération de poils) qui vient se faire <em>avant</em> le rendu. Bien que cela se fasse moins qu’il y a quelques années, le précalcul peut se révéler nécessaire pour rendre des plans qui sortent de l’ordinaire ; transitions bizarres, plans séquences, etc.</p>
<p>Les graphistes utilisant Houdini et ayant la responsabilité de plans complexes ainsi que les studios travaillant sur plusieurs projets de natures différentes (e.g. La publicité) s’appuient beaucoup sur ce système.</p>
<p>Plus un plan est complexe et spécifique, plus il est difficile de mettre en place un mécanisme de prédiction poussé. Le temps passé à généraliser une solution étant trop important et/ou le nombre de plan concerné étant trop faible.</p>
<p>Il y a un dernier cas où vous n’avez pas beaucoup le choix : Si la taille de votre ligne de commande est trop importante, vous devez sérialiser les arguments. Ça semble absurde, mais pour m’être déjà retrouvé dos au mur (une ligne de commande dont le nombre d’argument a augmenté au fil des semaines), écrire un JSON dans un dossier temporaire plutôt que d’avoir à repasser sur toute la gestion de la commande (on était en fin de production) peut vous sauver la mise.</p>
<p>Passer par un dossier partagé a le mérite de proposer une solution simple à comprendre pour tout le monde. On est clairement sur une approche artisanale de la ferme, tout en gardant une distinction claire entre ce qui est dans le pipeline et à l’extérieur.</p>
<p>Malgré ça, ne pas avoir de suivi granulaire des données générées par la ferme peut entraîner son lot de complication. Ainsi, une ferme multi-site devient un calvaire à gérer. Les dossiers temporaires n’étant pas au même endroit, soit vous faites un partage direct (ce qui implique une occupation continue de la bande passante), soit vous calez des jobs de synchro entre les jobs utilisant des machines de sites différents. Dans les deux cas, cela ajoute de la complexité à l’ensemble et il faut peser le pour et contre.</p>
<p>À titre personnel, j’ai pu remarquer que, dès lors qu’il faut synchroniser des choses, avoir un suivi précis est plus efficace : Gardez un suivi des choses à l’échelle du pipeline (asset, version, etc.) pour tout ce que vous pouvez anticiper et ne sortez la solution des fichiers temporaires de ferme que pour les exceptions.</p>
<p>D’un point de vue code, une petite API distribuée aux superviseurs leur évite d’avoir à gérer ça eux-mêmes : Une fonction qui crée le dossier temporaire, renvoi son chemin ainsi que le job chargé de supprimer de dossier en question est un bon début. Pour le nom du dossier temporaire généré par cette API, lui préfixer la date (2020_04_12) permet d’avoir rapidement un coup d’œil sur ce qui ne semble plus nécessaire.</p>
<p>Faites un suivi régulier de la taille de ces dossiers et impliquez les superviseurs qui l’utilisent, ils sont bien plus en mesure de savoir ce qui est nécessaire ou non.</p>
<p>Il va de soi qu’aucun autre script ne doit s’appuyer sur ce dossier. <em>Certains</em> pourraient avoir l’idée de s’en servir comme dossier d’échange dans le studio.</p>
<h3>Nettoyage des jobs</h3>
<p>On en a déjà un peu parlé, mais le nettoyage des jobs concerne plusieurs choses qui peuvent se faire à plusieurs moments du <em>cycle de vie</em> d’un job.</p>
<ul>
<li>Il y a le nettoyage qui se fait immédiatement après le job ou la chaîne de job. C’est celui qu’on fait naturellement quand le job est supposé avoir fini son travail (nettoyage des RIB).</li>
<li>L’autre est plus subtile et se fait au moment de la suppression du job du manager. En pratique, on supprime rarement le job, mais on procède à son archivage. Cela faisant, les fichiers de logs sont également supprimés du système de fichier.</li>
</ul>
<p>La plupart du temps, on aura un job dédié au nettoyage, mais certains jobs managers intègrent le concept de nettoyage en tant que paramètre du job. Cela peut prendre la forme de clef comme <em>clean_dir</em>, <em>clean_file</em>, <em>clean_files</em>, etc.</p>
<h3>Des jobs génériques, ou spécifique ?</h3>
<p>Au moment de l’écriture d’un job, une balance tend à apparaitre : Doit-on écrire un job qui fonctionnera pour tout type de tâche ? Ou doit-on écrire un job qui fonctionnera pour un besoin précis ? Doit-on gérer les choses de façon abstraite ou de façon concrète ? Ma réponse ne va pas énormément vous aider : Les deux mon capitaine !</p>
<p>Prenons un exemple : Vous souhaitez faire les jobs « d’export des personnages ». Exprimé de la sorte, c’est un besoin abstrait. En effet, « exporter un personnage » se fait, à priori, en plusieurs étapes :</p>
<ul>
<li>Exporter des courbes d’animation.</li>
<li>Exporter un ou plusieurs Alembics.</li>
<li>Calculer une ou plusieurs simulations de poils (cheveux, poils).</li>
<li>Baker un ou plusieurs systèmes de poils.</li>
<li>Calculer une, ou plusieurs simulations de vêtements.</li>
<li>Calculer et exporter des simulations au sol (zones de contact pour le FX).</li>
</ul>
<p>J’ai donné une liste aussi exhaustive que possible, mais il est évidant que peu de projets cochent toutes les cases et qu’il n’est pas forcément optimal de faire un export de personnage aussi granulaire que ça (tout dépend de votre budget).</p>
<p>La question de savoir si ont fait des jobs pour « exporter un personnage » ou « exporter un Alembic » se pose. Si on choisit de faire un job « d’export de personnages », vous vous rendrez vite compte qu’il doit être séparé en petits jobs. Ensuite, si on choisit de faire des jobs qui « exportent un Alembic », on risque de buter, plus loin, sur des problématiques <em>spécifiques</em> : Est-ce qu’on exporte un Alembic de la même façon pour un personnage que pour une simulation ? Dès lors, un besoin de faire un job qui « export un Alembic <em>FX</em> » apparaît et il est probable qu’il n’ait pas été anticipé. Dans le pire des cas, vous vous retrouvez avec un département FX complet à qui on a dit que les jobs « d’export d’Alembic » étaient près et qu’il pouvait commencer le travail.</p>
<p>Si tout ceci peut sembler relever du domaine académiques, c’est parce qu’il s’agit rarement de questions qui se posent en début de production, quand on met notre ferme en place, mais plutôt quand la nature de ce que fait votre ferme évolue (milieux de production ou entre deux saisons d’une série). Ces questions, mise de côté, agissent comme un ressort.</p>
<p>La raison pour laquelle j’insiste sur cette distinction, c’est pour que vous soyez en mesure de la « détecter » en production, au risque de perdre du temps en enchevêtrement de paradigmes et/ou discussion avec la production.</p>
<p>Quand des graphistes ou des superviseurs vous parleront, ils ne feront pas cette distinction. C’est vous le codeur, c’est votre travail de la faire et de l’expliquer. Dans notre exemple, si un superviseur pense qu’il va pouvoir exporter des Alembic de FX parce que vous lui avez fait un job d’export d’Alembic sans en définir correctement le contour, vous êtes en parti responsable de cette confusion. En gros, ne « pensez » pas les jobs suivant leur nom, mais suivant ce qu’ils font, et assurez-vous que vous n’êtes pas le seul.</p>
<p>On peut, légitimement, se demander s’il est utile d’avoir des jobs génériques qui font des choses dont la définition n’est pas (encore ?) clair aux yeux de la production. Quand un besoin est « ballant » (comprenez, on sait qu’on en a besoin, mais on ne sait pas si c’est la bonne façon de faire), il me semble que faire un job qui fait « le truc » est la solution.</p>
<h3>Gestion des différentes versions des logiciels sur la ferme</h3>
<p>Suivant la nature des projets que vous avez à sortir, il est fort probable que vous ayez à gérer différentes versions de logiciels et des plugins qui les compose.</p>
<p>Vous pouvez définir des versions « à la machine », mais cela vous prive de puissance de calcul, car vous risquez de ne pas pouvoir utiliser une machine au simple prétexte qu’elle n’a pas la bonne version de Maya d’installée.</p>
<p>On serait donc tenté d’avoir plusieurs versions d’un logiciel, disponible sur chaque machine. Dès lors qu’une machine rend disponible plusieurs versions d’un logiciel, vos jobs doivent pouvoir appeler la bonne version.</p>
<p>Les méthodes pour y arriver sont nombreuses aucune n’est parfaite et leur utilisation dépend beaucoup de l’organisation de votre studio (rien que ça). La plus naïve consiste à appeler l’exécutable d’un logiciel par un nom précis. Dans le cas de Maya, on aurait donc une ligne de commande :</p>
<pre>
<code class="language-bash">maya2019 -c "print 'toto'"</code></pre>
<p>J’ai quelque à priori vis-à-vis de cette méthode. Le principal étant la gestion des versions des plugins. En effet, si vous utilisez un plugin (e.g. Yeti pour Maya), il est probable que vous ayez à <em>glisser</em> de version en cours de production (vous commencez les nouveaux plans avec la nouvelle version, vous finissez les anciens plans avec l’ancienne).</p>
<p>Je rappelle l’évidence : Il est dangereux de changer de version d’un logiciel en cours de production. En revanche, certains bugs peuvent être tellement bloquants qu’ils remettent en question ce dogme. Il peut s’agir de problèmes corrigés dans une version mineure (2.5.0 -> 2.5.1), comme de problèmes plus graves qui remettent en cause l’utilisation de l’outil. C’est une situation qui peut apparaître quand on utilise un tout nouvel outil en production et qu’on réalise, trop tard, que ce qui était prévu de faire avec n’est pas possible à cause d’un bug corrigé dans une version majeure (2.5.0 -> 3.2.0).</p>
<p>À cela vient s’ajouter un dernier point, pratique, qui est le test : Vous êtes en 2.5.0 et que la version 2.5.1 corrige le problème, vous passez tout le plancher en 2.5.1. Plus tard, en production, un problème, différent mais qui semble lié apparait. Vous avez un doute : Est-ce que ce problème apparaissait en 2.5.0 ?</p>
<p>Et là on parle d’un seul plugin, mais certains logiciels sont amenés à faire tourner pas mal de plugins différents. Dès lors, vous voyez une matrice se dessiner : Version du logiciel/Version du plugin A/Version du plugin B/etc. Et si vous utilisez des plugins compilés « maison », c’est encore pire, car vous pouvez être amené à livrer une version plusieurs fois par jour.</p>
<p>Il est tout à fait possible de ne s’appuyer que sur le nom de l’exécutable dans la ligne de commande :</p>
<pre>
<code class="language-bash">maya2019-yeti2.5.0-rigNodeMaison3.6.5 -c "print 'toto'"
maya2019.sp1-yeti2.5.0-rigNodeMaison3.6.5 -c "print 'toto'"
maya2019.sp1-yeti2.5.1-rigNodeMaison3.6.4 -c "print 'toto'"
...</code></pre>
<p>Notez que <a alt="PeregrineLabs Ecosystem github repository" href="https://github.com/PeregrineLabs/Ecosystem">Ecosystem</a> de PeregrinLabs fonctionne un peu suivant ce principe, mais je suis dubitatif quant à la taille de tels lignes de commandes quand vos graphistes deviennent mordu de plugins maisons. Je lui préfère une approche via variables d’environnement :</p>
<pre>
<code class="language-bash">export MYENV_MAYA_VERSION=2019.sp1;
export MYENV_YETI_VERSION=2.5.0;
export MYENV_RIGNODEMAISON_VERSION=3.6.5;
myenv -- maya -c "print 'toto'"</code></pre>
<p>Ici, <em>myenv</em> est un exécutable qui consomme les variables d’environnement commençant par <em>MYENV_</em> pour en faire des environnements dans lequel la commande qui suit « -- » est exécuté.</p>
<p>Bien entendu, les commandes <em>export</em> ne sont pas exécutées par vous, mais envoyé avec le job au moment de la soumission :</p>
<pre>
<code class="language-python">my_env = {'MYENV_MAYA_VERSION': '2019.sp1',
'MYENV_YETI_VERSION': '2.5.0',
'MYENV_RIGNODEMAISON_VERSION': '3.6.5'}
job = {'command': 'myenv -- maya -c "print \'toto\'",
'env': my_env}
farm.submit(job)</code></pre>
<p>Je suis un adepte de cette méthode.</p>
<p>Arrivé ici, je ne peux pas ne pas vous parler de <a alt="Rez github repository" href="https://github.com/nerdvegas/rez">Rez</a>. Je ne l’ai jamais utilisé personnellement, mais j’en ai entendu beaucoup de bien.</p>
<p>Rez résout des environnements via un système de dépendance. La charge cognitive initiale due à son apprentissage est importante, mais il est considéré comme une brique solide d’un pipeline avec une bonne <a alt="Documentation de Rez" href="https://github.com/nerdvegas/rez/wiki">documentation</a>.</p>
<p>La gestion des environnements étant souvent central dans un studio, vous pouvez aussi être tenté d’écrire le vôtre. Ce n’est pas particulièrement difficile à faire (pour tout vous dire, je n’ai encore jamais fait autrement), mais c’est une maintenance en continue. L’avantage étant que ce gestionnaire s’en tiendra à faire <em>exactement</em> ce que vous voudrez, pas plus, pas moins.</p>
<p>Si vous choisissez de le faire en Python, gardez le module aussi isolé et indépendant que possible. S’il vous vient à l’idée de questionner la BDD sur les métadonnées d’un shot pour résoudre un environnement, vous fonctionnez peut-être à l’envers (et si vraiment vous n’avez pas le choix, passez par une variable d’environnement : MYENV_SHOT_NUMBER)</p>
<h3>Data mining</h3>
<p>C’est le sujet sexy du moment et il est difficile d’avoir un avis tranché tant le secteur « tâtonne » à déterminer si le temps de compréhension et d’interprétation des données justifie d’y dépenser de l’énergie.</p>
<p>La première erreur consisterait à utiliser des données de ferme pour répondre à des questions de production : Si vous voulez répondre à des questions de production (e.g. nombre de validation par semaine), analysez des données de production (e.g. Shotgun). En revanche il est très utile de comparer les données de ferme à des données de production, cette dernière influençant la première. Vous serez peut-être en mesure d’y trouver des corrélations intéressantes, voir, surprenantes.</p>
<p>Pour l’anecdote, sur un projet, on avait pu remarquer que plus on se rapproche de la fin du projet, plus le temps de rendu <em>moyen</em> par image diminuait, en parallèle de quelques plans qui voyaient leur temps de rendu augmenter drastiquement. On avait mis ça sur le compte du fait que, la <em>deadline</em> arrivant, les graphistes ne prenaient plus le risque d’augmenter les temps de rendu et forçait sur l’optimisation de leurs scènes pour éviter de les voir « revenir » de la ferme au motif d’un temps de rendu trop long, ce qui, par effet de débordement, diminuait la qualité générale. La pression qui peut apparaître en fin de production peut entraîner, à tous les niveaux de la hiérarchie, une diminution du zèle (que je crois) nécessaire à la sortie de belles images. Les superviseurs et les <em>leads</em> doivent alors se focaliser sur ce qui est essentiel à la qualité d’un plan. C’est d’ailleurs le moment ou le ratio qualité/temps de travail graphiste est le meilleur, les gens ayant pris l’habitude des erreurs à ne surtout pas faire et de ce qui plaît. Quant aux quelques rendus qui explosent ? C’est les laissés pour compte, les rendus qui ont un problème, mais qu’on a plus le temps de régler.</p>
<p>Mais je digresse…</p>
<p>Les données de ferme ne répondront qu’à des questions de gestion de ferme : Alimentation, charge, licence. Ces données sont très utiles pour élaborer correctement des budgets par juxtaposition. Il est, en effet, plus simple de définir les besoins d’une production quand on a déjà une base chiffrée de l’utilisation d’une ferme sur un projet.</p>
<h3>Avoir des <em>logs</em> explicites</h3>
<p>Isoler un job pour reproduire un bogue prend du temps, et ce, malgré le travail effectué pour simplifier cette démarche. Quand un problème apparaît, vous aurez rapidement le réflexe de lire le <em>log</em> du-dis job en le comparant avec les appels à <em>print()</em> de votre code. En faisant cela vous aurez rapidement une idée de <strong>où</strong> votre script a planté. Et si vous avez affiché les valeurs des différentes variables, vous serez potentiellement en mesure de reproduire le problème avec une simple commande.</p>
<p>Il est donc important d’avoir des <em>logs</em> descriptif et suffisamment claires. Il ne faut pas hésiter à être verbeux, car si vous n’affichez que quelques informations, vous risquez de vous retrouver face à un gros bloc de code dans lequel le problème apparaît, sans que vous puissiez être en mesure de déterminer ce qu’il fait.</p>
<p>Je vous invite également à ajouter des informations de temps afin de déceler les écarts qui peuvent se produire entre deux lignes :</p>
<pre>
<code>2018-01-22 10:53:00,500|CMD |INFO|...
2018-01-22 10:53:01,589|CMD |INFO|...
2018-01-22 10:53:01,589|CMD |INFO|...
2018-01-22 10:53:02,351|MAYA|INFO|...
2018-01-22 10:53:02,351|MAYA|INFO|...
2018-01-22 10:53:02,605|MAYA|WARN|...
2018-01-22 10:53:02,605|MAYA|INFO|...</code></pre>
<h3>Conclusion</h3>
<p>J’aurais pu aborder le cloud, mais le sujet est très large et je ne pense pas avoir assez d’expérience pour donner des conseils pertinents.</p>
<p>J’espère que ces deux billets vous auront plus.</p>
<p>À bientôt !</p>
<center>:marioCours:</center>La ferme de rendu (première partie) : Les jobsurn:md5:a7590f03534a62306e3a45057e1e6e982018-08-18T23:49:00+02:002020-05-18T16:21:10+02:00NarannScript et codefrgraphjobpythonrenderfarm<p>L’exécution de tâches (jobs) sur des gestionnaires de tâche (job manger, render manager, farm manager) est courante dans énormément de secteurs liés à l’informatique. Que ce soit la compilation de code, l’exécution et le rapport de tests unitaires, le calcul financier, ça pullule. :popcorn:</p>
<p>Internet regorge d’informations et de <s>bonnes</s> pratiques liées à l’exécution et la gestion des tâches sur des fermes de calcul. Pourtant, beaucoup de ces informations sont trop génériques et passent à côté de certains problèmes de fond, propres à notre industrie. :triste:</p>
<p>Je voudrais, modestement et de façon totalement subjective, faire un retour d’expérience autour de l’organisation et la structure de la ferme de calcul. Notez que je n’affirme pas avoir <em>LA</em> bonne méthode pour gérer les fermes de la façon la plus efficace qui soit, mais j’aborde et commente différentes problématiques que j’ai pu rencontrer.</p> <p>Je vais passer en revu et commenter différentes choses. Je tenterai, dans les parties suivantes, d’établir quelques règles et guides à suivre puis je finirai sur une explication de l’organisation du code. :banaeyouhou:</p>
<h3>Terminologie</h3>
<p>Avant de commencer je dois expliciter certains termes que je vais employer. :redface:</p>
<h4>Job manager</h4>
<p>C’est le cerveau. Il s’agit du « serveur » :</p>
<ul>
<li>Il stocke tous les <em>jobs</em>.</li>
<li>Il gère les dépendances entre les jobs en les envoyant dans le bon ordre et en attendant certains jobs avant d’en exécuter d’autres.</li>
<li>Il communique avec les <em>workers</em> en leur envoyant les <em>jobs</em>.</li>
<li>Il met à jour les statuts (pause/en cours/erreur).</li>
<li>Il gère les limites de licence pour s’assurer que les <em>jobs</em> ont toujours les licences qu’ils exigent.</li>
<li>etc.</li>
</ul>
<p>Le <em>job manager</em> embarque souvent une base de données (PostgreSQL, MySQL, etc.) et ne doit jamais être indisponible pour les <em>workers</em>. :siffle:</p>
<h4>Worker</h4>
<p>« Travailleur » en français. Il s’agit d’un petit programme qui tourne et communique avec le <em>job manager</em> pour recevoir et exécuter des <em>jobs</em>. On exécute souvent un <em>worker</em> par machine de calcul, mais il n’est pas rare d’exécuter plusieurs <em>workers</em> sur une seule machine. Parfois pour des raisons de licences liées à la machine (une machine Yeti, plusieurs exports de <em>.fur</em>), parfois pour des raisons d’équilibre de charge. Ainsi, plusieurs petits <em>jobs</em> qui demandent peu de ressources peuvent s’exécuter en parallèle sur une seule machine.</p>
<p>Notez qu’un gestionnaire de ferme évolué permet de gérer intelligemment les ressources d’une machine et de lancer plus ou moins de <em>jobs</em> suivant les exigences du <em>job</em> (licence, mémoire, etc.).</p>
<h4>Job</h4>
<p>Bien que le terme français « tâche » existe, il fait souvent spécifiquement référence, en production, à la tâche du graphiste (traduction de « <em>task</em> », qu’on peut retrouver dans Shotgun). J’utilise donc le terme de « <em>job</em> » pour faire référence à ce que doit exécuter un <em>worker</em>.</p>
<h3>Exigence des jobs</h3>
<p>Parfois appelé <em>job requirement</em>. C’est un concept qui est présent sous différentes formes dans la plupart des <em>job managers</em> du marché. Le principe est que chaque job contient des informations sur ce qui est nécessaire à son exécution. Plus ces informations sont complètes, plus le job manager pourra gérer au mieux la ferme. Le cas le plus courant est le type et le nombre de licences nécessaire à l’exécution du job.</p>
<p>Voici un exemple d’exigence de job :</p>
<pre>
<code class="language-python">{'requirement': {'license': {'nuke': 1},
{'sapphire': 1}
'cpu_count': '12+',
'memory_min': '8GB',
'memory_max': '64GB'}</code></pre>
<p>Le dictionnaire suivant spécifie que le job :</p>
<ul>
<li>Utilisera une licence Nuke et une licence Sapphire. Le job manager va donc n’assurer que ces licences sont disponibles avant le lancer le job.</li>
<li>Utilisera tous les CPU disponibles mais nécessite au minimum 12 CPU.</li>
<li>Utilisera au minimum 8 GB de RAM…</li>
<li>…mais n’utilisera jamais plus de 64 GB.</li>
</ul>
<p>L’aspect intéressant c’est que certaines informations peuvent aider le job manager à mieux organiser la soumission des jobs. Ainsi, en spécifiant <em>memory_max</em>, vous « garantissez » au job manger que le job ne fera jamais plus de 64 GB. Si, par exemple, vous avez des petits jobs qui n’utilisent qu’un seul CPU vous pourriez spécifier <em>cpu_count</em> à 1 et le job manager saurait que votre job ne va utiliser qu’un seul CPU.</p>
<p>C’est une sorte de « contrat » que vous passez avec le job manager. C’est donc à double tranchant. Si vous lui dites que votre job utilisera 16 GB et qu’il monte à 64 GB, le worker aura des soucis pour l’exécuter. :nannan: Bien entendu, c’est celui qui génère le job qui spécifie ses exigences. Comme ce sont souvent les TD qui codent les chaînes de job, les exigences sont dans le code. :gniarkgniark:</p>
<h3>Job de génération des fichiers de rendu</h3>
<p>Le job de rendu est le type de job le plus courant. C’est souvent celui pour lequel vous souhaitez mettre une ferme de rendu en place. :baffed:</p>
<pre>
Scène de travail -> images rendues
.mb/.gproject | .exr</pre>
<p>Comme ce sont souvent les jobs les plus longs, il est préférable de séparer la génération des fichiers de rendu (<em>.rib</em>, <em>.ass</em>, etc.) et le rendu de ces derniers en deux jobs distincts :</p>
<pre>
Scène de travail -> fichiers de rendu -> images rendues
.mb/.gproject | .rib/.ass | .exr</pre>
<p>La génération des fichiers de rendu consiste à ouvrir une scène (Maya/Guerilla/Katana/Poalobra) et à faire les exports des fichiers de rendu (<em>.rib</em>, <em>.ass</em>, etc.), sur un <em>range</em> d’images donné.</p>
<p>Dans le cas de Guerilla, on ouvre le <em>.gprojet</em>, on exporte un <em>range</em> d’images (paquet de 10 par exemple : 101-110, 111-120, etc.) en <em>.rib</em>.</p>
<p>Pourquoi ne pas exporter toutes les images plutôt que par petits paquets ? Tout simplement parce que le but est de garder la durée de vos jobs aussi consistante et contrôlable que possible. :laClasse:</p>
<p>Si on exportait « toutes les images », on aurait des temps très aléatoires suivant les plans du fait de la disparité de leur nombre d’images (certains plans font une trentaine d’images, d’autre plusieurs centaines). Si vous avez un bug sur la dernière image sur votre plan de 900 frames (oui parce qu’on a toujours un problème sur la PUTAIN de dernière image du plan le plus long du projet ! :injures: ), il vous faudrait tout relancer. Et si l’export dure 30 minutes, vous allez passer des heures à le déboguer. :casseTeteMur:</p>
<p>Diviser en paquet permet de rendre le temps humainement gérable.</p>
<p>Dans un monde idéal et instantané, on n’exporterait qu’une seule image par job. Ceci permettrait d’avoir le job de rendu de l’image en dépendance directe sur le job de génération de fichier de rendu. Mais ce n’est pas possible, il y a un temps minimum nécessaire à l’ouverture de l’application et de la scène. :septic:</p>
<p>La taille des paquets dépend de la lenteur de l’ouverture de la scène (Les scènes Maya peuvent être lentes si elles ne sont pas « vides »). Plus la scène est lente à ouvrir, plus il est intéressant d’augmenter la taille du paquet pour contrebalancer le temps d’ouverture de la scène.</p>
<p>J’utilise, en général, des paquets de 10 images pour Guerilla et 20 pour Maya. Mais c’est vraiment suivant la taille du projet. :redface:</p>
<p>Notez qu’il n’est pas toujours possible et/ou faisable d’avoir des jobs de génération de fichiers de rendu sur votre ferme de calcul. Par exemple, on peut vouloir rendre des ranges d’images directement depuis son logiciel favori (Maya à une époque et je suppose Blender, Cinema 4D, etc.). Cette méthode, plus « directe » peut se révéler chaotique dès lors que des problèmes apparaissent. Il faut être vigilant. :papi:</p>
<h3>Job de rendu</h3>
<p>C’est sûrement le type de job le plus long de la ferme de calcul. Je conseille fortement de n’avoir qu’une image par job.</p>
<p>Les informations importantes de tels jobs se situent dans le <em>log</em>. Certains <em>job manager</em> permettent de <em>parser</em> le <em>log</em> à la volée pour remonter des informations de façon claire (temps de rendu, fichiers manquants, etc.) voir, arrêter le job et gagner un temps précieux.</p>
<p>Si les temps de rendu sont importants (12 heures et plus) et si votre moteur de rendu le permet, il peut être intéressant d’avoir accès à l’image « en l’état » avant la fin du rendu (au bout de 10 minutes par exemples) ceci permet de pouvoir s’assurer qu’aucun truc grossier n’est présent/absent du rendu et d’éviter de perdre des heures inutiles. :reflexionIntense:</p>
<p>La technique du pauvre consisterait à lancer deux jobs de rendu utilisant les mêmes fichiers de rendu mais avec des arguments de la ligne de commande différents :</p>
<ul>
<li>Un avec une résolution faible et un faible nombre de samples ne dépassant pas 10 minutes.</li>
<li>Et un autre, le vrai de plusieurs heures.</li>
</ul>
<p>Mais cela reste très imparfait, car quand le « vrai » rendu peut rarement partir dans les mêmes conditions que le premier. Il peut ainsi se retrouver sur une autre machine. Le meilleur reste donc d’utiliser l’image du rendu en cours. Si le moteur ne le permet pas, vous pouvez lancer une seule frame, au milieu du plan (ou une frame toutes les 5 frame par exemple) avec une priorité plus élevée. Vous n’attendez ainsi pas la fin complète des images pour en vérifier quelques-unes. Mais cette technique aussi est imparfaite, car vous ne voyez pas l’ensemble du plan, juste quelques images.</p>
<p>Dans les deux cas, cela vous permet de générer des vidéos de « pré-rendu », mais forcement, avec tout cela, le suivi du rendu est alourdi. :septic:</p>
<h3>Job d’export Alembic</h3>
<p><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/logos/Alembic.png" style="margin: 0 auto; display: table;" />C’est sûrement le second job le plus courant : L’ouverture de scène puis l’export de son contenu au format Alembic.</p>
<p>La méthode bourrine consiste à ouvrir la scène et tout exporter dans un gros fichier <em>.abc</em>.</p>
<pre>
Ouverture de la scène Maya -> Export Alembic
.mb | .abc</pre>
<p>C’est lourd et complètement con. :vomit:</p>
<p>En pratique, il est plus pertinent de séparer les exports suivant leur nature. Ainsi, s’il y a 3 personnages dans la scène, l’animation de chaque personnage est exportée dans son Alembic respectif. Il est ainsi plus rapide de réexporter l’animation d’un seul personnage lors d’une <em>retake</em>.</p>
<p>Le plus intéressant étant encore de savoir ce que contient le plan à l’avance en vu de générer un job par personnage. :dentcasse:</p>
<h3>Job d’export ATOM</h3>
<p>À l’instar du rendu, plutôt que de passer directement par un job d’export Alembic, on pourrait passer par une étape intermédiaire consistant à exporter l’ATOM des contrôleurs d’animation, puis un second job, celui exportant l’Alembic, partirait d’une scène vide, amènerait une version du rig (la dernière par exemple) appliquerait l’ATOM sur les contrôleurs d’animation puis ferait l’export.</p>
<pre>
Rig
|
v
Scène de travail -> fichiers ATOM -> Maya -> Export anim
.mb | .atom | | .abc</pre>
<p>Mais on pourrait aller encore plus loin ! :onSeFendLaPoire:</p>
<p>Pourquoi ne pas en profiter pour faire un <em>preroll</em> automatique pour nos amis du <em>cloth</em> et du <em>FX</em> ?</p>
<ul>
<li>Une clé, en <em>T-pose</em>, à l’image 1.</li>
<li>Une clé, toujours en T-pose, mais avec le personnage placé à sa position de départ à l’image 50.</li>
<li>Un déplacement du personnage vers sa première position d’animation à l’image 70.</li>
<li>L’application de l’ATOM du début du plan (101) à la fin.</li>
<li>Export de l’Alembic.</li>
</ul>
<p>Pensez à garder les paramètres de numéro de frame modifiables afin que les départements puisse régénérer leur Alembic avec <em>preroll</em> si la version automatique n’est pas optimale.</p>
<h3>Job de génération de vidéo</h3>
<p>Ce job s’occupe de générer des vidéos, souvent <em>.mp4</em> depuis une séquence d’images (<em>.jpeg</em>, <em>.exr</em>, etc.). Ce dernier est loin d’être simple.</p>
<p>On peut utiliser Nuke pour bénéficier de tous les outils dont il dispose (écriture de texte, option d’export, etc.), mais Nuke n’est pas donné et nécessite une licence. :cayMal:</p>
<p>Le module Python <a href="https://pillow.readthedocs.io/en/latest/" hreflang="en">Pillow</a> est vraiment très bon. Je le recommande chaudement. :sauteJoie:</p>
<p>Pour les cartouches (bandes noir en haut et en bas, affichant des informations) je déconseille de calculer dynamiquement leur taille via un pourcentage. Gardez une taille fixe, connu et simple à retenir pour tout le monde (100 pixels, 150 pixels, 200 pixels, etc.).</p>
<pre>
+--------------------------------+
|text1 text4 text7 |
|text2 text5 text8 |
|text3 text6 text9 |
|--------------------------------|
| |
| |
| *picture* |
| |
| |
|--------------------------------|
|text10 text13 text16|
|text11 text14 text17|
|text12 text15 text18|
+--------------------------------+</pre>
<p>Neuf infos en haut et neuf infos en bas, soit 18 petits textes d’informations ! :sourit:</p>
<p>Liste non exhaustive d’informations intéressantes à mettre dans les cartouches :</p>
<ul>
<li>Frame : 150 (101-192).</li>
<li>Date.</li>
<li>Nom du graphiste.</li>
<li>Focale.</li>
<li>Watermark.</li>
<li>Colorimetrie.</li>
</ul>
<p>Vous pouvez également ajouter une ou plusieurs images (cartons) en début de vidéo avec plus d’informations (commentaire du graphiste, date des fichiers utilisés, mire colorimétrique, etc.).</p>
<h3>Job de synchro</h3>
<p>Que l’on travaille dans un studio multi-site ou qu’on utilise les ferme de rendu dans le nuage, les jobs de synchro sont monnaie courante. Ces jobs ne prennent quasiment aucune ressource et ne font souvent rien d’autre qu’attendre. En effet, ils sont rarement chargés de faire la synchro eux-mêmes mais passe par un service dédié auquel ils envoient des informations des fichiers à synchroniser (il faudra que je fasse un billet là dessus un jour). Ensuite, ils attendent que le service de synchro leur dise que les fichiers demandés ont été synchronisés. D’où l’importance de s’assurer qu’aucun worker dédié à une tâche lourde (rendu, export, etc.) n’exécute un job de synchro. Ce serait une perte de temps énorme. Pour cela il peut être pratique d’avoir une machine composée de dizaines de worker qui ne font qu’exécuter des jobs de synchro (c’est-à-dire attendre… :seSentCon: ).</p>
<p>Une fois ces jobs exécutés ils « débloquent » les jobs en dépendance qui peuvent s’exécuter sur leur worker avec la garantie que les fichiers nécessaires sont présents.</p>
<h3>Job de nettoyage</h3>
<p>Ce job sert tout simplement à supprimer les fichiers qui ne sont plus nécessaires. Ça peut être les <em>.rib</em> ou des <em>.vrmesh</em> généré à la volée par la chaîne de job mais qui ne servaient qu’au calcul d’une image.</p>
<p>Souvent appelés <em>cleanup jobs</em>, leur mécanisme est parfois pris en charge par le job manager qui ne s’exécute qu’au moment de la suppression des jobs. On ne peut donc pas directement parler de « job » mais plutôt de <em>cleanup list</em> (le job contient la liste des fichiers/dossier à supprimer) :</p>
<pre>
<code class="language-python">{'cleanup':['/path/to/file.rib',
'/path/to/folder']}</code></pre>
<p>Mais tous ne le gèrent pas comme ça et un job dédié reste la méthode traditionnelle.</p>
<p>Il fut un temps, ces jobs supprimaient les <em>shadow map</em>, les <em>point-clouds</em>, les <em>brick maps</em>, etc. qui pesaient très lourd.</p>
<p>C’est la fin de cette première partie ! J’espère que ça vous aura intéressé. La suite, un jour… :aupoil:</p>
<p>Edit : La <a href="https://www.fevrierdorian.com/blog/post/2020/05/16/La-ferme-de-rendu-seconde-partie-Les-jobs" hreflang="fr">seconde partie</a> est disponible.</p>
<center>:marioCours:</center>Compiler alembic 1.5.8 sous Linuxurn:md5:6a5737b3331337776254def765ae06982015-09-06T21:32:00+02:002018-08-09T13:49:05+02:00NarannScript et codealembiccompilationfr<figure style="float: left; margin: 0 1em 1em 0;"><img alt="abc_octopus_001_tn.png" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/abc_octopus_001_tn.png" style="width: 150px; height: 150px;" /></figure>
<p>Si vous avez déjà essayé de compiler <a href="https://github.com/alembic/alembic">alembic</a>, vous avez sûrement été confronté à pas mal de soucis. Compiler alembic n'est en effet pas simple et les méthodes pour y arriver sont nombreuses suivant ce que vous souhaitez faire.</p>
<p>Dans ce billet, je vous propose d'expliquer ligne à ligne un script bash afin de voir les différentes étapes. Notez que c'est plus un partage d’expérience qu'un vrai tuto. Il y a de fortes chances que ce script ne fonctionne pas sans modifications de votre part.</p>
<p>Je préfère prévenir: Mieux vaut être habitué à tout ce qui touche à la compilation. :mechantCrash:</p> <h3>Mon système</h3>
<p>Avant de commencer, et histoire que vous sachez sur quoi s’appuie ce tuto, sachez que je tourne sur un Linux Mint 17.1 Rebecca (Ubuntu 14.04):</p>
<pre>
<code class="language-bash">$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4</code></pre>
<p>Ça, c'est fait! :bravo:</p>
<h3>Prérequis</h3>
<p>On va commencer par ce qu'on ne va <u>pas</u> compiler mais qui est nécessaire au bon fonctionnement des opérations, à savoir: Les packages.</p>
<p>D'une manière générale, je vous conseille de lire les <em>READMEs</em> fournis avec chaque archive de fichiers sources car ils contiennent de précieuses indications.</p>
<p>Petite liste non exhaustives des packages nécessaires:</p>
<ul>
<li><em>zlib1g-dev</em>: 1.2.8 chez moi.</li>
<li><em>libhdf5-dev</em>: 1.8.11 chez moi.</li>
<li><em>libboost-thread1.54-dev</em>: Alembic spécifie qu'il fonctionne de boost 1.44 à 1.48 et que les versions suivantes posent des soucis avec Python. Comme on ne va pas compiler PyAlembic ici on est tranquille.</li>
<li><em>graphviz</em>: Pour la documentation Alembic. Pas obligatoire mais vous n'aurez pas les graphs d'héritage dans la documentation généré ce qui est toujours dommage. 2.36.0 chez moi.</li>
<li><em>cmake</em>: Le build system utilisé par Alembic. 2.8.0 ou supérieur, 2.8.12 chez moi.</li>
</ul>
<p>Installez <em>cmake-gui</em> tant qu'à faire, vous risquez d'en avoir besoin.</p>
<p>Je ne mentionne bien évidemment pas <em>build-essential</em> :tux001: .</p>
<h3>Ce qu'on va compiler</h3>
<p>Alembic palsambleu! :grenadelauncher:</p>
<p>Plus précisément, ce script va compiler:</p>
<ul>
<li>IlmBase 2.2.0</li>
<li>OpenEXR 2.2.0</li>
<li>Alembic 1.5.8</li>
</ul>
<blockquote>
<p>Pour votre information: Le <em>README</em> de Alembic spécifie qu'il ne nécessite qu'IlmBase 1.0.3 et OpenEXR 1.7.1. Vous pouvez toujours modifier mon script pour utiliser ces versions. J'ai teste et ça fonctionne également.</p>
</blockquote>
<h3>Le script</h3>
<p>Le voici brut de décoffrage :</p>
<pre>
<code class="language-bash">export MAINDIR=/home/user/code/alembic_test
mkdir my_env
wget http://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.0.tar.gz
tar -xzvf ilmbase-2.2.0.tar.gz
rm ilmbase-2.2.0.tar.gz
cd ilmbase-2.2.0
./configure --prefix=$MAINDIR/my_env
make -j8
make install
cd ../
export LD_LIBRARY_PATH=$MAINDIR/my_env/lib
wget http://download.savannah.nongnu.org/releases/openexr/openexr-2.2.0.tar.gz
tar -xzvf openexr-2.2.0.tar.gz
rm openexr-2.2.0.tar.gz
cd openexr-2.2.0
./configure --prefix=$MAINDIR/my_env --with-ilmbase-prefix=$MAINDIR/my_env
make -j8
make install
cd ../
wget https://github.com/alembic/alembic/archive/1.5.8.tar.gz
tar -xzvf 1.5.8.tar.gz
rm 1.5.8.tar.gz
cd alembic-1.5.8
cmake -DUSE_PYALEMBIC=OFF \
-DILMBASE_ROOT=../my_env \
-DCMAKE_SYSTEM_PREFIX_PATH=../my_env \
-DCMAKE_INSTALL_PREFIX=../my_env/usr/local ./
make -j8
make test
make install
doxygen Doxyfile</code></pre>
<h3>Explication ligne à ligne</h3>
<pre>
<code class="language-bash">export MAINDIR=/home/user/code/alembic_test</code></pre>
<p>Cette première ligne est importante. On créé la variable <em>MAINDIR</em> à laquelle on assigne le chemin vers notre dossier principal dans lequel on va faire nos affaires.</p>
<pre>
<code class="language-bash">mkdir my_env</code></pre>
<p>Ici on créé un dossier <em>my_env</em>. C'est une méthode que j'utilise souvent: Afin d'éviter d'avoir à installer mes librairies fraîchement compilées directement sur mon système (ce qui peut potentiellement tout casser si la librairie est importante et mal compilée), je défini un dossier comme étant mon système (on parle souvent de <em>prefix</em>). Ça nécessite souvent de bricoler deux trois choses mais ça finit toujours par marcher :baffed:.</p>
<h3>IlmBase</h3>
<pre>
<code class="language-bash">wget http://download.savannah.nongnu.org/releases/openexr/ilmbase-2.2.0.tar.gz</code></pre>
<p>On récupère l'archive des fichiers source de IlmBase (lien trouvé <a href="http://www.openexr.com/downloads.html" hreflang="en">ici</a>). On se retrouve donc avec un jolie <em>ilmbase-2.2.0.tar.gz</em> dans sont dossier.</p>
<pre>
<code class="language-bash">tar -xzvf ilmbase-2.2.0.tar.gz</code></pre>
<p>Hahaha! <a href="https://xkcd.com/1168/" hreflang="en">La fameuse</a> commande <em>tar</em>, impossible à retenir. :hihi:</p>
<p>En gros, on décompresse le contenu du fichier.</p>
<pre>
<code class="language-bash">rm ilmbase-2.2.0.tar.gz</code></pre>
<p>Une fois décompressé, je supprime l'archive parce que j'aime bien quand c'est propre. :gniarkgniark:</p>
<pre>
<code class="language-bash">cd ilmbase-2.2.0</code></pre>
<p>On rentre maintenant dans le dossier qu'on vient d'extraire.</p>
<pre>
<code class="language-bash">./configure --prefix=$MAINDIR/my_env</code></pre>
<p>Première apparition du <em>prefix</em> dont je vous ai parlé tout à l'heure. En gros, on exécute le script de configuration. Le script de configuration va s'assurer que tout ce qui est nécessaire à la compilation est présent sur le système. En lui spécifiant un <em>prefix</em>, on lui indique dans quel dossier on souhaite placer les fichiers compilés. Le <em>$MAINDIR</em> vient appeler la variable créer tout en début de script.</p>
<pre>
<code class="language-bash">make -j8</code></pre>
<p>D'ordinaire, la commande <em>make</em> s'occupe de compiler les fichiers sources. L'argument <em>-j8</em> indique le nombre de job à utiliser lors de la compilation pour gagner du temps. Si vous avez <em>ein grô</em> PC, <em>ein grô</em> processeur et de la grosse mémoire, vous pouvez mettre <em>ein grô</em> chiffre (et <em>ein</em>-versement :smileFou: )</p>
<pre>
<code class="language-bash">make install</code></pre>
<p>Si la compilation c'est bien passée, vous pouvez faire un <em>make install</em> pour envoyer les fichiers compilés se dispatcher dans le dossier <em>my_env</em>.</p>
<pre>
<code class="language-bash">cd ../</code></pre>
<p>On sort du dossier. S'en est fini d'IlmBase!</p>
<h3>OpenEXR</h3>
<pre>
<code class="language-bash">export LD_LIBRARY_PATH=$MAINDIR/my_env/lib</code></pre>
<p>Alors alors, une petite explication s'impose: Durant la phase de configuration de la compilation d'OpenEXR, ce dernier génère un petit code utilisant la librairie IlmBase afin de vérifier certaines choses. Comme vous le savez, nous avons sauvé les fichiers d'IlmBase dans un environnement spécial, en dehors du système principale (dans <em>my_env</em>). Pour que la phase de configuration d'OpenEXR se passe bien, il faut ajouter le dossier contenant les librairies à la variable d'environnement utilisée pour trouver les librairies des binaires exécutés par le système.</p>
<p>C'est ce que fait cette ligne. (Plus d'infos sur <em>LD_LIBRARY_PATH</em> <a href="http://www.linuxcertif.com/doc/keyword/LD_LIBRARY_PATH/" hreflang="fr">ici</a>)</p>
<pre>
<code class="language-bash">wget http://download.savannah.nongnu.org/releases/openexr/openexr-2.2.0.tar.gz
tar -xzvf openexr-2.2.0.tar.gz
rm openexr-2.2.0.tar.gz
cd openexr-2.2.0</code></pre>
<p>Je ne vais pas m’étaler sur cette partie, c'est la même que précédemment.</p>
<pre>
<code class="language-bash">./configure --prefix=$MAINDIR/my_env --with-ilmbase-prefix=$MAINDIR/my_env</code></pre>
<p>Le script de configuration d'OpenEXR dispose d'un argument supplémentaire (<em>--with-ilmbase-prefix</em>) permettant de lui donner un chemin vers un emplacement non standard où est placé la librairie IlmBase à utiliser. Je ne rentre pas dans les détails. Si vous voulez comprendre, ouvrez le fichier <em>configure</em> et cherchez cette variable :dentcasse:.</p>
<pre>
<code class="language-bash">make -j8
make install
cd ../</code></pre>
<p>On compile, on installe dans <em>my_env</em> et on sort.</p>
<h3>Alembic</h3>
<p>Et voici la dernière ligne droite!</p>
<pre>
<code class="language-bash">wget https://github.com/alembic/alembic/archive/1.5.8.tar.gz</code></pre>
<p>Notez que j'ai récupéré le chemin de l'archive sur <a href="https://github.com/alembic/alembic/releases" hreflang="en">la page des releases</a>.</p>
<pre>
<code class="language-bash">tar -xzvf 1.5.8.tar.gz
rm 1.5.8.tar.gz
cd alembic-1.5.8</code></pre>
<p>Je passe. :reveBinaire:</p>
<pre>
<code class="language-bash">cmake -DUSE_PYALEMBIC=OFF \
-DILMBASE_ROOT=../my_env \
-DCMAKE_SYSTEM_PREFIX_PATH=../my_env \
-DCMAKE_INSTALL_PREFIX=../my_env/usr/local ./</code></pre>
<p>Et voila <strong>la</strong> ligne! On va la décortiquer:</p>
<ul>
<li><em>cmake</em>: La commande du générateur de <em>makefile</em> de Alembic. Vous remarquez qu'Alembic n'utilise pas l'habituel <em>configure</em> qui est une méthode assez ancienne et laborieuse (il suffit d'ouvrir un script de configuration pour s'en convaincre). <a href="http://cmake.org/" hreflang="en">CMake</a> permet une gestion plus fine des options de compilation pour générer des <em>makefiles</em> aux petits oignions.</li>
<li><em>USE_PYALEMBIC=OFF</em>: On déclare (d'où le <em>-D</em> au début de chaque argument) la variable <em>USE_PYALEMBIC</em> à <em>OFF</em>. En faisant ça, on va simplement dire à CMake de ne pas générer la partie responsable de la compilation de PyAlembic (pour les raisons évoquées plus haut).</li>
<li><em>ILMBASE_ROOT=../my_env</em>: On défini que toute les librairies sont à aller chercher dans <em>my_env</em>.</li>
<li><em>CMAKE_SYSTEM_PREFIX_PATH=../my_env</em>: Le chemin à privilégier pour aller cherche les librairies nécessaires à la compilation (cf: <a href="http://www.cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_PREFIX_PATH.html" hreflang="en">doc</a>).</li>
<li><em>CMAKE_INSTALL_PREFIX=../my_env/usr/local</em>: L'équivalent du <em>prefix</em> d'un script <em>configure</em>. Notez que j'ai ajoute <em>/usr/local</em> car c'est l'endroit ou Alembic viendrait stocker ces librairies si on ne lui précisait pas.</li>
<li><em>./</em>: On spécifie simplement qu'il faut exécuter <em>cmake</em> dans le dossier courant.</li>
</ul>
<p>Si vous souhaitez savoir ce qui ce passe sous le capot, exécutez <em>cmake-gui ./</em> après avoir exécuté la ligne précédente.</p>
<pre>
<code class="language-bash">make -j8</code></pre>
<p>C'est pas si long que ça. :nannan:</p>
<pre>
<code class="language-bash">make test</code></pre>
<p>Comme la quantité de message affiché est énorme, vous ne saurez pas forcement clairement si la compilation c'est bien passé. Le plus simple pour tester est d'exécuter la suite de test fournie.</p>
<p>Si tout fonctionne:</p>
<pre>
<code class="language-bash">make install</code></pre>
<p>Allez dans <em>my_env/usr/local/alembic-1.5.8/lib/static/</em>:</p>
<figure style="margin: 0 auto; display: block;"><img alt="alembic_static_001.png" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/alembic_static_001.png" style="width: 248px; height: 310px; display: block; margin: 0px auto;" /></figure>
<p style="text-align: center;">Vous voila l'heureux propriétaire d'une librairie Alembic statique!</p>
<p>N'oubliez pas de générer la documentation:</p>
<pre>
<code class="language-bash">doxygen Doxyfile </code></pre>
<p>Pour l'ouvrir (et vous en aurez besoin!): <em>alembic-1.5.8/doc/html/index.html</em>.</p>
<h3>Le viewer</h3>
<p>Allez dans <em>my_env/usr/local/alembic-1.5.8/bin:</em></p>
<figure style="margin: 0 auto; display: block;"><img alt="alembic_bin_dir_001.png" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/alembic_bin_dir_001.png" style="width: 159px; height: 153px; display: block; margin: 0px auto;" /></figure>
<p style="text-align: center;">Des petites choses que je vous laisse essayer par vous même.</p>
<p>Pour le fun, faites:</p>
<pre>
<code class="language-bash">./SimpleAbcViewer /path/to/any.abc</code></pre>
<p>Et miracle!</p>
<figure style="margin: 0 auto; display: block;"><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/abc_octopus_001.png"><img alt="abc_octopus_001.png" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/.abc_octopus_001_m.png" style="width: 560px; height: 435px; display: block; margin: 0px auto;" /></a></figure>
<p>Gardez la touche <em>shift</em> enfoncé et naviguez comme dans Maya. (Des raccourcis sont disponible pour, entre autre, jouer l'animation)</p>
<p>Notez qu'il faut avoir précédemment setté la variable <em>LD_LIBRARY_PATH</em> pour pointer vers les librairies nécessaires (un message d'erreur dans le terminal vous le rappellera :trollface: )</p>
<h3>Linker les librairies Alembics dans vos programme</h3>
<p>Je vous préviens tout de suite cette partie sera <s>encore</s> plus chaotique que la précédente. :pasClasse:</p>
<p>Pour faire simple: On va écrire un petit programme mais on va surtout tenter de compiler ce programme avec les librairies.</p>
<p>Personnellement, j'utilise <a href="https://netbeans.org/features/index.html" hreflang="en">NetBeans IDE</a> mais vous devriez pouvoir configurer ça sur n'importe quel IDE et/ou chaîne de compilation. Créez vous un projet vierge (<em>abc_test</em> dans mon cas).</p>
<h4>Les headers</h4>
<p>Voici la liste des dossiers des headers:</p>
<ul>
<li><em>../my_env/usr/local/alembic-1.5.8/include</em>: Le dossier des headers d'Alembic</li>
<li><em>../my_env/include/OpenEXR</em>: Le dossier des headers de IlmBase</li>
<li><em>/usr/include</em>: Le dossier des headers de votre système (pour HDF5 entre autre)</li>
</ul>
<figure style="margin: 0 auto; display: block;"><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/abc_compile_config_001.png"><img alt="abc_compile_config_001.png" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/.abc_compile_config_001_m.png" style="width: 560px; height: 270px; display: block; margin: 0px auto;" /></a></figure>
<h4>Le standard</h4>
<p>Choisissez le standard C++11:</p>
<figure style="margin: 0 auto; display: block;"><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/abc_compile_config_002.png"><img alt="abc_compile_config_002.png" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/.abc_compile_config_002_m.png" style="width: 560px; height: 120px; display: block; margin: 0px auto;" /></a></figure>
<p>On va écrire du C++ de hipster!!! :laClasse: :</p>
<h4>Les librairies</h4>
<p>Et la liste des librairies statiques (l'ordre est important):</p>
<ul>
<li>../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcGeom.a</li>
<li>../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcFactory.a</li>
<li>../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbc.a</li>
<li>../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcCoreHDF5.a</li>
<li>../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcCoreOgawa.a</li>
<li>../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicAbcCoreAbstract.a</li>
<li>../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicOgawa.a</li>
<li>../my_env/usr/local/alembic-1.5.8/lib/static/libAlembicUtil.a</li>
<li>../my_env/lib/libHalf.a</li>
<li>../my_env/lib/libIex.a</li>
<li>../my_env/lib/libIexMath.a (Pas sure que celle librairie existe avec IlmBase 1.0.3...)</li>
<li>../my_env/lib/libIlmImf.a</li>
<li>../my_env/lib/libIlmThread.a</li>
<li>../my_env/lib/libImath.a</li>
<li>boost_thread</li>
<li>hdf5</li>
<li>hdf5_hl</li>
</ul>
<p>Les trois dernières ne sont pas des liens directs car elles font partie du système. Ce qui nous donne:</p>
<figure style="margin: 0 auto; display: block;">
<p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/abc_compile_config_003.png"><img alt="abc_compile_config_003.png" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2015_09_06_compiler_alembic_linux/.abc_compile_config_003_m.png" style="width: 560px; height: 344px; display: block; margin: 0px auto;" /></a></p>
</figure>
<h4>Le programme</h4>
<p>Allez! Faites moi un gros copier/coller :hehe: :</p>
<pre>
<code class="language-cpp">#include <Alembic/Abc/All.h>
#include <Alembic/AbcCoreFactory/All.h>
int main(int argc, char** argv) {
// Create needed variables
Alembic::AbcCoreFactory::IFactory factory;
Alembic::AbcCoreFactory::IFactory::CoreType coreType;
// open the abc file
auto archive = factory.getArchive("/path/to/alembic_octopus.abc", coreType);
if (archive.valid())
{
std::cout << "Archive name: " << archive.getName() << std::endl;
std::cout << "Number TimeSampling: " << archive.getNumTimeSamplings() << std::endl;
std::cout << "Archive version: " << archive.getArchiveVersion() << std::endl;
// retrieve the top IObject of the hierarchy
auto top = archive.getTop();
std::cout << "top valid?: " << top.valid() << std::endl;
std::cout << "top name: " << top.getName() << std::endl;
std::cout << "top full name: " << top.getFullName() << std::endl;
std::cout << "top num children: " << top.getNumChildren() << std::endl;
auto parent = top.getParent();
std::cout << "parent of the top valid?: " << parent.valid() << std::endl; // of course not!
auto header = top.getHeader();
std::cout << "top header name: " << header.getName() << std::endl;
std::cout << "top header full name: " << header.getFullName() << std::endl;
auto metadata = top.getMetaData();
std::cout << "metadata size: " << metadata.size() << std::endl;
std::cout << "metadata serialize: " << metadata.serialize() << std::endl;
// iterate over abc metadatas
for(auto &it : metadata)
{
auto key = it.first;
auto value = it.second;
std::cout << key << " = " << value << std::endl;
}
};
return 0;
}
</code></pre>
<p>Et voici ce qu'affiche ce programme une fois exécuté:</p>
<pre>
<code>Archive name: /home/narann/code/cpp/abc_test/alembic_octopus.abc
Number TimeSampling: 2
Archive version: 10000
top valid?: 1
top name: ABC
top full name: /
top num children: 1
parent of the top valid?: 0
top header name: ABC
top header full name: /
metadata size: 4
metadata serialize: _ai_AlembicVersion=Alembic 1.0.0 (built Aug 5 2011 16:14:46);_ai_Application=Maya 2012-2.1.SPI x64 AbcExport v1.0;_ai_DateWritten=Tue Aug 9 13:14:24 2011;_ai_Description=Exported from: /mcp/Alembic_Octopus_Example/alembic_octopus.mb
_ai_AlembicVersion = Alembic 1.0.0 (built Aug 5 2011 16:14:46)
_ai_Application = Maya 2012-2.1.SPI x64 AbcExport v1.0
_ai_DateWritten = Tue Aug 9 13:14:24 2011
_ai_Description = Exported from: /mcp/Alembic_Octopus_Example/alembic_octopus.mb
</code></pre>
<p>Si vous êtes arrivé jusqu'ici: Félicitation! Vous pouvez maintenant commencer a jouer avec Alembic. Je vous invite à fouiller les codes d'exemples (à commencer par celui ci) et à lire la documentation que vous avez générée (Mais si! Rappelez vous, dans <em>../alembic-1.5.8/doc/html/</em>). :RTFM:</p>
<p>J'espère que ce billet vous aura aidé et donné envie de pousser tout ça plus loin.</p>
<p>Dorian</p>
<p style="text-align: center;">:marioCours:</p>Ocarina Baby DragonTooth de Songbirdurn:md5:83c50f748c8c8e9e4136ec3d3f2aec952014-12-14T23:39:00+01:002015-09-06T05:49:56+02:00NarannMes coups de coeurfrocarina<figure style="float: left; margin: 0 1em 1em 0;"><img style="float: left; width: 150px; height: 150px;" class="media" alt="baby_dragontooth_songbird_tn.png" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_tn.png" /></figure><p>Cela fait un moment que j'ai cet ocarina mais du fait du peu d’intérêt pour ce type d'instrument sur la scène francophone je ne voyais pas de raisons d'en parler.</p><p>J'ai cependant l'impression que ça change et que certains artisans francophones s'y mettent tout comme certains musiciens, eux aussi français, sortent de l'ombre pour échanger. :sourit:</p> <p>Comme il s'agit de musique, il est assez normal qu'il soit difficile, sur internet, de savoir en quelles quantités les gens s’intéressent à cet instrument millénaire, mais la quantité de personnes semblant chercher des conseils pour leur premier ocarina est bien visible et force est de constater qu'elle n'est pas négligeable (en gardant à l'esprit qu'on est sur un marché de niche).</p><p>C'est un peu pour tous ces anonymes que je partage mon avis sur l'ocarina <a hreflang="en" href="http://www.songbirdocarina.com/products/baby-dragontooth-ocarina-in-metallic-lustre">Baby DragonTooth</a> de <a hreflang="en" href="http://www.songbirdocarina.com/">Songbird</a> que j'ai depuis presque un an.</p><h3>Ocarina, l'inception</h3><p>Comme la plupart des clampins qui ont découvert l'ocarina, ça s'est fait avec <a hreflang="fr" href="http://fr.wikipedia.org/wiki/The_Legend_of_Zelda:_Ocarina_of_Time">The Legend of Zelda: Ocarina Of Time</a> (OOT pour les intimes). Je soupçonne que la version 3DS ressortie en 2012 (combiné à l'ouverture du magasin <a hreflang="fr" href="http://www.buyocarina.com/fr/">Buy Ocarina</a> au marché français) n'est pas étranger aux "regain" que j'observe. Oui, cet épisode de Zelda est un monument du jeu vidéo pour milles raisons que je n’évoquerais pas. Mais évoquez ce jeu à la plupart des gens qui l'ont parcouru lors de sa sortie original en 1995 et vous pourrez sûrement lire les traits de <a hreflang="en" href="https://imgur.com/gallery/qtfK183">la nostalgie</a> sur le visage du preu joueur qui vous contera ces déambulations dans l'univers si particulier (entre drôle et macabre) de cet épisode.</p><p>Mais OOT c’était il y a vingt ans, pourquoi s'y intéresser maintenant? Et bien simplement parce que je fais parti de ses artistes ratés qui aimeraient jouer un instrument <a hreflang="fr" href="http://www.bouletcorp.com/2010/03/09/pas-de-repos-pour-les-guerriers-2/">sans avoir le moindre don pour la musique</a> et encore moins le temps d'apprendre...</p><p>Je ne me souvient plus ou j'ai pu tomber sur une info me montrant que les ocarinas existaient vraiment mais après avoir vaguement hésité (le mot <em>vaguement</em> fait ici référence au temps d’hésitation de l'occidental moyen devant un objet désiré soit de l'ordre de quelques millisecondes), je décidais d'en acquérir un. J'ai suivi les conseils de la vidéo de David Erick Ramos <a hreflang="en" href="https://www.youtube.com/watch?v=9DbSBYlMsr8">Choosing Your First Ocarina (Octalk!)</a> et opté pour un ocarina six trous qu'il juge plus facile à apprendre/maîtriser, un moyen pour moi de tester sans sauter sur un modèle jugé plus traditionnel à douze trous (La question existentielle qui porta immédiatement préjudice dans mon esprit de primate fut: "Douze trous, dix doigts. Embrouille? Esbroufe? Que veut on me cacher?").</p><p>J'ai également pu compter sur la bienveillance de mes proches:</p><blockquote><p>Lol, j'ai tapé "Ocarina" sur google, je connaissais pas mais j'étais sur que c'était une merde de ce genre :P. Le truc inutile, geek et ou faut apprendre des trucs.</p></blockquote><blockquote><p>mdr! Et tu va aussi foutre les collants blancs et la tunique verte pourri? XD</p></blockquote><blockquote><p>J'crois qu'j'préfère encore quand tu code...</p></blockquote><p>C'est donc sous ce tonnerre d'encouragements que je partais à la recherche d'un ocarina six trous sympathique. :baffed:</p><h3>Un ocarina, oui mais lequel?</h3><p>Première chose: Je ne voulais pas (mais alors pas du tout) un ocarina estampillé Zelda (je n'ai jamais été un grand fan de l'extension à profusion des jeux vidéos dans le monde réel). Je me demandais souvent pourquoi ce magnifique ocarina que je voyais s'afficher sur mon écran avait ce ridicule symbole de la triforce que je ne me voyais pas avoir sous le pif à chaque utilisation tel un <a hreflang="fr" href="http://www.bouletcorp.com/2010/08/20/la-bete-en-moi/">jinggle de pub pour enfant</a>: "Joue de l'ocarina pour avoir de la triforce en toi". <em>Orh yeah...</em> Si les ocarinas jouaient sur des fréquences plus basse, ça aurait presque pu marcher. Mais la <a hreflang="fr" href="https://fr.wikipedia.org/wiki/Tessiture">tessiture</a> particulière de ce type d'instrument rappelant plus le cri d'un oisillon puceau en détresse que le raclement de gorge d'un Arnold sous testostérone rends caduque toute tentative d'assimilation à une quelconque forme de virilité, aussi relative soit elle (L'univers Hylien est plus proche du transgenre japonnais que l'univers de prépubère en rûte d'un quelconque GTA...).</p><p>Les modèles six trous ne courant pas les rues et comme l’idée de souffler dans une <a href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_015.jpg">testicule de taureau</a> m’était également désagréable, mes choix s'en retrouvaient implicitement réduits.</p><p>Je me tournai finalement vers Songbird et plus précisément le Baby DragonTooth qui était à la fois original tout en ayant une forme un peu classe.</p><p>Pas grand chose à dire sur l’envoi. Acheté le 20 février 2014 et reçu trois semaines plus tard. Faut pas être pressé (adepte de l'achat compulsif, te voila prévenu). C'est bien protégé et il est arrivé <s>sans se presseeeeer</s> nickel.</p><p>L'emballage est.. <em>(on s'en fout)</em>. Un petit feuillet est inclus avec d'un coté la gamme ainsi qu'une liste de tablatures de différents airs. Mon premier fut Joyeux Anniversaire, sûrement le plus simple de tous et le plus facile à caler quelque part (on commence toujours par sa famille, les seuls capable de supporter nos âneries, le sourire de compassion faisant foi...).</p><p>Puis vient la bête, ou plutôt, sa dent (oui parce que <em>tooth</em>, c'est de l'anglais... Et ça veut dire dent... Donc <em>dragontooth</em> ça veut dire... Enfin vous avez l’idée :dentcasse: ):</p><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_001.jpg"><img style="width: 560px; height: 419px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_001.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_001_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_002.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_002.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_002_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_003.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_003.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_003_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_004.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_004.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_004_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_005.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_005.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_005_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_006.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_006.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_006_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_007.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_007.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_007_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_008.jpg"><img style="width: 420px; height: 560px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_008.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_008_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_009.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_009.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_009_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_010.jpg"><img style="width: 560px; height: 419px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_010.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_010_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_011.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_011.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_011_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_012.jpg"><img style="width: 420px; height: 560px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_012.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_012_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_013.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_013.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_013_m.jpg" /></a></p></figure><figure style="margin: 0 auto; display: block;"><p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/baby_dragontooth_songbird_014.jpg"><img style="width: 560px; height: 420px; display: block; margin: 0px auto;" class="media" alt="baby_dragontooth_songbird_014.jpg" src="https://www.fevrierdorian.com/blog/public/billets/2014_12_05_baby_dragontooth_songbird/.baby_dragontooth_songbird_014_m.jpg" /></a></p></figure><blockquote><p>Note: Les photos ne dates pas du premier jours.</p></blockquote><p>Premier constat: Ilaybôôôôh! :3 #kawaiiface</p><p>Sérieusement, ça été le coup de foudre! J'adore le reflet bleu pétrole ainsi que la forme général de l'objet. Il dispose d'un coté légèrement aplati bien classe.</p><p>Les trous sont très bien placé, pas de crampes possibles. J'aurais apprécié un très léger creux aux niveaux des doigts pour bien les sentir quand on joue.</p><h3>Sensation de fragilité</h3><p>Les ocarinas ont la réputation d’être aussi solides qu'un cours de bourse (je ne parle plus du taureau). Comme il est assez difficile de "tester" la solidité d'un objet sans avoir à le casser je vais plutôt parler de <em>sensation de fragilité</em>. Force est de constater que, sûrement due à sa petitesse, ce modèle semble assez robuste et devrait résister à la plupart des chocs (un peu comme un mug).</p><h3>Le son</h3><p>Avant de commencer:</p><ol><li>Je ne suis pas musicien.</li><li>Je n'ai aucun autre ocarina pour comparer.</li><li>Je suis un <s>autodidacte</s> boulet pour ce qui est des instruments à vent (mes cours de flûte y sont sûrement pour beaucoup...).</li></ol><p>Bref, avant de prendre cette review pour argent comptant sachez que vous marchez sur un terrain miné par le noobisme et le haut niveau d’incompétence qui le caractérise. Si vous êtes de ceux là, vous compatirez sûrement. Les autres, vous noterez le ton humoriste bancale visant à ne point faire perdre son temps au lecteur quand bien même il ne trouverait moindre valeur au sujet présent dans cette colonne.</p><p>D'un point de vue purement acoustique j'ai été agréablement surpris. Le son est super jolie :3 #kawaiifaceagain. La différence de souffle nécessaire entre le Do grave (tous les trous bouchés) et le Mi aigu n'est pas énorme et viens naturellement. Un soprano est par définition aigu et celui ci ne déloge pas à la règle. Il est tout à fait possible d'y jouer pendant plusieurs heures mais n’hésitez pas à utiliser des boules Quies si vous jouez longtemps une partition aigu.</p><p>Il n'y a pas des masses de vidéos sur Youtube l'utilisant mais j'ai réussi à en trouver <a hreflang="en" href="https://www.youtube.com/watch?v=EIFULXnc6R8">quelques</a> <a hreflang="en" href="https://www.youtube.com/watch?v=LEZ7CSpJkts">unes</a>.</p><p>J'ai l'impression que l'ocarina produit un son différent (plus strident) lors des premières secondes d'utilisation si ce dernier a été stocké dans un endroit froid. C'est peut être mes oreilles qui déconnent (ou que le mot <em>froid</em> est à prendre au sens montréalais mais ça s'est produit plus d'une fois. Dans ces cas la je bouche le trou principal avec mon pouce, ouvre tous les autres et souffle fort quelques secondes pour "vider" l'air froid et chauffer l'ocarina (ne retirez pas votre pouce à ce moment là ou vos tympan vous lâcheront pour de bon). Si quelqu'un pouvait me confirmer que la chaleur sert a quelque chose dans le son généré qu'il n’hésite pas. :)</p><h3>Tessiture</h3><blockquote><p>Pour les boulets qui ont ratés mon lien plus tôt <a hreflang="fr" href="https://fr.wikipedia.org/wiki/Tessiture">le revoici</a>.</p></blockquote><p>Le Baby DragonTooth est donc un Soprano et produit un son très clair sans nécessairement être extrêmement fort. J'avais très peur de ne pas pouvoir jouer en intérieur mais cela ne pose finalement aucun problème. Je n'ai malheureusement pas d'appareil pour mesurer les décibels et vous donner une idée.</p><p>La page du produit stipule qu'il dispose d'une étendue de Sol4 à Si5 (G5-B6) soit de la touche 59 à 75 d'un piano classique (783,991Hz à 1975,53Hz d'<a hreflang="fr" href="https://fr.wikipedia.org/wiki/Fr%C3%A9quences_des_touches_du_piano">après Wikipedia</a>). En pratique, la première note sonne plutôt comme un Fa# (demi ton avant le Sol). Quand on souffle un peu plus fort on obtient en effet un Sol. C'est suivant la force du souffle (les premières notes y étant beaucoup plus sensible que les dernières).</p><p>Gardez bien à l'esprit que la première note n'est donc pas un Do (comme le petit papier livré avec vous le fait croire). Quand vous jouez seul ce n'est pas très important, vous transposez naturellement. Mais si vous voulez profiter de cet ocarina pour apprendre les bases du solfège il faut le savoir. En effet, si vous voulez vous caler sur une mélodie existante (groupe ou vidéo) commençant par un Do, vous aurez l'impression de jouer cinq tons au dessus.</p><p>Pas de quoi paniquer cela dit, mais si vous avez un esprit ultra cartésien vous serez sûrement tenté d'aller, comme un michu, acheter un accordeur... Juste pour tester... (J'ai acheté un accordeur ok?... Juste pour tester...).</p><h3>Accordage</h3><blockquote><p>Je me demandais: Quelle est le mot qui défini qu'un instrument à vents est accordé ou non? Le mot anglais est <em>tuning</em> mais je n’ai pas trouve l’équivalent français et j'ai comme l'impression que parler d'accordage dans le cas d'un instrument dénudé de toute forme de corde est un non sens. Si vous avez la réponse, un grand merci d'avance pour me laisser un commentaire. :)</p></blockquote><p>Voila.</p><p>Donc, maintenant que les blaireaux du solfège ont sautés sur la rubrique commentaires en vue d'aller étaler leur science ça laisse du temps aux membres de la catégorie suivante que sont les blaireaux ignares de parler de l'accordage. :trollface:</p><p>Vous l'aurez compris, un ocarina ne s'accorde qu'une fois: A la fabrication. Si vous le l'avez pas compris, sachez qu'un ocarina est fait en argile cuit (et si vous ne voyez pas le rapport, vous pouvez vous jeter d'un pont). Une tentative de sauvetage est peut être possible (limer certains trous) mais il vaut mieux demander de l'aide sur <a hreflang="en" href="http://theocarinanetwork.com/">The Ocarina Network</a> (TON pour les intimes), le seul forum fureté par des spécialistes...</p><p>Il est important de constater que la force du souffle joue sur le timbre de la note. Ainsi, j'ai remarqué que, tous les trous bouchés et en fonction du souffle, on peut faire varier de presque une note. Jouer en face d'un accordeur et vous le verrez sûrement jouer au yoyo sur les notes basses. Sur certaines partitions, ça peut vous <s>sauver</s> (on parlerais plus de cache misère).</p><p>Je sais que certains artisans utilisent des <a hreflang="en" href="http://www.sixthstreetocarina.com/blogs/frequently-asked-questions/1243212-how-are-sixth-street-ocarinas-tuned">souffleurs electroniques</a> en vue d'accorder leurs ocarinas. C'est sûrement la meilleur façons de faire. Je ne sais pas si Songbird utilise ça mais si quelqu'un a l'info vous pouvez allez rejoindre les blaireaux du solfège plus bas qui ne vont pas tarder à revenir.</p><h3>Truc aux possesseurs</h3><p>Chris Gale a pris l'habitude de nous gratifier d'une ou plusieurs astuces suivant le modèle d'ocarina qu'elle test, chose que je trouve tout à fait appréciable et je vais donc faire de même ici (même si je ne prétend pas avoir son niveau d'expertise):</p><ul><li>N'englobez pas le bec de l'ocarina avec vos lèvres, pressez simplement (et doucement) le bout de vos lèvres dessus. D'une, ça évite (un peu) de dégueulasser votre ocarina avec votre salive (il vous en remercie) et de deux, ça vous permet de vous servir subtilement de votre bouche (mâchoire/langue) pour contrôler le flux d'air que vous lui envoyez (un peu comme quand vous sifflez). Vous ressentirez aussi beaucoup plus ce flux d'air si il touche vos lèvres là ou, en englobant le bec, vous ne sentirez rien. Notez aussi que plus vous gober le bec de l'ocarina, plus vous aurez du mal à fabriquer le fameux "tseuh!" entre chaque note. Sur un morceau lent, on peut s'appuyer sur le contrôle de sa respiration pour couper une note mais sur morceau rapide l’échec est radicale. La pose des lèvres est importante ne soyez pas timide, tentez des trucs différents. Ça permet de <em>sentir</em> votre instrument. Essayez de souffler à un rythme constant en déplaçant votre ocarina, vous verrez que le son qui sort varie de même.</li><li>Les notes aiguës menacent parfois de "souffler" au lieu de sonner. D'une manière générale, augmentez légèrement le flux d'air sur les notes aiguës. Plus subtilement, ramener (très) légèrement l'ocarina proche de votre poitrine ou tournez le (l'ocarina, pas vous!) très légèrement sur le cote (ramenez la pointe vers vous). En faisant cela, vous ne devriez pas avoir à faire varier votre souffle pour sortir les notes aiguës (ce qui est appréciable sur des morceaux rapides).</li><li>Si vous êtes amené à jouer des partitions aiguës longtemps en intérieur, mettez des boules Quies afin d’éviter de finir sourd rapidement. Pour peu que la pièce dans laquelle vous jouez résonne un peu, les sons aiguës mettront beaucoup de temps à se <em>dégrader</em> et ce n'est vraiment pas agréable.</li><li>Commencez toujours par jouer un nouveau morceau en vitesse réduite. On est souvent tenté de suivre la mélodie originale mais, tel un Schumacher junior qui veut sentir l'ivresse de l'asphalte, il y a plus de chances que vous sentiez le sable des sacs de protections du premier virage (et ça c'est si vous avez de la chance...). Je vous assure que vous apprenez plus vite en jouant lentement (paradoxal hein? :zinzin: ) un doigté parfait plutôt qu'en ratant toutes les notes. Comme c'est un ocarina six trous, certains doigtés s’enchaînent d'une manière peu naturel (c'est d’ailleurs une remarque que j'entends souvent contre les ocarinas six trous).</li><li>Quand vous commencez à connaître le doigté d'une mélodie (pas avant sinon ça ne marche pas des masses), essayez d'avoir une approche plus instinctive. Chacun a sa méthode. Fermez les yeux et bougez doucement votre corps au fil de votre souffle. Ça ne doit pas être un effort vraiment conscient mais juste un moyen de se laisser porter par la musique que vous jouez. Comme si vous vous berciez dans votre propre musique. Vous serez surpris de voir que parfois vous jouerez un morceau entier sans la moindre fausse note et sans que vous ayez pour autant réfléchi à ou vous placiez vos doigts. C'est la gratification suprême du débutant, celui de <em>jouer</em> un instrument.</li><li>Jouez ce qui vous fait plaisir! Vous n’êtes plus un petit enfant incrédule, vous avez bouffé de la musique comme n'importe quel ado frustré. Bien que piteux musicien, votre oreille est affûté. Vous savez reconnaître une fausse note. L'adulte de la droiture droite qui est en vous saura vous le faire remarquer, vous rappeler que vous êtes une bouse fini, que vous perdez votre temps et n'irez nul part. Il rira de vous comme ce pauvre gars qui vous prenait toujours votre dessert au collège. Mais rassurez vous, c'est tout a fait normal: Le monde qui nous entoure nous flatte constamment. Nous ne supportons pas la difficulté car elle nous rappel qu'on est sacrément con. Nous estimons que nos divertissements doivent être simples (ce qui ne nous élève pas). Réapprendre à apprendre, c'est accepter de se mentir et faire disparaître son incompétence derrière un regards d'enfant: Celui qui jubile à chaque mètre parcouru et franchit des montagnes sans vraiment s'en rendre compte. Quand les premiers morceaux sortiront, vous prendrez conscience que cette incompétence sciemment nié disparaît progressivement au profit d'un plaisir bien réel.</li></ul><h3>Prix</h3><p>Moi ça été $29.95 + $12.00 (FDP vers Canada) = $41.95</p><h3>Conclusion</h3><p>Petit coucou aux gros flemmards qui sautent ici sans lire la moindre ligne des sections précédentes, tu te reconnaîtra (On le fait tous! :nannan: ).</p><p>Et bien très satisfait de mon achat. Je l'utilise souvent. J’essaie d'en faire quelques minutes par jour et ça vient doucement.</p><p>Ne vous laissez pas rebuter par le discours New Age propre à Songbird (si tu est à l’écoute des sons de ton <em>coaaarh</em> ton <em>aaaame</em> va s’élever au huitième ciel grâce aux vibrations acoustiques naturelles utilisées depuis des millénaires par les civilisations <em>ancestraaaaales</em> pour soigner les maladies rares et raffermir l'esprit... <a hreflang="fr" href="https://www.youtube.com/watch?v=t7xG7uVOfVs&t=10s">Ouaih ouaih...</a>).</p><p>J'invite les artisans français (et européen) à se faire connaître ainsi qu'a <a hreflang="fr" href="http://www.partition-ocarina.fr/">partition ocarina</a> (site horrible mais communauté adorable) à les mettre en avant car comme tout secteur de niche, il ne se développe que par le bouche à oreille. :franceHappy:</p><p>A bientôt!</p><p>Dorian</p><p style="text-align: center;">:marioCours:</p>Le livre Multithreading for Visual Effectsurn:md5:2f9c1853ac39265f4efdfd1782d0fbd02014-08-24T19:13:00+02:002014-09-19T15:48:46+02:00NarannMes coups de coeurfrhoudinilibeelivremultithreadingopenclopensubdivopenvdbprestotbbvfx<p><img src="https://www.fevrierdorian.com/blog/public/billets/2014_04_24_multithreading_for_vfx_review/multithreading_for_vfx_review_tn.png" alt="multithreading_for_vfx_review_tn.png" style="float:left; margin: 0 1em 1em 0;" title="multithreading_for_vfx_review_tn.png, août 2014" height="150" width="150" />En code, rien ne vaut un bon bouquin. Vous entendrez souvent ce conseil de la part de développeurs expérimentés.</p>
<p>Aujourd'hui je vous propose un billet sur le livre <a href="http://www.crcpress.com/product/isbn/9781482243567" hreflang="en">Multithreading for Visual Effects</a>.</p>
<p>Ce livre a été publié après <a href="http://s2013.siggraph.org/attendees/courses/events/multithreading-and-vfx" hreflang="en">la session de cours Multithreading and VFX</a> au Siggraph 2013 où un certain nombre de studios présentaient comment ils avaient utilisés le multithreading dans leur outils. Les papers étaient suffisamment intéressant pour qu'ils décident de les rassembler en un livre.</p>
<p>Dans la mesure ou ce type de livre fait parti du marche de <em>niche</em> que sont les VFX, parlons en! :enerve:</p> <p><a href="https://www.fevrierdorian.com/blog/public/billets/2014_04_24_multithreading_for_vfx_review/multithreading_for_vfx_review_001.jpg" title="multithreading_for_vfx_review_001.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2014_04_24_multithreading_for_vfx_review/.multithreading_for_vfx_review_001_m.jpg" alt="multithreading_for_vfx_review_001.jpg" style="display:block; margin:0 auto;" title="multithreading_for_vfx_review_001.jpg, août 2014" height="560" width="420" /></a></p>
<h3>Chapitre 1: Introduction et vue d'ensemble (par James Reinders)</h3>
<p>Le prologue est une ode au multithreading (Sans blagues :siffle: ). James Reinders est ingénieur pour Intel et on peut dire qu'il connait son boulot!</p>
<p>J'ai pas mal apprécié comment les multiples facettes du multithreading sont présentées (multi tache vs multi thread, vectorisation, etc...) ainsi que les explications techniques sur comment chacune d'elle fonctionne, avec leurs avantages et inconvénients.</p>
<p>Je me dois de parler de la manière dont <a href="https://en.wikipedia.org/wiki/Threading_Building_Blocks" hreflang="en">TBB</a> est présenté. TBB est généralement considéré comme une (la?) librairie de choix pour des logiciels multithreadés (Presque tous les logiciels du livre l'utilisent). Et de ce que j'ai pu lire de la documentation, c'est mérité. Pour être tout à fait honnête, TBB me donne réellement envie de maitriser les templates C++ et je comprends parfaitement qu'on puisse se tourner vers TBB rien que pour ça.</p>
<p>Mais je trouve dommage que James n'ai pas passé autant de temps et d'amour à présenter les alternatives. Je sais bien, c'est un bonhomme d'Intel mais clairement, TBB reçoit tellement d'éloge dans ce chapitre (et tout au long du livre) qu'il en occulte complètement les autres librairies (qui sont "présentées" mais pas de manière aussi clair que TBB).</p>
<p>Bref, vous comprenez assez vite le message: <em>TBB cay bon, mangez-en!</em> :banaeyouhou:</p>
<h3>Chapitre 2: Houdini: Multithreader un logiciel existant (par Jeff Lait)</h3>
<p><img src="https://www.fevrierdorian.com/blog/public/logos/.houdini_black_s.png" alt="houdini_black.png" style="display:block; margin:0 auto;" title="houdini_black.png, sept. 2014" height="56" width="240" /></p>
<p>Bon nombre des informations qu'on peut trouver ici et la, y compris dans les livres (celui ci inclus), sont très académiques. Elles expliquent comment vous <em>devriez</em> écrire du code.</p>
<p>Ce chapitre est très intéressant car il prend l'approche inverse: Comment convertir du vieux code en un code <em>scalable</em> (qui se parallélise efficacement). Jeff présente différents problèmes concrets, la ou les les méthodes qu'ils ont choisis pour les résoudre, ce qu'ils ont perdus au passage et ce qu'il ont pu sauver.</p>
<p>Ce que j'adore dans ce chapitre est à quel point il est anti-académique. Certains code d’exemples m'ont vraiment fait réagir: "Je pourrais faire ça... Ça serait bizarre mais ça marcherait plutôt pas mal!". Le meilleur exemple est la façon dont Houdini gère la latence de création des threads (car créer un thread est assez long) quand la quantité de données a calculer ne vaux pas le temps de création des threads. Accrochez vous: If data_count < 5000 : compute in the main thread! :IFuckTheWorld:</p>
<p>Vous ne trouverez jamais ce genre de "solutions" dans un livre académique mais vous ne trouverez jamais plus efficace que ça sans avoir à modifier le code à milles endroits! :D</p>
<p>Chaque exemple est intéressant (bien que souvent très spécifique). Les lires entraine votre cerveaux sur <em>pourquoi</em> vous devriez faire les choses de tel ou tel façon et comment réagir quand vous voyez un truc qui semble louche.</p>
<p>Je dois avouer que j'avais lu <a href="http://www.multithreadingandvfx.org/course_notes/MultithreadingHoudini.pdf" hreflang="en">le paper original de Side Effect</a> après le Siggraph 2013. J'ai d'ailleurs remarqué qu'un petit travail de réorganisation a été fait et certains exemples m'ont paru mieux expliqué. Je me rappel avoir été un peu perdu avec certains des exemples du paper original mais je n'ai eu aucun soucis à comprendre ceux du livre. J'en conclu donc qu'une bonne relecture a été faite (ou que je suis plus capable de lire du code qu'avant ce qui est fort probable).</p>
<p>J'ai été super frustré par la fin du chapitre qui arrive trop vite... On en veut encore! :grenadelauncher: Plus d'exemples, plus de problèmes tordu (mais concrets) à résoudre!</p>
<p>Si vous souhaitez en savoir plus sur comment Houdini fonctionne sous le capot, <a href="https://www.fevrierdorian.com/blog/post/2013/04/24/Houdini-sous-le-capot">j'avais traduis</a> <a href="http://forums.odforce.net/topic/17105-short-and-sweet-op-centric-lessons/#entry104263" hreflang="en">un post très intéressant</a> que je vous invite à lire.</p>
<h3>Chapitre 3: Le système d’exécution de Presto: Architecturer pour le Multithreading (par George ElKoura)</h3>
<p>L'interactivité est le mot clef de ce chapitre.</p>
<p>La première partie n'est pas particulièrement intéressante. George explique les ambitions et les contraintes techniques. C'est chouette pour la partie animation (temps de latence, interactivité, etc...), mais il passe définitivement trop de temps sur des choses que tu connais souvent déjà sans vraiment expliquer les implications spécifiques à Presto ("La vectorisation du code est importante car blabla", "Il faut organiser ces données comme ça sinon blabla"). Le coté très général des conseils fait presque redondance avec le premier chapitre. Il aurait peut être été préférable de développer tout ces points dans le premier chapitre (plutôt que de brosser TBB dans tous les sens :trollface: ) et laisser chacun des autres chapitre se focaliser sur l’implémentation.</p>
<p>Cela dit, la second partie est vraiment superbe! Vous y trouverez pleins de petites choses qu'il faut avoir en tête si vous souhaitez développer pour des logiciel d'animation. La gestion des threads dans Presto (tout particulièrement la manière dont le graph d'evaluation est compilé puis exécuté) m'a semblé vraiment moderne, bien pensé et concret. Par concret j'entends qu'on est pas dans des choses génériques mais vraiment spécifiques. La gestion du temps (cache des géométries), la gestion de l'interface utilisateur avec une anticipation des valeurs modifié pour mieux préparer les "batchs" de thread (si un utilisateur modifie les contrôleurs d'un personnage, on peut vraisemblablement penser qu'il ne modifiera pas le cache des autres, inutile d’arrêter leurs threads car un thread coute cher à lancer rappelez vous). Chaque détail permettant d'éviter des ralentissements et d'augmenter le "rendement" des graphs est présenté.</p>
<p>Tout ceci est très clairement expliqué même si ça manque un peu de détails à mon gout (j'aime bien avoir des explications de cas un peu tordu).</p>
<p>Ils finissent avec un petit exemple de code (2 pages) sur comment multithreader un déformer géométrique simple. C'est un exemple intéressant car il présente un cas assez général mais qu'on peu rencontrer souvent en prod quand on est rigger. Vous vous en doutez, la solution est tout aussi "général".</p>
<p>Si vous souhaitez avoir un survole de comment Presto fonctionne sous le capot il y a de fortes chances que vous appreciez ce chapitre. Le mot "survol" est le bon terme car George ne se focalise que sur l'aspect multithreading (c'est le titre du livre donc difficile de le lui reprocher...) mais il parle souvent de choses implicites qu'il est difficile de cerner si on n'est pas habitué a Presto (j'imagine). A la fin du chapitre, on ne sais pas vraiment a quoi ressemble un rig dans Presto (relation entre les nodes et la UI, les nodes les plus bizarres/spéciaux/gros, etc...). Cette "abstraction" de certains détails techniques est souvent frustrante. :injures:</p>
<p>J'ai également ressenti une sorte de "tout va pour le mieux dans le meilleur des mondes". Comme si il n'y avait jamais eu de complication, aucun souci spécifique au multithreading (qui est pourtant un techno assez jeune en terme de paradigme), aucun "on a pensé faire ça car blabla mais on c'est rabattu sur ça car blabla". Sincèrement, en développement il y a toujours des moments ou la vision "abstraite" des concepts (vision nécessaire à l’architecture général d'un logiciel, surtout dans les premières années) se mange les cas non prévus de la réalité dans les dents. Des moments ou un juste milieu, un moindre mal est nécessaire. C'est d'autant plus flagrant quand on compare avec le chapitre suivant.</p>
<h3>Chapitre 4: LibEE: Évaluation en Parallèle des Rigs des Personnages (par Martin Watt)</h3>
<p>Celui là m'a littéralement surpris! Il se lit comme une histoire!</p>
<p>J’avoue que les premières page ne m'ont pas particulièrement fait sauté au plafond . LibEE est présenté comme un graph de dépendance (Dependency Graph ou DG) multithreadé... On pourrais presque lire entre les lignes "Le DG de Maya est totalement à l'Ouest alors on l'a réécrit". Le problème c'est qu'un graph de dépendance "a la Maya" ne rime pas forcément avec performance, surtout dans le cas d'un rig. Le fait que chaque node soit exécuté indépendamment puis passe son résultat au node suivant via des attributs, le tout badigeonné d'une dépendance inter-attributs (ou certains attributs d'entrés peuvent modifier certains des attributs de sortie mais pas tous) et vous obtenez un graph qui passe plus de temps a évaluer les dépendances pour savoir quel node exécuter qu’exécuter réellement les calculs des nodes (avouez que c'est con quand le node ne fait au final qu'une simple addition :siffle: ). Et c'est vrai pour Maya comme pour n'importe quel autre implémentation qui suit cette approche. La vitesse d’exécution d'un graph dépend souvent énormément de son "allure" et il n'est pas rare qu'un graph un peu tordu plombe drastiquement les performances. J'ai tendance à préférer l'approche compilé d'un <a href="http://www.sidefx.com/docs/houdini13.0/vex/" hreflang="en">VEX</a> ou d'un Presto mais en lisant le reste du chapitre, je ne suis pas sur que la RnD de Dreamworks ait pu disposer de toute la latitude (et des ressources?) nécessaires.</p>
<p>Après, je ne suis pas dev a Dreamworks et il y a peut être un autre paquet de bonnes raisons... :dentcasse:</p>
<p>Mais bien que l'équipe de Dreamworks semble avoir fait un travail colossale pour contourner les limitations qu'implique ce type de DG, plusieurs sections tendrons a confirmer mes (humbles) hypothèses.</p>
<p>Et quand je parle de travail colossale je ne suis pas en dessous de la vérité. C'est la raison principal pour laquelle ce chapitre est exceptionnel: Vous "sentez" la production. Vous savez, les deadlines, les riggers qui râle sur les nouveaux outils "car-le-vieu-DG-de-Maya-il-aytay-mieu". Je suis un gros fan des quelques exemples de discussions entre les différentes personnes impliquées dans le projet (avec une petite copie d'un mail bien senti lol). Il explique aussi comment ils on eu à <em>apprendre</em> à leurs riggers à <em>penser</em> multithreadé (chose qui n'était pas prévu au départ). La présentation des différents outils de bench fourni au riggers pour qu'ils puissent trouver eu même les <em>critical paths</em> de leur code et qu'ils les "coupe" ou "blend" la logique en un seul node (un autre argument pour se tenir loin d'un DG IMHO). LibEE semble avoir suivi la situation typique d'une prod de VFX/anim ou les outils sont développé et utilisé en même temps (à l'inverse de Presto ou l'équipe semble avoir travaillé loin de la production, même si ce n'est pas clairement dit, avant de relâcher leurs outils). D'un point de vu développement, gérer une tel situation est un art en soit dans la mesure ou les objectifs/contraintes changent au fil du temps et ce chapitre explique (un petit peu) comment gérer ça.</p>
<p>Si vous êtes développeur et que vous pensez que les studios de VFX échouent car ils ne sont pas assez carré, passez ce chapitre, vous allez détester car c'est vraiment des situations de terrain, du vécu. Les autres, si vous voulez bosser en VFX il y a vraiment de quoi vous inspirer (ou vous faire fuir, au choix!). Et les derniers, ceux qui bossent déjà en VFX habitué des <em>patchs-en-prod-parce-que-cay-super-plus-urgent-que-les-autres-urgences</em>, vous allez vous sentir moins seul! :baffed:</p>
<p>Le second point concerne les expériences et tests qu'ils ont effectués en prod ou de manière plus formel. J’étais déçu que le chapitre Presto ne mentionne que quelques points techniques souvent assez connus (effet <a href="https://fr.wikipedia.org/wiki/Non_Uniform_Memory_Access" hreflang="fr">NUMA</a>, bande passante de la RAM, Hyperthreading, etc...) sans fournir aucun graph ou expliquer en quoi ces problèmes impactaient directement Presto. Ici, non seulement les gars de Dreamworks expliquent ce qui les amènes à faire tel ou tel test mais ils fournissent une quantité de détails impressionnant qui explique, de manière très clair, <em>comment</em> et <em>pourquoi</em> tel ou tel effet apparait. Et on peut dire qu'ils ont poussé le bouchon très loin. Une fois encore, tout ces exemples ne sont pas académiques, ce sont de vrais <em>use cases</em> décortiqués et analysés très clairement. Certains tests expliquent même pourquoi un effet attendu n'est, en pratique, pas un problème.</p>
<p>C'est donc un chapitre super chouette! Un des plus intéressant du livre. J'aurais aimé que le chapitre de Presto sois aussi poussé.</p>
<h3>Chapitre 5: Fluides: Simulation sur le CPU (par Ronald D. Henderson)</h3>
<p>lol celui la est massif. Si vous aimez le code et les maths vous allez être servi! :aupoil:</p>
<p>Il y a quelques codes de comparaison entre OpenMP et TBB (avec du lamdbas C++11, première fois que j'en vois! :gne2: ). Si vous n'êtes pas à l'aise avec le C++, ça risque d'être assez dure (et un peu déroutant).</p>
<p>Pour ce qui est des maths, je dois admettre que j'ai pas mal rigolé chaque fois que je me disais "tin mais je capte rien! :baffed: " après quelques équations (et leur magistrales explications).</p>
<p>Dans les autres chapitres du livre, beaucoup d'explications se font sous la forme de phrases assez basiques tel que: "Nous mesurons l’efficacité des cores en divisant le temps de calcul par le nombre de core" (enfin un truc compréhensible quoi...). Dans ce chapitre, de tel phrases serait plutôt présenté sous la forme d'une petite équation de math bien senti suivi d'un: "Ou <em>p</em> est la dérivé relationnel d'infrastructure de la logarithme du nombre de core et <em>n</em> est le temps de calcul exprimé en second (ISU-CIPM 1967)". Bien sûr j'exagère mais dans la mesure ou je ne suis pas habitué aux équations mathématiques c'est exactement l'effet que ça m'a fait à la première équation. Et j'ai d'autant plus rie quand, après m’être bien trituré pour déchiffrer et comprendre tout ça j'ai réalisé qu'on pouvait résumer ça en une phrase... Haha! :smileFou:</p>
<p>Mais j'ai eu l’opportunité de travailler avec des matheux (ou des gens qui apprécient les maths d'une manière général) et ils ont cette tendance à jouer avec les équations mathématiques partout, y compris pour des trucs anodins (comme le temps moyen que passe les gens à la cantines en fonction des menus proposé, ou bien le temps de retards moyen des réunions suivant les dates de livraison) et ça m'a rappelé tout ces moments assez drôles. :jdicajdirien:</p>
<p>J'ai également apprécié la petite présentation de <a href="http://www.openvdb.org/" hreflang="en">OpenVDB</a>. J'ai souvent entendu parlé de OpenVDB mais je n'ai jamais compris comment il marchait sous le capot. Cette petite présentation est parfaite! Il y a un bon nombre de librairies open source utilisé dans en CGI mais elles manques souvent de présentations faciles à comprendre alors qu'il y a beaucoup de graphistes qui, bien qu'il ne soit pas développeurs, sont assez techniques et pourrais bénéficier d'une meilleur compréhension de ces librairies dans leur travail au quotidien. Donc les VFX boys, cette présentation est pour vous!</p>
<p>Pour le reste, c'était malheureusement trop <em>mathématiques</em> pour moi. Si vous trouvez difficile de lire des équations mathématiques, vous serez perdu dans ce chapitre. Ça été le cas en ce qui me concerne et je ne peut pas vraiment dire si ce qui est présenté dans le reste du chapitre est intéressant ou classique. En fait, je pense que vous pourriez vous en tirer si vous êtes un temps soit peu habitué au solver de fluides/liquides.</p>
<h3>Chapitre 6: Bullet Physics: Simulation avec OpenCL (par Erwin Coumans).</h3>
<p><img src="https://www.fevrierdorian.com/blog/public/logos/.Bullet_Physics_Logo.svg_s.png" alt="Bullet_Physics_Logo.svg.png" style="display:block; margin:0 auto;" title="Bullet_Physics_Logo.svg.png, sept. 2014" height="90" width="240" /></p>
<p>Un chapitre bien sympa auquel je ne m'attendais pas vraiment. :bravo:</p>
<p>Je ne suis pas spécialement intéressé par la physique (au sens mécanique) mais je dois admettre qu'Erwin a fait un gros travail pour décortiquer et présenter sa librairie. C'est un des deux chapitre à aborder <a href="http://en.wikipedia.org/wiki/OpenCL" hreflang="en">OpenCL</a> et le calcul GPU (le second étant le chapitre sur OpenSubdiv) mais c'est surement celui qui le fait le mieux. Bien que l'aperçu soit assez rapide, il décrit très bien les différentes étapes de créations des batchs de calcul pour le GPU et revient dessus à chacune des (très) nombreuses étape de détection de collision/calcul de répulsion. C'est d’ailleurs un très bon aperçu de comment fonctionne un moteur physique ce qui est quelque chose de très intéressant à savoir quand bon bosse en VFX.</p>
<p>Il y a aussi beaucoup de petits schémas qui sont tous très clair, chacun ne se focalisant que sur un seul problème. J'ai tendance à préférer cette approche plutôt que peu de gros schémas qui essaient d’expliquer milles choses.</p>
<p>Il fini sur des choses plus avancés.</p>
<p>Surement le chapitre le plus clair et plaisant à lire. :marioCours:</p>
<h3>Chapitre 7: OpenSubdiv: Interoperating GPU Compute and Drawing (par Manuel Kraemer)</h3>
<p>J'attendais ce chapitre avec impatience et je n'ai pas été décu. Très chouette, il est composé de deux parties.</p>
<p>La première partie explique succinctement comment la subdivision itérative Catmull Clark fonctionne, sont histoire, pourquoi une "subdivision universelle" était nécessaire, et comment ils ont choisi de la multithreader. Si vous n'êtes pas familier avec la structure de donnée géométrique en arc dur (dite <a href="http://www.flipcode.com/archives/The_Half-Edge_Data_Structure.shtml" hreflang="en">hard edge data structure</a>) vous allez surement être un peu paumé. Manuel donne quelques benchs timides et conclu que le design itératif de la subdivision Catmull Clark, bien que nécessaire pour le rendu non temps réelle (<em>off-line rendering</em>), ne s'adapte pas très bien au contraintes du temps réel. J'ai été assez surpris qu'il n'aborde pas du tout comment ils organisent leur données dans la mémoire pour améliorer le cache et bénéficier d'une meilleur vectorisation. Je ne suis pas un expert, mais après avoir lu les chapitres précédents, je me demande sérieusement si il n'y avait pas d'autres axes de recherche pour améliorer les performances. Je suppose que Manuel n'en parle pas car les gains n'était pas substantiels. Qui sait... Mais pour avoir suivi le développement d'OpenSubdiv de loin, j'ai l'impression qu'ils ont rapidement sauté sur le GPU. Comme si c'était quelque chose de plus important pour le studio (Presto semble s'appuyer dessus) et que le code CPU serait "suffisant". Les nouvelles versions de Renderman s'appuyant massivement sur du raytracing (et donc, une subdivision CPU), j'en viens à me demander si Renderman s'appuie réellement sur OpenSubdiv.</p>
<p>La seconde partie présente comment OpenSubdiv utilise le GPU (et OpenGL) pour permettre un rendu d'une subdivision temps réelle en utilisant les shaders de tesselation. Une fois encore, j'ai bien apprécié la présentation du fonctionnement d'un GPU, leurs différences (contraintes surtout) par rapport à un CPU et comment organiser et préparer ces batchs pour en bénéficier. Les Edges Creases (un "truc" très présent dans OpenSubdiv) semble avoir été un problème (tous comme la gestion des triangles/quads/polys le sont dans une algo de subdivision) mais je trouve qu'ils ont été assez ingénieux dans la façon de le gérer: Ils ont séparé chaque set de géométrie pour lui appliquer différents algorithmes. Dans la mesure ou la topologie ne change pas, le processus de séparation est fait une seul fois et le GPU ne fait que recalculer les position des points avec le shader de tesselation associé à son "set de topologie". Une approche intéressante et pragmatique pour appliquer différents algorithmes de subdivision à une seule géométrie (bien que ça semble pas mal compliquer le code).</p>
<p>Je dérive un peu pour étaler mon point de vu: Je me demande vraiment si il était pertinent de garder les Creases dans OpenSubdiv. Le concept s’accommode parfaitement avec le principe de <em>surface limite</em> cher aux rendu REYES. Mais:</p>
<ul>
<li>Cette approche ne semble pas particulièrement adapté au ray tracer qui nécessite une vrai géométrie à raycaster.</li>
<li>Des Creases un peu fort nécessite d'augmenter pas mal le niveau de subdivision pour avoir un rendu correct là où un simple bevel ou une double edge aurait fait la blague (mais complexifié la géométrie c'est vrai). Encore une fois, quand on cherche à diminuer la taille des géométrie pour les faire rentrer dans la mémoire, augmenter drastiquement la subdivision pour récupérer l'équivalent d'un bevel me semble un peu dommage.</li>
<li>Il semble complexifier et fragmenter pas mal de partie du code.</li>
<li>Code fragmenté? Structure de donnée fragmenté? Quel impact sur la mémoire (accès et taille)?</li>
</ul>
<p>Une des dernière alternative que je vois au coté gourmand (en géométrie) du Creases si on ne peut pas subdiviser à tout va c'est de modifier la normale au niveau de l'edge (pouah! Le temps de calcul du truc). Mais cela faisant, vous devez prendre l'information en compte au niveau du shading et ce n'est pas ce que OpenSubdiv est supposé faire (sans compter les petits soucis qu'on pourrait avoir pour "blender" les normales). Mais il faut avouer que ça marcherait pas mal!</p>
<p>Bref, je me demande si les Creases n'était pas un truc dont Pixar (et seulement Pixar) a besoin. Ça pourrait expliquer pourquoi un concept aussi "exotique" se retrouve dans OpenSubdiv.</p>
<p>Notez que <a href="http://www.fxguide.com/quicktakes/opensubdiv-3-0-coming/" hreflang="en">la prochaine version de OpenSubdiv arrive</a> et semble avoir bénéficier des contributions et feedbacks d'autres studios (Dreamworks?). On dirait qu'ils ont isolés la partie algorithme de la partie structure de donnée ce qui est clairement une bonne chose car devoir passer sa géométrie dans une structure de donnée externe pour la récupérer ensuite est vraiment contre productif. Je suis content de voir qu'OpenSubdiv semble aller dans la bonne direction. C'est d'autant plus important qu'il devient une partie intégrante de Maya. :)</p>
<h3>Conclusion</h3>
<p>Et bien... Un très bon livre! :D</p>
<p>Il y a une inconsistance entre les chapitres. Certains vont vraiment au bout des choses (LibEE, Fluids, Bullets), et d'autres ne font qu'effleurer la surface (Houdini, mais surtout Presto).</p>
<p>Donc ce livre est exactement ce qu'il prétend être: Multithreading for Visual Effects. Et il ne se focalise que là dessus, laissant certains points, qui mériteraient aussi d’être abordé, de coté. Mais les logiciels choisis son tellement intéressants (Sérieusement: Side Effect, Pixar, Dreamworks!) que ça en est frustrant!</p>
<p>Le livre est super classe (hard cover! :laClasse: ) bien que le prix me semble un chouilla élevé (hard cover? :ideenoire: ). Mais il s'agit d'un domaine de niche donc on va pas cracher dans la soupe (vous en connaissez vous des livres à lire dans votre canap' qui parle des logiciels les plus cools du monde? :sauteJoie: ). Et puis faut bien l'avouer, la plupart du temps c'est votre studio qui va l'acheter... :P</p>
<p>Est ce qu'il vaut le coup? Si vous avez à coder des softwares lié au VFX, n'hésitez pas une seconde! Vous n'y trouverez pas de ressources "clef en main" mais c'est un peu comme si vous aviez une discussion avec des gars brillants qui partage leur expériences autour de leur logiciel. Pas sur que vous puissiez trouver de tels infos ailleurs.</p>
<p>Si vous êtes un graphiste un peu technique, je ne vous recommanderais pas forcément de le prendre. Il est vraiment tourné autour du développement, vous n'y trouverez pas de trucs et astuces en multithreading pour améliorer votre travail. Cela dit, je vous encourage à le piquer à votre RnD ( :IFuckTheWorld: ) et à le feuilleter. Le début de chacun des chapitre est souvent assez clair.</p>
<p>Après avoir lu ce livre, je me dis que je pourrais bien payer pour d'autres du même genre qui passe en revu et en profondeur (un truc advanced j'entends) l'architecture des logiciels de VFX (Houdini, Presto, Maya, Nuke). Comment les données transitent, a quoi ressemble les structures de données utilisées, comment un type spécifique d'opération s’exécute, etc... Et pas juste un aperçu. Les docs des logiciels sont souvent trop timide sur le sujet.</p>
<p>Avant, j'étais incapable de lire et comprendre de tels codes. Je tentais de déduire le fonctionnement et la logique sous-jacente des logiciels que j'utilisais par dichotomie. Maintenant que je sais lire du code, autant y aller et voir directement le code des dis logiciels! :D</p>
<p>Malheureusement, je suis passé à coté de pas mal d'informations intéressantes à cause de mon incapacité à lire des équations de mathématiques. Je veux dire, je sais ce qu'est une matrice 4x4, les raisons pour lesquelles ont pourrait les utiliser, je sais ce qu'est un dot product, un produit en croix et comment je peut les utiliser pour obtenir un effet souhaité mais c'est à peut près tout... Je m’énervais contre moi même de n'être pas capable de déchiffrer les équations (qui au final ne semblaient pas super compliqué :nervous: ). Idem pour certains morceaux de code que j'ai eu du mal a comprendre :gne: . Je suis passé à coté de pas mal de choses dans le chapitre des Fluids.... :triste:</p>
<p>Ce qui est très intéressant c'est d'avoir rassemblé des cours d'une session Siggraph sur un sujet donnée et d'en faire un livre. J'adore l'idée car je passe des heures derrière un ordi et j'apprécie de pouvoir lire ce genre de chose sur du papier plutôt que mon écran de PC (lire un PDF me fatigue assez vite).</p>
<p>Je n'ai aucune idée du succès du livre mais j'ai entendu pas mal de gens intéressé donc on pourrait imaginé... Je ne sais pas... "Alembic/OpenVDB/OpenEXR under the hood and use cases" avec un maximum d'explications sur les fondations et l'infrastructure, jusqu'aux points clefs du code. :smileFou:</p>
<p>J'espère que vous avez apprécié cette humble review! N'hésitez pas à commentez si vous avez vous aussi lu ce livre. Je serais curieux de savoir ce que vous en pensez. :sourit:</p>
<p>Dorian</p>
<center>:marioCours:</center>
Mental ray for Maya: Diminuer le flicking du Final Gatherurn:md5:6e1993769c1741aa9a0e7135c7951be62013-07-20T17:11:00+02:002013-07-27T22:53:45+02:00NarannInfographie 3D - Boulotfinal gatherflickingfrmayamental ray<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_tn.png" alt="fg_diminuer_flicking_tn.png" style="float:left; margin: 0 1em 1em 0;" title="fg_diminuer_flicking_tn.png, juil. 2013" height="150" width="150" />Dans ce billet je vous propose d'utiliser <del>une méthode simple</del> un bricolage (on parle de mental ray for Maya là :baffed: ) visant à diminuer le flicking du Final Gather.</p>
<p>Vous allez voir que l'approche est un peu particulière mais elle vise tout bêtement à reproduire le comportement de l'option <a href="http://www.spot3d.com/vray/help/maya/150R1/render_params_advancedimap.htm#basic" hreflang="en">Interp. samples</a> de Vray.</p> <h3>Avant propos</h3>
<p>Avant de commencez sachez que c'est vraiment Vray qui m'a poussé à remettre mon nez dans mental ray, en particulier le merge des maps de Final Gather. Alors que tout est très simple dans Vray, dans mental ray, c'est la douleur (comme d'hab :septic: ).</p>
<p>En gros on va devoir injecter du script dans les <em>Pre render frame MEL</em> et <em>Post render frame MEL</em> pour recréer le comportement de Vray.</p>
<p>Mental ray permet déjà de "merger" des maps de Final Gather, mais l’intégration dans Maya ne permet pas de faire ça de manière temporelle simplement.</p>
<h3>La scène</h3>
<p>J'ai pris le genre typique de scène qui met à genou le Final Gather (et la plupart des méthodes de chaching de GI de manière générale):</p>
<ul>
<li>Aucune light (<em>Default Light</em> désactivées).</li>
<li>Un couloir blanc.</li>
<li>Du coté non visible de la caméra: Une plaque avec un material émissive blanc.</li>
<li>A l'intersection: Des sphère et des cubes colorés animés.</li>
<li>Et de l'autre coté, notre camera.</li>
</ul>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_001.png" title="fg_diminuer_flicking_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/.fg_diminuer_flicking_001_m.jpg" alt="fg_diminuer_flicking_001.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_001.png, juil. 2013" height="470" width="560" /></a></p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_003.png" title="fg_diminuer_flicking_003.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/.fg_diminuer_flicking_003_m.jpg" alt="fg_diminuer_flicking_003.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_003.png, juil. 2013" height="420" width="560" /></a></p>
<center>C'est un cas extrême, ne l’oublions pas. :papi:</center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_002.png" alt="fg_diminuer_flicking_002.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_002.png, juil. 2013" height="408" width="405" /></p>
<p>Le nombre de rebonds diffus du Final Gather est volontairement élevés pour gonfler les artefacts. Tout est présent pour avoir quelque chose de bien dégueulasse. :aupoil:</p>
<h3>Final Gather sans interpolation</h3>
<p>Voici un rendu de la scène avec un Final Gather calculé à chaque frame, sans interpolation (Mode <em>Automatic</em>):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_anim_001.gif" alt="fg_anim_001.gif" style="display:block; margin:0 auto;" title="fg_anim_001.gif, juil. 2013" height="240" width="320" /></p>
<p>Comme vous pouvez le voir, ça flick pas mal. :ideenoire:</p>
<p>Comment résoudre ça? <a href="http://docs.autodesk.com/MENTALRAY/2014/ENU/mental-ray-help/files/manual/fg_copy.html" hreflang="en">fg_copy</a> est ton amis! :dentcasse: (Plus spécifiquement, l'option <em>-f</em>).</p>
<p>Bon, en fait il est déjà possible de donner des maps de Final Gather à Maya pour qu'il les merges avant rendu. C'est d’ailleurs cette approche qui va servir de base à notre "interpolation temporelle".</p>
<h3>Interpoler les frames?</h3>
<p>En gros, le principe consiste simplement à "mélanger" plusieurs maps de Final Gather entre elles pour atténuer l'effet de flicking.</p>
<p>Pour une frame <em>n</em>, on merge les maps de Final Gather <em>n</em>-2, <em>n</em>-1, <em>n</em>, <em>n</em>+1, et <em>n</em>+2 (deux avants, deux après).</p>
<p>Dans la pratique, mental ray va mélanger les points de Final Gather ayant des normales similaires en utilisant le <em>Min Radius</em> de la scène. Deux points de Final Gather ayant une normale similaire (Je suppose que c'est le paramètre <em>Normal Tolerance</em> de la section <em>Final Gather Quality</em> qui est utilisé) et dont la distance n’excède pas le <em>Min Radius</em> de la scène (qui, si il est à zéro, correspond à 10% du diamètre de la scène) voient leur couleur et leur position mélangées.</p>
<p>Vous pouvez voir la taille de la scène que mental ray utilise dans le log:</p>
<pre>
RC 0.2 41 MB info : scene extent: (-12.34,-0.45,-17.07) : (15.83,10.96,12.07)
</pre>
<p>Cette approche est loin d'être parfaite et vous verrez qu'elle ne résout pas tous les problèmes. En effet, l'effet de <em>ghosting</em> <a name="ghosting"></a> qu'elle génère, bien que plus acceptable qu'un flicking par frame, reste particulièrement disgracieux, en particulier sur les rendus avec peu de textures/variations de couleurs (surfaces "douces"). Cela dit, cet effet peut passer totalement inaperçu sur certaines séquences (notamment les mouvements de caméra), c'est donc au cas par cas.</p>
<h3>Baker le Final Gather</h3>
<p>Pour pouvoir merger les maps de Final Gather avant rendu (5 maps dans notre cas) il faut, dans un premier temps, toutes les générer. Et c'est ce qu'on va faire. :enerve:</p>
<p>Comme il va s'agir de deux fg maps avant et deux fg maps après, pour un frame range de 101 à 110, on a donc les maps de Final Gather de l'image 99 à l'image 112.</p>
<p>Personnellement, je procède par <em>RenderLayers</em> en overridant les scripts <em>Pre render frame MEL</em> et <em>Post render frame MEL</em> pour entrer le nom de la fg maps à la valeur de la frame courante (<em>fgmap.0012.fgmap</em> par exemple) juste avant que le rendu ne se lance.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_004.png" alt="fg_diminuer_flicking_004.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_004.png, juil. 2013" height="200" width="374" /></p>
<p>Par exemple, pour le <em>RenderLayer</em> qui va générer les fg maps, j'override avec ça:</p>
<pre class="mel mel">python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.updateFgFiles()"</span><span style="color: #009900;">)</span>
python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.cleanFgParams()"</span><span style="color: #009900;">)</span></pre>
<p>Bon, c'est pas super sexy mais comme vous le savez, les <em>Pre render frame MEL</em> et <em>Post render frame MEL</em> ne supporte que... Le MEL... On demande donc à du MEL d’exécuter du Python! :baffed:</p>
<p>En mode "lisible", les deux appels sont les suivants:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> preframe
<span style="color: #008000;">reload</span><span style="color: black;">(</span>preframe<span style="color: black;">)</span>
preframe.<span style="color: black;">updateFgFiles</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Et:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> preframe
<span style="color: #008000;">reload</span><span style="color: black;">(</span>preframe<span style="color: black;">)</span>
preframe.<span style="color: black;">cleanFgParams</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Et bien entendu, il y a un petit fichier <em>preframe.py</em> à placer dans votre: <em>maya\scripts</em>.</p>
<h4>Le script</h4>
<p>Voici le contenu du <em>preframe.py</em>, contenu que je vais détailler, fonction après fonction.</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">def</span> cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[0]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[1]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[2]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[3]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> updateFgFiles<span style="color: black;">(</span><span style="color: black;">)</span>:
cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>
frame = <span style="color: #008000;">int</span><span style="color: black;">(</span><span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span> query=<span style="color: #008000;">True</span> <span style="color: black;">)</span><span style="color: black;">)</span>
fgmapFile = <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># "fgmap.0012.fgmap"</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"Set fgmap file for frame %s -> %s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>frame, fgmapFile<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherRebuild"</span>, <span style="color: #ff4500;">1</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Rebuild On</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, fgmapFile, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> prepareFgFiles<span style="color: black;">(</span><span style="color: black;">)</span>:
cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>
frame = <span style="color: #008000;">int</span><span style="color: black;">(</span><span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span> query=<span style="color: #008000;">True</span> <span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherRebuild"</span>, <span style="color: #ff4500;">2</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Freeze</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame-<span style="color: #ff4500;">2</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[0]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame-<span style="color: #ff4500;">1</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[1]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[2]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[3]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame+<span style="color: #ff4500;">2</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span></pre>
<p>Vous pouvez vous copier ça dans un <em>maya/scripts/preframe.py</em>.</p>
<h5>cleanFgParams()</h5>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[0]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[1]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[2]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[3]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[4]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[5]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span></pre>
<p>Cette fonction vide les entrées des attributs de <em>file</em> du Final Gather (met une chaine de caractère vide). Elle est utilisée juste avant de setter les noms des maps de Final Gather à sauver, et juste après (en <em>Post render frame</em>), pour être sur de vider les champs de texte.</p>
<h5>updateFgFiles()</h5>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> updateFgFiles<span style="color: black;">(</span><span style="color: black;">)</span>:
cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>
frame = <span style="color: #008000;">int</span><span style="color: black;">(</span><span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span> query=<span style="color: #008000;">True</span> <span style="color: black;">)</span><span style="color: black;">)</span>
fgmapFile = <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># "fgmap.0012.fgmap"</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"Set fgmap file for frame %s -> %s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>frame, fgmapFile<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherRebuild"</span>, <span style="color: #ff4500;">1</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Rebuild On</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, fgmapFile, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span></pre>
<p>Cette fonction est utilisé en <em>Pre render frame</em> du <em>RenderLayer</em> et sert à donner un nom correct de map de Final Gather à sauver. Elle récupère le numéro de la frame courante et génère un nom de fg map à la frame.</p>
<p>Le petit "bricolage":</p>
<pre class="python python"><span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span></pre>
<p>Sert à transformer le numéro de la frame en chaine de caractère (string) puis de lui appliquer un <em>zero fill</em> de 4, pour transformer "10" en "0010" par exemple.</p>
<h5>prepareFgFiles() <a name="prepareFgFiles"></a></h5>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> prepareFgFiles<span style="color: black;">(</span><span style="color: black;">)</span>:
cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>
frame = <span style="color: #008000;">int</span><span style="color: black;">(</span><span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span> query=<span style="color: #008000;">True</span> <span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherRebuild"</span>, <span style="color: #ff4500;">2</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Freeze</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame-<span style="color: #ff4500;">2</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[0]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame-<span style="color: #ff4500;">1</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[1]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[2]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[3]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame+<span style="color: #ff4500;">2</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span></pre>
<p>Et dernière fonction, en <em>Pre render frame</em> du <em>RenderLayer</em>, qui sert à remplir tous les champs de maps à merger lors du rendu finale. C'est le même mécanisme de génération des numéros de frame mais on en génère deux avant et deux après.</p>
<p>A ce moment là, vous avez un Final Gather en mode <em>Freeze</em>, qui va merger vos fichiers avant de les utiliser tel quel.</p>
<h3>Les render layers</h3>
<p>Voici une petite description de mes <em>RenderLayers</em>. :)</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_005.png" alt="fg_diminuer_flicking_005.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_005.png, juil. 2013" height="183" width="419" /></p>
<p>Oubliez FG_RAW qui ne servait qu'à rendre en mode <em>Automatic</em>.</p>
<h4>masterLayer</h4>
<p>Le <em>RenderLayer</em> de base est préparé de manière à recevoir les <em>Secondary FinalGather Maps</em>.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_009.png" alt="fg_diminuer_flicking_009.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_009.png, juil. 2013" height="289" width="456" /></p>
<p>Notez que, comme tous les multiattributs de Maya, si ces valeurs sont vides, les blocs sont supprimés à l'ouverture de la scène. C'est la raison pour laquelle je met le mot <em>temp</em> dedans. Dans tous les cas, je supprime ces valeurs juste avant le rendu de la frame (via la fonction <em>cleanFgParams()</em> si vous avez suivi). C'est uniquement pour empêcher Maya de supprimer les entrées (Workaroundman! :nousfesonslemal: ).</p>
<blockquote><p>Note: Chaque entrée à un <em>temp</em> avec un numéro différent juste pour que je puisse rendre le <em>RenderLayer</em> FG_RAW sans que mental ray tente de merger une fg map qu'il est en train de générer.</p></blockquote>
<p>Mais en principe, jamais une map de Final Gather nommé "temp" ne devrait être créé durant notre process. Si c'est le cas c'est que quelque chose ne marche pas. :zinzin:</p>
<h4>FGMAPONLY</h4>
<p>Ce <em>RenderLayer</em> sera le premier lancé. C'est celui qui générera les maps de Final Gather (avec deux frames avant et deux frames après).</p>
<p>Les <em>Pre render frame MEL</em> et <em>Post render frame MEL</em> à overrider (via <a href="http://download.autodesk.com/global/docs/maya2014/en_us/index.html?url=files/Vari_Work_with_attribute_overrides.htm,topicNumber=d30e650035" hreflang="en">Create Layer Override</a>) sont les suivants:</p>
<pre class="mel mel">python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.updateFgFiles()"</span><span style="color: #009900;">)</span>
python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.cleanFgParams()"</span><span style="color: #009900;">)</span></pre>
<p>Il ne faut pas oublier de passer ce <em>RenderLayer</em> en <em>Multiframe</em> (<em>Optimize for Animation</em> dans Maya). Mais l'attribut n'est pas overrideable depuis l'interface. :trollface:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_006.png" alt="fg_diminuer_flicking_006.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_006.png, juil. 2013" height="201" width="450" /></p>
<p>Il faut passer par le node <em>miDefaultOptions</em> que vous pouvez sélectionnez en tapant la commande MEL suivante:</p>
<pre class="mel mel">select miDefaultOptions</pre>
<p>Et en allant chercher le <em>Final Gather Mode</em> dans les <em>Extra Attributes</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_007.png" alt="fg_diminuer_flicking_007.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_007.png, juil. 2013" height="116" width="306" /></p>
<p>Simple comme bonjours... :mayaProf:</p>
<p>Comme l'image rendue à ce moment là ne nous sert pas, vous pouvez overrider le <em>Render Mode</em> pour ne rendre que l'étape de Final Gather:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_005a.png" alt="fg_diminuer_flicking_005a.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_005a.png, juil. 2013" height="83" width="420" /></p>
<h4>RENDERONLY</h4>
<p>C'est dans ce <em>RenderLayer</em> que sera fait le "vrai" rendu. L'idée étant de setter les différentes fg maps (<em>n</em>-2, <em>n</em>-1, <em>n</em>, <em>n</em>+1 et <em>n</em>+2) pour que mental ray les merges en <em>Freeze</em> (pas de rebuild), et nous calcule le résultat final.</p>
<pre class="mel mel">python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.prepareFgFiles()"</span><span style="color: #009900;">)</span>
python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.cleanFgParams()"</span><span style="color: #009900;">)</span></pre>
<p>Je vous invite à relire la fonction <a href="https://www.fevrierdorian.com/blog/post/2013/07/20/Mental-ray-for-Maya-Diminuer-le-flicking-du-final-gather#prepareFgFiles">prepareFgFiles()</a> pour bien comprendre ce qu'on fait.</p>
<h3>La ligne de commande</h3>
<p>Et vient le moment de lancer le rendu! :popcorn:</p>
<p>Je vous donne les deux lignes de batch pour que vous puissiez vous en inspirer:</p>
<pre>
"C:\Program Files\Autodesk\Maya2013\bin\render.exe" -mr:v 4 -mr:rt 4 -cam camera1 -rl FGMAPONLY -s 99 -e 126 -preFrame "python(\"import preframe\nreload(preframe)\npreframe.updateFgFiles()\")" -postFrame "python(\"import preframe\nreload(preframe)\npreframe.cleanFgParams()\")" -proj "D:\3D\VracProject" "D:\3D\VracProject\scenes\tuto_flick_mr_maponly.ma"
"C:\Program Files\Autodesk\Maya2013\bin\render.exe" -mr:v 4 -mr:rt 4 -cam camera1 -rl RENDERONLY -preFrame "python(\"import preframe\nreload(preframe)\npreframe.prepareFgFilesNoAnim()\")" -postFrame "python(\"import preframe\nreload(preframe)\npreframe.cleanFgParams()\")" -proj "D:\3D\VracProject" "D:\3D\VracProject\scenes\tuto_flick_mr_maponly.ma"
</pre>
<blockquote><p>Note: J'ai personnellement rencontré quelques soucis avec les overrides des <em>Pre render frame MEL</em> et <em>Post render frame MEL</em>. C'est la raison pour laquelle je les écris "en dur" dans la ligne de commande.</p></blockquote>
<h4>La génération des maps de Final Gather (première ligne)</h4>
<p>A la fin de l’exécution de la première ligne, vous deviez avoir quelque chose dans le dossier <em>renderData/mentalray/finalgMap</em> qui ressemble à ça:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_008.png" alt="fg_diminuer_flicking_008.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_008.png, juil. 2013" height="418" width="351" /></p>
<h4>Seconde ligne, le rendu!</h4>
<p>Ne tergiversons pas.</p>
<p>Avant:
<img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_anim_001.gif" alt="fg_anim_001.gif" style="display:block; margin:0 auto;" title="fg_anim_001.gif, juil. 2013" height="240" width="320" /></p>
<p>Après:
<img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_anim_002.gif" alt="fg_anim_002.gif" style="display:block; margin:0 auto;" title="fg_anim_002.gif, juil. 2013" height="240" width="320" /></p>
<center>FG Merge. Parce que je le vaux bien. :smileFou:</center>
<p>Bon, j’admets que le gif n'est pas ce qu'il y a de mieux pour juger de la qualité d'une image mais vous remarquerez que c'est beaucoup plus stable. Vous remarquerez aussi sur le mur droit, l'effet de ghosting <a href="https://www.fevrierdorian.com/blog/post/2013/07/20/Mental-ray-for-Maya-Diminuer-le-flicking-du-final-gather#ghosting">dont je parlais</a> précédemment.</p>
<p>Encore une fois, on est dans un cas extrême:</p>
<ul>
<li>Une surface émissive, aucune source de lumière direct.</li>
<li>Un couloir.</li>
<li>Des paramètres de rebonds élevés.</li>
<li>Des paramètres de qualité de Final Gather bas.</li>
</ul>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/tuto_flick_mr_maponly.7z">Voici la scène</a> pour que vous puissiez voir un peu les paramètres.</p>
<p>Important: Suivez bien vos logs car (et je ne trouve pas ça super :pasClasse: ) le mode <em>Freeze</em> n'est pas aussi bête qu'il en a l'air (<a href="http://docs.autodesk.com/MENTALRAY/2014/ENU/mental-ray-help/files/manual/options.html#finalgathering" hreflang="en">voir doc</a>). Si une map de Final Gather n'existe pas pas, il la créé quand même. Vous aurez donc l'impression qu'à la fin de ces deux lignes tout c'est bien passé (les map de Final Gather sont là ainsi que les images) mais tout flickera...</p>
<p>Quelques infos à vérifier (exemple pour le calcule de la frame 123):</p>
<pre>
RC 0.2 26 MB info : option: rebuild freeze
RC 0.2 26 MB info : option: files D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0121.fgmap
RC 0.2 26 MB info : option: D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0122.fgmap
RC 0.2 26 MB info : option: D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0123.fgmap
RC 0.2 26 MB info : option: D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0124.fgmap
RC 0.2 26 MB info : option: D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0125.fgmap
</pre>
<pre>
RCFG 0.2 27 MB info : finalgather map is frozen, using loaded from file(s)
</pre>
<p>Je vous invite donc, dans un premier temps, à lancer ces deux lignes séparéments et à vous assurer du résultat entre chacune.</p>
<p>J'insiste sur le fait que vous n'y arriverez pas forcément du premier coup. Il y a pas mal de choses à faire mine de rien. Donc persévérez et sachez que c'est possible. :hehe:</p>
<h3>Variante</h3>
<p>Comme vous pouvez le constater, le flicking des objets animés (sphères et cube) ne change pas des masses. On peut donc envisager plusieurs variantes pour résoudre le soucis.</p>
<p>La première approche consiste à générer deux séquence de fg map distinctes:</p>
<ul>
<li>L'une uniquement pour les objets animé (on passe les objets statiques en <em>primary rays</em> off) avec des paramètres de Final Gather plus élevés. Nommé <em>fg_anim.####.fgmap</em>.</li>
<li>L'autre uniquement pour les objets statiques (on passe les objets animés en <em>primary rays</em> off de manière à conserver leurs rebonds diffus sur la fg map des objets statiques). Nommé <em>fg_static.####.fgmap</em>.</li>
<li>Et on merge l'ensemble après.</li>
</ul>
<p>On peut aussi aller un cran au dessus dans la complexité (au point ou on en est :pasClasse: ) et utiliser la commande <a href="http://docs.autodesk.com/MENTALRAY/2014/ENU/mental-ray-help/files/manual/fg_copy.html" hreflang="en">fg_copy</a> pour merger les fg maps d'anim avec un radius plus élevé pour aller chercher les points de Final Gather plus loins dans l'espace. Cela dit, je ne suis pas sur que cette méthode offre de meilleurs résultats mais c'est à tester.</p>
<p>Et enfin, vous pouvez <a href="https://www.fevrierdorian.com/blog/post/2013/02/10/Combiner-les-importons-avec-le-Final-Gather">combiner ça avec les importons</a> pour avoir de meilleur points de Final Gather. :)</p>
<h3>Conclusion</h3>
<p>Je pensais que c'était impossible mais finalement, on peut bien "faker" l'option <a href="http://www.spot3d.com/vray/help/maya/150R1/render_params_advancedimap.htm#basic" hreflang="en">Interp. samples</a> de Vray dans mental ray. :sourit:</p>
<p>Bien sur, faire ça de cette manière n'est pas forcément super évident, et il est une fois de plus regrettable de devoir se battre à ce point pour profiter d'un feature accessible via un simple slider dans Vray... :redface:</p>
<p>Toutefois, j'espère que ce billet vous aura intéressé et que vous y avez appris des choses. Je pense que si on arrive à mettre ce genre de système en place, on diminue pas mal l'un des principaux soucis du Final Gather (ou techniques similaires) dans les animations.</p>
<p>N'hésitez pas à me faire des retours dans les commentaires si il manque des éléments. :dentcasse:</p>
<p>A bientôt!</p>
<p>Dorian</p>
<center>:marioCours:</center>
Houdini sous le capoturn:md5:3db5a45143dd84afde3d8d6788c164f32013-04-24T19:27:00+02:002013-07-26T17:58:04+02:00NarannInfographie 3D - Boulotfrhoudininodescriptsoptraduction<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_tn.png" alt="houdini_sous_le_capot_tn.png" style="float:left; margin: 0 1em 1em 0;" title="houdini_sous_le_capot_tn.png, mai 2013" height="150" width="150" />Ceci est la traduction d'<a href="http://forums.odforce.net/index.php?/topic/17105-short-and-sweet-op-centric-lessons/#entry104263" hreflang="en">un billet de old school</a>, posté il y a quelques mois, qui explique, très clairement, le fonctionnement interne de Houdini. Ce post est une mine d'or et je conseille à quiconque souhaitant s'améliorer sur Houdini d'en prendre connaissance.</p>
<p>Il y a beaucoup de lecture, je me suis permis de mettre des titres et quelques images pour "classer" un peu l'ensemble (le bonhomme aborde beaucoup de chose d'une traite). Alors accrochez-vous! :grenadelauncher:</p>
<p>Bien entendu, si vous êtes totalement débutant sur Houdini il n'est pas forcément pertinent de lire tout ça sans avoir suivi quelques tutos (vous partiriez en courant :hihi: ), même succinct sur comment utiliser le soft.</p> <blockquote><p>Ce que j'aimerais voir plus souvent, comme quelqu'un qui saute d'une application à l'autre (pas complètement débutant donc)... C'est une explication de comment Houdini fonctionne "sous le capot". Il y a des brides d'information un peu partout mais la plupart des tutoriaux se focalisent sur l'effet qu'ils essaient de faire.</p></blockquote>
<p>Il n'y a aucun secret sur comment Houdini fonctionne. Tout ce qui se fait dans Houdini peut être exprimé par un node. Que le node soit un opérateur codé en C++, en VEX (ou les nodes VOP représentant les fonctions VEX), Python, ou bien Digital Asset Houdini (HDA), chaque node fait son boulot et met son résultat en cache. Il n'y a pas de niveau plus bas que les nodes.
Dans Houdini, les nodes sont les routines atomiques/fonctions/programmes les plus bas niveaux. Par exemple, un node de type SOP prend la géométrie entrante et la traite en un bloc, puis cache son résultat qu'on peut visualiser:</p>
<ul>
<li>Dans le viewport.</li>
<li>Via bouton milieu de souris (MMB) sur un node pour connaitre ses statistiques.</li>
<li>Dans la Details View, pour voir les valeurs des attributs.</li>
</ul>
<p>Si il s'agit d'un SOP modificateur, il aura une dépendance sur son node d'entrée. S'il y a un changement en amont, le node actuel sera contraint de s'évaluer. S'il y a un paramètre en référence sur un autre node et que le node est marqué comme "dirty" et affecte ce node, ce node sera contraint de s'évaluer.</p>
<h3>Graph et dépendances</h3>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_005.png" alt="houdini_sous_le_capot_005.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_005.png, mai 2013" height="232" width="256" /></p>
<p>Pour expliquer la structure de "cooking" (ndt: dans Houdini, calculer une node se dit "cook") d'un graph de SOP, pour chaque cook (changement de frame, de paramètre, etc...), le graph commence à partir du node en Display/Render (ndt: petit rectangle bleu) puis remonte la chaine à la recherche de nodes ayant changé et évalue les dépendances pour chacun d’entre eux en demandant également à ces nodes s'il y a eu des changements, et ce, jusqu'à ce qu'il atteigne les nodes en tête du graph.</p>
<p>Vous pouvez configurer quelques options dans le <em>Performance Monitor</em> pour forcer Houdini à fonctionner de l'ancienne manière (H11) et voir l'arbre de l'ordre d'évaluation si vous le souhaitez. Faites-le. Il est "obligatoire" que vous le fassiez si vous souhaitez avoir une meilleure compréhension de Houdini. Il faut absolument que vous utilisiez le <em>Performance Monitor</em> si vous voulez voir comment les graphs sont évalués car il se base sur l'ordre de création ainsi que les dépendances. Oui, supprimer et remettre un object peut et va changer l'ordre d'évaluation et peut parfois résoudre des crashs. Si vous n'avez pas encore utilisé le <em>Performance Monitor</em>, alors allez-y. Utilisez-le. N'oubliez cependant pas de le désactiver car il a un impact significatif sur les performances.</p>
<p>Une autre astuce est d'utiliser le bouton milieu de souris (MMB) sur un node pour voir ce qu'il a mis en cache depuis sa dernière évaluation. L'utilisation de la mémoire, les attributs actuellement stockés, etc... Le bouton milieu de souris (MMB) est aussi utilisé que le bouton gauche. Vous pouvez voir si le node est "time dependent" ou non, ce qui aura une incidence sur la façon dont il s'évalue et comment cela affectera ses nodes en dépendance.</p>
<p>Vous pouvez utiliser le bouton droit de souris sur le node et ouvrir la <em>Dependency view</em> de cet opérateur qui vous listera toutes ses références et ses dépendances. Vous pouvez utiliser la touche "d" sur le graph puis, dans le <em>Network Editor Display Options</em>, onglet <em>Dependency</em>, activer un certain nombre d'options (liens et halos) permettant de mieux comprendre les dépendances entre les nodes.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_005_NetworkEditorDisplayOptions.png" alt="houdini_sous_le_capot_005_NetworkEditorDisplayOptions.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_005_NetworkEditorDisplayOptions.png, mai 2013" height="429" width="565" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_005_LocalDependency.png" alt="houdini_sous_le_capot_005_LocalDependency.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_005_LocalDependency.png, mai 2013" height="260" width="369" /></p>
<h3>Un File System</h3>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_004.png" alt="houdini_sous_le_capot_004.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_004.png, mai 2013" height="466" width="237" /></p>
<p>Houdini est un file system, en mémoire, et sur le disque sous la forme d'un fichier "cpio" .hip. Si vous le souhaitez, vous pouvez utiliser un shell puis lancer la commande <code>hexpand</code> sur un fichier .hip. Ceci "étalera" le fichier Houdini sous la forme d'une structure de dossier que vous pouvez lire et modifier si vous le souhaitez. Puis rassembler le tout avec la commande <code>hcollapse</code>.
Si vous voulez vraiment voir comment Houdini fonctionne en bas niveau, c'est par ici qu'il faut commencer. C'est simplement une commande Houdini <code>hscript</code> qui construit les nodes dans les dossiers des nodes eux-mêmes. Chaque node est construit en trois fichiers distincts:</p>
<ul>
<li>Le fichier qui ajoute le node et le connecte aux autres nodes.</li>
<li>Le fichier de paramètre qui définit les paramètres des nodes.</li>
<li>Et un fichier qui récupère les infos supplémentaires du node.</li>
</ul>
<p>Si vous lockez un SOP, les informations de ce node seront rassemblées de manière binaire en un seul et même fichier.</p>
<p>C'est pour cette raison que les fichiers .hip sont très petit et qu'il est inutile et déconseillé de locker ces SOP. Il est préférable de cacher un node sur le disque plutôt que de le locker, mais rien ne vous empêche de le faire. Quand vous ouvrez un fichier .hip, tous les nodes sont ajoutés, connectés, leurs paramètres sont modifiés, et les nodes sont "cookés"/évalués.
Il existe différents types de graph de node et les nodes d'un type spécifique ne fonctionnent que dans le dossier d'un type de node spécifique. C'est ce qui fait qu'on peut se retrouver bloquer un peu partout, surtout si on a l'habitude de travailler avec le <em>Build Desktop</em>, que je n'apprécie pas au passage. Il est conseillé d'avoir un arbre quelque part dans l'interface pour visualiser comment le graph se présente quand vous travaillez. C'est également très pratique pour naviguer rapidement dans votre scène. Le <em>Technical Desktop</em> est un bon choix pour quand on récupère le travail de quelqu'un d'autre car il y a une arborescence et quelques autres volets tels que la Details View, Render Scheduler et autres.
Si vous voulez utiliser le <em>Technical Desktop</em> et comprendre un travail fait avec le <em>Build Desktop</em>, ouvrez simplement graph puis l'onglet Parameter et maintenant le coté droit est identique au <em>Build Desktop</em>, mais vous pouvez voir la <em>Tree View</em> et voir ou les nodes sont mis.</p>
<h3>Technical Desktop</h3>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_003.png" alt="houdini_sous_le_capot_003.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_003.png, mai 2013" height="426" width="454" /></p>
<p>Un nouveau fichier Houdini est un livre non lu, bourré d'idées intéressantes. En utilisant un Desktop qui expose un onglet <em>Tree View</em>, vous pouvez rapidement voir ce que l'utilisateur a essayé de faire en quelques secondes. Encore une fois, utilisez le <em>Technical Desktop</em> dès le départ si vous utilisez le <em>Build Desktop</em> (si vous me connaissez, vous saurez que je vous forcerais à avoir une <em>Tree View</em> sous les yeux).
Vous pouvez rapidement traverser la scène et inspecter le graph.
Si ce n'est pas assez, vous pouvez ouvrir le Performance Monitor et voir quel node fait le plus gros travail.</p>
<p>Il n'y a pas vraiment besoin de vidéos, juste un fichier .hip. Bien entendu, c'est une vraie aide si la scène est commentée et les nodes nommés en fonction de leurs intentions.</p>
<h3>SOP</h3>
<blockquote><p>Les questions comme: Où les Attributs sont stockés, comment les récupérer (en VOP, Python, Expression), Différence entre les Primitives / Points / Vertex / Volume, les différents Contexts, tout spécialement les explications plus générales sur les contextes DOP / POP... Solver et Multisolver... comme un tutorial Houdini général mais de niveau intermédiaire vers avancé...</p></blockquote>
<p>Restons sur les SOPs.</p>
<p>Dans Houdini, les attributs sont une partie intrinsèque de la géométrie qui est cachée par chaque SOP et non une entité distincte qui doit être gérée indépendamment. C'est ce qui rend les SOPs si élégant. Ce fil entre deux SOP, c'est la géométrie qui passe d'un SOP à l'autre, ces attributs et tout le reste. Et non un lien par attribut (ce qui, dans d'autres logiciels, peut être un attribut géométrie, un attribut paramètre, etc...). Ceci permet à Houdini de manipuler une énorme quantité de géométrie avec beaucoup d'attributs très facilement. Tous les SOPs feront de leur mieux pour traiter les attributs en conséquence (certains mieux que d'autres et pour ces dernier, n'hésitez pas à envoyer un RFE ou un Bug à Side Effects pour voir si quelque chose peut être fait).</p>
<p>Vous pouvez créer des attributs géométriques supplémentaires dans Houdini en utilisant ces SOPs:</p>
<ul>
<li>Point SOP créé les attributs de point "standard".</li>
<li>Vertex SOP créé les attributs de vertex "standard".</li>
<li>Primitive SOP créé les attributs de primitive "standard".</li>
<li>Utilisez le SOP <em>Attribute Create</em> pour créer un attribut ponctuel de différentes classes (float, vector, etc...) de point, vertex, primitive ou Detail.</li>
<li>Utilisez les VEX/VOPs pour créer un attribut de point standard et ponctuel.</li>
<li>utilisez les SOPs Python pour créer un attribut de géométrie standard ou ponctuel.</li>
</ul>
<h4>Distinction entre point et vertex</h4>
<p>Dans Houdini, une clarification qui doit être faite est la distinction entre un attribut de type "point" et un attribut de type "vertex". Certains logiciels utilisent le terme vertex pour désigner soit les attributs de point ou les attributs de prim/vertex. Les jeux vidéos sont attaché à cette définition rendant la confusion encore plus grande, mais hélas, ce n'est pas correct. Dans Houdini, vous devez faire la distinction entre les attributs de point et les attributs de vertex très tôt. Un attribut de point est le niveau le plus bas qu'une information puisse avoir. Par exemple, vector4 P position (la quatrième information étant le poids dans le cas des NURBS) et un attribut de point qui définit un point dans l'espace. C'est tout ce dont vous avez besoin: des points. Pas de primitives ou autres. Vous pouvez ensuite instancier des choses sur ces points au moment du rendu.
Vous pouvez assigner n'importe quel attribut supplémentaire sur ce point.</p>
<p>Pour construire une Primitive, vous avez besoin d'un point (dans le cas d'une Primitive vextex) pour référencer une position et un poid. Dans le cas d'un polygone, ce sont les vertex du polygone qui indexent ces points. Vous pouvez visualiser ces informations dans la <em>Details View</em> en inspectant les attributs des vertex. Le numéro de vertex est présentés sous la forme <primitive_number>:<vertex_number> et la première colonne ("Point Num") affiche à quels points chaque vertex se réfère.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_001.png" alt="houdini_sous_le_capot_001.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_001.png, mai 2013" height="382" width="726" /></p>
<p>Bien évidemment, vous pouvez avoir plusieurs vertex se référant au même point et c'est ce "partage" qui permet d'obtenir un shading doux par défaut, sans utiliser les normales des vertex (car les normales des points seront moyennées automatiquement par les vertex se partageant ce point).</p>
<p>Par exemple, dans le cas d'une Primitive Sphere, il y a un simple point dans l'espace, puis une primitive de type sphere avec un seul vertex qui référence la position du point pour placer la sphère. Puis il y a les données intrinsèques de la sphère (qui seront disponible dans la prochaine version majeur (ndt: Houdini 12)) ou vous pourrez voir les propriétés de cette sphère tel que ces bornes (ou vous pouvez augmenter le diamètre), son volume, etc...</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_002.png" alt="houdini_sous_le_capot_002.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_002.png, mai 2013" height="438" width="725" /></p>
<p>Les autres types de primitives composés d'un seul point et d'un seul vertex sont les primitives de type volume, metaball, grille VDB, archive Alembic, etc...</p>
<p>Comment, par exemple, un SOP de type <em>Transform</em> sait faire la différence entre transformer une primitive de type sphère une primitive de type sphère polygonale? La réponse est qu'il a été programmé pour gérer les primitives de type sphères d'une manière compatible avec n'importe quelle géométrie polygonale. Il en est de même avec les Volumes. Il a été programmé pour gérer les volumes de manière à donner à l'utilisateur final le résultat souhaité.</p>
<p>Cela signifie que tout SOP correctement codés se chargera tout type de primitives de manière cohérente. Certains SOPs sont uniquement destinés aux surfaces paramétriques (<em>Basis</em>, <em>Refine</em>, <em>Carve</em>, etc) et d'autres pour les polygones (PolySplit, etc), mais pour la plupart, la majorité des SOPs peuvent fonctionner avec tous les types de primitives.</p>
<h4>Les Attributs</h4>
<p>Qu'en est-il des attributs? Le SOP <em>Carve</em>, par exemple, peut découper n'importe quelle géométrie polygonale à un point donné. Il interpolera probablement bilinéairement tous les attributs présents sur la géométrie entrante puis cachera le résultat. C'est ce comportement automatique pour n'importe quel point, vertex, primitive et attributs particuliers qui fluidifie le travail avec le s SOP.</p>
<p>Comment Houdini sait quoi faire avec les vertex attributs quand la position P, la vélocité V et la normale de la surface N doivent être traités différemment?
Si par exemple, vous exécuter une rotation via un SOP <em>Transform</em> et que la géométrie a des normales N, des vecteurs de vélocité V, et un cache de position "rest", chaque attribut sera traité correctement (en fait, N car c'est un attribut par défaut connu mais pour les attributs customs, vous pouvez spécifier une "intention" sur le vecteur qui lui dira si il doit être considéré comme un type vecteur, une position en 3 float, ou de type normal de surface). C'est ce comportement automatique avec les attributs combiné au fait que vous n'ayez pas à gérer les attributs à la main qui rend l'utilisation des SOPs facile et puissant, sans avoir à recourir au code.</p>
<p>Rappelez-vous que chaque SOP est un petit programme en lui-même. Il aura ses propres comportements, ces propres variables locales s'il supporte plusieurs attributs dans son code, ses propres paramètres, sa propre manière de gérer les différents types de primitive (polygones, NURBS, Beziers, Volumes, grilles VDB, Metaballs, etc...). Si vous traitez chaque SOP comme un plugin autonome, vous êtes sur la bonne voie. Chaque SOP a sa propre page d'aide rédigée correctement qui vous expliquera ce que ce plugin fait, ce que font les paramètres, les variables locales disponibles s'il y en a, les nodes liés à ce node, et finalement, des fichiers d'exemple que vous pouvez loader dans la scène courante ou une scène externe.</p>
<p>Beaucoup de hardcores users d'Houdini amassent de l’expérience uniquement en trainant dans les fichiers d'exemples de l'aide et c'est une très bonne façon d’apprendre Houdini car tout y est fait de node. Pour vous dire, si nous avions locké la géométrie dans les fichiers d'aides, le téléchargement de Houdini prendrait des gigas. Il n'y a donc que des nodes dans l'aide et ce sont les seules choses que vous ayez besoin de connaitre.</p>
<p>Je ne vais pas parler des DOPs pour l'instant car c'est un type différent pensé pour la simulation. Invariablement, un graph de DOPs finit par être référencé par un SOP pour aller chercher de la géométrie. Donc au final, c'est juste de la géométrie, et donc, des SOPs.</p>
<h3>Shelf Tools et scripts</h3>
<blockquote><p>Je grince des dents quand je vois quelqu'un utiliser un <em>Shelf Tools</em> dans un tutoriel... (Je veux savoir exactement ce qui se passe)</p></blockquote>
<p>Les <em>Shelf Tools</em> où ils sont, mais je comprends ce que vous voulez dire. Oui, il n'y a rien qui puisse connecter un tas de node dans différents graphs puis vous référencer le tout. Faites cela pour en partant de zéro pour une simulation une ou deux fois, super! Faites cela des dizaines de fois par semaine, et bien c'est là que les <em>Shelf Tools</em> et les HDAs vous rendent la vie plus simple.</p>
<p>Mais ne soyez pas effrayés par les <em>Shelf Tools</em>. Tout ces outils sont de simples scripts qui placent les opérateurs ensembles et set les valeurs des paramètres pour vous. Il n'y a aucune quand au fichier .hip que vous sauvez. Si vous êtes du genre ultra-hardcore, vous ne sauvez même pas le fichier .hip et que vous refaites toutes les connections de zéro, tout le temps, chaque fois d'une manière un peu différente, en évoluant, en apprenant. Donc si vous trouvez cette logique des <em>Shelf Tools</em> aussi détestable, sachez que chaque fois que vous ouvrez une scène .hip, vous trichez. :P
Ca me rappelle l'argument du menuisier quant à ce qui est construit à la main et ce qui ne l'est pas. Je veux dire, à partir du moment où vous utilisez n'importe quoi d'autres que vos ongles et vos dents pour travailler le bois, vous trichez, mais nous ne faisons pas la distinction pour autant. Les menuisiers utilisent du métal et du verre pour travailler le bois parce que les ongles mettent trop de temps à repousser et les dents endommagées ne repoussent pas. Mais je m'égare...</p>
<p>Racontez ça à des power users d'autres applications qui ont tapé un code propre et impec mais qui ont toujours peur que la prochaine release rende leurs routines favorites obsolètes. Avec des nodes, vous avez un type et des paramètres. S'ils ne changent pas de build en build, ils se chargeront correctement. Je peux ouvrir des fichiers d'avant qu'il soit de type .hip, quand ils portaient l'extension .mot (de Sage pour ceux qui se souviennent) de 1995. Ils s'ouvrent toujours, avec quelques petites erreurs tout de même mais ils s'ouvrent toujours. Un SOP <em>Point</em> est un SOP <em>Point</em> et un SOP <em>Copy</em> est un SOP <em>Copy</em>. Aucune crainte que des trucs deviennent obsolètes. Il suffit de taper la commande "ophide" dans le textport de Houdini et vous pourrez encore trouver les SOPs Limb et Arm (wtf?). LOL!</p>
<p>La première chose que je fais chaque matin? Téléchargez la dernière build. Lire le log des builds. S'il y a quelque chose d'intéressant dans cette build, je fais un test en partant de zéro.
Alors lisez forums si vous avez le temps et répondez à des questions à partir de zéro si vous le pouvez.
Tout ça au nom de la pratique.</p>
<p>Comme précisé plus haut, gardez à l'esprit qu'un fichier .hip est une simple collection de fichier de script dans un système de dossier sauvé sur le disque. Un HDA Houdini est de la même vaine. Idem pour un <em>Shelf Tool</em>: Un script qui ajoute et connecte des nodes et change leurs paramètres. Générer un amas de géométrie et le stocker dans un node de shape ne vous permettra jamais de savoir comment vous y êtes arrivés.</p>
<p>Afin d'aider l'utilisateur à suivre qui a créé quoi, vous pouvez utiliser le raccourcis clavier "N" dans n'importe quel graph pour basculer le nom du node entre:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_006.png" alt="houdini_sous_le_capot_006.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_006.png, mai 2013" height="150" width="205" /></p>
<center>Label par défaut</center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_007.png" alt="houdini_sous_le_capot_007.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_007.png, mai 2013" height="161" width="264" /></p>
<center>Le nom de l'outil qui a créé ce node</center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_008.png" alt="houdini_sous_le_capot_008.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_008.png, mai 2013" height="159" width="221" /></p>
<center>Rien</center>
<p>Ceci combiné aux options d'affichages des dépendances du graph (raccourci "d") devrait vous permettre de comprendre ce que chaque <em>Shelf Tool</em> fait dans votre scene.</p>
<h3>Naviger dans le File System Houdini</h3>
<p>Si vous voulez creuser encore plus profond, vous pouvez utiliser l'onglet textport et utiliser les commandes:</p>
<ul>
<li><code>opcf</code> (aliasé en <code>cd</code>)</li>
<li><code>opls</code> (aliasé en <code>ls</code>)</li>
<li><code>oppwd</code> (aliasé en <code>oppwd</code> et <code>pwd</code>)</li>
</ul>
<p>Et ce, pour naviguer dans la scène Houdini via le textport comme vous le feriez dans un shell unix.</p>
<p>Une commande que j'aime montrer à ceux qui s’intéressent plus particulièrement à comment Houdini fonctionne. Taper (par exemple):</p>
<pre class="bash bash"><span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>obj</pre>
<p>Puis de faire un:</p>
<pre class="bash bash">opls <span style="color: #660033;">-al</span></pre>
<p>Et voir leur liste de node:</p>
<pre class="bash bash"><span style="color: #000000; font-weight: bold;">/</span> -<span style="color: #000000; font-weight: bold;">></span> <span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>obj
<span style="color: #000000; font-weight: bold;">/</span>obj -<span style="color: #000000; font-weight: bold;">></span> opls
grid_object1
<span style="color: #000000; font-weight: bold;">/</span>obj -<span style="color: #000000; font-weight: bold;">></span> opls <span style="color: #660033;">-al</span>
<span style="color: #660033;">----h---</span> ipr_camera <span style="color: #7a0874; font-weight: bold;">(</span><span style="color: #000000;">3</span><span style="color: #7a0874; font-weight: bold;">)</span> May <span style="color: #000000;">3</span> <span style="color: #000000;">17</span>:<span style="color: #000000;">22</span> Narann <span style="color: #7a0874; font-weight: bold;">[</span>rwxrwxrwx<span style="color: #7a0874; font-weight: bold;">]</span>
d---e--- grid_object1 <span style="color: #7a0874; font-weight: bold;">(</span><span style="color: #000000;">5</span><span style="color: #7a0874; font-weight: bold;">)</span> May <span style="color: #000000;">3</span> <span style="color: #000000;">17</span>:<span style="color: #000000;">31</span> Narann <span style="color: #7a0874; font-weight: bold;">[</span>rwxrwxrwx<span style="color: #7a0874; font-weight: bold;">]</span>
<span style="color: #660033;">----h---</span> ipr_camera1 <span style="color: #7a0874; font-weight: bold;">(</span><span style="color: #000000;">3</span><span style="color: #7a0874; font-weight: bold;">)</span> May <span style="color: #000000;">3</span> <span style="color: #000000;">17</span>:<span style="color: #000000;">26</span> Narann <span style="color: #7a0874; font-weight: bold;">[</span>rwxrwxrwx<span style="color: #7a0874; font-weight: bold;">]</span>
<span style="color: #660033;">----h---</span> ipr_camera2 <span style="color: #7a0874; font-weight: bold;">(</span><span style="color: #000000;">3</span><span style="color: #7a0874; font-weight: bold;">)</span> May <span style="color: #000000;">3</span> <span style="color: #000000;">17</span>:<span style="color: #000000;">26</span> Narann <span style="color: #7a0874; font-weight: bold;">[</span>rwxrwxrwx<span style="color: #7a0874; font-weight: bold;">]</span></pre>
<p>Vous aurez un retour très similaire à ceux qu'on peut avoir dans un shell unix listant les fichiers. Gardez à l'esprit que Houdini "EST" un file system avec des dépendances complexes supplémentaires toutes triées pour vous.
Il y a des permissions <code>user/group/other</code>. Oui, vous pouvez utiliser <code>opchmod</code> (non aliasé en <code>chmod</code> mais ça se fait facilement faite avec la commande <em>hscript</em> <code>alias</code>) pour changer la permission des nodes.</p>
<p>Par exemple:</p>
<p><code>opchmod 000 *</code> supprimera toutes les permissions read/write/execute de tous les nodes du répertoire courant.</p>
<pre class="bash bash"><span style="color: #000000; font-weight: bold;">/</span>obj -<span style="color: #000000; font-weight: bold;">></span> opchmod 000 <span style="color: #000000; font-weight: bold;">*</span>
<span style="color: #000000; font-weight: bold;">/</span>obj -<span style="color: #000000; font-weight: bold;">></span> opls <span style="color: #660033;">-al</span>
<span style="color: #660033;">----h---</span> ipr_camera <span style="color: #7a0874; font-weight: bold;">(</span><span style="color: #000000;">3</span><span style="color: #7a0874; font-weight: bold;">)</span> May <span style="color: #000000;">3</span> <span style="color: #000000;">17</span>:<span style="color: #000000;">46</span> Narann <span style="color: #7a0874; font-weight: bold;">[</span>---------<span style="color: #7a0874; font-weight: bold;">]</span>
d---e-cs grid_object1 <span style="color: #7a0874; font-weight: bold;">(</span><span style="color: #000000;">5</span><span style="color: #7a0874; font-weight: bold;">)</span> May <span style="color: #000000;">3</span> <span style="color: #000000;">17</span>:<span style="color: #000000;">46</span> Narann <span style="color: #7a0874; font-weight: bold;">[</span>---------<span style="color: #7a0874; font-weight: bold;">]</span>
<span style="color: #660033;">----h---</span> ipr_camera1 <span style="color: #7a0874; font-weight: bold;">(</span><span style="color: #000000;">3</span><span style="color: #7a0874; font-weight: bold;">)</span> May <span style="color: #000000;">3</span> <span style="color: #000000;">17</span>:<span style="color: #000000;">46</span> Narann <span style="color: #7a0874; font-weight: bold;">[</span>---------<span style="color: #7a0874; font-weight: bold;">]</span>
<span style="color: #660033;">----h---</span> ipr_camera2 <span style="color: #7a0874; font-weight: bold;">(</span><span style="color: #000000;">3</span><span style="color: #7a0874; font-weight: bold;">)</span> May <span style="color: #000000;">3</span> <span style="color: #000000;">17</span>:<span style="color: #000000;">46</span> Narann <span style="color: #7a0874; font-weight: bold;">[</span>---------<span style="color: #7a0874; font-weight: bold;">]</span></pre>
<p>Et devinez quoi? Les paramètres ne sont plus disponibles à la modification.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_04_24_Houdini_sous_le_capot/houdini_sous_le_capot_009_opchmod.png" alt="houdini_sous_le_capot_009_opchmod.png" style="display:block; margin:0 auto;" title="houdini_sous_le_capot_009_opchmod.png, mai 2013" height="438" width="646" /></p>
<p>N'oubliez pas de le dire à vos victimes ou de leur régler leur souci sinon vous pourriez bien perdre votre job tout seul. <code>opchmod 777 *</code> rend les permissions. Un <code>opls -al</code> vous permettra de le vérifier.</p>
<p>Maintenant, vous savez ce que notre système de licence fait aux états des nodes. Vous pouvez modifier les permissions <code>read</code> et <code>execute</code> de tous les nodes, mais la permission <code>write</code> d'un node DOP ou SOP n'est modifiable que si vous possedez la licence Houdini FX.</p>
<p>Tout en sachant ça, le fichier .hip est vraiment un livre d'histoire avec beaucoup de façon de savoir qui a créé quel node et quand, quel tool il a utilisé pour le créer, quelles dépendances possèdent ce node, est ce qu'il est time dépendant, et plus... Tout ça en inspectant rapidement.</p>
<h3>Conclusion</h3>
<p>Après tout ça, apprendre Houdini se résume simplement à apprendre chaque node et pratiquer, pratiquer, pratiquer. Oh! Et si vous ne le savez pas encore, beaucoup de nodes ont une histoire riche (certains sont plus vieux de 30 ans) et peuvent faire beaucoup de choses. Donc inspirez-vous de tout ça, lisez la documentation des nodes, étudiez les fichiers d'exemples et avancez. Plus vous maitriserez de nodes, plus vous aurez de manière de résoudre un problème, plus vite vous travaillerez et meilleurs vous serez. Plus vous ferez ça et plus pertinents seront vos choix. La courbe d'apprentissage est sans fin, visuel et WYSIWYG. B)</p>11 Second Club Mars 2013urn:md5:8f8c5c69c764482debe77824365b62102013-04-23T18:24:00+02:002020-07-23T08:57:31+02:00NarannInfographie 3D - Boulot11secondclubenfrlightingmayavray<iframe src="https://player.vimeo.com/video/64650552" width="640" height="360" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>
<h3>Français</h3>
<p>Camille Campion a gagné le <a href="http://www.11secondclub.com/competitions/march13/winner" hreflang="en">11 Second Club du mois de Mars</a>! :bravo:</p>
<p>C'est moi qui me suis chargé de son lighting. C'était surtout un bon gros prétexte pour bosser le soir sur VRay et rendre de l'anim full CG. :hehe:</p>
<p>Les délais étaient tendus (un mois pour tout faire), heureusement que Camille anime rapidement (il ne le dit nulle part parce qu'il est modeste mais il a animé ça en 15 jours!). Les ressources étaient assez limitées (C'est ça... Quand on a l'habitude de bosser avec des render farms, ça fait tout drôle de revenir sur son PC maison. :baffed: ).</p>
<p>Le rigg utilisé fut <a href="http://www.animschool.com/DownloadOffer.aspx" hreflang="en">Malcom</a> (j'en profite pour dire que ce rigg voit ses assignations faites "par face" ce qui est assez gênant au moment du lighting).</p>
<p>Au niveau lighting:</p>
<ul>
<li>Une grosse area light blanche sur la vitre.</li>
<li>Une grosse area light orange dans le bar.</li>
<li>Quelques IES au plafond.</li>
</ul>
<p>Si vous avez regardé <a href="http://www.11secondclub.com/competitions/march13/winner" hreflang="en">la version du 11 Second Club</a>, vous remarquerez que la version de ce billet est plus "orange". En fait, Camille m'a aidé à calculer les images sur sa machine et le profil IES (peu d'utilité à utiliser une IES la dessus, j'en conviens) a été oublié. Donc les quelques lumières IES de la scène ne rendaient rien... :dtcFuckSmile:</p>
<p>Les IES étant subtiles et le temps manquant, on a continué la dessus pour la version 11 Second Club. :smileFou:</p>
<p>Merci à <a href="http://deex.info/wordpress2/" hreflang="en">Damien Bataille</a> pour ses conseils avisés.</p>
<p>Le compo à été fait sous Nuke.</p>
<h3>English</h3>
<p>Camille Campion won the <a href="http://www.11secondclub.com/competitions/march13/winner" hreflang="en">11 Second Club of March</a>! :bravo:</p>
<p>I've been in charge of its lighting. It was mainly a by opportunity to work with VRay and render full CG animation. :hehe:</p>
<p>Deadline was short (a month for everything). Fortunately, Camille is a very quick animator (he don't say this anywhere because it's an humble guy but he animate this in 15 days!). Hardware was also very limited (When you're used to deal with renderfarms, it's weird to go back on your home PC. :baffed: ).</p>
<p>The rigg used was <a href="http://www.animschool.com/DownloadOffer.aspx" hreflang="en">Malcom</a> (I want to say that rigg has its material assignments done "by face" which is quite annoying during lighting).</p>
<p>Lighting</p>
<ul>
<li>A big white area light on the window.</li>
<li>A big orange area light in the bar.</li>
<li>Some IES on the ceil.</li>
</ul>
<p>If you've watch <a href="http://www.11secondclub.com/competitions/march13/winner" hreflang="en">the 11 Second Club version</a>, you will notice the version of this post is more "orange". Acutally, Camille help me to render images on its computer and the IES profile (not so usefull on a such shot, I agree) has been lost. So, the few IES light was actually blacks... :dtcFuckSmile:</p>
<p>As IES effect was subtle and we don't have much time, we continue that way for the 11 Second Club version. :smileFou:</p>
<p>Thanks to <a href="http://deex.info/wordpress2/" hreflang="en">Damien Bataille</a> for its good advices.</p>
<p>Compositing has been made in Nuke.</p>Picker une couleur a l’extérieur de l'interface Maya 2011 (Qt)urn:md5:d6833b33a3e32ce6681af0878f388a5c2013-03-28T20:13:00+01:002013-07-26T17:58:58+02:00NarannInfographie 3D - Boulotfrmayapickerqt <p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_28_color_picker/maya_color_picker_tn.png" alt="maya_color_picker_tn.png" style="float:left; margin: 0 1em 1em 0;" title="maya_color_picker_tn.png, mar. 2013" height="150" width="150" />Un billet "ultra express"! :marioCours:</p>
<p>Depuis Maya 2011 et son passage à Qt, le <em>picker</em> de couleur a été modifié et il n'est plus possible de picker une couleur en dehors de l'interface Maya.</p>
<p>En fait si, et c'est tout con a faire! :hehe:</p>
<p>Il suffit, une fois le <em>picker</em> sélectionné, de cliquer dans l'interface Maya mais de garder le bouton gauche enfoncé et de glisser à l’extérieur de l'interface pour récupérer la couleur qui vous intéresse.</p>
<p>J'avoue, <a href="http://mayafeedback.autodesk.com/forums/160518-small-annoying-things-to-fix-in-maya-forum/suggestions/2993858-allow-the-color-picker-to-operate-on-anything-visi" hreflang="en">je n'ai pas trouvé ça tout seul</a>. :baffed:</p>Voxelisation polygonale via l'API Python Mayaurn:md5:d298e12473f80efc972d74e54de4a4ed2013-03-16T12:04:00+01:002013-07-26T17:59:21+02:00NarannScript et codeapifrgeometriemayapythonvoxel<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_vray_tn.png" alt="voxel_vray_tn.png" style="float:left; margin: 0 1em 1em 0;" title="voxel_vray_tn.png, mar. 2013" height="150" width="150" />Et encore un petit script prétexte à utilisation de l'API Maya. :sauteJoie:</p>
<p>L’idée est de reproduire l'effet de géométrie en voxel que vous avez peut être <a href="http://www.bilderzucht.de/blog/3d-pixel-voxel/" hreflang="en">déjà observe</a>.</p>
<p>Rien de bien méchant et ça existe surement déjà et en plus rapide. Pour tout vous dire, je n'ai même pas cherché. Le but est encore une fois d'utiliser l'API. :baffed:</p> <h3>Principe</h3>
<p>Avant de commencez, sachez que ce problème est un cas d’école. Il y a mille et une façons de le résoudre, chaque méthode ayant ces avantages et ces inconvénients. :reflechi:</p>
<p>La méthode choisi ici est la plus simple: Pour chaque vertex de la géométrie, on trouve la position "arrondi au cube près" et on génère un cube.</p>
<p>La seule difficulté de cette implémentation est, une fois la position du vertex récupéré, de savoir ou doit être le centre du cube. :seSentCon:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_vray.png" title="voxel_vray.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/.voxel_vray_m.jpg" alt="voxel_vray.png" style="display:block; margin:0 auto;" title="voxel_vray.png, mar. 2013" height="420" width="560" /></a></p>
<h3>Le code</h3>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
voxelStep = <span style="color: #ff4500;">1.0</span>
sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span>
inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
pointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
inMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>pointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span>
grpName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>empty=<span style="color: #008000;">True</span><span style="color: black;">)</span>
voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>pointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span> :
cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span>
myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpName<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span></pre>
<h3>Les explications</h3>
<pre class="python python">sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Fondamentalement, une <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_selection_list.html,topicNumber=cpp_ref_class_m_selection_list_html" hreflang="en">MSelectionList</a> est une liste de MObject.</p>
<p>Je n'ai jamais vraiment compris ce que la notion de "sélection" voulait dire avec cet objet car il ne "sélectionne" rien. :bete:</p>
<p>La particularité d'une <em>MSelectionList</em> c'est de pouvoir récupérer le <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MObject</a> d'un node Maya depuis son nom, ce qu'on fait plus loin.</p>
<pre class="python python">dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>C'est toujours assez difficile d'expliquer ce qu'est le DAG de Maya et encore plus un DAG path. :gne:</p>
<p>Pour faire un gros résumé:</p>
<ul>
<li>DAG: C'est la "hiérarchie" (parent/enfant) de Maya.</li>
<li>DAG path: C'est le "chemin" d'un node en passant par la hiérarchie (et donc, en ayant toutes ces transformations).</li>
</ul>
<p>Beaucoup de fonctions de l'API Maya nécessitent un DAG path pour pouvoir fonctionner.</p>
<p>Par exemple, on ne peut récupérer les coordonnées dans l'espace des vertices d'un node de shape si on ne connait pas le chemin par lequel on y arrive. Deux instances ont un seul node de shape mais il s'agit bien de deux DAG path différents et les coordonnes dans l’espace du même vertex auront deux valeurs possibles suivant "par ou on passe".</p>
<pre class="python python">sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span></pre>
<p>On ajoute l'object Maya "pSphere1" dans la <em>MSelectionList</em> et on récupère son DAG path (le zéro étant l'index dans la liste de sélection).</p>
<p>Nous avons donc "converti" "pSphere1" (qui ne veut rien dire en API Maya) en vrai <em>MObject</em>.</p>
<pre class="python python">inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span></pre>
<p>Par défaut, tout ce qu'on récupère dans l'API ce sont des <em>MObjects</em>. En général, on vérifie le type du <em>MObject</em> via un <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MObject.apiType()</a> ou <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_dag_node.html,topicNumber=cpp_ref_class_m_fn_dag_node_html" hreflang="en">MObject.hasFn()</a></p>
<p>Mais là on part du principe que le user a bien donné une mesh. :siffle:</p>
<p>La fonction <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MFnMesh</a> permet d'avoir à disposition un "vrai" objet (en terme programmation j'entend) sur lequel on va pouvoir agir pour récupérer des informations. C'est un <em>function set</em>.</p>
<p>Je vous invite a lire <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">la documentation</a> afin de comprendre le comment du pourquoi des <em>function sets</em>.</p>
<p>Nous avons donc un object _inMesh_ qui va nous servir a récupérer les vertices de notre mesh.</p>
<pre class="python python">pointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
inMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>pointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span></pre>
<p>La première ligne créée un tableau de point <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html" hreflang="en">MPointArray</a> pour accueillir les positions des vertices de notre mesh.</p>
<p>Et la second ligne remplit le tableau avec les coordonne des vertices en espace monde (position par rapport au centre de la scène, non par rapport au centre de l'objet lui même).</p>
<pre class="python python">grpName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>empty=<span style="color: #008000;">True</span><span style="color: black;">)</span></pre>
<p>Ici on ne fait que créer un groupe vide qui accueillera les cubes que nous allons créer par la suite. C'est juste qu'il est plus pratique de supprimer un groupe avec tout dedans que de sélectionner tous les cubes à la pogne. :dentcasse:</p>
<pre class="python python">voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>On créé une liste qui servira a stocker les identifiant (ou code) des zones ou les cubes on déjà été générés. J'y reviens plus bas.</p>
<pre class="python python">cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep</pre>
<p>Et voici la petite ligne qui fait tout. Vous allez voir, elle est très simple.</p>
<p>Alors, qu'est ce qu'on cherche à faire avec ça?</p>
<p>On va partir du principe que la taille voulue des cubes fait 2.0.</p>
<p>On tombe sur un vertex positionné en X à 10.2. Bien entendu, on ne va pas placer notre cube au centre du vertex (à 10.2). On n'obtiendrais pas du tout l'effet voulu. :nannan:</p>
<p>Il nous faut la position exacte du cube. Il faut "comptez en nombre de cube".</p>
<p>Comment savoir à quoi correspond la distance 10.2 en cube de taille 2.0? En faisant tout simplement: 10.2/2.0. Soit 5.1.</p>
<p>Comme on ne peut pas avoir 5.1 cubes, on arrondi via la fonction round(). Dans le cas de round(5.1), on obtient 5 (5.7 aurait donne 6).</p>
<p>On sait donc maintenant que si on crée un cube de taille 2.0, il faut le déplacer de 5 fois sa taille pour qu'il englobe le vertex. On multiplie donc la valeur arrondi (5) par la taille d'un cube (2) pour obtenir une nouvelle position: La position du cube, non plus calée sur le vertex mais calée sur la grille du voxel.</p>
<p>Et voila, vous avez capté! :laClasse:</p>
<p>On fait ça pour les trois axes.</p>
<pre class="python python">cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span></pre>
<p>Ici, on créé une "signature" (en string) de la position du cube et on la stock dans une liste. Comme ça, si on retombe sur un vertex qui, une fois arrondi, se retrouve au même endroits qu'un cube déjà existant, on ne le crée pas (pas de doublons! :hehe:).</p>
<p>Bien que cette manière de faire <del>semble un peu</del> est très bizarre (convertir les positions des vertex en string), j'ai eu l'impression que c’était la méthode la plus simple pour gérer une grille dont on ne connait pas la taille sans trop de lignes de code.</p>
<p>Mais si vous en avez une autres assez rapide à mettre en place, n’hésitez pas. :D</p>
<pre class="python python">myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpName<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span></pre>
<p>Après c'est simple:</p>
<ul>
<li>On créé notre cube de la taille voulu (2.0).</li>
<li>On lui applique un bevel parce que ça rend bien. :smileFou:</li>
<li>On le parente au groupe.</li>
<li>On le place à la position calculée plus tôt.</li>
</ul>
<p>Et on repart pour un autre vertex!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_maya_api_001.png" alt="voxel_maya_api_001.png" style="display:block; margin:0 auto;" title="voxel_maya_api_001.png, mar. 2013" height="387" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_maya_api_002.png" alt="voxel_maya_api_002.png" style="display:block; margin:0 auto;" title="voxel_maya_api_002.png, mar. 2013" height="598" width="616" /></p>
<p>Encore une fois: Ce script n'est pas optimisé du tout, c'est plus un prototype grossier qu'un outil de prod. Il suffit de lui donner un mesh un peu lourd pour s'en rendre compte. :mechantCrash:</p>
<h3>Conclusion</h3>
<p>Ainsi s’achève ce billet express.</p>
<p>Vous avez vue, c'est assez bête dans le principe. Bon, encore une fois on aurait pu faire différemment et surement plus efficace (essayez avec <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=a5b3f852a00f3dc096c136cbe04057733" hreflang="en">MFnMesh.allIntersections()</a>).</p>
<p>Personnellement, jouer avec l'API Maya m'amuse toujours autant. :)</p>
<p>A bientôt!</p>
<center>:marioCours:</center>
<h3>EDIT 2013/03/17</h3>
<p>Je n'ai pas pu résister à l'appel de la méthode <a href="http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=aa6c4717b168258664d399fb92e2cdcee" hreflang="en">MFnMesh.allIntersections()</a>. Voici donc une version bien plus optimisé que précédemment (avec structure d’accélération <a href="http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=aa6c4717b168258664d399fb92e2cdcee" hreflang="en">MFnMesh.autoUniformGridParams()</a>).</p>
<p>La principale différence comparé au code précédent est qu'ici nous ne passons plus par chaque vertex mais nous envoyons des rayon sur trois grille (X, Y, Z).</p>
<p>La seconde différence est que ce code fonctionne sur un mesh animé (1 a 24 ici). Testez, l'effet est assez sympa. :)</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
startFrame = <span style="color: #ff4500;">1</span>
endFrame = <span style="color: #ff4500;">24</span>
voxelSize = <span style="color: #ff4500;">20.0</span>
voxelStep = <span style="color: #ff4500;">0.5</span>
sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span>
inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
grpReelNames = <span style="color: #008000;">dict</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curTime <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>startFrame, endFrame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span> :
grpName = <span style="color: #483d8b;">"frameGrp_%s"</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">int</span><span style="color: black;">(</span>curTime<span style="color: black;">)</span>
grpReelName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>name=grpName, empty=<span style="color: #008000;">True</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">0.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime-<span style="color: #ff4500;">0.1</span><span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">1.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime<span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">0.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime+<span style="color: #ff4500;">1</span><span style="color: black;">]</span><span style="color: black;">)</span>
grpReelNames<span style="color: black;">[</span>curTime<span style="color: black;">]</span> = grpReelName
<span style="color: #ff7700;font-weight:bold;">for</span> grpReelName <span style="color: #ff7700;font-weight:bold;">in</span> grpReelNames :
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">cmds</span>.<span style="color: black;">objExists</span><span style="color: black;">(</span>grpReelName<span style="color: black;">)</span> :
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">delete</span><span style="color: black;">(</span>grpReelName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curTime <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>startFrame, endFrame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span> :
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span>curTime<span style="color: black;">)</span>
voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#I use while just because xrange with floats is impossible</span>
i = -voxelSize/<span style="color: #ff4500;">2.0</span>
<span style="color: #ff7700;font-weight:bold;">while</span> i <span style="color: #66cc66;"><</span>= voxelSize/<span style="color: #ff4500;">2.0</span> :
j = -voxelSize/<span style="color: #ff4500;">2.0</span>
<span style="color: #ff7700;font-weight:bold;">while</span> j <span style="color: #66cc66;"><</span>= voxelSize/<span style="color: #ff4500;">2.0</span> :
<span style="color: #ff7700;font-weight:bold;">for</span> axis <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">[</span><span style="color: #483d8b;">"zSide"</span>, <span style="color: #483d8b;">"ySide"</span>, <span style="color: #483d8b;">"xSide"</span><span style="color: black;">]</span> :
z = <span style="color: #ff4500;">0</span>
y = <span style="color: #ff4500;">0</span>
x = <span style="color: #ff4500;">0</span>
zOffset = <span style="color: #ff4500;">0</span>
zDir = <span style="color: #ff4500;">0</span>
yOffset = <span style="color: #ff4500;">0</span>
yDir = <span style="color: #ff4500;">0</span>
xOffset = <span style="color: #ff4500;">0</span>
xDir = <span style="color: #ff4500;">0</span>
<span style="color: #ff7700;font-weight:bold;">if</span> axis == <span style="color: #483d8b;">"zSide"</span> :
x = i
y = j
zOffset = <span style="color: #ff4500;">10000</span>
zDir = -<span style="color: #ff4500;">1</span>
<span style="color: #ff7700;font-weight:bold;">elif</span> axis == <span style="color: #483d8b;">"ySide"</span> :
x = i
z = j
yOffset = <span style="color: #ff4500;">10000</span>
yDir = -<span style="color: #ff4500;">1</span>
<span style="color: #ff7700;font-weight:bold;">elif</span> axis == <span style="color: #483d8b;">"xSide"</span> :
y = i
z = j
xOffset = <span style="color: #ff4500;">10000</span>
xDir = -<span style="color: #ff4500;">1</span>
raySource = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span> x+xOffset, y+yOffset, z+zOffset <span style="color: black;">)</span>
rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatVector</span><span style="color: black;">(</span>xDir, yDir, zDir<span style="color: black;">)</span>
faceIds=<span style="color: #008000;">None</span>
triIds=<span style="color: #008000;">None</span>
idsSorted=<span style="color: #008000;">False</span>
space=<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span>
maxParam=<span style="color: #ff4500;">99999999</span>
testBothDirections=<span style="color: #008000;">False</span>
accelParams=inMesh.<span style="color: black;">autoUniformGridParams</span><span style="color: black;">(</span><span style="color: black;">)</span>
sortHits=<span style="color: #008000;">False</span>
hitPoints = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
hitRayParam=<span style="color: #008000;">None</span>
hitFacePtr = <span style="color: #008000;">None</span><span style="color: #808080; font-style: italic;">#OpenMaya.MScriptUtil().asIntPtr()</span>
hitTriangle=<span style="color: #008000;">None</span>
hitBary1=<span style="color: #008000;">None</span>
hitBary2=<span style="color: #008000;">None</span>
hit = inMesh.<span style="color: black;">allIntersections</span><span style="color: black;">(</span>raySource,
rayDirection,
faceIds,
triIds,
idsSorted,
space,
maxParam,
testBothDirections,
accelParams,
sortHits,
hitPoints,
hitRayParam,
hitFacePtr,
hitTriangle,
hitBary1,
hitBary2<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> hit :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #808080; font-style: italic;"># for each interestected points</span>
<span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>hitPoints.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span> :
cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span>
myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpReelNames<span style="color: black;">[</span>curTime<span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
j += voxelStep
i += voxelStep</pre>
<center><i>Désolé pour le code horizontal. :dentcasse:</i></center>
<p>Je ne fais pas de commentaires supplémentaires car je pense que si vous avez compris le premier code, celui ci est tout aussi simple. :gniarkgniark:</p>
<h3>EDIT 2013/03/19</h3>
<p>Justin Israel a fait <a href="https://groups.google.com/d/msg/python_inside_maya/1pYjnkTB5l0/JUFgz8LSD-gJ" hreflang="en">une remarque très pertinante</a> concernant ma <em>voxelIdList</em>. J'ai appris un truc ducoup. Je vous traduis son message ici:</p>
<p>Si tu utilise <em>voxelIdList</em> pour chercher la signature d'un cube déjà placé, le fait que ce soit une liste va progressivement ralentir la recherche au fil qu'elle se remplit, car <em>x in list</em> est de complexité O(n). Tu devrais utiliser un <em>set()</em>:</p>
<pre class="python python">voxelIdSet = <span style="color: #008000;">set</span><span style="color: black;">(</span><span style="color: black;">)</span>
...
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdSet :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
voxelIdSet.<span style="color: black;">add</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span></pre>
<p>Un type set est de complexité O(1) donc faire un <em>x in set</em> va instantanément trouver l'item depuis sa signature, à l'inverse d'une liste qu'une faut parcourir entièrement.</p>Les User Attributes de VRayurn:md5:212f5e2b8e46bf1b868a9437b8f0afe42013-02-17T21:36:00+01:002020-12-08T10:29:10+01:00NarannInfographie 3D - Boulotfrmayaproxyuser attributesvrayvrmesh<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_tn.png" alt="user_attr_vray_tn.png" style="float:left; margin: 0 1em 1em 0;" title="user_attr_vray_tn.png, fév. 2013" />Voici un tuto que je voulais faire depuis pas mal de temps. :dentcasse:</p>
<p>VRay propose, depuis un bon moment maintenant, d’utiliser des attributs par objet pour contrôler certaines valeurs. Ça peut aller assez loin mais bien souvent, ce sont les shaders qui en profite. L’idée est d’appliquer un seul et unique shader à un grand nombre d’objet, mais que chaque objet ait des attributs « spéciaux » avec une valeur bien particulière qui se verront appliquer sur les attributs du shader. Si vous vous rappelez bien, je <a href="https://www.fevrierdorian.com/blog/post/2011/04/16/Mental-ray-3.9-Les-User-Data-Shaders-ou-Shader-Package">vous avais présenté</a> une feature similaire sous mental ray (Les User Data).</p>
<p>Mais avant même de faire ça, ce tuto va vous expliquer comment exporter puis importer des vrmesh et refaire des assignations de shader à l’intérieur des-dits vrmesh ! Bref, un gros programme ! :bravo:</p> <h3>Les vrmesh (ou proxy)</h3>
<h4>C’est quoi ?</h4>
<p>Pour faire très brièvement : Vous connaissez le principe du tiling des textures ? (Non ? <a href="https://www.fevrierdorian.com/blog/post/2009/01/14/Le-mappage-en-m%C3%A9moire-des-textures-dans-Mental-Ray-expliqu%C3%A9-%28Memory-mapped-textures%29.">Hop ! Hop ! Hop !</a> Et plus vite que ça ! :nannan: ) Et bien le principe est le même, mais pour de la géométrie : Les vrmesh sont des mesh « prêt à être raycasté ». La géométrie y est stockée dans une grille (voxel).</p>
<p>Ainsi, vous ne chargerez que « la boite » nécessaire au rendu. Votre vrmesh peut faire une taille astronomique, seuls les éléments nécessaires au calcul seront chargés/déchargés à la volée. Au même titre que les maps tilé, ceci a pour effet d’augmenter les IO sur vos disques, mais vous donne une énorme flexibilité.</p>
<p>N’ayez donc pas peur de la taille, les vrmesh sont faits pour ! :D</p>
<h4>Pratique</h4>
<p>On va faire simple, voici une scène avec deux cycles d’animation de 24 frames :</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_001.png" title="user_attr_vray_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/.user_attr_vray_001_m.jpg" alt="user_attr_vray_001.png" style="display:table; margin:0 auto;" title="user_attr_vray_001.png, fév. 2013" /></a></p>
<center><i>Ça ne se voit pas mais ces deux bonhommes marchent ! :onSeFendLaPoire:</i></center>
<p>À cette étape, le principe est d’assigner à chaque « groupe de shader », un shader quelconque. C’est le nom qui importe, car c’est ce nom-là qui sera stocké dans le vrmesh :</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_002.png" alt="user_attr_vray_002.png" style="display:table; margin:0 auto;" title="user_attr_vray_002.png, fév. 2013" /></p>
<center><i>« lambert5 » ça aurait dû être « bras » mais je suis complètement passé à côté. :baffed:</i></center>
<p>Même si ce n’est pas obligatoire, essayez de garder une cohérence dans les noms entre tous vos objets. Ainsi, si vous avez des personnages de foule à exporter. Assignez-leur à tous les shaders "bras", "corp", "tete", "cheuveux", "tenuHaut", "tenuBas", etc. Avec un peu de script, vous gagnerez du temps plus tard.</p>
<p>Sélectionnez le groupe de mesh que vous souhaitez exporter :</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_003.png" alt="user_attr_vray_003.png" style="display:table; margin:0 auto;" title="user_attr_vray_003.png, fév. 2013" /></p>
<p>Puis allez dans <em>Create/V-Ray/Create proxy</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_004.png" alt="user_attr_vray_004.png" style="display:table; margin:0 auto;" title="user_attr_vray_004.png, fév. 2013" /></p>
<p>Vous arrivez devant une interface <del>horrible</del> sobre :</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_005.png" alt="user_attr_vray_005.png" style="display:table; margin:0 auto;" title="user_attr_vray_005.png, fév. 2013" /></p>
<p>Cochez <em>Export animation</em>. Si votre mesh est animé et que vous envisagez d’utiliser du motion blur sur votre rendu final, cochez aussi <em>Export velocity</em>. Les deux valeurs vont créer l’intervalle qui permettra de calculer le vecteur de direction à stocker dans les vertex. C’est à partir de ça que VRay calculera le motion blur de la géométrie. Soyez vigilant sur cet intervalle. On peut avoir des (mauvaises) surprises. Si vous ne comptez pas rendre avec du motion blur, décochez cette option, ce sera des informations en moins à stocker dans la géométrie.</p>
<p>Dans mon cas j’ai aussi coché <em>Use playback range</em> mais avisez en fonction. :reflechi:</p>
<p>Avec <em>Face in preview</em>, Chaos Group nous gratifie une fois de plus d’une petite feature à priori anodine mais très intéressante en pratique : Le principe est de sauver les index de <em>n</em> face qui seront affiché dans le viewport plus tard.</p>
<p>Quand on fait des proxy, principalement pour alléger sa scène, on se retrouve souvent à devoir gérer des grosses bouding box à la place. Si le pipeline le permet, vous aurez peut-être même un proxy fait par un autre département (on ne compte même pas de temps nécessaire). La, les bonhommes de Chaos Group ont eux la réflexion très intéressante : « Pourquoi pas faire un proxy sympa, qui correspond à quelque chose mais vraiment allégé? ». Le pari est entièrement réussi, vous verrez ça plus tard. :)</p>
<p>Si vous souhaitez réimporter directement le proxy nouvellement créé, vous pouvez, si vous le voulez cliquer sur <em>Automatically create proxies</em>.</p>
<p>Pour le reste, je vous conseille vivement de regarder <a href="http://www.spot3d.com/vray/help/maya/150R1/vrayproxy_params.htm#export_gui" hreflang="en">la doc</a>.</p>
<p>Une fois que c’est fait, on clique sur <em>Create Proxy</em>!</p>
<p>Faites ça pour tous les personnages que vous avez à exporter.</p>
<center>:longBar:</center>
<p>Pour importer tout ça :</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_006.png" alt="user_attr_vray_006.png" style="display:table; margin:0 auto;" title="user_attr_vray_006.png, fév. 2013" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_007.png" alt="user_attr_vray_007.png" style="display:table; margin:0 auto;" title="user_attr_vray_007.png, fév. 2013" /></p>
<p>Tadaaaa:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_008.png" alt="user_attr_vray_008.png" style="display:table; margin:0 auto;" title="user_attr_vray_008.png, fév. 2013" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_009.png" alt="user_attr_vray_009.png" style="display:table; margin:0 auto;" title="user_attr_vray_009.png, fév. 2013" /></p>
<p>Et voilà nos 100 faces par objet. Ça claque hein ? :aupoil:</p>
<p>Plus sérieusement, vous pouvez en mettre des centaines, votre viewport ne bronchera pas. De plus, ces proxys correspondent au modèle final. L’approximation vous gênera beaucoup moins qu’un model low ou pire, une bounding box.</p>
<p>Mais allons zyeuter les options:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_010.png" alt="user_attr_vray_010.png" style="display:table; margin:0 auto;" title="user_attr_vray_010.png, fév. 2013" /></p>
<p>Si on coche <em>Bounding box</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_011.png" alt="user_attr_vray_011.png" style="display:table; margin:0 auto;" title="user_attr_vray_011.png, fév. 2013" /></p>
<center><i>Pour les nostalgiques. :trollface:</i></center>
<p>Si vous n’êtes pas satisfait de l’approximation, Chaos Group a pensé à vous : <em>Show whole mesh</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_012.png" alt="user_attr_vray_012.png" style="display:table; margin:0 auto;" title="user_attr_vray_012.png, fév. 2013" /></p>
<p>Les autres options sont plus spécifiques. Je ne rentre pas plus dans les détails. Si ça vous intéresse : <a href="http://www.spot3d.com/vray/help/maya/150R1/vrayproxy_params.htm#parameters" hreflang="en">:RTFM:</a>.</p>
<p>Notez juste que la vitesse de l’animation est configurable (animable aussi ! :gniarkgniark: ) et que dans notre cas (et la plupart des cas) elle est cyclique (<em>Loop</em>).</p>
<p>Allez dans les onglets plus à droite. VRay nous a déjà connecté un shader et…</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_013.png" alt="user_attr_vray_013.png" style="display:table; margin:0 auto;" title="user_attr_vray_013.png, fév. 2013" /></p>
<p>Oh miracle ! Nos slots sont là! :laClasse:</p>
<p>Vous n’aurez plus qu’à drag and droper les shaders à utiliser.</p>
<p>Notez qu’à trop vouloir être gentil, VRay nous créé un shader pour chaque vrmesh importé. Sauf que si vous importez deux fois le même vrmesh, vous aurez deux shaders qui font là même chose. N’hésitez pas à en virer un et à appliquer un seul shader par type de vrmesh.</p>
<p>Maintenant passons aux choses sérieuses! :enerve:</p>
<h3>Les User Attributes</h3>
<p>Sélectionnez le node de <em>transform</em> d’un des vrmesh et ajoutez-y des <em>User attributes</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_014.png" alt="user_attr_vray_014.png" style="display:table; margin:0 auto;" title="user_attr_vray_014.png, fév. 2013" /></p>
<p>Dans ces <em>User attributes</em> (paumé tout en bas de votre <em>Attribute Editor</em>) ajoutez la chose suivante :</p>
<pre>[bash]
casquetteColor=1,1,0;
</pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_015.png" alt="user_attr_vray_015.png" style="display:table; margin:0 auto;" title="user_attr_vray_015.png, fév. 2013" /></p>
<p>Là normalement vous commencez à comprendre. :siffle:</p>
<p>Sur le second, créez une valeur similaire :</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_016.png" alt="user_attr_vray_016.png" style="display:table; margin:0 auto;" title="user_attr_vray_016.png, fév. 2013" /></p>
<p>Maintenant créez un shader, un <em>blinn</em> dans mon exemple (Oui, je sais, c’est mal). Donnez-lui un nom clair (ça va vite devenir le foutoir dans vos connexions donc c’est vous qui voyez :redface: ):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_017.png" alt="user_attr_vray_017.png" style="display:table; margin:0 auto;" title="user_attr_vray_017.png, fév. 2013" /></p>
<p>Ajoutez un node <em>VRayUserColor</em> et entrez la valeur de votre attribut :</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_018.png" alt="user_attr_vray_018.png" style="display:table; margin:0 auto;" title="user_attr_vray_018.png, fév. 2013" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_019.png" alt="user_attr_vray_019.png" style="display:table; margin:0 auto;" title="user_attr_vray_019.png, fév. 2013" /></p>
<p>Mettez la couleur par défaut qui vous convient. Ce sera la couleur utilisée si le <em>User Attribute</em> n’est pas présent/valide.</p>
<p>Vous avez des exemples de syntax juste en dessous du node (encore une bonne idée).</p>
<p>Connectez ce node à la <em>color</em> de votre shader (dommage que Maya n’utilise pas la <em>default color</em> dans l’hypershade, ça serait toujours plus sympa qu’un truc tout noir.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_020.png" alt="user_attr_vray_020.png" style="display:table; margin:0 auto;" title="user_attr_vray_020.png, fév. 2013" /></p>
<p>Connectez votre shader aux deux <em>VRayMeshMaterial</em>, dans le slot... "casquette":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_022.png" alt="user_attr_vray_022.png" style="display:table; margin:0 auto;" title="user_attr_vray_022.png, fév. 2013" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_021.png" alt="user_attr_vray_021.png" style="display:table; margin:0 auto;" title="user_attr_vray_021.png, fév. 2013" /></p>
<p>Puis rendez :</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_023.png" alt="user_attr_vray_023.png" style="display:table; margin:0 auto;" title="user_attr_vray_023.png, fév. 2013" /></p>
<center><i><a href="http://www.youtube.com/watch?v=8RLPG88u3lM">La musique de victoire</a> (à écouter avec :smileFou: )</i></center>
<p>Faîtes pareil pour tous les shaders (sauf le corps qu’on se garde pour après :jdicajdirien: ):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_024.png" alt="user_attr_vray_024.png" style="display:table; margin:0 auto;" title="user_attr_vray_024.png, fév. 2013" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_025.png" alt="user_attr_vray_025.png" style="display:table; margin:0 auto;" title="user_attr_vray_025.png, fév. 2013" /></p>
<p>Bon, voilà pour les couleurs. Vous pouvez faire de même avec les <em>VRayUserScalar</em> et les attributs à virgule.</p>
<p>Maintenant attaquons-nous au corp:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_026.png" alt="user_attr_vray_026.png" style="display:table; margin:0 auto;" title="user_attr_vray_026.png, fév. 2013" /></p>
<center><i>Juste un node de file.</i></center>
<p>Imaginons que vous ayez quatre textures :</p>
<ul>
<li>checker_default.png</li>
<li>checker_four.png</li>
<li>checker_etc.png</li>
</ul>
<p>La seule variable dans votre texture se situe à la fin (et, pourquoi pas, dans le chemin).</p>
<p>Mettez donc ça dans votre chemin de fichier :</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_027.png" alt="user_attr_vray_027.png" style="display:table; margin:0 auto;" title="user_attr_vray_027.png, fév. 2013" /></p>
<p>Et appliquez la variable à vos différents <em>User Attributes</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_028.png" alt="user_attr_vray_028.png" style="display:table; margin:0 auto;" title="user_attr_vray_028.png, fév. 2013" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_029.png" alt="user_attr_vray_029.png" style="display:table; margin:0 auto;" title="user_attr_vray_029.png, fév. 2013" /></p>
<p>Rendez !</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_030.png" alt="user_attr_vray_030.png" style="display:table; margin:0 auto;" title="user_attr_vray_030.png, fév. 2013" /></p>
<center><i><a href="http://www.youtube.com/watch?v=8RLPG88u3lM">On se la remet ?</a></i></center>
<p>Et si on s’enflamme un peu :
<img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_031.png" alt="user_attr_vray_031.png" style="display:table; margin:0 auto;" title="user_attr_vray_031.png, fév. 2013" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_032.png" alt="user_attr_vray_032.png" style="display:table; margin:0 auto;" title="user_attr_vray_032.png, fév. 2013" /></p>
<p>Si vous êtes amené à faire souvent (ou pas) des foules, vous devriez sérieusement envisager cette solution.</p>
<h3>Petites notes</h3>
<p>Mine de rien, et comme les <em>User Data</em> de mental ray, c’est un peu laborieux à mettre en place. Mais si vous ou un collègue sait scripter, il n’est pas trop difficile de mettre ça en place sur une production.</p>
<p>On me souffle à l’oreillette qu’il se peut (c’est non confirmé donc à prendre avec des pincettes) que Deex (que je remercie pour m’avoir fait découvrir VRay) intègre ça à <a href="http://arsenal.deex.info/" hreflang="en">Arsenal</a>. On peut donc espérer un truc simple (mais soyez pas trop pressé :sourit: ).</p>
<h3>Conclusion</h3>
<p>J’espère que ce tuto assez costaud vous aidera dans vos productions.</p>
<p>A bientôt !</p>
<p>Dorian</p>
<center>:marioCours:</center>
Combiner les importons avec le Final Gatherurn:md5:cab3340e2aee80c7934ba48337a5594a2013-02-10T19:36:00+01:002013-07-26T18:00:07+02:00NarannInfographie 3D - Boulotautodeskfinal gatherfrimportonsmental ray<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_render_002_29sec_tn.png" alt="fg_render_002_29sec_tn.png" style="float:left; margin: 0 1em 1em 0;" title="fg_render_002_29sec_tn.png, fév. 2013" height="150" width="150" />Ne vous est il jamais arrivé d'avoir un éclairage un peu vicieux ou monsieur <em>Final Gather</em> vous propose un bloblotage du pire effet? La seule solution étant souvent de monter les temps de rendu (quand c'est encore possible ^^') pour cacher la misère.</p>
<p>Et encore, parfois, monter tous les paramètres comme un goret n'est pas suffisant, tout simplement parce que le <em>Final Gather</em> ne peut pas gérer ce cas.</p>
<p>Depuis peu, une solution permettant de régler drastiquement (mais pas toujours) ce soucis a été mise au point: C'est les importons.</p> <p>L'utilisation des importons est plus large qu'une simple variante du <em>Final Gather</em>. Ils s'inscrivent comme une évolution importante (c'est le cas de le dire) de l'illumination globale de mental ray.</p>
<h3>Dans un premier temps, configurer un <em>Final Gather</em> correct.</h3>
<p>La question sur comment configurer le <em>Final Gather</em> n'est pas simple tant le résultat peut être variable suivant la scène. Je vais partir du principe qu'on ne souhaite pas un <em>Final Gather</em> parfait mais un "effet" de <em>Final Gather</em> (une illumination indirect sur un seul rebond). Je vous invite à regarder <a href="http://docs.autodesk.com/MENTALRAY/2013/ENU/mental-ray-help/files/manual/node76.html#opt:finalgather_trace_depth" hreflang="en">la doc</a>.</p>
<h4>L'accuracy</h4>
<p>C'est le nombre de sample envoyé dans la scène PAR point de <em>Final Gather</em> pour déterminer la valeur de l'illumination en ce point. Si il n'y en a pas assez, le point en question ne sera pas stable. On peu le mettre à 200-500 pour la plupart des cas mais on est parfois obliger de monter sur des valeurs proches de 1000 sur des cas vraiment extrême.</p>
<p>Si vous avez une valeur de <em>point density</em> élevé, l<em>'accuracy</em> peut drastiquement augmenter le temps de rendu.</p>
<h4>Point density</h4>
<p>C'est un facteur du nombre de point de <em>Final Gather</em> shoote depuis la camera. Une valeur de 2.0 double le nombre de point de <em>Final Gather</em> à calculer et donc, le temps de génération du <em>Final Gather</em>.</p>
<p>C'est le premier paramètre que je vous invite à augmenter avant de booster l<em>'accuracy</em>. Gardez à l'esprit que l<em>'accuracy</em> fait effet d'exposant. Ne faites pas varier l'un sans prendre en compte la valeur de l'autre. Une valeur de 10 sur un rendu de prod avec des surfaces de couleur uniforme est envisageable. Avec des textures, les variations peuvent ne pas sauter au yeux et on peu pratiquement rester sur du 2.0-5.0.</p>
<h4>Point interpolation</h4>
<p>C'est le nombre de point de <em>Final Gather</em> qui seront interpolés pour renvoyer une valeur d'illumination indirect au sample qui le demande. Cette valeur change drastiquement l'allure visuelle du <em>Final Gather</em>. Plus il y a d'interpolation, plus c'est doux mais aussi plus ça flick en cas d'animation si le <em>Final Gather</em> n'est pas baké. Si votre scène a un éclairage contrasté, je vous conseille de descendre cette valeur pour rendre le bloblotage général moins visible (plus granuleux disons) on peut tout à fait la mettre à 1.</p>
<p>Notez que vous pouvez augmenter cette valeur en parallèle de votre <em>point density</em>, car plus vous avez de point, plus vous pouvez les interpoler sans que le flicking ne soit trop visible.</p>
<h4>Le reste</h4>
<p>En vrac je fait les choses suivantes:</p>
<ul>
<li><em>Secondary Diffuse Bounce</em>: 0</li>
<li><em>Reflection</em>, <em>Refraction</em> et <em>Max Trace Depth</em> (du <em>Final Gather</em> toujours): 0</li>
</ul>
<p>Pourquoi tout ça à zéro? Parce qu'au delà d'augmenter les temps de rendu et les potentiels artefacts, un seul diffuse bounce suffit à créer un "effet" d'illumination indirect convaincant.</p>
<ul>
<li>Si vous faites de l'animation mettez le mode <em>multiframe</em> (<em>Optimize for Animation</em> dans Maya).</li>
<li>Si vous voulez un <em>Final Gather</em> en brute force, passer en <em>force</em> (<em>No FG Caching</em>). Pour chaque sample, un nouveau point de <em>Final Gather</em> sera créé. Bien entendu, c'est (très) long.</li>
</ul>
<p>Je ne vais pas développer, c’était principalement un rappel. Passons au Importons! :hehe:</p>
<h3>Les importons c'est quoi?</h3>
<p>Je pourrais passer pas mal de temps à tenter d'expliquer ce que sont les importons mais je trouve que <a href="http://docs.autodesk.com/MENTALRAY/2013/ENU/mental-ray-help/files/manual/importons.html" hreflang="en">la documentation officielle</a> explique cela très clairement. Je vous propose une traduction:
Les importons sont des "particules virtuelles". Sur certains aspects, ils sont similaire aux photons: Ils rebondissent dans la scène.
Il y a toutefois des différences significatives: Contrairement aux photons, les importons ne distribuent pas d’énergie.
Au lieu de cela, ils contiennent une couleur décrivant avec quel facteur une illumination à un point donné contribuerait à l'image finale.
De ce fait, ils fournissent des information au moteur pour l'aider gérer ses calculs de manière à obtenir une image d'une meilleur qualité visuelle tout en diminuant les ressources nécessaires.</p>
<p>Contrairement aux photons, les importons sont émis depuis la camera et rebondissent vers les lights. C'est le fonctionnement inverse des photons.
Cela dit, le fonctionnement interne des lights fait qu'il est possible d'appliquer un photon shader aux importons.
Pour des raisons de simplicité, mental ray n'introduit pas de nouveau type de shader mais permet d'utiliser les photons shaders pour les importons.</p>
<p>Les importons servent de mécanisme supplémentaire qui améliore la qualité et les performances du rendu.
Il fait office de mécanisme principale pour driver la qualité des irradiance particles.
Les importons peuvent également êtres utilisé pour améliorer la qualité des photon maps d'illumination globale.
Ils permettent de réduire drastiquement la taille (et donc, la vitesse de consultation) des photon map en procédant à un merging adaptatif déterminé via les importons.
Un tel mécanisme adaptatif est supérieur au merging fixe déjà existant.</p>
<p>Si ils sont activés, les importons sont shootés avant que la photon map ne soit créé.
Ils sont utilisés durant la creation de la photon map d'illumination globale.
Comme ils ne sont pas d'utilisation autre que la recherche des photons pertinents, les importons sont jetés une fois que la photon map est créée.</p>
<p>L'importons map est contrôlé par les <a href="http://docs.autodesk.com/MENTALRAY/2013/ENU/mental-ray-help/files/manual/node76.html#importon" hreflang="en">options</a> de rendu de la scène. <a href="http://docs.autodesk.com/MENTALRAY/2013/ENU/mental-ray-help/files/manual/limitations.html#importon" hreflang="en">Certaines limitations</a> sont connus.</p>
<p>En gros, vous l'avez compris, on "shoot" des particules puis la camera qu'on fait rebondir dans la scène et on détermine quelles sont les zones de la scène qui on de l'importance "visuelle" dans l'image final. De ce fait, on est capable de savoir quelle sont les zones de la scène qu'il va être important de sampler/"finalgatheurer" et on oublie les autres. Le <em>Final Gather</em> était jusqu'à présent très bête, mais avec les importons, il l'est beaucoup moins. :jdicajdirien:</p>
<h3>Autodesk, encore et toujours</h3>
<p>Bon, ça commence à me saouler de leur taper dessus mais vous allez voir qu'une fois encore on bataille contre son soft pour faire marcher le truc...
Pour une raison qui m’échappe totalement, Autodesk a décidé que si l'utilisateur cochait <em>Final Gather</em>, il n'avait pas d’intérêt à cocher <em>Importons</em> (ni <em>Irradiance Particles</em>). L'option devient donc grisée.
Oui, c'est stupide. Le choix a due être fait un jour, sans trop lire la documentation et n'a jamais été changé depuis (sont ils seulement au courant?).
Bref, on ne va pas s’éterniser, je vous invite très fortement à télécharger mon tool <a href="Mhttp://forums.cgsociety.org/showthread.php?f=87&t=971719" hreflang="en">enjoyMentalRayStringOptions</a> pour pouvoir outrepasser les limitations imposées par Maya.</p>
<p>Dans la mesure ou cocher <em>Final Gather</em> désactive les importons, il faut cocher <em>Final Gather</em> depuis les <em>Render Settings</em> de Maya PUIS <em>Importons</em> depuis mon interface... L'inverse désactiverait les Importons.</p>
<p>Notez qu'a chaque fois que vous relancez la fenêtre <em>Render Settings</em>, le <em>Final Gather</em> désactive les importons... Gardez la donc ouverte! (Oui oui, je sais... :pasClasse: )</p>
<p>Il existe aussi un tool que je n’ai pas teste mais qui se veut complètement intégré à Maya. Il est présenté <a href="https://elementalray.wordpress.com/2012/08/10/new-maya-rendering-ui-testing/" hreflang="en">ici</a> et la page du script est <a href="https://code.google.com/p/maya-render-settings-mental-ray/" hreflang="en">ici</a>.</p>
<h3>Et le petit tuto!</h3>
<p>Une fois que vous avez compris la partie ci dessus, vous allez voir que c'est très (très) rapide! :D</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_001.png" alt="fg_001.png" style="display:block; margin:0 auto;" title="fg_001.png, fév. 2013" height="209" width="411" /></p>
<center><i>PIF Cocher le FG!</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_002.png" alt="fg_002.png" style="display:block; margin:0 auto;" title="fg_002.png, fév. 2013" height="204" width="483" /></p>
<center><i>PAF cocher les importons!</i></center>
<p>BOUM Lancez le rendu! :grenadelauncher:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_render_001_27sec.png" title="fg_render_001_27sec.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/.fg_render_001_27sec_m.jpg" alt="fg_render_001_27sec.png" style="display:block; margin:0 auto;" title="fg_render_001_27sec.png, fév. 2013" height="315" width="560" /></a></p>
<center><i>Avant... (27sec)</i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_render_002_29sec.png" title="fg_render_002_29sec.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/.fg_render_002_29sec_m.jpg" alt="fg_render_002_29sec.png" style="display:block; margin:0 auto;" title="fg_render_002_29sec.png, fév. 2013" height="315" width="560" /></a></p>
<center><i>Après... (29sec)</i></center>
<p>On constate une nette amélioration au niveau des artefacts sur les murs. En effet, mental ray a projeté ses Final Gathers Points en prenant en compte l'importance (calculé au début du rendu) de ces points dans l'image finale.</p>
<p>Je vous invite aussi à regarder le log en mode <em>Progess</em> pour vous assurer que les Importons sont bien shoote depuis la camera.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_003.png" alt="fg_003.png" style="display:block; margin:0 auto;" title="fg_003.png, fév. 2013" height="310" width="209" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_004.png" alt="fg_004.png" style="display:block; margin:0 auto;" title="fg_004.png, fév. 2013" height="163" width="468" /></p>
<h3>Conclusion</h3>
<p>J'espère que ces petites explications vous aiderons à améliorer vos rendu en <em>Final Gather</em>. :sourit:</p>
<p>A bientôt!</p>
<center><i>:marioCours:</i></center>
Déplacer récursivement un fichier vers un emplacement qui n'existe pas (encore)urn:md5:a1d4379e3684e05628c9d310dc5c3e012013-02-08T10:37:00+01:002013-07-26T18:00:27+02:00NarannScript et codefrpython<p><img src="https://www.fevrierdorian.com/blog/public/logos/Python_logo_150.png" alt="Python_logo_150.png" style="float:left; margin: 0 1em 1em 0;" title="Python_logo_150.png, mar. 2009" height="150" width="150" />C'est rien du tout mais à chaque fois que je dois déplacer un fichier vers un dossier qui n'existe pas je me retape cette fonction à la mano avec les désagrément que peut engendrer l'utilisation des <em>os.mkdir</em> et <em>os.rename</em>. :casseTeteMur:</p>
<p>Comme ce genre de fonction peut intéresser des gens je la met ici pour en faire profiter tout le monde (afin de participer activement et agrandir cette intelligence collective qu'est l'internet... Pourquoi je dis ça? Euh... Et bien en fait j'essais de grapiller quelques lignes sinon le bloc du code se place mal... :seSentCon: )</p> <p>Vous allez voir que c'est assez simple et facilement modifiable.</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> recursivelyMoveFile<span style="color: black;">(</span>srcFile, dstFile<span style="color: black;">)</span> :
curFolderPath = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">sep</span> <span style="color: #808080; font-style: italic;"># get the os separator. "/" on linux.</span>
<span style="color: #808080; font-style: italic;"># we regenerate the folder path we want to create the file in and check, at every step the folder exists</span>
<span style="color: #ff7700;font-weight:bold;">for</span> folder <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">(</span>dstFile<span style="color: black;">)</span>.<span style="color: black;">split</span><span style="color: black;">(</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">sep</span><span style="color: black;">)</span> :
curFolderPath = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">(</span>curFolderPath, folder<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isdir</span><span style="color: black;">(</span>curFolderPath<span style="color: black;">)</span> :
<span style="color: #dc143c;">os</span>.<span style="color: black;">mkdir</span><span style="color: black;">(</span>curFolderPath<span style="color: black;">)</span>
<span style="color: #dc143c;">os</span>.<span style="color: black;">rename</span><span style="color: black;">(</span>srcFile, dstFile<span style="color: black;">)</span></pre>
<p>Vous voyez? Rien de bien sorcier. :sourit:</p>
<p>Notez que la fonction ne vérifie rien du tout. Soyez donc vigilant avec le <em>os.rename</em>.</p>
<p>Bon... Comme je suis gentil je vous propose une fonction blindé anti-pythonic pour le coup (pour les copieurs/colleurs foux qui ne prennent pas le temps de lire :aupoil: )</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> recursivelyMoveFile<span style="color: black;">(</span>srcFile, dstFile<span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">(</span>srcFile<span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">Exception</span><span style="color: black;">(</span><span style="color: #483d8b;">"srcFile is not a file -> %s"</span>, srcFile<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">(</span>dstFile<span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">Exception</span><span style="color: black;">(</span><span style="color: #483d8b;">"dstFile already exist -> %s"</span>, dstFile<span style="color: black;">)</span>
curFolderPath = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">sep</span> <span style="color: #808080; font-style: italic;"># get the os separator. "/" on linux.</span>
<span style="color: #808080; font-style: italic;"># we regenerate the folder path we want to create the file in and check, at every step the folder exists</span>
<span style="color: #ff7700;font-weight:bold;">for</span> folder <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span><span style="color: black;">(</span>dstFile<span style="color: black;">)</span>.<span style="color: black;">split</span><span style="color: black;">(</span><span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">sep</span><span style="color: black;">)</span> :
curFolderPath = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">(</span>curFolderPath, folder<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isdir</span><span style="color: black;">(</span>curFolderPath<span style="color: black;">)</span> :
<span style="color: #dc143c;">os</span>.<span style="color: black;">mkdir</span><span style="color: black;">(</span>curFolderPath<span style="color: black;">)</span>
<span style="color: #dc143c;">os</span>.<span style="color: black;">rename</span><span style="color: black;">(</span>srcFile, dstFile<span style="color: black;">)</span></pre>
<p>Codez bien! (Et vite surtout! :gniarkgniark: )</p>Deex VRay Arsenal, un petit tool qui fait gagner beaucoup de tempsurn:md5:9bf448d41828ce5285db9e2a01c7ec402013-02-07T00:00:00+01:002013-07-26T18:00:55+02:00NarannInfographie 3D - Boulotarsenaldeexfrmayapasserender layerrenduvray<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_tn.png" alt="VRay_Arsenal_tn.png" style="float:left; margin: 0 1em 1em 0;" title="VRay_Arsenal_tn.png, janv. 2013" height="150" width="150" />Ça fait un moment que j’entends Damien (alias <a href="http://deex.info/" hreflang="en">Deex</a>) parler de son tool. J'ai même eu l'occasion d'être sur la même prod que lui quand il réfléchissait aux grandes lignes. Quand il m'a dit que son outil était enfin fini, je n'ai pas pu m’empêcher de lui demander une licence pour tester ça. :sourit:</p>
<p>L'idée de ce billet est de présenter les principales <em>features</em> d'<a href="http://arsenal.deex.info/" hreflang="en">Arsenal</a>. Vous allez voir que malgré une approche qui peut sembler un peu trop user friendly, cette outil est un vrai outil de production qui facilite pas mal le boulot.</p>
<p>C'est 'tipar mon canard! :sauteJoie:</p> <blockquote><p>Note: Avant de commencer je vous invite à cocher la case <em>Use V-Ray VFB</em> dans l'onglet <em>VRay Common</em> des <em>Render Settings</em>:</p></blockquote>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_012_useVRay_frameBuffer.png" alt="VRay_Arsenal_012_useVRay_frameBuffer.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_012_useVRay_frameBuffer.png, janv. 2013" height="369" width="428" /></p>
<p>Ceci vous donnera accès à une sorte de <em>Render View</em> spécial VRay qui permet (en plus de pas mal de choses) de visualiser directement les passes (en haut à gauche).</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_011_frameBuffer.png" alt="VRay_Arsenal_011_frameBuffer.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_011_frameBuffer.png, janv. 2013" height="137" width="331" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_sampleImage.png" alt="VRay_Arsenal_sampleImage.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_sampleImage.png, janv. 2013" height="319" width="301" /></p>
<center><i>Mon image de test: Une HDR de <a href="http://www.hdrlabs.com/sibl/archive.html">sIBL</a>, et beaucoup de talent! :smileFou:</i></center>
<h3>L'installation</h3>
<p>J'ai trouvé <a href="http://arsenal.deex.info/wordpress/documentation/installation/" hreflang="en">l'installation</a> un peu rebutante. Ce n'est pas dans ce qu'on à l'habitude de voir avec la plupart des scripts.</p>
<p>En premier lieu, il faut installer PyQt ce qui, en temps normal, n'est pas une mince affaire (en particulier sous Windows). Heureusement, un installeur "pas prise de tête" est proposé sur le site. C'est bien senti.</p>
<p>L'installation du tool à proprement parler est, quant à elle, pensé pour simplifier l’intégration en prod (via des variables d'environnements) mais peut se révéler assez rebutante pour les gens n'ayant pas forcément l'habitude de jouer avec leur <em>Maya.env</em>. D'autant que je suis sur qu'il aurait été possible de faire une installation "à l'ancienne" (les scripts dans le dossier <em>script</em>, les shelves dans le dossier <em>shelves</em>, etc...), c'est juste que ce n'est pas forcément propre et qu'à chaque MAJ ça aurait été un peu plus compliqué alors qu'avec le système actuel, il suffit de remplacer un seul dossier. :laClasse:</p>
<h3>La fenêtre de base</h3>
<p>Quand on ouvre, on a ça:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_001.png" alt="VRay_Arsenal_001.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_001.png, janv. 2013" height="933" width="536" /></p>
<p>Nous passerons les onglets en revu plus bas.</p>
<p>Il y a un bouton <em>épingler</em>. Une fois coche, Arsenal restera au devant de vos fenêtres. Sinon, il se comporte comme une fenêtre Maya.</p>
<p>Il y a aussi le bouton <em>close</em> en bas dont je ne vous ferais pas l'affront de vous expliquer l'utilité. :reflexionIntense:</p>
<p>Il y a trois onglets que nous allons voir dans l'ordre:</p>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2013/02/07/Deex-VRay-Arsenal-un-petit-tool-qui-fait-gagner-beaucoup-de-temps#pass">Pass</a>, qui regroupe les <em>Render Layer</em> de Maya et le système de passe de VRay. Leur utilisation combinées étant fréquente.</li>
<li><a href="https://www.fevrierdorian.com/blog/post/2013/02/07/Deex-VRay-Arsenal-un-petit-tool-qui-fait-gagner-beaucoup-de-temps#quality">Quality</a>, qui est une feature fort pratique à l'utilisation: Un slider, de 0 a 99 pour augmenter ou diminuer la qualité du rendu via une augmentation progressive de vos paramètres de rendu.</li>
<li><a href="https://www.fevrierdorian.com/blog/post/2013/02/07/Deex-VRay-Arsenal-un-petit-tool-qui-fait-gagner-beaucoup-de-temps#toolbox">ToolBox</a>, qui regroupe pleins de petits outils qui font gagner pas mal de temps.</li>
</ul>
<h3>L'onglet <em>Pass</em> <a name="pass"></a></h3>
<p>Sur la partie haute vous avez, tout bêtement, les <em>Render Layer</em> de Maya (injustement nommé "Pass" dans les trois boutons de gauche). Cliquez sur un de ces <em>Render Layer</em> vous le sélectionne de la même façon que Maya. C'est juste pour gagner du temps. Pour des rendus ne nécessitant pas de passes complexes vous ne devriez pas en avoir besoins.</p>
<p>Notez que vous pouvez faire un <em>Set global to on</em> sur vos <em>RenderLayers</em> directement depuis cette fenêtre (voir <a href="http://download.autodesk.com/global/docs/maya2013/en_us/index.html?url=files/Rendering_nodes_Render_Layer_render_attributes.htm,topicNumber=d30e656262" hreflang="en">doc Maya</a>).</p>
<p>Rentrons maintenant dans la partie la plus intéressante. :popcorn:</p>
<h4>Properties control</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_onglet_propertiesControl.png" alt="VRay_Arsenal_onglet_propertiesControl.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_onglet_propertiesControl.png, janv. 2013" height="607" width="473" /></p>
<p>Ce système est extrêmement rapide pour préparer vos passes.</p>
<p>C'est très (très) simple:</p>
<ul>
<li>Vous sélectionnez un objet/groupe, vous faites <strong>Add</strong> et hop!</li>
<li>Vous voulez supprimez un objet/groupe? <strong>Remove</strong>!</li>
<li>Vous voulez voir les objets ayant une propriété particulière? <strong>Select</strong>!</li>
</ul>
<p>C'est à la fois limpide mais aussi beaucoup plus rapide que la méthode "conventionnelle" visant à gérer ces propriétés de manière granuleuse sur les groupes/objets. Ici, le système est inversé: Vous gérez les objets en fonction de leurs propriété.</p>
<p>Je trouve ça beaucoup plus clair. :redface:</p>
<p>Voici une explication rapide des différentes <em>properties</em>. Pour plus de détails, se référer à la <a href="http://arsenal.deex.info/wordpress/documentation/" hreflang="en">documentation officielle</a>.</p>
<h5>Black hole</h5>
<p>Les objets ayant cette <em>property</em> ne seront pas affiché dans le rendu (ils seront noir) et leur alpha sera vide.</p>
<p><strong>Receive shadow</strong>: Les objet <strong>Black hole</strong> ayant cette <em>property</em> recevront les ombres et les répercuteront dans leurs alphas (pour un sol par exemple).</p>
<h5>GI</h5>
<ul>
<li><strong>Generate off</strong>: Les objets ayant cette <em>property</em> ne seront pas considéré lors de la génération de l'illumination globale (pour séparer la <strong>GI</strong> entre les personnages et le décors par exemple).</li>
<li><strong>Receive off</strong>: Les objets ayant cette <em>property</em> ne recevront aucune illumination indirects.</li>
</ul>
<h5>Visibility</h5>
<ul>
<li><strong>Primary off</strong>: Les objets ayant cette <em>property</em> ne seront pas visibles sur les rayons primaires (ils seront donc visibles dans les reflections/refractions/GI).</li>
<li><strong>Reflection off</strong>: Les objets ayant cette <em>property</em> ne seront pas visibles dans les rayons de reflections (peut être pratique pour empêcher un objet complexe de se refléter si il n'apporte pas grand chose au rendu final).</li>
<li><strong>Refraction off</strong>: Les objets ayant cette <em>property</em> ne seront pas visibles dans les rayons de refractions.</li>
</ul>
<h5>Shadows</h5>
<p><strong>Cast off</strong>: Les objets ayant cette <em>property</em> ne projetteront pas d'ombres.</p>
<h4>On fly control</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_002_onFlyControl.png" alt="VRay_Arsenal_002_onFlyControl.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_002_onFlyControl.png, janv. 2013" height="447" width="475" /></p>
<p>Je ne suis pas adepte des terminologies utilisées dans cet onglet. Je vais donc essayer d’être clair. :gne:</p>
<h5>Light select</h5>
<p>Ici on cherchera à sortir des passes par light ou pour toutes les lights. Ces <em>pass</em> seront nommé sous la forme <em>nomDeMaLightShape_normal</em>, <em>nomDeMaLightShape_diffuse</em>, etc...</p>
<ul>
<li><strong>Manual</strong>: Si vous souhaitez sortir les "contributions" uniquement pour certaines lights données, c'est ici que vous le ferez.
<ul>
<li><strong>Normal</strong>: Et c'est là ou ça prête pas mal à confusion: <em>Normal</em> ici ce n'est pas la normale au sens vecteur mais normal au sens "classique". C'est donc une passe contenant toute l'illumination direct d'une light. C'est tout. Notez que c'est la terminologie utilisé par VRay...</li>
<li><strong>Diffuse</strong>: <em>Pass</em> d'éclairage direct de la light ne contenant que les informations de diffusion.</li>
<li><strong>Raw</strong>: <em>Pass</em> contenant les informations "brut" de l'éclairage direct de la light</li>
<li><strong>Specular</strong>: <em>Pass</em> de spéculaire pour la light donnée.</li>
</ul></li>
<li><strong>Automatic</strong>: Disons que si vous avez comptez de toute façon sortir toute les passes pour toutes les lights de la scène, passez directement par là. Vous pouvez également combiner le mode <strong>Automatic</strong> pour un type de passe avec le mode <strong>Manual</strong> pour un autre type de passe.
<ul>
<li><strong>Specular</strong>: Sort toute les <em>pass</em> de spéculaire pour toute les lights de la scène.</li>
<li><strong>Raw</strong>: Idem que <strong>Specular</strong> mais pour le <strong>Raw</strong>.</li>
<li><strong>Diffuse</strong>: Idem mais pour la <strong>Diffuse</strong>.</li>
<li><strong>Normal</strong>: Idem mais pour la passe <strong>Normal</strong>.</li>
</ul></li>
</ul>
<h5>Arsenal settings</h5>
<p>Ici on a mes options préférées. Même si on ne peut pas deviner à quoi elles servent au premier abord. :siffle:</p>
<ul>
<li><strong>VrayMtl Lambert</strong>: Cocher cette case transformera, à la volé, au moment du rendu, tout les matériaux de votre scène en <em>VrayMtl Lambert</em>, ce qui est très pratique pour vérifier son éclairage globale. La ou cette option se distingue d'un <em>RenderLayer</em> avec un <em>material override</em>, c'est que les materiaux conserveront leur displacement/bump/normalmap si ils en ont.</li>
<li><strong>Material ID on fly</strong>: Cocher cette case génèrera des <em>Material ID</em> à la volé au moment du rendu en utilisant les noms des matériaux (sans les namespaces) comme "référence". Ainsi, vous obtiendrez toujours la même couleur de <em>Material ID</em> tant votre material porte le même nom. Bien entendu, il faut avoir créé un <em>Render Element</em> de type <em>Material ID</em>. Notez également que cette feature est compatible avec les VRayblendMaterial: Si vous avez blendé plusieurs materials (avec des masques) dans un VRayblendMaterial, chaque material aura son ID propre dans la pass.</li>
<li><strong>Proxy Objects ID on fly</strong>: Même principe que pour les <em>Material ID</em> mais cette fois dans le cas des proxy VRay.</li>
</ul>
<h4>Matte control</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_003_matteControl.png" alt="VRay_Arsenal_003_matteControl.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_003_matteControl.png, janv. 2013" height="252" width="473" /></p>
<p>Celui là aussi je l'aime bien tellement il est simple! :dentcasse:</p>
<p>Le principe est de générer des passes de "masque". Vous savez? Les masques RGB:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_013_passMask.png" alt="VRay_Arsenal_013_passMask.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_013_passMask.png, janv. 2013" height="369" width="324" /></p>
<p>En gros:</p>
<ul>
<li>Vous faites <strong>Create new</strong>.</li>
<li>Vous donnez un nom à votre passe de mask (<strong>Mask name</strong>). Si vous ne donnez pas de noms à votre passe de masque elle sera supprimée. :mitraille:</li>
<li>Vous sélectionnez un/des objets/groupes.</li>
<li>Vous faites <strong>add in red</strong> (ou green/blue).</li>
<li>Vous rendez et vous avez votre passe de mask.</li>
<li>Vous pouvez recommencer la manœuvre pour une autre passe.</li>
</ul>
<p>C'est tellement simple que je ne vois même pas quoi ajouter! :redface:</p>
<p>Ah si! Ce systeme outrepasse une limitation de VRay. Exemple:</p>
<ul>
<li>Vous avez un objet_A avec un ID 1.</li>
<li>Un objet_B avec un ID 2.</li>
<li>Ces deux objets sont dans un group avec ID 3.</li>
<li>Créez un <em>Render Element</em> <em>Multi Matte</em> et passez le <em>Red object ID</em> à 3.</li>
<li>Rendez et regardez votre passe de multimatte.</li>
</ul>
<p>Elle est noire. Pourquoi? Parce que VRay ayant remarqué que les objets dans le groupe ont un object ID, il ne les affiche pas dans la passe.</p>
<p>Avec le systeme de Arsenal, si vous demandez à ce que le groupe soit rouge, il sera rouge!</p>
<h4>FastControl</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_004_fastControl.png" alt="VRay_Arsenal_004_fastControl.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_004_fastControl.png, janv. 2013" height="183" width="474" /></p>
<p>Cet onglet rassemble juste les principales options de VRay pour les activer/désactiver rapidement.</p>
<h3>L'onglet <em>Quality</em> <a name="quality"></a></h3>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_005_qualityTab.png" alt="VRay_Arsenal_005_qualityTab.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_005_qualityTab.png, janv. 2013" height="773" width="492" /></p>
<center><i>Un gros slider qui change de couleur! :D</i></center>
<p>Ici on est clairement dans la partie qui plaira au personnes qui ne sont pas forcément des as du rendu. Et pourtant, vous allez voir qu'il peut très largement faire de l’œil à des personnes ayant l'habitude de modifier leurs paramètres à la main. :dentcasse:</p>
<p>La première chose à faire pour activer tout ça est de cliquer sur <strong>Save actual settings</strong> qui se chargera de mettre de coté vos paramètres actuels, puis sur <strong>Optimize</strong> pour mettre en place les différentes expressions. A partir de ce moment là, vous pouvez commencer à modifier le slider principale pour faire varier la qualité du rendu.</p>
<h4>Preset Type</h4>
<p>Ici, on parle plus de configuration du slider que de <em>Preset</em>. Il y a deux <em>Preset Type</em>:</p>
<ul>
<li><em>deeX_interior</em></li>
<li><em>deeX_exterior</em></li>
</ul>
<p>Ces deux choix représentent la majorité des cas de lighting.</p>
<p>Les presets en question sont de simple fichier texte que vous pouvez retrouver dans:</p>
<pre class="bash bash">C:\Users\Narann\Documents\maya\deeXVRayArsenal\<span style="color: #000000;">2013</span>\win64\scripts\presets</pre>
<p>Ces fichiers, à la syntaxe relativement simple, permettent de piloter les ranges (min et max) des attributs de VRay qui seront ensuite modifié par votre slider. Si je ne dis pas de bêtises, la relation entre le slider et les paramètres qu'il drive est <a href="http://fr.wikipedia.org/wiki/Logarithme" hreflang="fr">logarithmique</a> pour éviter que les paramètres n'explosent vos temps de rendus quand vous commencer à vous monter haut dans le slider. :grenadelauncher:</p>
<p>Si la possibilité de créer ces propres presets n’intéressera surement pas les lighters <del>du dimanche</del> occasionnels, le fait de pouvoir mettre sois même ces paramètres <em>min</em> et <em>max</em> à piloter peut être très intéressant pour les lighters à poil dûr qui ont leurs petites habitudes. :papi:</p>
<p>D’ailleurs, il aurait presque fallu que cette outil soit indépendant de VRay pour pouvoir, par exemple, piloter Mental Ray. :baffed:</p>
<blockquote><p>Si vous souhaitez empêcher la modification d'un attribut par Arsenal il suffit de le locker.</p></blockquote>
<h4>Offset quality</h4>
<p>On a ici une très bonne initiative. L'idée étant d'appliquer un offset au slider pour certains paramètres.</p>
<p>Par exemple, supposons que mon slider soit à <em>50</em>. Je trouve que le sampling est un peu sensible et augmente les temps de rendu trop rapidement. Pourtant, je suis satisfait du reste.... Qu'à cela ne tienne, il me suffit de cocher la case <strong>Image sampler</strong> puis de taper <em>-10</em> pour que les paramètres du sampler s'applique comme si mon slider globale était à <em>40</em>! :youplaBoum:</p>
<h4>Optimize material(s)</h4>
<p>Encore une initiative intéressante: Vous déterminer deux valeurs extrêmes de subdivision (sampling), cliquez sur le bouton <strong>Optimize material(s)</strong> et Arsenal va déterminer la valeur à utiliser en fonction de la valeur de <strong>Glossiness</strong> du material.</p>
<p>Attention toutefois, le système est un peu trop "bête". Si une texture est connecté au paramètre de glossiness, la valeur subdiv est déterminé en fonction de la valeur de l'attribut, même si elle ne correspond à rien en temps que tel. Une option telle que <em>skip connected</em> pourrait être intéressante.</p>
<p>Notez la checkbox à droite <strong>LayerMode</strong>. Si vous la cocher avant de cliquer sur le bouton <strong>Optimize material(s)</strong> et que vous êtes dans un <em>RenderLayer</em> (autre que le <em>masterLayer</em>), les attributs seront modifié uniquement sur le <em>RenderLayer</em> en cours.</p>
<h4>Optimize light(s)</h4>
<p>Pas sur que ce bouton soit très utile. Pour l'instant il ne fait que passer le paramètre de Subdiv des VRayLights de la scène à 5...</p>
<h3>L'onglet <em>ToolBox</em> <a name="toolbox"></a></h3>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_006_toolBoxTab.png" alt="VRay_Arsenal_006_toolBoxTab.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_006_toolBoxTab.png, janv. 2013" height="707" width="494" /></p>
<p>Ici vous trouverez des petits outils très spécifiques.</p>
<h4>Proxy</h4>
<p>Les <a href="http://www.spot3d.com/vray/help/maya/150R1/vrayproxy_params.htm" hreflang="en">VRayProxy</a> sont une petite merveille. On les a utilisés sur <a href="http://www.allocine.fr/film/fichefilm_gen_cfilm=178179.html" hreflang="fr">Or Noir</a> pour faire des cycles d'animation de cheval changeant de vitesse.</p>
<h5>Multi proxy importer</h5>
<p>Ce bouton permet d'importer plusieurs VRayProxy car, aussi surprenant que cela puisse paraitre, il n'est pas possible de faire cela en standard...</p>
<h5>Shader auto connect</h5>
<p>Quand vous exportez un VRayProxy, des "slots d'assignations" y sont créés. Le principe est de pouvoir conserver, à l’intérieur du proxy, les assignations de vos shaders pour les réassigner.</p>
<p>Une fois le VRayProxy réimporté dans votre scène, vous pouvez lui appliquer un <a href="http://www.spot3d.com/vray/help/maya/150R1/vraymeshmtl_params.htm" hreflang="en">VRayMeshMaterial</a> (pour info, il est déjà créé au moment de l'import). Ce material affichera les différents slots d'assignations de votre proxy pour y connecter vos propres shaders.</p>
<p>Le bouton <strong>Shader auto connect</strong> permet de reconnecter automatiquement les shaders en fonction de leur noms, ce qui évite d'avoir à le faire sois même. Sur des proxy ayant des dizaines voir des centaines d'assignations, c'est plutôt pratique...</p>
<h5>Ignore namespace</h5>
<p>Cette case permet d'ignorer les namespaces des shaders de votre scène pour les réassigner au proxys. Dans le principe, cela permet d'avoir ces shaders en référence externe (avec un namespace) sans pour autant que ça ne pose problème de les reconnecter via leur noms.</p>
<h4>ID</h4>
<p>Une fois de plus, ici nous avons des outils pour assigner très rapidement des ID aux matériaux et aux objets.</p>
<p>L’intérêt est ensuite de sortir ces ID sous forme de passe à donner à vos amis du compositing.</p>
<h4>Material ID</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_008_materialID.png" alt="VRay_Arsenal_008_materialID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_008_materialID.png, janv. 2013" height="431" width="383" /></p>
<ul>
<li><strong>Add on</strong>: Permet d'appliquer les attributs de <em>Material ID</em> aux materials sélectionnés/tout les materials/tout les shading engine.</li>
<li><strong>Set on</strong>: Applique un ID unique aux attributs de <em>Material ID</em> des materials sélectionnés/tout les materials/tout les shading engine. Ce numéro est généré aléatoirement en se servant du nom du material. Cette méthode est à double tranchant: Si vous changez le nom d'un material puis que vous recliquez sur <em>Set on</em>, la couleur de son ID changera également. Notez que la checkbox <strong>Material ID on fly</strong> de l'onglet <em>Pass</em>/<em>On fly control</em> fait la même chose directement à la volée. :)</li>
<li><strong>Remove on</strong>: Supprime les attributs de <em>Material ID</em> des materials sélectionnés/tous les materials/tous les shading engine.</li>
</ul>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_014_materialID.png" alt="VRay_Arsenal_014_materialID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_014_materialID.png, janv. 2013" height="115" width="528" /></p>
<p>Dernier point, pour que tout cela fonctionne, n'oubliez pas de créer une pass (un <em>Render Element</em>) de <em>Material ID</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_007_materialID.png" alt="VRay_Arsenal_007_materialID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_007_materialID.png, janv. 2013" height="568" width="437" /></p>
<h4>Object ID</h4>
<p>Même principe que pour les <em>Material ID</em> mais au niveau des objets.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_015_objectID.png" alt="VRay_Arsenal_015_objectID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_015_objectID.png, janv. 2013" height="84" width="451" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_010_objectID.png" alt="VRay_Arsenal_010_objectID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_010_objectID.png, janv. 2013" height="621" width="570" /></p>
<center><i>Notez la différence avec la passe de ''Material ID''.</i></center>
<p>Chaque objet aura un identifiant différent.</p>
<p>Une fois de plus, n'oubliez pas de créer le <em>Render Element</em> <em>Object ID</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_009_objectID.png" alt="VRay_Arsenal_009_objectID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_009_objectID.png, janv. 2013" height="549" width="429" /></p>
<h4>Material control</h4>
<p>Cette partie vous permet d'appliquer quelques valeurs clefs à tous les materials/aux material sélectionnés en un seul clique.</p>
<ul>
<li>Reflection/Refraction Subdivs</li>
<li>Reflection/Refraction Interpolation</li>
<li>Reflection/Refraction Max depth</li>
</ul>
<p>Une option <strong>Trace</strong> (Reflection/Refraction) pour activer/désactiver complètement le raytracing aurait put être intéressante ici. :dentcasse:</p>
<h4>Lights control</h4>
<p>On peut pas faire plus simple: Appliquer une valeur de <strong>Shadow subdivs</strong> à toutes les lights/les lights sélectionnées de la scène en un clique.</p>
<h3>Techniquement, comment ça marche?</h3>
<p>Hahaha! La particularité de cet outil est de ne pas modifier votre scène quand vous générez les passes (Sauf les <em>RenderLayers</em> de Maya bien évidement), que vous faites du "On the fly" ou même du Matte.</p>
<p>Mais alors? Comment qu'il fait? :gne2:</p>
<p>Et bien il utilise une feature de VRay que je rêverai de voir généralisé sur tout les moteurs tant elle est monstrueuse en terme de possibilités: L'API Python <a href="http://www.spot3d.com/vray/help/maya/150R1/render_scene_access.htm" hreflang="en">VRay Scene Access</a>.</p>
<p>Je quote la doc, c'est très clair:</p>
<blockquote><p>The V-Ray scene access python API allows you to modify the V-Ray scene after it is translated by the V-Ray for Maya translator, and before it is rendered and/or exported to a .vrscene file.
The scene access API allows you to expand the V-Ray for Maya translator by providing custom translation for constructs that are not recognized by V-Ray, or for modifying the scene before rendering without changing the original Maya scene.</p></blockquote>
<p>Boum! Rien que ça! :sauteJoie:</p>
<p>En gros, ça permet de piloter le moteur avant le rendu. On peut donc imaginer des trucs de dingue comme générer une scène directement dans VRay, à la volé, sans avoir recours à un fichier vrscene. Pour ceux qui ont déjà travaillé avec Renderman, ça veut dire fini la génération des RIB bien lourd qui ne servent qu'une fois. Ici, on peut piloter VRay via du Python!</p>
<p>J'ai posé la question sur <a href="http://forum.nvidia-arc.com/showthread.php?8097-Python-to-controle-mental-ray" hreflang="en">le forum de Nvidia ARC</a> (ancien Mental Image) tant je trouvais ça génial mais je ne suis pas sur que ça change grand chose. :triste:</p>
<p>Bref, l'outil de Damien utilise ça, et c'est ce qui lui permet de conserver une scène Maya propre et de pouvoir intervenir directement au niveau de VRay avant le rendu.</p>
<h3>Conclusion</h3>
<p>Pour avoir testé le truc de fond en comble je peut vous assurez qu'il vaut (très) largement son prix (69€! :bravo:).</p>
<ul>
<li>Le temps gagné à préparer ses passes est monstrueux et vous évite d'avoir à passer par toutes les fenêtres de VRay.</li>
<li>Le fait de pouvoir contrôler les objets en fonction de leurs <em>properties</em> et pas l'inverse est tout ce qu'il a de plus simple.</li>
<li>La gestion des Mattes RGB est aussi très simple.</li>
<li>Des Material/Object ID on the fly qui marchent en quelques cliques... Il ne reste aucun prétexte pour ne pas les utiliser.</li>
<li>La VRayMtlLambert on the fly meriterai une section à elle toute seule tant elle peut être pratique.</li>
<li>Le Quality slider va loin avec les preset et les offsets.</li>
</ul>
<p>Ce qu'il manque:</p>
<ul>
<li>Pouvoir exporter son "set de passe" dans un fichier texte pour l'importer plus tard (c'est prevu!).</li>
<li>Encore plus d'options <em>On the fly</em> (Je suis fan du principe).</li>
<li>Une API Python aussi simple à utiliser que les boutons. Genre <em>arsenal.pass.propertiesControl.blackHole.add("myObject")</em>. Mais à part moi je ne suis pas sûr que ça serve à grand monde! :trollface:</li>
</ul>
<p>Bon, je vends <del>un peu</del> complètement le truc mais oui: Il vaut le coup! :aupoil:</p>
<p>Au delà du fait que je vous invite à vous ruer sur cet outil, j'espère que si vous le possédez déjà, cette présentation/tuto vous permettra d'aller encore plus loin! :)</p>
<p>A bientôt!</p>
<center><i>:marioCours:</i></center>
Powerslide sur GOG en full HD avec AA et tout et tout!urn:md5:f4f530250f4937eec114f9e8190639e12013-01-16T10:28:00+01:002013-07-26T18:01:14+02:00NarannMes coups de coeurabandonwarefrgogjeux vidéopowerslideretrogaming<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_tn.png" alt="Powerslide_GOG_tn.png" style="float:left; margin: 0 1em 1em 0;" title="Powerslide_GOG_tn.png, janv. 2013" height="150" width="150" />J'ai acheté <a href="http://www.gog.com/gamecard/powerslide" hreflang="en">Powerslide sur GOG</a> il y a quelques temps. :gniarkgniark:</p>
<p>Pour info, <a href="http://fr.wikipedia.org/wiki/GOG.com" hreflang="fr">GOG</a> était l’abréviation de <em>Good Old Games</em> et proposait (et propose toujours) des jeux sans DRM, à des prix attractifs et surtout une compatibilité avec les nouveaux systèmes.</p>
<p>C'est précisément ça qui m'a amené à dépenser la moitié moins du prix d'une peinte parisienne (c'est le premier argument qui m'est passé par la tête au moment de payer, allez savoir pourquoi...) pour ce jeu dont j'ai dû passer plus de temps sur la démo que sur la plupart des autres jeux que j'avais pu acheter à l’époque. :baffed:</p> <h3>Envie de rejouer aux jeux de ton enfance? Ça y est, t'est vieux!</h3>
<p>Tout commence par un screenshot du jeu sur lequel je tombe totalement par hasard sur un forum (sur un thread inspiré du genre "Un screenshot, devinez le jeu"). Je me dis "tiens il était cool ce jeu, ça serait bien d'y rejouer, juste pour voir" :siffle: .</p>
<p>Après pas mal de recherches, je trouve quelques isos du CD original et lance une installation.</p>
<p>Et paf! C'est la que tu te prend dix ans d’évolution des OS dans la tronche... Faire fonctionner correctement ce jeu en version original relève de l'exploit (au sens français comme au <a href="http://fr.wikipedia.org/wiki/Exploit_%28informatique%29" hreflang="fr">sens informatique</a>). Certains semblent y être arrivé. Ne m'étant jamais vraiment tourné vers du "revival" de jeux fonctionnant via 3dfx j'ai du me contenter d’échecs à répétition... :pasClasse:</p>
<p>Après plusieurs soirées (parce que oui, quand quelque chose comme ça me résiste, je suis du genre assez bête pour insister) à tenter par tout les moyens de faire fonctionner ce jeu, j'ai du me résilier à abandonner. <em>Powerslide</em> allait rejoindre la longue liste des jeux qu'il n'est plus possible de faire fonctionner sans prise de tête (<a href="http://www.google.fr/search?q=shadow+of+the+empire+win+7" hreflang="fr">Shadow Of The Empire</a>... Snif...).</p>
<p>Puis je réalise que GOG (que je connaissais déjà) propose une version. Il faut savoir que GOG fait un gros travaille pour rendre les anciens jeu jouable sur les nouveaux systèmes sans que l'utilisateur ait justement à bricoler. T'installe, tu lance, tu enjoy! :banaeyouhou:</p>
<p>4.74 euros plus tard (tant pis, je boirai un verre d'eau à ma prochaine sortie... :dentcasse: ) + le temps régler le wrapper <a href="http://www.zeus-software.com/downloads/nglide" hreflang="en">nGlide</a> intégré, je branche mon pad, lance le même circuit + la même voiture que la démo et m'affale dans mon siège, détendu, pour profiter des quelques minutes de nostalgie qui s'offre à moi.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_nGlide_001.png" alt="Powerslide_GOG_nGlide_001.png" style="display:block; margin:0 auto;" title="Powerslide_GOG_nGlide_001.png, janv. 2013" height="215" width="242" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_nGlide_002.png" alt="Powerslide_GOG_nGlide_002.png" style="display:block; margin:0 auto;" title="Powerslide_GOG_nGlide_002.png, janv. 2013" height="302" width="396" />
Ces vieux jeux étant pensés pour être projetés sur tube cathodique, n'hésitez pas à monter le Gamma de à 1.2. Pensez aussi à conserver les proportions en 4:3, sinon, ça risque de vous donner le mal de mer durant les virages.</p>
<p>Pour le reste: A fond!</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_amd_override_001.png" title="Powerslide_GOG_amd_override_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_amd_override_001_m.jpg" alt="Powerslide_GOG_amd_override_001.png" style="display:block; margin:0 auto;" title="Powerslide_GOG_amd_override_001.png, janv. 2013" height="299" width="560" /></a></p>
<center><i>J'ai également overridé tous les paramètres à fond au niveau du driver, ça coute pas cher.</i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_menu.png" title="Powerslide_GOG_menu.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_menu_m.jpg" alt="Powerslide_GOG_menu.png" style="display:block; margin:0 auto;" title="Powerslide_GOG_menu.png, janv. 2013" height="420" width="560" /></a></p>
<center><i>Le circuit. Souvenirs souvenirs... :hehe:</i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_logo_3dfx_en_aa.png" title="Powerslide_GOG_logo_3dfx_en_aa.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_logo_3dfx_en_aa_m.jpg" alt="Powerslide_GOG_logo_3dfx_en_aa.png" style="display:block; margin:0 auto;" title="Powerslide_GOG_logo_3dfx_en_aa.png, janv. 2013" height="315" width="560" /></a></p>
<center><i>Avouez que vous l'aviez oublié celui là. :sourit:</i></center>
<p>Mais aussitôt l'accelerateur poussé, je déchante.</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_001.jpg" title="Powerslide_GOG_001.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_001_m.jpg" alt="Powerslide_GOG_001.jpg" style="display:block; margin:0 auto;" title="Powerslide_GOG_001.jpg, janv. 2013" height="420" width="560" /></a></p>
<p>Premier virage: Bim! Raté. Second virage: Bim! Reraté... (Je me redresse sur mon siège: putain... o_O).</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_002.jpg" title="Powerslide_GOG_002.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_002_m.jpg" alt="Powerslide_GOG_002.jpg" style="display:block; margin:0 auto;" title="Powerslide_GOG_002.jpg, janv. 2013" height="420" width="560" /></a></p>
<p>Après 30 minutes de jeu, le constat est sans appel: Les jeux étaient bien plus durs AAAAAvant, alors que j'étais beaucoup plus jeune et que je n'avais pas noté de difficulté particulière. Au contraire, j'avais gardé le sentiment d'un jeu d'arcade... Et le pire c'est que c'est cette combinaison (voiture/circuit) qui était utilisé pour te "vendre" le jeu. :seSentCon:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_003.jpg" title="Powerslide_GOG_003.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_003_m.jpg" alt="Powerslide_GOG_003.jpg" style="display:block; margin:0 auto;" title="Powerslide_GOG_003.jpg, janv. 2013" height="420" width="560" /></a></p>
<p>Sur le premier tour d'un nouveau circuit, il y a de forte chance que vous passiez plus de temps sur le dos que sur les roues. Au début ça fait bizarre mais après on se surprend à se lancer un défi à chaque découverte d'un circuit: "Celui là, je roule doucement et je me ramasse pas!", et puis non... :onSeFendLaPoire:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_004.jpg" title="Powerslide_GOG_004.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_004_m.jpg" alt="Powerslide_GOG_004.jpg" style="display:block; margin:0 auto;" title="Powerslide_GOG_004.jpg, janv. 2013" height="420" width="560" /></a></p>
<p>Et c'est au bout d'une première heure qu'on commence à comprendre que négocier un virage est en fait une affaire de maitre et c'est ce que ce jeu exige: De la maitrise.</p>
<p>Puis tout devient naturel, on se met à sourire lorsqu'on double un adversaire sans le toucher sur un virage pris a la perfection.</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_005.jpg" title="Powerslide_GOG_005.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_005_m.jpg" alt="Powerslide_GOG_005.jpg" style="display:block; margin:0 auto;" title="Powerslide_GOG_005.jpg, janv. 2013" height="420" width="560" /></a></p>
<center><i>Là, ça ne se voit pas mais je suis en train de partir completement à droite... :nevroz: </i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/Powerslide_GOG_006.jpg" title="Powerslide_GOG_006.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_11_Powerslide_Sur_GOG/.Powerslide_GOG_006_m.jpg" alt="Powerslide_GOG_006.jpg" style="display:block; margin:0 auto;" title="Powerslide_GOG_006.jpg, janv. 2013" height="420" width="560" /></a></p>
<p>Notez qu'un mode multijoueur est dispo en IPX, Modem (des trucs de vieux) mais surtout: TCP/IP. Vous devriez donc pouvoir jouer contre un/des amis (à qui vous aurez suffisamment bourré le mou je suppose) sur internet en <a href="http://forum.hardware.fr/hfr/WindowsSoftware/redirection-ip-serveur-sujet_266697_1.htm" hreflang="fr">bricolant un peu</a>. :smileFou:</p>
<h3>Lapin d'musiques?</h3>
<p>Durant mes premières parties, j'ai pu constater que la musique était absente (les mauvaises langues diront qu'aux vues de la qualité musicale du titre ce n'est pas plus mal :trollface: ), malgré mes allez-retour dans les options. En fouillant le dossier du jeu, je vois des mp3 contenants, justement, les musiques. J’envoie un mail au support pour leur faire remarquer mon soucis sans trop y croire (après tout, je m'en fout, quand je joue à des jeux comme ça c'est du Nirvanna ou du Vitalic qui tourne derrière).</p>
<p>Et c'est à ma grande surprise que quelques heures plus tard, le support m'indique avoir corrigé le soucis et m'invite a retélécharger le jeu...</p>
<p>Et ça fonctionne! :banaeyouhou:</p>
<p>C'est donc avec plaisir que je me suis plongé quelques soirées dans ce jeu bien fun qui s'apparente plus a un jeu de dérapage qu'un "jeu de voiture" et que j'ai pu apprécier le travail et la réactivité de GOG.</p>
<h3>GOG c'est bien mais...</h3>
<p>...la liste des jeux en français est finalement assez maigre. C'est bien dommage d'ailleurs...</p>
<p>Je vous propose donc un site alternatif français qui c'est lancé dans le même business: <a href="http://www.dotemu.com/fr" hreflang="fr">DotEmu</a>.</p>
<p>Bon, le catalogue est moins fournit (je trouve qu'il complète bien celui de GOG), mais la plupart des jeux proposés le sont en français (Les Fallout, Gobliiins, Les Commandos, etc...).</p>
<p>En espérant que ce billet coup de cœur rappel des souvenirs à certains...</p>
<p>A bientôt!</p>
<center><i>:marioCours:</i></center>
<h3>Liens</h3>
<ul>
<li><a href="http://www.grospixels.com/site/powerslide.php" hreflang="fr">Test de Powerslide sur Grospixels</a></li>
<li><a href="http://www.emunova.net/veda/test/1050.htm" hreflang="fr">Test de Powerslide sur Emunova</a></li>
</ul>Résoudre les ralentissements réseaux dûs a Nuke, After Effect et autres logiciels du genreurn:md5:df614baf78d8e7e03917d6ade3fef5852012-10-14T12:46:00+02:002020-12-10T09:53:01+01:00NarannScript et codeafter effectfrnukepythonralentissementreseau<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_10_14_nuke_reseau_lenteurs/nuke_reseau_tn.png" alt="nuke_reseau_tn.png" style="float:left; margin: 0 1em 1em 0;" title="nuke_reseau_tn.png, oct. 2012" />Nuke, After Effect et surement d'autres, peuvent se révéler très "gourmand" en termes d’accès disque. A tel point qu'ils peuvent rapidement mettre des disques réseaux à genoux si plusieurs stations font des rendus.</p>
<p>Dans ce billet je vous propose une explication rapide du comment du pourquoi ainsi qu'une petite classe Python qui fait office de prototype pour solutionner le probleme.</p> <h3>C'est quoi le problème?</h3>
<p>Rien de mieux qu'un cas concret pour énoncer la chose:</p>
<p>Sur la fin de <a href="http://www.allocine.fr/film/fichefilm_gen_cfilm=170530.html" hreflang="fr">Lorax</a>, il y avait un grand nombre de « compeurs » (compositeurs) qui rendaient leurs images en même temps et les estimations affichées par Nuke étaient parfois surprenantes (3h-4h restantes pour des compos simples). En surveillant un fichier en train de s’écrire, je me suis rendu compte qu’il montait très lentement. Il était à 100ko, puis passait à 300ko, puis au bout de dix secondes passait à 400ko, etc. J’en ai conclu que le réseau était surchargé… :reflexionIntense:</p>
<p>Je me suis rappelé que nous avions eu le même souci sur <a href="http://www.allocine.fr/film/fichefilm_gen_cfilm=183772.html" hreflang="fr">Les Contes De La Nuit</a>. Notre infrastructure était, certes, beaucoup plus modeste mais 3 After Effect en rendu plombaient l’intégralité du réseau. La solution trouvée à ce moment la fut de rendre en local puis de copier les fichiers une fois finis. Les effets furent immédiats : Le réseau ne ramait plus. :sauteJoie:</p>
<p>J’ai donc tenté de rendre le compo d’un graphiste en local pour voir si cela diminuait le problème. Les plus gros fichiers à lires étant les fichiers sources, car plus nombreux, et l’image rendu étant relativement légère, je ne me faisais pas trop d’illusions. Et pourtant, une fois de plus, le constat est sans appel : Le rendu se finissait en 10-15 minutes (je ne rigole pas) au lieu de 3-4h… :laClasse:</p>
<p>Je me suis dit que c’était l’écriture qui devait poser problème. Pourtant, quand je copiais la séquence d’image fraichement calculée, la copie était très rapide. Je suis donc passé voir les graphistes les uns après les autres et en un gros après midi, le réseau était complètement désengorgé et les rendus passèrent tous.</p>
<p>Mais ce n’était pas une solution. Il fallait comprendre pourquoi Nuke n’arrivait pas à écrire ces images rapidement sur le réseau. Après avoir <a href="http://forums.thefoundry.co.uk/phpBB2/viewtopic.php?p=29117" hreflang="en">posé la question</a> sur la mailing list nuke-users (ou j’ai pu constater que je n’étais pas seul mais que The Foundry semblait ne rien pouvoir faire), j’ai commencé à « profiler » Nuke pour savoir comment il s’y prenait (<a href="http://fr.wikipedia.org/wiki/Strace" hreflang="fr">strace</a> et <a href="http://linux.die.net/man/1/inotifywatch" hreflang="en">inotifywatch</a> sont tes amis :dentcasse: ).</p>
<p>Les conclusions semblent évidentes, mais il est toujours bon de vérifier par la pratique ce qu’on soupçonne :</p>
<p>Sous Nuke, si vous écrivez un EXR compressé <em>zip 1 line</em>, en 1920 × 1080, Nuke fera un peu moins de 900 (et des brouettes) accès en écriture sur le fichier. Si vous êtes en <em>zip 16 lines</em>, il fera environs 70 accès (1080/16). Et en non compressé c’est réellement 1080 accès. :trollface:</p>
<p>Dans les faits, compresser en <em>zip 16 line</em> n’est pas super efficace si les images doivent être lues par Nuke. Et suivant l’infrastructure de votre réseau, écrire ligne à ligne peut le mettre complètement à plat. Il est difficile d’expliquer comment finalement peu de rendus Nuke sont nécessaires pour plomber un réseau, même si ce dernier est costaud. J’ai la sensation que c’est lié au multithreading : Nuke lit des images (souvent beaucoup en même temps) sur le réseau pendant qu’il y écrit.</p>
<p>La solution la plus évidente reste donc d’écrire l’image (les images) rendu sur le disque local et de la copier en une fois (un seul accès) sur le disque réseau. Si vous ne disposez pas des ressources techniques nécessaires ou bien tout simplement de temps, c’est l’approche la plus simple mais sur des projets plus conséquents ça peut rapidement devenir rébarbatif et (ne l’oublions pas) source d’erreurs.</p>
<p>Il y a plusieurs solutions et je m’étais penché sur un prototype que je trouvais intéressant car facile à mettre en place.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_10_14_nuke_reseau_lenteurs/problems.jpg" alt="problems.jpg" style="display:table; margin:0 auto;" title="problems.jpg, oct. 2012" /></p>
<h3>Le principe</h3>
<ul>
<li>Vous lancez un thread python qui va surveiller un dossier.</li>
<li>Toutes les trois secondes le thread va lister les fichiers présents dans le dossier et voir si leur nom correspond à une expression régulière (le « modèle » de votre nom de fichier).</li>
<li>Si le fichier semble être un fichier que vous souhaitez déplacer une fois fini, il cherche ce même fichier avec ".finished" (exemple : "toto.001.exr.finished").</li>
<li>Si ce fichier existe, il déplace le fichier d’origine et supprime son équivalent en ".finished" puis relance la boucle principale.</li>
<li>Une fois le rendu fini, vous donner l’ordre au thread de s’arrêter.</li>
</ul>
<p>Comme vous pouvez le voir, ce système nécessite que vous créiez un fichier ".finished" à chaque fois qu’une image est finie. C’est dû au fait qu’il est impossible pour le thread de savoir à quel moment une image est fini de calculer et complète (et ducoup, quand il peut le lancer la copie). La création de ce ".finished" peut être géré de mille façons différentes (Pour Maya, un "Post render frame" fait l’affaire) donc je ne rentre pas dans les détails. :siffle:</p>
<h3>Le code</h3>
<p>Voici le code brut de décoffrage :</p>
<pre>[python]
import os, threading, re, time
class MoverThread( threading.Thread ) :
def __init__( self, dirTocheck, dirToMoveIn, patternToCheck, force=False ) :
threading.Thread.__init__( self )
self._terminate = False
self.dirTocheck = dirTocheck
self.dirToMoveIn = dirToMoveIn
self.force = force
# regex pattern
self.patternToCheck = patternToCheck
self.rePattern = re.compile( patternToCheck )
# sanity check
if not os.path.isdir(self.dirTocheck) :
raise Exception( "The given directory (dirTocheck) is not a valid directory -> %s" % self.dirTocheck )
if not os.path.isdir(self.dirToMoveIn) :
raise Exception( "The given directory (dirToMoveIn) is not a valid directory -> %s" % self.dirToMoveIn )
def run( self ) :
filesNotMoved = []
while not self._terminate :
# we wait 3 seconds before do anything
time.sleep( 3 )
# for every "entry" (file or folder) in the folder we check it have the good pattern. If it has, we check for a ".finished" file
for entry in os.listdir( self.dirTocheck ) :
# check the current entry is "compliant" with the given regex
if not self.rePattern.match( entry ) :
continue
srcFilePath = os.path.join( self.dirTocheck, entry )
dstFilePath = os.path.join( self.dirToMoveIn, entry )
if os.path.isfile( srcFilePath+".finished" ) :
# destination file aready exist?
if os.path.isfile( dstFilePath ) and not self.force:
# don't add the entry if it is already in the list
if not entry in filesNotMoved :
filesNotMoved.append( entry )
continue
# move the file to it new location
os.rename( srcFilePath, dstFilePath )
os.remove( srcFilePath+".finished" )
print "File %s moved to %s" % ( entry, self.dirToMoveIn )
break # restart the while loop to avoid to continue the list of file we maybe have removed: ".finished"
print "Terminated!"
for fileNotMoved in filesNotMoved :
print "Already exists: Can't move %s to %s" % ( fileNotMoved, self.dirToMoveIn )
def join( self ) :
self._terminate = True
threading.Thread.join( self )
</pre>
<p>Comme vous pouvez le voir (ou pas), tout ce passe dans un thread.</p>
<p>Ça s’utilise comme ça :</p>
<pre>[python]
import waitFinishAndCopy
myMoverThread = waitFinishAndCopy.MoverThread("/a/local/path/", "/a/network/path/", "^toto\.[0-9]{4}\.exr$")
myMoverThread.start()
# start rendering, do rendering, end rendering.
myMoverThread.join()
</pre>
<p>Et voila !</p>
<h3>Conclusion</h3>
<p>J’espère que ce modeste prototype vous inspirera si vous rencontrez des lenteurs sur votre réseau. :mechantCrash:</p>
<p>Je vous conseille de faire un peu de profilage réseau sur vos principales applications, surtout si elles sont utilisées par beaucoup de monde. Leur comportement est toujours intéressant (et parfois surprenant).</p>
<p>Passez une bonne journée !</p>
<p>Dorian</p>
<center>:marioCours:</center>
Neo N64 Myth: Douleur, pleurs, joie!urn:md5:ad3b04d6d82f1d6616825258a2f871db2012-09-30T21:13:00+02:002013-07-26T18:02:22+02:00NarannMes coups de coeurfrlinkerneo n64 mythnintendo 64<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2231_tn.jpg" alt="IMG_2231_tn.jpg" style="float:left; margin: 0 1em 1em 0;" title="IMG_2231_tn.jpg, sept. 2012" height="150" width="150" />Et un petit billet détente pour vous parler non pas de pipeline mais de <a href="http://fr.wikipedia.org/wiki/Retrogaming" hreflang="fr">retrogaming</a> et plus spécifiquement d'une console qui me tiens particulièrement à cœur: La Nintendo 64. :dentcasse:</p>
<p>Ici je vais vous parler du <em>Neo N64 Myth</em> et des galères que j'ai rencontrées en l'utilisant.</p>
<p>On est déjà loin des problématiques de displacement mapping et de render pass là!</p> <h3>Ma vie</h3>
<p>Ma première console fut la Super Nintendo (SNES). Je vous passe la nostalgie des interminables parties de <a href="http://www.sitesnes.com/test/ptl$$Super_Mario_World$$Elie46.htm" hreflang="fr">Super Mario World</a>/<a href="http://www.sitesnes.com/test/crs$$Super_Mario_Kart$$Elie46.htm" hreflang="fr">Super Mario Kart</a>/<a href="http://www.scrolling.fr/test-international-superstar-deluxe-snes/2011/03/15" hreflang="fr">ISS Deluxe</a>/<a href="http://www.sitesnes.com/test/ptl$$Donkey_Kong_Country$$Elie46.htm" hreflang="fr">Donkey Kong Country</a>/etc... Quelques années plus tard, nous achetâmes une Nintendo 64 (N64) qui restera surement la console sur laquelle je passa (perdis? :seSentCon: ) le plus de temps.</p>
<p>Début 2010, l'envie me revint de faire une partie de Super Mario 64. :joue: La recherche d'un émulateur open source de qualité m'amena à <a href="http://code.google.com/p/mupen64plus/" hreflang="en">mupen64plus</a> sur lequel je codais brièvement. Puis à la suite d'une bonne affaire sur Priceminister je me rachetais quelques titres (le marché de l'occasion de la N64 étant bien fourni et très accessible financièrement parlant).</p>
<p>Vivant à Paris, j'ai pris l'habitude d’économiser l'espace et un énième joujou, aussi nécessaire soit il (forcement :sourit: ), n'allait pas dans ce sens. Je me suis donc demandé si il n'existait pas un moyen d'avoir tout ses jeux sur une seule et même cartouche.</p>
<p>Ceci m'amena sur le chemin tortueux des linkers N64 (le mot est lâché).</p>
<h3>Linkers N64?</h3>
<p>En fait, il y en a eu très peu (ça change doucement, le retrogaming étant un vrai marché) et ça existe depuis longtemps, c'est juste cher et introuvable.</p>
<p>Le premier fut le <a href="http://en.wikipedia.org/wiki/Doctor_V64" hreflang="en">Doctor V64</a> (ou V64) sorti grosso modo en même temps que la N64 (1996):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/Dv64-n64.jpg" alt="Dv64-n64.jpg" style="display:block; margin:0 auto;" title="Dv64-n64.jpg, sept. 2012" height="406" width="412" /></p>
<center><i>V'la le pavé!</i></center>
<p>Review en français <a href="http://www.gamekult.com/blog/jimmy130/143714/essai-du-linker-nintendo-64-doctor-v64-junior.html" hreflang="fr">ici</a>.</p>
<p>Le second fut <a href="http://en.wikipedia.org/wiki/Z64" hreflang="en">Mr. Backup Z64</a> (ou Z64):</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/Z64-1.jpg" title="Z64-1.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.Z64-1_m.jpg" alt="Z64-1.jpg" style="display:block; margin:0 auto;" title="Z64-1.jpg, sept. 2012" height="462" width="560" /></a></p>
<center><i>Très jolie sur une cheminée...</i></center>
<blockquote><p>Notez que si vous avez déjà eu affaire à des roms de jeux N64, vous avez surement déjà dut tomber sur les extensions .z64 et .v64. Ces extensions viennent de la machine qui a servi à "dumper" les cartouches. Les roms .v64 ayant l'alignement des bits du Doctor V64 (<a href="http://fr.wikipedia.org/wiki/Endianness#Little_endian" hreflang="en">little endian</a>) et les .z64 l'alignement des bits du Mr. Backup Z64 (<a href="http://fr.wikipedia.org/wiki/Endianness#Big_endian" hreflang="en">big endian</a>). Ça vous la coupe hein! :dentcasse:</p></blockquote>
<p>En dehors de ces deux gros objets qui ne sont plus fabriqués depuis longtemps, sont très rare et donc hors de prix, il n'y avait rien. Nada. Que dalle. Peanut. Peau de balle. :triste:</p>
<p>Mais les choses ont changé avec l'arrivée du <a href="https://en.wikipedia.org/wiki/NEO_N64_Myth_Cart" hreflang="en">Neo N64 Myth</a>. D’après mes recherches, il semble qu'ils bossaient déjà dessus <a href="http://gueux-forum.net/index.php?showtopic=165341&view=findpost&p=1141290" hreflang="fr">en 2006</a>!</p>
<p>C'est un produit qui s'est fait attendre et qui est sorti pile-poil au moment ou j'en cherchais un...</p>
<h3>Neo N64 Myth, c'est bien?</h3>
<p>Autant le dire tout de suite: C'est galère! Si vous comptez brancher vos jeux, vous affaler dans votre canapé à tapoter le bouton <em>START</em> d'impatience avec un paquet de chips entre les jambes vous n'y êtes pas du tout! Sérieusement, il marche impeccablement bien mais le temps nécessaire pour le rendre utilisable et la douleur qu'il a fallu est sans nom. :nervous:</p>
<p>Pour comprendre pourquoi tant de pleurs, il faut revoir l'historique de ce linker, que j'ai subi, bien malgré moi...</p>
<p>Quand le <em>Neo N64 Myth</em> est sorti, c’était une hardware "vide". Juste un truc avec des connecteurs qui pouvait faire booter des roms N64... Un truc cher pour bidouilleurs avertis.</p>
<p>Comme je suis du genre tête brulée (c'est pas toujours une qualité hein! :nannan: ), je me suis renseigné vite fait, ça avait l'air de marcher, j'ai foncé!</p>
<p>Quand j'ai reçu le bouzin, j'ai vite déchanté. Aucun manuel, aucune doc, rien! Les adeptes du retrogaming sont surement habitués à recevoir des bidules électroniques sans mode d'emploi mais ça fait quand même tout drôle quand on voit le prix. :bete:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/NEON64MythCartPackaging.jpg" title="NEON64MythCartPackaging.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.NEON64MythCartPackaging_m.jpg" alt="NEON64MythCartPackaging.jpg" style="display:block; margin:0 auto;" title="NEON64MythCartPackaging.jpg, sept. 2012" height="560" width="420" /></a></p>
<center><i>Avouez que ça ne donne pas confiance pour la suite...</i> :seSentCon:</center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2223.jpg" title="IMG_2223.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2223_m.jpg" alt="IMG_2223.jpg" style="display:block; margin:0 auto;" title="IMG_2223.jpg, sept. 2012" height="373" width="560" /></a></p>
<center><i>Au moins on ne peut pas leur reprocher de claquer de la tune en marketing...</i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2225.jpg" title="IMG_2225.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2225_m.jpg" alt="IMG_2225.jpg" style="display:block; margin:0 auto;" title="IMG_2225.jpg, sept. 2012" height="373" width="560" /></a></p>
<center><i>Le slot pour brancher la mémoire flash (on y reviendra), le connecteur USB à gauche et un bouton inutile à droite.</i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2224.jpg" title="IMG_2224.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2224_m.jpg" alt="IMG_2224.jpg" style="display:block; margin:0 auto;" title="IMG_2224.jpg, sept. 2012" height="373" width="560" /></a></p>
<center><i>Et un slot pour brancher une cartouche N64 d'origine.</i></center>
<blockquote><p>Sur cette dernière image, je vais parler d'un point qui me semble important: Pour lutter contre le piratage, Nintendo a toujours utilisé des puces "certifiées" (dont le fonctionnement était gardé secret) intégrées aux cartouches. Bien qu'il semble que des bidouilleurs aient <a href="http://www.emutalk.net/threads/53217-N64-PIF-CIC-NUS-6105-Algorithm-Finally-Reversed" hreflang="en">récemment</a> réussi à émuler certaines de ces puces par <a href="https://fr.wikipedia.org/wiki/R%C3%A9troing%C3%A9nierie" hreflang="fr">rétroingénierie</a>, il n'y a pour l'instant pas beaucoup de possibilités pour réussir à booter un programme sur la N64... C'est la raison pour laquelle vous devez brancher un jeu officiel. Juste pour pouvoir faire booter le linker. :sourit:</p></blockquote>
<p>Je me suis donc mis à déambuler sur le <a href="http://www.neoflash.com/forum/" hreflang="en">forum officiel</a> (qui ramait comme pas permis à l’époque) et commençais à me faire la main sur le truc.</p>
<p>Comme vous le savez (ou pas, dans ce cas vous allez apprendre des choses :hehe: ), la N64 utilise des cartouches à base de mémoire flash pour avoir un accès rapide aux données (pas de loading sur N64!).</p>
<p>La team à l'origine du <em>Neo N64 Myth</em> a donc décidé de séparer le hardware flash du circuit principale (une bonne chose) et de livrer la cartouche avec un "bloc" de mémoire flash (le machin blanc sur l'image) de 512Mb, soit 64Mo ce qui est très peu si vous comptez mettre toutes vos roms dessus. La ou ils n'ont pas été malin (ça peut se comprendre, ils ont été les premiers et ont donc eu à "tâter" le marché), c'est qu'il n'y a pas de slot pour carte SD ou de truc simple à manipuler, juste une mémoire flash brute.</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2222.jpg" title="IMG_2222.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2222_m.jpg" alt="IMG_2222.jpg" style="display:block; margin:0 auto;" title="IMG_2222.jpg, sept. 2012" height="373" width="560" /></a></p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2220.jpg" title="IMG_2220.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2220_m.jpg" alt="IMG_2220.jpg" style="display:block; margin:0 auto;" title="IMG_2220.jpg, sept. 2012" height="373" width="560" /></a></p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2227.jpg" title="IMG_2227.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2227_m.jpg" alt="IMG_2227.jpg" style="display:block; margin:0 auto;" title="IMG_2227.jpg, sept. 2012" height="373" width="560" /></a></p>
<p>Le problème c'est que mettre une rom la dessus relève du chemin de croix. :grenadelauncher: Il faut utiliser un soft moisi avec des drivers moisis qui chient dans la colle avec Vista et plus (oui, sur le forum officiel on m'a conseillé d’utiliser XP... :pasClasse: ). Bref, c'est dur, très dur.</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/Neo2_Ultra_Menu_N64.png" title="Neo2_Ultra_Menu_N64.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.Neo2_Ultra_Menu_N64_m.jpg" alt="Neo2_Ultra_Menu_N64.png" style="display:block; margin:0 auto;" title="Neo2_Ultra_Menu_N64.png, sept. 2012" height="389" width="560" /></a></p>
<center><i>Le premier soft.</i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/Neo2_Pro_Manager_N64.png" title="Neo2_Pro_Manager_N64.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.Neo2_Pro_Manager_N64_m.jpg" alt="Neo2_Pro_Manager_N64.png" style="display:block; margin:0 auto;" title="Neo2_Pro_Manager_N64.png, sept. 2012" height="350" width="560" /></a></p>
<center><i>Le second...</i></center>
<p>Les choses ont évolué au fil du temps et un nouveau produit est sorti: Le <a href="http://www.ic2005.com/shop/product.php?productid=119&cat=0&featured=Y" hreflang="en">NEO2 Pro 1024M Flash Cart</a>.</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2228.jpg" title="IMG_2228.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2228_m.jpg" alt="IMG_2228.jpg" style="display:block; margin:0 auto;" title="IMG_2228.jpg, sept. 2012" height="373" width="560" /></a></p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2229.jpg" title="IMG_2229.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2229_m.jpg" alt="IMG_2229.jpg" style="display:block; margin:0 auto;" title="IMG_2229.jpg, sept. 2012" height="373" width="560" /></a></p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2230.jpg" title="IMG_2230.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2230_m.jpg" alt="IMG_2230.jpg" style="display:block; margin:0 auto;" title="IMG_2230.jpg, sept. 2012" height="373" width="560" /></a></p>
<center><i>C'est Inception...</i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2231.jpg" title="IMG_2231.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2231_m.jpg" alt="IMG_2231.jpg" style="display:block; margin:0 auto;" title="IMG_2231.jpg, sept. 2012" height="373" width="560" /></a></p>
<center><i>La cartouche dans la cartouche dans la...</i></center>
<p>Ce produit est une mémoire flash qui dispose d'un slot pour carte SD. Bien entendu, un second passage en caisse est nécessaire (de toute façon, le retrogaming c'est un truc de riche! :redface: ).</p>
<p>Avec lui est livré le "USB SlimLoader IV". Un truc qu'on est sensé utiliser pour brancher le <em>Neo2 Pro</em> sur son PC mais qui n'a jamais marché chez moi (on peut utiliser directement le <em>Neo N64 Myth</em> à la place)...</p>
<p>Le dev: ChillyWilly (je n'ai aucune idée de si il s'agit d'un dev de l’équipe principale mais il est en tout cas très respecté) proposa donc ce qui aurait du être en standard depuis le début: Un menu directement sur la N64 qui va chercher les roms sur la carte SD, les copie dans la mémoire flash de la carte et lance le jeu! (Rien que de le dire ça fait du bien).</p>
<p>Mais voila, à force de développer son menu, il se retrouva bloqué par le design du firmware du <em>Neo N64 Myth</em>. Qu'a cela ne tienne, la team qui écoutait ces remarques, proposa un nouveau firmware pour la carte... Le problème c'est que pour pouvoir flasher sa carte, il faut encore acheter quelque chose: Le <a href="http://www.ic2005.com/shop/product.php?productid=98&cat=0&featured=Y" hreflang="en">Neo Dual Programmer</a>. (Putain mais... :grenadelauncher: ).</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/IMG_2236.jpg" title="IMG_2236.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2012_09_29_Neo_N64_Myth/.IMG_2236_m.jpg" alt="IMG_2236.jpg" style="display:block; margin:0 auto;" title="IMG_2236.jpg, sept. 2012" height="373" width="560" /></a></p>
<p>Et si vous voulez avoir une idée de la procédure:</p>
<div class="external-media" style="margin: 1em auto; text-align: center;">
<iframe width="480" height="360" src="http://www.youtube.com/embed/T2DNRSAjJ2s" frameborder="0" allowfullscreen></iframe>
</div>
<p>Elle est encore loin la partie de Mario Kart 64 dans mon canap'... T_T</p>
<p>Mais on commence à voir le bout <del>de notre compte bancaire</del>. Encore une fois, rien n'est expliqué nulle part et on chope les infos et les binaires sur le forum. D'ailleurs, pour les <del>heureux</del> possesseurs de ce linker, je vous conseille de suivre cette <a href="http://www.neoflash.com/forum/index.php/topic,6273.0.html" hreflang="en">FAQ</a>. Elle est très bien faite, très bien expliqué. Dès que je dois faire une modif sur le truc je repasse par là.</p>
<p>Le dernier soucis que j'ai eu a failli définitivement me décourager (en fait il m'a découragé quelques mois). J'ai <a href="http://www.neoflash.com/forum/index.php/topic,7430.msg53471.html#msg53471" hreflang="en">trouvé la solution</a> récemment après une énième tentative et ça m'a fait bien bizarre:</p>
<blockquote><p>Si vous branchez le <em>Neo2 Pro</em> trop "fort" sur le <em>Neo N64 Myth</em> (jusqu'au "click" qui peut naturellement vous faire croire que "c'est bon!"), il n'est plus reconnu par votre PC... En fait, il est reconnu une fraction de seconde et s’éteint directement après. Personne d'autre ne semble avoir eu le soucis et je suis bien content d'avoir trouvé le truc. Mais voila... Encore une fois: Galère...</p></blockquote>
<h3>Pourquoi je vous parle de tout ça?</h3>
<p>C'est une question qu'on peut se poser... Si le <em>Neo N64 Myth</em> est si mauvais que ça, pourquoi en parler? :jdicajdirien:</p>
<p>Et bien parce qu'une fois que vous avez fait le plus dur: Ça marche! Vous mettez vos backups de jeu sur votre carte SD, vous branchez le tout ensemble, vous allumez et vous <em>enjoyez</em>! (J'adore cet anglicisme!).</p>
<p>Le second point, et pas des moindres, c'est que le succès du <em>Neo N64 Myth</em> malgré le prix a lancé pas mal de team sur des projets similaires.</p>
<p>Il semble que les plus connus soient:</p>
<ul>
<li>Le <a href="http://krikzz.com/index.php?route=product/product&product_id=54" hreflang="en">Everdrive 64</a>. Fait par une team qui à l'habitude des linkers. Il y a une review et des infos sur <a href="http://www.metagames-eu.com/forums/oldies/everdrive-64-a-128711.html" hreflang="fr">un thread</a> du forum Metagames.</li>
<li>Le <a href="http://64drive.retroactive.be/" hreflang="en">64drive</a>. Ressemble beaucoup au Everdrive 64 dans le principe. Notez que c'est le même gars qui a lancé une campagne de fond pour faire une radio au rayon X des puces CIC 6102, 6105 et PIF de la N64 par <a href="http://decap.mameworld.info/" hreflang="en">Dr Decap</a>. Il semble être tout seul sur ce projet mais pour m’intéresser depuis longtemps à la N64 et son émulation, ce n'est pas la première fois que j’entends parler de ce mec.</li>
<li>Le <a href="http://www.ed64plus.com/" hreflang="en">ED64PLUS</a>. Aucune info la dessus. Je ne sais pas qui est derrière. Il semble très intéressant sur le papier mais je vous conseillerai d'attendre des retours avant de vous jeter dessus.</li>
</ul>
<p>En plus d’être constamment en rupture de stock, le <em>Everdrive 64</em> et le <em>64drive</em> proposent des modèles sans CIC, il faut bien <a href="http://shop.retrogate.com/EverDrive-64-v2-ED64.htm" hreflang="en">se renseigner</a> avant. Le <em>ED64PLUS</em> semble fonctionner comme le <em>Neo N64 Myth</em>. C'est donc à vous de brancher un jeux original disposant d'une puce CIC.</p>
<p>Le <em>Everdrive 64</em> et le <em>64drive</em> se valent. Le <em>ED64PLUS</em> semble être une pale copie des deux premiers. Le <em>Neo N64 Myth</em> est a oublier. Trop compliqué. Il aura eu le mérite d'être le premier du genre mais, en tant que tel, souffre de soucis de maturité.</p>
<p>Avec le recul, si je devais en prendre un maintenant, je choisirai le 64drive. Le (les?) gars de <a href="http://www.retroactive.be/" hreflang="en">Retroactive</a> est réputé et fiable. Dans tout les cas, s'inscrire sur un forum avec des users utilisant le même linker que vous ne sera pas du luxe si vous galérez.</p>
<h3>Intérêt</h3>
<p>L’intérêt de tels Linkers, est a relativiser.</p>
<p>Voici grosso modo mon avis:</p>
<p>Points positifs:</p>
<ul>
<li>Tous les jeux sur une seule cartouche, c'est un gain de place non négligeable.</li>
<li>La plupart du temps ces linkers offrent la possibilité de sauvegarder sur la carte SD ce qui permet de backuper ses saves sur son ordi.</li>
<li>Jouer à des <a href="http://www.romhacking.net/?page=hacks&genre=&platform=27&hacksearch=Go" hreflang="en">roms hackées</a>. Certaines sont excellentes (<a href="http://www.youtube.com/watch?v=DaSMuON5-QU">Super Mario 74</a>, extrêmement difficile, et <a href="http://www.youtube.com/watch?v=Te4c4Am0LIs" hreflang="en">Goldeneye: X</a> le multi de Goldeneye avec le moteur de Perfect Dark et plus encore! :bravo: ).</li>
<li>Les anciens forums des joueurs de N64 se <a href="http://www.shootersforever.com/forums_message_boards/viewforum.php?f=46" hreflang="en">réveillent</a> et commencent doucement à parler de ces linkers.</li>
</ul>
<p>Points négatifs:</p>
<ul>
<li>Le prix. Le <em>Neo N64 Myth</em> était très cher (200$), même si une concurrence commence à apparaitre et que les prix vont surement descendre, ils restent élevés. Les prix des jeux N64 sont <a href="http://www.amazon.fr/s/ref=sr_hi_3?rh=n%3A530490%2Cn%3A!548014%2Cn%3A3327551%2Cn%3A548022&bbn=548022&ie=UTF8&qid=1348764906" hreflang="fr">relativement bas</a> et il n'est pas rare de trouver des offres sur ebay de personnes cherchant à se débarrasser de leur collection. En cherchant bien vous devriez pouvoir récupérer <a href="http://www.reddit.com/r/n64/comments/zn6dv/rn64s_favorite_games_list_results/" hreflang="en">les</a> <a href="http://www.jeuxvideo.com/forums/1-9-7565575-1-0-1-0-votre-top-10-des-meilleurs-jeux-64.htm" hreflang="fr">principaux</a> <a href="http://retro-sanctuary.com/TOP%20100%20N64%20GAMES%20page%205.html" hreflang="en">titres</a> de la console pour moins de 100euros.</li>
<li>Le bidouillage. Encore une fois, plus il y aura de Linkers N64 qui apparaitront plus ils tendrons à être facile d’accès mais ils y a toujours deux trois petites choses à savoir.</li>
<li>Le <em>Neo N64 Myth</em> ne supporte pas la sauvegarde externe (celle sur *Controller Pack*) directement sur SD. Il faut donc avoir une carte mémoire pour les jeux la nécessitant (Top Gear Rally, ISS64, etc...).</li>
<li>Le prix.</li>
<li>Le prix.</li>
<li>Le prix.</li>
</ul>
<h3>Conclusion</h3>
<p>J’espère que ce petit billet vous aura apprit des choses et servira aux aficionados de la Nintendo 64 qui souhaiteraient s'y remettre.</p>
<p>A bientôt!</p>
<p>Dorian</p>
<center>:marioCours:</center>
<h3>Liens intéressants</h3>
<ul>
<li><a href="http://64dd.net/modules/hardware/index.php?system=n64&section=main&id=46" hreflang="en">Review du Neo N64 Myth par 64dd.net</a></li>
<li><a href="http://www.dcemu.co.uk/vbulletin/threads/284897-Review-NEO-N64-Myth-Flash-Cart-SPEC-v2" hreflang="en">Review: NEO N64 Myth Flash Cart SPEC v2</a></li>
<li><a href="http://forums.benheck.com/viewtopic.php?f=5&t=35583&p=415099" hreflang="en">Dr Neo, The Myth 64 cart (nintendo 64 flash cart)</a>. Milieu de page.</li>
<li><a href="http://www.nesworld.com/n64-64drive-final.php" hreflang="en">The only Nintendo64 cartridge you'll ever need!</a></li>
</ul>Python: multiprocessing vs threadingurn:md5:4f56575f42207bed61294840682bab712012-04-04T22:52:00+02:002013-07-26T18:03:56+02:00NarannScript et codefrmultiprocesspythonthread<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_04_04_threadingVSmultiprocess/threadingVSmultiprocess_tn.png" alt="threadingVSmultiprocess_tn.png" style="float:left; margin: 0 1em 1em 0;" title="threadingVSmultiprocess_tn.png, avr. 2012" height="150" width="150" />Ce billet va tenter de montrer, principalement par l'exemple les différences entre le module <em>threading</em> et le module... <em>multiprocessing</em>! Je vous propose donc deux petits codes pour démarrer rapidement.</p>
<p>La plupart des TD Maya on déjà entendu parler du threading. Non pas pour savoir a quoi ça servait exactement mais parce que Maya a toujours eu du mal avec ce machin la. :gne:</p>
<p>Cela dit, ce billet est très général et n'aborde pas les problèmes de threading dans Maya. :cayMal: Juste les différences entre le module <em>threading</em> et <em>multiprocessing</em>.</p> <h3>Le module threading</h3>
<p>Pour faire simple, avec ce module vous "fabriquez" des objets de type Thread, chacun ayant une tache a accomplir. Vous lancez ces objets dans la nature et vous attendez qu'ils reviennent...</p>
<p>Comment ça se passe dans la pratique? :reflechi:</p>
<p>Déjà, il vous faut une fonction suffisamment lourde pour mettre à genoux votre machine:</p>
<pre class="python python">K = <span style="color: #ff4500;">50</span>
<span style="color: #ff7700;font-weight:bold;">def</span> CostlyFunction<span style="color: black;">(</span>z<span style="color: black;">)</span>:
result = <span style="color: #ff4500;">0</span>
<span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, K+<span style="color: #ff4500;">2</span><span style="color: black;">)</span>:
result += z <span style="color: #66cc66;">**</span> <span style="color: black;">(</span><span style="color: #ff4500;">1</span> / k<span style="color: #66cc66;">**</span><span style="color: #ff4500;">1.5</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">return</span> result</pre>
<p>Honteusement pompée du fil Stack Overflow: <a href="http://stackoverflow.com/a/4415314" hreflang="en">multiprocess or threading in python?</a>.</p>
<p>Comme je vous disais, il vous faut un objet de type Thread:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">threading</span>
K = <span style="color: #ff4500;">50</span>
<span style="color: #ff7700;font-weight:bold;">class</span> CostlyThread<span style="color: black;">(</span><span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span>, value<span style="color: black;">)</span>:
<span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">value</span> = value
<span style="color: #ff7700;font-weight:bold;">def</span> run<span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;"># notre fonction</span>
result = <span style="color: #ff4500;">0</span>
z = <span style="color: #008000;">self</span>.<span style="color: black;">value</span>
<span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, K+<span style="color: #ff4500;">2</span><span style="color: black;">)</span>:
result += z <span style="color: #66cc66;">**</span> <span style="color: black;">(</span><span style="color: #ff4500;">1</span> / k<span style="color: #66cc66;">**</span><span style="color: #ff4500;">1.5</span><span style="color: black;">)</span>
<span style="color: #008000;">self</span>.<span style="color: black;">value</span> = result</pre>
<p>Et on balance tout!</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">time</span>
<span style="color: #808080; font-style: italic;"># timer</span>
startTime = <span style="color: #dc143c;">time</span>.<span style="color: #dc143c;">time</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># va stocker les threads qu'on va lancer.</span>
threadList = <span style="color: black;">[</span><span style="color: black;">]</span>
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span><span style="color: #ff4500;">100000</span><span style="color: black;">)</span> : <span style="color: #808080; font-style: italic;"># on lance cent milles threads</span>
curThread = CostlyThread<span style="color: black;">(</span>i<span style="color: black;">)</span>
curThread.<span style="color: black;">start</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># on lance le thread</span>
threadList.<span style="color: black;">append</span><span style="color: black;">(</span>curThread<span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># on ajoute le thread a la list</span>
<span style="color: #808080; font-style: italic;"># maintenant qu'on a tout lance, on récupère tout!</span>
resultList = <span style="color: black;">[</span><span style="color: black;">]</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curThread <span style="color: #ff7700;font-weight:bold;">in</span> threadList :
curThread.<span style="color: black;">join</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># on attend que le thread ait fini</span>
result = curThread.<span style="color: black;">value</span> <span style="color: #808080; font-style: italic;"># on récupère sa valeur</span>
resultList.<span style="color: black;">append</span><span style="color: black;">(</span>result<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #dc143c;">time</span>.<span style="color: #dc143c;">time</span><span style="color: black;">(</span><span style="color: black;">)</span> - startTime</pre>
<p>Sauvez tout ça dans un fichier python puis lancez un moniteur système et regardez bien ce que Python va faire...</p>
<p>21.4 secondes plus tard... :siffle:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_04_04_threadingVSmultiprocess/threadingVSmultiprocess_002.png" alt="threadingVSmultiprocess_002.png" style="display:block; margin:0 auto;" title="threadingVSmultiprocess_002.png, avr. 2012" height="131" width="239" /></p>
<p>Vous remarquerez que Python n'a pas tous les processeurs à fond. :redface:</p>
<p>En fait, le threading n'a pas pour vocation de faire "beaucoup de petits calculs". Il n'est pas efficace pour ce genre de chose (je ne rentre pas dans les détails, c'est très bien expliqué dans le fil Stack Overflow: <a href="http://stackoverflow.com/questions/34020/are-python-threads-buggy" hreflang="en">Are Python threads buggy?</a>).</p>
<p>Un des principal intérêt du threading c'est sa mise en place. Il est très simple à utiliser. On va principalement s'en servir pour faire des opérations nécessitant de la "latence".</p>
<p>Je pense qu'on doit pouvoir aller plus loin mais personnellement, je l'utilise souvent quand je dois faire plusieurs appels réseaux dans une boucle (récupérer la date de modification d'une liste de fichiers) c'est beaucoup plus efficace que d'attendre a chaque fois que le réseau nous donne la main pour redemander une informations similaire juste après.</p>
<p>Pas convaincu?</p>
<blockquote><p>25709 existences de fichier vérifiées en 38.35 secondes :IFuckTheWorld:</p></blockquote>
<p>J'ose même pas imaginer le temps que ça aurait pris si j'avais fait la même chose en récursif...</p>
<p>Après, le réseau en question était solide (c'est pas du routeur de promotion Carrefour! :baffed: ).
Cela dit, suivant l’opération faite par le thread vous pouvez plomber le réseau (ce sera toujours moins pire que Nuke et After Effect en rendu cela dit :trollface: )... Donc faite attention.</p>
<p>Viens maintenant le problème de "calcul brut". Si le threading ne permet pas d'utiliser l’intégralité de son processeur, comment puis-je faire des calculs très lourds?</p>
<p>C'est la seconde partie de ce billet! :hehe:</p>
<h3>Le module multiprocessing</h3>
<p>Ce module est un petit peu plus subtil à utiliser et il y a plusieurs façons de faire.</p>
<p>La méthode que nous allons voir est l'une des (la?) plus efficace:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> multiprocessing <span style="color: #ff7700;font-weight:bold;">as</span> mp
<span style="color: #808080; font-style: italic;"># timer</span>
startTime = <span style="color: #dc143c;">time</span>.<span style="color: #dc143c;">time</span><span style="color: black;">(</span><span style="color: black;">)</span>
pool = mp.<span style="color: black;">Pool</span><span style="color: black;">(</span><span style="color: black;">)</span>
asyncResult = pool.<span style="color: black;">map_async</span><span style="color: black;">(</span>CostlyFunction, <span style="color: #008000;">xrange</span><span style="color: black;">(</span><span style="color: #ff4500;">100000</span><span style="color: black;">)</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># on lance aussi cent milles operations</span>
resultList = asyncResult.<span style="color: black;">get</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># asyncResult.get() is a list of values</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #dc143c;">time</span>.<span style="color: #dc143c;">time</span><span style="color: black;">(</span><span style="color: black;">)</span> - startTime</pre>
<p>0.9 secondes plus tard (Et ouai, faut suivre...)</p>
<p>Vous n'avez peut être rien vu mais si vous avez toujours votre moniteur système d'ouvert avec un graph de charge processeur, vous verrez que pendant ce court laps de temps, tous les processeurs étaient en pleine activité...</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_04_04_threadingVSmultiprocess/threadingVSmultiprocess_001.png" alt="threadingVSmultiprocess_001.png" style="display:block; margin:0 auto;" title="threadingVSmultiprocess_001.png, avr. 2012" height="135" width="223" /></p>
<p>Ajoutez un zéro et regardez comment votre processeur il pleure sa maman (on gaspille aussi plein d’énergie hein! :seSentCon: ).</p>
<p>Je m’arrête la. Le module multiprocessing offre beaucoup plus de possibilités (gestion d'une <em>queue</em>). Mais pour l'instant, je n'ai jamais eu a l'utiliser. :pasClasse:</p>
<h3>Conclusion</h3>
<p>J’espère que ce petit billet vous aura donné des bouts de code suffisant pour débuter. Personnellement, j’apprécie beaucoup la simplicité du module <em>threading</em> qu'on peut sortir dans pas mal de situations des lors qu'on touche au fichiers.</p>
<p>A bientôt!</p>
<p>Dorian</p>
<center>:marioCours:</center>
Du métal plus métalique?urn:md5:5b23c6224f24f84afb5897f290a32de12012-03-18T22:15:00+01:002013-07-26T18:04:15+02:00NarannInfographie 3D - Boulotbrdffrfresnelmental raymetalmia_material_x<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_tn.png" alt="metal_plus_metalique_tn.png" style="float:left; margin: 0 1em 1em 0;" title="metal_plus_metalique_tn.png, mar. 2012" height="150" width="150" />Hello tous!</p>
<p>Comme j'en bouffe tous les jours en ce moment j'ai décidé vous faire partager le très intéressant billet de Master Zap: <a href="http://mentalraytips.blogspot.fr/2007/10/making-better-metal-with-miamaterial.html" hreflang="en">Making Better Metal with mia_material</a>.</p>
<p>Ceux qui l'ont déjà lu en long en large, passez votre chemin, je ne fait que répéter les dires du maitre.</p>
<p>Les autres, avec un peu de chance, vous allez apprendre quelque chose aujourd'hui. :hehe:</p> <h3>Un bon metal, c'est d'abord une bonne map HDR</h3>
<p>Avant de commencer, aller récupérer une des magnifiques image HDR de <a href="http://www.hdrlabs.com/sibl/archive.html" hreflang="en">sIBL Archive</a>.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/sibl_archive_icon_light.png" alt="sibl_archive_icon_light.png" style="display:block; margin:0 auto;" title="sibl_archive_icon_light.png, mar. 2012" height="120" width="140" /></p>
<p>Elles sont toutes sous licence <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/fr/" hreflang="fr">Creative Commons Attribution-Noncommercial-Share Alike 3.0</a>, ce qui veut dire que vous ne pouvez pas gagner d'argent avec et que si vous les utilisez (comme ça va être le cas ici) il faut citer l'auteur et partager votre travail à l'identique.</p>
<p>Pour le choix de l'image. J'ai toujours eu une préférence pour Factory Catwalk:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/Factory_Catwalk.png" alt="Factory_Catwalk.png" style="display:block; margin:0 auto;" title="Factory_Catwalk.png, mar. 2012" height="256" width="512" /></p>
<h3>Et go Maya!</h3>
<p>Comme d'hab', ouvrez Maya, mental ray, preset <em>Production</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/maya_preset_production.png" alt="maya_preset_production.png" style="display:block; margin:0 auto;" title="maya_preset_production.png, mar. 2012" height="238" width="237" /></p>
<p><em>Filter</em> en <em>Triangle</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/maya_filter_triangle.png" alt="maya_filter_triangle.png" style="display:block; margin:0 auto;" title="maya_filter_triangle.png, mar. 2012" height="145" width="196" /></p>
<p>Puis on créé une IBL:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/maya_create_ibl.png" alt="maya_create_ibl.png" style="display:block; margin:0 auto;" title="maya_create_ibl.png, mar. 2012" height="126" width="395" /></p>
<p>Et on y met notre hdr:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/maya_ibl_hdr.png" alt="maya_ibl_hdr.png" style="display:block; margin:0 auto;" title="maya_ibl_hdr.png, mar. 2012" height="151" width="418" /></p>
<p>Créez une sphère et appliquez lui un <em>mia_material_x</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_001.png" alt="metal_plus_metalique_001.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_001.png, mar. 2012" height="480" width="640" /></p>
<p>Virez le <em>highlight</em> (je le fait car je ne peux <del>pas</del> plus le supporter personnellement):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_002.png" alt="metal_plus_metalique_002.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_002.png, mar. 2012" height="214" width="452" /></p>
<center><i>Mettez la Specular Balance a 0.0</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_003.png" alt="metal_plus_metalique_003.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_003.png, mar. 2012" height="480" width="640" /></p>
<p>Pour avoir une colorimétrie correct, on applique une correction gamma:</p>
<pre class="mel mel">createNode mip_gamma_gain<span style="color: #339933;">;</span>
setAttr <span style="color: #ff0000;">"mip_gamma_gain1.gamma"</span> <span style="color:#800080;">2.2</span><span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f mip_gamma_gain1.<span style="color: #202020;">message</span> perspShape.<span style="color: #202020;">miLensShader</span><span style="color: #339933;">;</span></pre>
<p>Et voila:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_004.png" alt="metal_plus_metalique_004.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_004.png, mar. 2012" height="480" width="640" /></p>
<p>Bon, pour faire du métal, a priori, on met la <em>color</em> en blanc et le <em>weight</em> a zéro, la <em>reflectivity</em> a 1.0 et on coche la case <em>Metal Material</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_005.png" alt="metal_plus_metalique_005.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_005.png, mar. 2012" height="480" width="640" /></p>
<p>Comme on est des graphistes :smileFou: on met aussi de la couleur:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_006.png" alt="metal_plus_metalique_006.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_006.png, mar. 2012" height="480" width="640" /></p>
<p>Le centre de la sphère semble sombre ça ne ressemble pas vraiment à du métal. :reflechi:</p>
<p>Si vous connaissez un peu le mia_material, vous savez qu'il existe un paramètre dans l'onglet <a href="http://docs.autodesk.com/MENTALRAY/2012/ENU/mental%20ray%203.9%20Help/files/shaders/architectural/arch_mtl.html#BRDF" hreflang="en">BRDF</a> pour donner les deux valeurs extrêmes de reflection quand la surface est face à la camera et quand celle ci est complètement perpendiculaire (<em>0/90 Degree Reflection</em>). Avec un autre paramètre qui définit la courbe pour passer d'une valeur à l'autre.</p>
<p>Pour faire du metal à priori, il suffit de monter la première valeur (<em>0 Degree Reflection</em>) à une valeur proche de 1.0:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_007.png" alt="metal_plus_metalique_007.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_007.png, mar. 2012" height="141" width="439" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_008.png" alt="metal_plus_metalique_008.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_008.png, mar. 2012" height="480" width="640" /></p>
<center><i>Oaaah De l'or qui briiiiille! :bete: </i></center>
<p>Bon, et bien en fait, vous n'y êtes pas complètement. :sourit:</p>
<p>Là vous avez fait un bête miroir jaune... Dans le fond c'est assez proche du résultat final. Mais un des grand challenge du "Physical Based" c'est bien que ce n'est plus simplement une question d'oeil. :redface:</p>
<p>Et pour avoir un comportement de métal plus réaliste, il va falloir regarder du coté de Fresnel (si si).</p>
<p>Donc vous cochez la case:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_009.png" alt="metal_plus_metalique_009.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_009.png, mar. 2012" height="133" width="438" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_010.png" alt="metal_plus_metalique_010.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_010.png, mar. 2012" height="480" width="640" /></p>
<center><i>Ne vous inquiétez pas, ce n'est pas le résultat final! :seSentCon: </i></center>
<p>Maintenant, ce que vous allez faire, c'est mettre l'index de refraction (oui oui, reFRACtion) a 25:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/metal_plus_metalique_011.png" alt="metal_plus_metalique_011.png" style="display:block; margin:0 auto;" title="metal_plus_metalique_011.png, mar. 2012" height="480" width="640" /></p>
<p>La différence est assez subtile (comparez avec le <em>Keep image</em> de Maya) mais le second se rapprochera beaucoup plus d'un métal physiquement correct, notamment sur les bords.</p>
<p>En fait, pour le métal, la courbe qui définit la valeur de reflection en fonction de l'angle de la surface par rapport à la camera est très particulière (voir sur le billet de Zap). Quand elle se rapproche de 90 dégrées, elle descend pour remonter en flèche.</p>
<p>Quand à la théorie. Je doit avouez ne pas maitriser la chose (Master Zap non plus d’ailleurs):</p>
<blockquote><p>Metals are indeed not refractive, and are indeed not dielectrics (meaning, electrical insulators). They are Conductors, and for some baroque reason these are also considered to have an "Index of Refraction".</p></blockquote>
<p>Comme je suis du genre curieux, j'ai fait un peu de recherche (d'un niveau bien trop élevé pour moi) et j'en ai conclu que (Attention, grand moment de mathématique a trois francs six sous :dentcasse: ):</p>
<ul>
<li>L'index de réfraction est le "ralentissement" de la lumière quand il entre dans un "milieu" (un material).</li>
<li>Cet index dépend de plusieurs paramètres dont la perméabilité diélectrique (material conducteur, ou pas). En effet, la lumière est une onde électromagnétique (Maxwell is watching you) donc (et c'est ça qui est fou!) la conduction d'un material joue un rôle dans la "déformation" de la lumière qui le traverse.</li>
<li>Les coefficients de Fresnel intervenant à l'intersection de deux milieux ayant des indices de réfraction différent (toi tu rentre déformé comme ci, toi tu rebondie déformé comme ça) explique la relation qu'il y a entre l'index de réfraction et la reflection d'un material. En gros, lors d'une "transition" entre deux milieux ayant des index de réfraction différent, il y a une reflection qui se forme.</li>
</ul>
<p>Perso, ça donne envie d’être physicien. J'imagine la révolution que ça a dut être dans la physique optique ça. D’ailleurs, si un physicien passe dans le coin et que je me suis complètement planté, qu'il se fasse un plaisir de m'insulter violemment dans les commentaires, je suis tout ouïe à une explication clair du phénomène. :seSentCon:</p>
<p>Bref:</p>
<blockquote><p>Fresnel + index de refraction sur un material 100% opaque = variation de la reflection.</p></blockquote>
<h3>Liens</h3>
<p>Parce que je serai ravis de savoir si vous avez mieux compris que moi. :baffed:</p>
<ul>
<li><a href="https://fr.wikipedia.org/wiki/%C3%89quations_de_Maxwell" hreflang="fr">Équations de Maxwell</a></li>
<li><a href="https://fr.wikipedia.org/wiki/Indice_de_r%C3%A9fraction" hreflang="fr">Indice de réfraction</a></li>
<li><a href="https://fr.wikipedia.org/wiki/Permittivit%C3%A9_di%C3%A9lectrique" hreflang="fr">Permittivité diélectrique</a></li>
<li><a href="https://fr.wikipedia.org/wiki/Perm%C3%A9abilit%C3%A9_magn%C3%A9tique" hreflang="fr">Perméabilité magnétique</a></li>
<li><a href="https://fr.wikipedia.org/wiki/Coefficients_de_Fresnel" hreflang="fr">Coefficients de Fresnel</a></li>
</ul>
<p>Je vous invite aussi a regarder <a href="https://www.fevrierdorian.com/blog/public/billets/2012_03_18_metal_plus_metalique/palette_coating.pdf" hreflang="fr">ce PDF</a> pour Maxwell Render (<a href="http://forum.maxwellrender.fr/viewtopic.php?f=20&t=2025" hreflang="fr">auteur: Roch</a>).</p>
<p>Sur ce pdf, on voit que la reflection varie en fonction d'un curieux paramètre nomme <em>Nd</em> (Quand il passe a 10, on voit très clairement une reflection). Et <a href="https://en.wikipedia.org/wiki/ND#Science_and_medicine" hreflang="en">d’après Wikipedia</a>, <em>Nd</em> correspond a l'index de réfraction d'un medium dont la longueur d'onde est de 589.592 nanomètre (<a href="https://fr.wikipedia.org/wiki/Raies_de_Fraunhofer" hreflang="fr">La raie de Fraunhofer</a> <em>D</em> du spectre solaire. <a href="https://en.wikipedia.org/wiki/Sodium" hreflang="en">Le sodium</a>... Et oui, ça renvoi loin et je vois pas pourquoi cette valeur la en particulier...).</p>
<p>En espérant vous avoir apporté quelque chose...</p>
<p>A bientôt!</p>
<center><i>:marioCours:</i></center>
<h3>MAJ 20 Mars 2012</h3>
<p>Suite au <a href="https://www.fevrierdorian.com/blog/post/2012/03/18/Du-metal-plus-metalique#c4412" hreflang="en">commentaire</a> de Tristan, <a href="http://forum.mentalimages.com/showthread.php?9274-What-represent-25-50-IOR-values-for-mia_materials..." hreflang="fr">j'ai demandé</a> sur le forum de mental image d’où sortait ces index de refraction bizarre (25, 40, 50 etc...). <em>rlevenne</em> <a href="http://forum.mentalimages.com/showthread.php?9274-What-represent-25-50-IOR-values-for-mia_materials...#post37779" hreflang="en">explique clairement</a> qu'il s'agit d'un hack pour imiter la courbe de reflection du métal mais que les valeurs ne représentait rien mathématiquement parlant. Apparemment, le mia_material utilise <a href="https://en.wikipedia.org/wiki/Schlick%27s_approximation" hreflang="en">l'approximation de Schlick</a> pour calculer la BRDF. Cette approximation est rapide mais ne gère pas les index de réfraction complexes. Pour faire un métal physiquement correct, il faut une autre information: L’Extinction (la valeur <em>k</em> sur <a href="http://refractiveindex.info/?group=METALS&material=Gold" hreflang="en">cette page</a>). C'est un paramètre que Maxwell expose. Bref, si vous êtes intéressé, lisez le post. :sourit:</p>Noël de Geek: Levitron et OpenGL Superbibleurn:md5:ccb9bda689de59470b9d41b2f1939f382011-12-31T02:00:00+01:002013-07-26T18:05:23+02:00NarannMes coups de coeuraimantfrgeeklevitronlivrenoelopengltoupie<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/Levitron_Omega_Noel_tn.png" alt="Levitron_Omega_Noel_tn.png" style="float:left; margin: 0 1em 1em 0;" title="Levitron_Omega_Noel_tn.png, déc. 2011" height="150" width="150" />Hello tous!</p>
<p>J’espère que vous avez passé de bonnes fêtes! Bonne année à tous! (Oui, je sais, ce n'est pas encore passé mais il est 2h du matin et on est le 31 donc vu qu'a priori aujourd'hui tout le monde va préparer sa soirée, personne ne verra ce billet avant 2012... A part, peut être, les GoogleBots... :hehe: )</p>
<p>Avant de continuer, je vous prévient de suite, c'est un billet complètement osef. :seSentCon:</p>
<p>J'avais juste envie de vous faire partager ce que j'ai reçu pour Noël! :sourit:</p> <h3>Levitron Omega, c'est plus fort que toi!</h3>
<p>Ça, c'est ma môman qui me l'a offert! C'est un peu le cadeau de geek par excellence (elle connait son fiston ma môman). :hehe:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/Levitron_Omega_Noel_001.jpg" title="Levitron_Omega_Noel_001.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/.Levitron_Omega_Noel_001_m.jpg" alt="Levitron_Omega_Noel_001.jpg" style="display:block; margin:0 auto;" title="Levitron_Omega_Noel_001.jpg, déc. 2011" height="560" width="373" /></a></p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/Levitron_Omega_Noel_002.jpg" title="Levitron_Omega_Noel_002.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/.Levitron_Omega_Noel_002_m.jpg" alt="Levitron_Omega_Noel_002.jpg" style="display:block; margin:0 auto;" title="Levitron_Omega_Noel_002.jpg, déc. 2011" height="560" width="373" /></a></p>
<p>C'est une toupie qui flotte sur un aimant en anneau. Il faut quelques minutes pour équilibrer le socle (tout doit être parfaitement plat sinon la toupie "glisse") et lester la toupie (sinon elle est trop légère et pas assez stable).</p>
<p>Pour voir le truc en <em>live</em>:</p>
<center><iframe width="580" height="423" src="http://www.youtube.com/embed/W6cFsS8-mqI" allowfullscreen></iframe></center>
<p>Et si vous <del>êtes pauvre</del> ne voulez pas dépenser d'argent la dedans, vous pouvez toujours vous <a href="http://heligone.free.fr/levitron/" hreflang="en">la fabriquer vous même</a>. :trollface:</p>
<h3>OpenGL Superbible, la <em>Bible-tout-court</em> peut aller se rhabiller...</h3>
<p>Sur les conseils de mon ancien collègue <a href="http://www.linkedin.com/in/adrienherubel" hreflang="fr">Adrien Herubel</a>, j'ai profité de l'occasion pour demander la <a href="http://www.amazon.com/OpenGL-SuperBible-Comprehensive-Tutorial-Reference/dp/0321712617/" hreflang="en">OpenGL SuperBible: Comprehensive Tutorial and Reference (5th Edition)</a> (avouez que le nom en jette! :laClasse: ).</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/OpenGL_Superbible5_001.jpg" title="Wright_MECH_5E.qxd"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/.OpenGL_Superbible5_001_m.jpg" alt="Wright_MECH_5E.qxd" style="display:block; margin:0 auto;" title="Wright_MECH_5E.qxd, déc. 2011" height="560" width="450" /></a></p>
<p>Ça fait longtemps que je tâte OpenGL sans jamais vraiment entrer dedans...</p>
<p>Il faut dire que j'ai commencé à une période "difficile" dans l'histoire de l'API car OpenGL 3 (la "nouvelle" version de l'API donc) a rendu complètement obsolète l'ancienne façon de faire (soit 90% des ressources disponibles sur le net...). Les spécifications d'OpenGL ont évoluées très lentement en quasiment dix ans. Donc forcément, l'arrivé de cette version 3 qui remet beaucoup (tout?) en cause fout un bon gros coup de vieux <a href="http://nehe.gamedev.net/tutorial/lessons_01__05/22004/" hreflang="en">aux tutoriaux</a> considérés comme les meilleurs du web...</p>
<p>Et je vous assure qu'il y a encore un an, quasiment tout ce que je trouvais sur OpenGL sur le net avait un arrière goût de poisson pourri (<a href="http://www.lemonde.fr/technologies/article/2010/11/02/la-memoire-de-geocities-compilee-en-un-fichier_1434450_651865.html" hreflang="fr">RIP</a> <a href="http://thepiratebay.org/torrent/5923737/Geocities_-_The_Torrent" hreflang="en">GeoCities</a> :baffed: #geek). les choses changent... Rapidement même... :sourit:</p>
<p>En effet, c'est assez déprimant d'apprendre (difficilement) des choses dont on ne sait même pas si elles seront encore utilisées d'ici quelques années. Et pire, dont la philosophie n'a même plus de sens... :pasClasse:</p>
<p>Ce livre a l'avantage (et c'est aussi pour ça que je l'ai acheté) de faire complètement abstraction de "comment que c'est qu'on faisait avant" et de se focaliser sur une approche moderne.</p>
<p>Et pour tout vous dire, au bout de quelques semaines:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/level-up.jpg" title="level-up.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_12_29_Noel_De_Geek/.level-up_m.jpg" alt="level-up.jpg" style="display:block; margin:0 auto;" title="level-up.jpg, déc. 2011" height="400" width="560" /></a></p>
<center><i>Image honteusement pompé sur le net sans l'accord de <a href=http://www.psyetgeek.com/level-up-les-niveaux-de-mecanisme-de-defense-dans-le-jeu-video>son propriétaire</a>... Merci a lui en tout cas! :sourit:</i></center>
<p>Bref, ça roxx du poney et c'est super intéressant! :banaeyouhou:</p>
<p>Voili voulou!</p>
<p>A bientôt et bonne année!</p>
<p>Dorian</p>
<center><i>:marioCours:</i></center>