PROJET AUTOBLOG


blog.fevrierdorian.com

source: blog.fevrierdorian.com

⇐ retour index

Le gris moyen

vendredi 9 juin 2023 à 21:34

Bonjour, aujourd’hui on va parler de la notion de « gris moyen ».

Nous allons voir que, comme beaucoup de choses liées à la couleur, c’est un concept faussement simple, on essaiera de savoir comment le déterminer et pourquoi on ne se posait pas vraiment la question avant.

 

Le gris moyen, c’est quoi ?

Ce qu’on appelle couramment le gris moyen est une teinte perçue comme étant à mi-parcours entre le noir et le blanc.

Dans la 3D, cette valeur de gris est souvent utilisé comme intensité de référence (couleur de fond/du sol), pour faire valider ses tournettes ou ses lightings.

Quelle est sa valeur ?

On parle souvent d’un gris à 18 %. Ce pourcentage n’est pas vraiment un standard, mais plutôt une convention. Pourquoi 18 % exactement ? Il semblerait que les « inventeurs » soient Ansel Adams et Fred Archer, deux photographes américains qui l’ont formalisé dans ce qui est appelé le Zone system.

Je ne vais pas rentrer dans les détails. Ce qui est intéressant dans ce système est que le gris moyen est à 18 %, ce qui est très éloigné d’une valeur qu’on pourrait naïvement définir à 50 %, mais pourquoi ?

Expérimentation

Si votre moniteur est calibré en sRGB (80 cd/m2 d’intensité lumineuse) et que vous êtes dans une pièce moyennement éclairée : Si je vous donne l’image ci-dessus et que je vous demande d’y mettre, à l’œil (sans regarder les valeurs), un rectangle dont l’intensité du gris se situerait à mi-parcours entre le noir et le blanc, il est fort probable que vous obteniez quelque chose comme ça :

Pourtant…

Vous pensez que votre gris ainsi trouvé est à mi-chemin entre le blanc et le noir, mais si vous aviez les moyens de mesurer l’intensité lumineuse de ce gris sortant de votre écran (avec un luxmètre), vous vous rendriez compte qu’il n’est pas à 50 % de l’intensité lumineuse du blanc, mais à environs 22 %.

Une autre façon de le dire, c’est que la perception de l’intensité lumineuse par l’œil humain n’est pas linéaire : Pour une dynamique donnée (un range d’intensité minimum, perçu comme, noir et maximum, perçu comme blanc), l’œil semble être à l’aise avec les informations tournant autour des 18-20 % de cette dynamique.

Avec un peu de chance, vous vous êtes déjà jeté sur votre color picker favori pour déterminer la valeur de gris d’un pixel de votre rectangle. Vous trouvez à peu près 50 % (118/255). Là, vous vous dites que votre pixel est bien à environ 50 % et que je vous raconte des bêtises, mais si vous être à l’aise avec l’espace colorimétrique sRGB, vous savez surement pourquoi.

La fonction de transfert électro-optique (pixel vers l’écran) sRGB est approximativement une courbe gamma 2.2. Ce qui veut dire que quand on donne une valeur de 0.5 à notre moniteur calibré sRGB, il ne renvoie pas 50 % d’intensité lumineuse, mais à peu près 0.22 (22 %, ce qui est très proche de 18 %) :

>>> 0.5**2.2
0.217637640824031

Et on arrive à la raison qui m’a poussé à écrire ce billet :

La raison pour laquelle on ne se prenait pas la tête avec le gris moyen « avant » (en sRGB), c’est parce que qu’une valeur de pixel à 50 % était traduite en 22 % d’intensité lumineuse, ce qui était perçu comme un gris moyen. Donc nos têtes de graphistes ont fait la relation : Gris moyen = pixels à 50 %, mais ça n’est vrai qu’en sRGB. C’est un accident du fait qu’on utilise principalement des moniteurs sRGB.

Petit parenthèse pour chercher la petite bête : Pour avoir un gris 18 % (et non 22 %), il faut faire mettre un pixel à 45.9 %. Ça ne change pas le propos de mon billet.

Mais alors se pose la question suivante : Quelle valeur de pixel faudrait-il pour avoir une intensité lumineuse à 50 % sur notre moniteur sRGB ?

En inversant la fonction gamma 2.2 :

>>> 0.5**(1/2.2)
0.7297400528407231

Donc pour avoir une intensité lumineuse à 50 %, il faudrait des pixels à 73 %, 186/255, ce qui donne l’image suivante :

Difficile de dire que vous voudriez faire valider vos persos dans un tel gris, pas vrai ?

Vous pouvez picker le gris, vous aurez 186.

Pourquoi on a besoin de savoir ça maintenant ?

Comme vu précédemment, la particularité des moniteurs sRGB faisant qu’on ne se prenait pas vraiment la tête avec ça, mais les choses changent assez vite avec OCIO, ACES et le HDR.

Si vous utilisez OCIO dans un espace sRGB, vous avez peut-être remarqué qu’une valeur de couleur à 0.5 est parfois correctement représenté dans vos logiciels et apparait clair, comme notre seconde image. Vous aurez alors peut-être le réflexe de la diminuer aux alentours des 20 %, ce qui est tout à fait normal. ACES n’y échappe pas :

Est-ce que c’est ce que vous voulez faire ? La réponse est « ça dépend de votre moniteur de référence ». Si votre moniteur de référence est un moniteur sRGB, alors ces valeurs feront l’affaire. Si c’est un moniteur HDR vous devez vous poser pas mal de questions d’espaces colorimétriques, mais il est possible que 18 % de la valeur maximum soit beaucoup trop claire, tant les moniteurs HDR peuvent monter haut. J’avoue ne pas avoir assez creusé le sujet.

En espérant que ce billet vous aura appris quelques trucs.

:marioCours:

Crash du viewport Maya : L’option Consolidate World

dimanche 26 février 2023 à 17:55

Aujourd’hui on va parler d’une option bien spécifique du viewport de Maya : Consolidate World.

On va d’abord définir ce qu’est la consolidation et comment elle a été mise en place dans le Viewport 2.0. On finira par lister des cas spécifiques d’utilisation avec lesquels cette option est difficilement compatible et où la documentation elle-même propose de le désactiver. :aupoil:

Note : Dans ce billet, le mot « rendu » fait référence au rendu temps-réel, sur GPU. On y est pas forcément habitué, alors je préfère le préciser. :reflechi:

Qu’est-ce que la consolidation ?

On va s’éviter un cours sur le fonctionnement d’un GPU, mais en très gros, un GPU ne peut « rendre » qu’un seul matériau à la fois. Quand il y a deux matériaux, il rend deux fois avant d’afficher l’image sur le moniteur. Un rendu est appelé un draw call, il peut donc y avoir plusieurs draw call pour une image affichée.

Dans le cadre d’un GPU, un matériau est à comprendre comme « programme qui calcule un pixel donné » ; souvent la surface d’une géométrie en fonction de sa normale, d’une source de lumière, etc. Le terme technique est shader. Les inputs d’un objet (la normale, les textures) ne font pas partie du shader, ils sont définis à l’objet et sont passés au shader juste avant son exécution, au moment du calcul du pixel. Changer de shader est assez couteux, car il nécessite de redéfinir une bonne partie du pipeline du GPU : On change le programme à exécuter (shader), et les blocs d’entrées et de sortie de ce programme (le terme technique est device state). Ce fonctionnement est spécifique au rasterizer GPU, on pourrait presque parler de contrainte matériel. Ce problème n’existe pas en path tracing (disons qu’on a d’autres problèmes :pasClasse: ).

Ce qu’on appelle la « consolidation », c’est le fait de regrouper le rendu des objets utilisant un même shader, afin d’utiliser le minimum de draw call ; on rend une seule fois beaucoup d’objets.

C’est comme ça que fonctionnent les jeux vidéos : Ils ont assez peu de shaders et chaque objet dispose de ses paramètres propres. L’art du rendu temps-réel consiste à faire le moins de draw call possible en jouant avec les paramètres par objet et les buffers de rendu. Je vous invite à lire la présentation The devil is in the details sur le pipeline de rendu de Doom 2016, qui utilise une centaine de shader, pas plus. :jdicajdirien:

Maya dans tout ça

Dans ses premières années, avant le Viewport 2.0, Maya affichait chaque nœud de shape indépendamment. Il y avait (quasiment) autant de draw call que de nœud de shape à afficher. C’est pour ça qu’il était facile d’écrire ses propres locators en OpenGL. C’était surtout inefficace, car plus il y avait de nœuds, plus il y avait de draw call, et c’est là qu’intervient le Viewport 2.0, pour unifier (entre autres) l’ordre dans lequel les choses sont affichées.

VP2 fonctionne comme une base de donnée dédié à l’affichage. Fini la belle époque où l’on pouvait afficher n’importe quoi grâce à des appels OpenGL personnalisés. Avec VP2 on met des choses à afficher (appelées Render Items) dans une boite dédiée et il s’occupe du reste ; c’est lui qui contrôle de pipeline d’affichage.

Plus de détails sur VP2

La période de transition entre le « Viewport Legacy » et le « Viewport 2.0 » a été longue, et ce dernier resta longtemps optionnel. Comme vous le devinez, les développeurs de plugins tiers ont dû recoder la partie graphique de leur plugin pour la rendre compatible VP2.

C’est pour les aider qu’Autodesk sortie en 2017 une série de documents « Viewport 2.0 API Porting Guide » :

La première chose qui vient en tête quand on lit la table des matières de ces documents, c’est à quel point ils pourraient vous aider à briller en soirée, entre une citation de Proudhon et Hanouna. Votre serviteur a essayé, ça ne fonctionne pas. Ces documents n’aident en rien, le problème ne vient donc pas de là. :petrus:

Non, ce que je vous propose c’est une traduction partielle d’une section du premier document, qui concerne spécifiquement la consolidation. Section 3.6 : Categorization and Consolidation, sous section Consolidation (page 9) :

Les Render Items sont catégorisés en séries de listes (appelé buckets), suivant qu’ils soient persistants ou transitoires, ainsi que d’autres paramètres d’affichage. […] La catégorisation peut être utilisée pour regrouper les items ayant des propriétés d’affichage similaires, ce qui peut améliorer les performances en évitant des changements de device states inutiles.

La consolidation est une fonctionnalité de VP2 utilisée pour diminuer le « problème des petits paquets » (small batch problem) : C’est-à-dire lorsque le coût des appels systèmes requis par la préparation et l’envoi d’énormément de petits paquets de géométries entraîne des pertes de performances importantes. Pour aider à résoudre ce problème, les Render Items peuvent être consolidés (fusionnés). Notez que, par conséquent, les Render Items originellement créés peuvent ne pas être ceux utilisés au rendu. Par défaut, VP2 utilise un mode hybride, combinant la consolidation statique traditionnelle et la consolidation multi-draw.

Note : Le multi-draw, c’est le fait de dessiner un même objet plusieurs fois, mais avec des transforms différents, en un seul rendu.

Je m’arrête ici, le document continue, et nécessite une compréhension un peu plus poussée de ce qu’est un Render Item.

Ce qu’en dit la documentation

Comparé aux documents dédiés aux développeurs, la documentation n’entre pas énormément dans les détails, mais donne une idée du problème que l’option Consolidate World cherche à résoudre. Voici la traduction :

Cette option essaie de combiner les caches de géométries des shapes utilisant des matériaux identiques. Cela peut drastiquement améliorer les performances de nombreuses scènes, en échange d’une utilisation mémoire plus importante.

Quand il combine les géométries, Consolidate World déplace les vertices de plusieurs objets dans un nouvel espace local (object-space) partagé. Donc si votre shader de plug-in s’appuie sur les coordonnées locales d’un objet, Consolidate World cassera ces coordonnées et votre shader risque de ne pas s’afficher correctement (Ndt : Plus d’informations sur la page Troubleshoot working with CgFX shaders, le tuto GLSL, ainsi que celle sur la sémantique des shader).

Pour utiliser cette fonction, les normales des vertices doivent être renormalisées. Par conséquent, les matériaux n’utilisant pas de normales de longueur unitaire ne sont pas compatibles avec Consolidate World.

Quand un objet change/est déformé, il est non-consolidé. Il est ensuite reconsolidé s’il reste inchangé pendant quelques frames. L’option Consolidate World peut donc être à l’origine d’un objet s’affichant différemment dans le viewport peu de temps après avoir été modifié ou désélectionné. Si cela vous gêne, désactiver cette option.

Astuce : Si votre Maya devient instable, essayez de désactiver cette option.

Important : Certaines opérations subissent de légers retards quand cette option est activée.

Voilà pour la seconde traduction. On note une confiance assez relative dans leur feature. :seSentCon:

La section Optimize Viewport 2.0 de la documentation ajoute des informations intéressantes :

L’option Consolidate World améliore les performances en combinant les géométries de tous les objets dans le champ de la caméra afin de réduire le nombre d’appels au GPU. Cette option est activée par défaut.

Pour profiter au maximum de Consolidate World, mutualisez les shaders autant que possible, en particulier sur les petits objets statiques. Vous pouvez le faire en sélectionnant File > Optimize Scene Size et activer l’option Remove duplicate: Shading networks. Pour ne visualiser qu’un seul matériau dans votre viewport et ainsi bénéficier au maximum de Consolidate World, vous pouvez activer Shading > Use Default Material dans le menu Panel. Si les matériaux ne sont pas nécessaires à votre workflow, cela améliore considérablement les performances dans les scènes lentes.

Remarque : L’activation de Consolidate World augmente l’utilisation mémoire du GPU. Voir Manage GPU memory for Viewport 2.0 pour plus d’informations.

C’était la dernière traduction de ce billet ! :redface:

Notez que le frustrum de la caméra semble être pris en compte par la consolidation.

Résumé

S’il fallait résumer très brièvement, la consolidation c’est le fait d’assembler toutes les géométries pouvant être rendu ensemble, en une seule fois, afin de générer le minimum de draw call.

Ça c’est pour la théorie. :dentcasse:

Le vrai monde

On imagine facilement que ce genre de mécanisme fonctionne assez bien en jeu vidéo, qui détermine à l’avance si les objets sont statiques ou non. :gne:

En pratique, la liberté laissée par notre logiciel chéri peut mettre à mal ce mécanisme qui peut se retrouver sur-sollicité, en particulier en animation ; plusieurs personnages animés en blocking + translate, caméra et objets qui bougent, puis s’arrêtent, etc. On comprend assez vite que l’approche consistant à organiser le rendu du GPU suivant qu’un objet est statique, transformé ou déformé peut être mise en défaut avec le travail d’animation, où le contenu de la scène est réévalué à chaque frame.

Le réindexage en blocs de rendu peut être constant si bien que, malgré que Consolidate World devrait en principe toujours fonctionner (il est activé par défaut), il n’est pas rare qu’il se retrouve être à l’origine de nombreux crash. Parfois c’est le pilote qui est responsable, parfois c’est Maya. J’imagine que le multithreading et l’asynchronie rendent cette feature complexe à maintenir dans un logiciel comme Maya.

Comme vous l’avez remarqué plus haut, la situation est telle que la documentation recommande carrément de la désactiver « en cas d’instabilité ». :bete:

Le doute

À ce stade, il est difficile d’affirmer quoi que ce soit sur les crash rencontrés en animation. En effet il n’est pas simple de déterminer la raison exact d’un crash, en particulier ceux lié à l’affichage. Ces derniers sont assez difficiles à isoler. Quand ils le sont, ils peuvent être corrigés sur une version, puis un autre problème apparait, et il suffit, une fois encore de désactiver Consolidate World pour que le problème disparaisse…

Je reviens sur mon hypothèse celons laquelle cette feature est complexe à maintenir correctement dans un logiciel comme Maya, dont une partie de l’affichage est gérée de façon asynchrone. Je pense que Consolidate World restera une option dont le fonctionnement sera aléatoire en animation, d’où mon conseil de la désactiver en production ; en particulier en animation, où le temps de l’animateur est précieux.

Voici quelques exemples trouvés ici et là qui laissent entendre que Consolidate World est la raison de leur problème. Si on ne peut pas affirmer que la consolidation effectivement est l’origine du problème, l’accumulation des cas fait peser des doutes sur la stabilité de cette option.

Matthew's Animation Antics - Software Problems :

As the rigs got more complex throughout the animation process it became more apparent that more had to be done to speed up the scenes. Natasha found that turning off “consolidate world” option in the Viewport 2.0 options prevented any further crashes.

Reddit - disappearing texture problem

Even though you fixed the problem, good chance it has something to do with Viewport 2.0's "Consolidate World" feature. Sometimes I get texture issues in the Viewport and I've narrowed it down in the past to that.

Change-log Maya 2018 :

- Consolidate World breaks geometry override with custom resource handles
[…]
- Crash when selecting node with custom resource handles with consolidate world on

My bet is one the performance options in the Viewport 2 option dialog, such as Vertex Cache and Consolidate World. I suspect there might be some refreshing going on.

Malcolm 2.0 Public Version :

Next try: Viewport, Renderer>Viewport 2.0 Options, Turn off Consolidate World.

My scene has crashed 5 times in the last hour, anything i could look to troubleshoot it?

Meanwhile, one of our Rigger found a surprising workaround : In the VP2 performance settings, disable the "Consolidate World" option

Mimic :

In Maya 2018, consolidation is disabled to facilitate this shader. It shouldn't affect anything of value, you probably don't even know what it is. But if you'd rather it didn't do that, untick the "Maya 2018 Consolidate World Fix" in the Ragdoll Preferences and reload the plug-in or restart Maya.

Conclusion

Si vous avez des crash en animation, désactivez Consolidate World et croisez les doigts.

:marioCours:

Les groupes de job par utilisateur

dimanche 5 février 2023 à 13:43

…pour sauver ta prod et faire ton bonheur. :siffle:

Dans ce billet, nous allons parler d’un système permettant de résoudre un problème récurent de suivi asynchrone, côté utilisateur.

Bon, une explication aussi générique que ça ne vous avance pas des masses… Donc on va le dire autrement :

Quand un graphiste publie sa scène, il n’est pas rare de vouloir lancer un certain nombre de choses immédiatement après la publication, sans le bloquer. S’ensuit un torrent de propositions plus archaïque les unes que les autres pour ne serait-ce que définir le problème : Bienvenue dans le monde magique de l’asynchronie ! :sauteJoie:

Mais putain, Dorian, tu vas encore faire un billet beaucoup trop long avec des termes super générique qui raconte rien ! J’ai des graphistes à aider, moi !

Ne vous inquiétez pas, on va essayer d’aller à l’essentiel.

Le problème

On va être très concret, et lister quelques problèmes récurrents :

Plus un pipeline fait de choses pour délester le graphiste, plus cette question se pose. :reflechi:

Il y a un paquet d’options disponibles ; Ouvrir un terminal, un subprocess Maya, passer par un microservice (qui ouvre Maya, bien joué, Roger ! :aupoil: ), etc.

Elles sont toutes assez limitées ; Sature la RAM locale, le message d’erreur dans le terminal n’est pas lu/remonté, Maya crash en fond et les microservices c’est opaque pour l’utilisateur.

Le problème est tellement difficile à gérer qu’il n’est pas rare que tout le monde abandonne et choisissent de bloquer le graphiste qui « en profitera pour faire une pause café ». Bon, je sais qu’un graphiste qui boit du café c’est important pour la productivité, mais un lead/sup peut être amené à publier énormément de choses sur des scènes problématiques, et ça serait vraiment con que votre lead modé fasse un arrêt en publiant sa cinquantième retake de brins d’herbe… :nevroz:

Abrège, Dorian…

En gros soit le graphiste ne voit rien, soit il perd du temps. Et je ne parle même pas d’isoler un crash (je sais, je sais, les scripts ne sont pas supposés planter, mais dès que ton input c’est une scène faite par un humain, c’est dogmatique de penser que tout va rouler).

Notez que parmi les options disponibles, je n’ai pas parlé de la farm : On pourrait parfaitement publier la scène puis envoyer une série de job sur la farm pour faire le boulot. :dentcasse:

Le problème est que l’utilisateur ne voit pas ce qui s’y passe et les UI de farm sont souvent imbitables. Et c’est le début de la solution que je propose. :redface:

La solution

Le problème de la farm, on l’a vu, c’est que personne ne la regarde parce que :

Pourtant, la farm est surement le moyen de plus simple de garder un job qui échoue tout en permettant de reproduire le problème (et si vos jobs ont des effets de bords, je vous invite à lire mes deux billets sur le sujet :papi: ).

L’approche présentée plus bas part du principe qu’on peut résoudre ces problèmes de façon pragmatique, sans demander un effort de dev trop important ; vous vous doutez que si j’en parle, c’est que j’ai déjà utilisé un mécanisme similaire et que ça fonctionnait. :laClasse:

L’idée est donc de permettre à l’utilisateur de suivre les jobs qu’il envoie, sans lui exposer le détail des jobs en question ; autres que wip/done/pending.

Quel que soit votre système de farm, chaque job a un identifiant. Quelle que soit la tache à faire, il est rare qu’un seul job suffise. Pour un simple playblast, vous aurez souvent la génération des images depuis Maya, l’ajout de cartons et la génération du fichier vidéo.

On va grouper tous ces jobs dans un ensemble qu’on va appeler un job group. Et il va falloir stocker cette entrée dans une base de donnée dédiée (et non, bordel non : ShotGrid n’est pas une base de donnée dédiée, mais d’autres en parlent mieux que moi).

Pseudo-implémentation

Donc vous sortez votre base de donnée SQL préférée, et vous me créez une table dédiée :

Même pas besoin de mettre une clef primaire, c’est tellement magique !

Notez que certains gestionnaires de farm permettent déjà de grouper les jobs, il peut alors être pertinent de pointer vers l’id du groupe de job de votre gestionnaire de farm plutôt que faire la liste vous-même.

Faites un QTableWidget avec deux colonnes :

Label affichera un très court descriptif du job group : « Playblast anim sq9999sh001 », « Export Alembic toto_001 sq9999sh001 ».

Progress affiche l’avancée générale des jobs : « WIP 2/5 », « DONE ».

Vous pouvez ajouter quelques boutons comme :

Ça donne un truc comme ça :

Designer est ton ami, mais j’y ai quand même passé beaucoup trop de temps. :slow_clap:

Et paf, vous avez une UI qui liste les jobs envoyé par l’utilisateur sous formes de groupes, les détails n’étant pas affichés. Ce n’est pas un bug, c’est une feature qui fait que votre UI n’est pas lourde ni repoussante.

Si vous avez suivi, vous comprendrez que la simplicité de cette UI lui permet de pouvoir être lancé depuis n’importe quel logiciel (e.g. juste après un publish) et est résolument tourné vers utilisateur qui peut maintenant publier à la chaine avant d’aller prendre sa pause café (notre problème original), et surtout, a enfin un moyen précis de communiquer sur ce qui est bloqué ou non, sans être dans le noir, tout en permettant aux TD/Wrangler de l’aider rapidement. :banaeyouhou:

Comme nous passons par une base de donnée dédiée, les job groups ne sont pas perdus après un redémarrage. :zinzin:

Les requêtes à la farm peuvent être assez lourdes si on est bête (ce qui n’est surement pas votre cas, si vous lisez ce blog :petrus: ). Voici quelques idées pour améliorer l’implémentation :

Autre truc important : Cette UI doit pouvoir être lancé pour n’importe quel utilisateur (par une variable d’environnement, par exemple), pour simplifier le travail des TDs qui peut accéder directement aux problèmes de publish d’un graphiste sans lui demander quoi que ce soit. Ils peuvent même, dans la grâce éternelle qui les caractérise si bien, cliquer sur le « refresh erreur » à sa place.

Conclusion

Si vous n’êtes pas convaincu, c’est que vous manquez simplement d’ouverture d’esprit, vous avez tort, vous aimez Node.js, le chocolat aux raisins, Dieu, et vous êtes une personne méprisable qui aurait surement vendu sa grand-mère en 40.

Je ne vous félicite pas…

:marioCours:

Récupérer les valeurs par défaut de kick

samedi 10 décembre 2022 à 23:47

Aujourd’hui, un petit bout de code très simple pour récupérer les valeurs par défaut que Arnold (ou la ligne de commande kick) donne à ses nœuds quand on ne les lui fournit pas.

Ce type d’information est utile, entre autres, dans un pipeline qui génère ses .ass lui-même. :siffle:

Bonne lecture ! :sauteJoie:

La ligne de commande kick

La première chose à faire avec une ligne de commande, c’est de regarder d’accéder à sa page d’aide :

$ kick --help

Dès lors, on identifie rapidement deux arguments intéressants :

-nodes [n|t]        List all installed nodes, sorted by Name (default) or Type
-info [n|u] %s      Print detailed information for a given node, sorted by Name or Unsorted (default)

L’argument -nodes liste les nœuds et -info donne des informations sur les paramètres de ces nœuds :

$ kick -nodes
built-in nodes sorted by name:
 abs                              shader
 add                              shader
 alembic                          shape (procedural)
 ambient_occlusion                shader
 aov_read_float                   shader
 aov_read_int                     shader
...

Le nœud options étant le nœud de paramètres de rendu d’Arnold, il y a de fortes chances que la ligne de commande qui vous intéresse en venant ici soit :

$ kick -info options
node:         options
type:         options
output:       (null)
parameters:   119
multioutputs: 0
filename:     <built-in>
version:      7.1.4.1

Type          Name                              Default
------------  --------------------------------  --------------------------------
INT           AA_samples                        1
INT           AA_seed                           1
FLOAT         AA_sample_clamp                   1e+30
FLOAT         indirect_sample_clamp             10
BOOL          AA_sample_clamp_affects_aovs      false
INT           AA_samples_max                    20
FLOAT         AA_adaptive_threshold             0.015
INT           threads                           0
ENUM          thread_priority                   low
BOOL          abort_on_error                    true
BOOL          abort_on_license_fail             false
BOOL          skip_license_check                false
RGB           error_color_bad_texture           1, 0, 0
RGB           error_color_bad_pixel             0, 0, 1
RGB           error_color_bad_shader            1, 0, 1
STRING[]      outputs                           (empty)
STRING[]      light_path_expressions            (empty)
NODE[]        aov_shaders                       (empty)
INT           xres                              320
INT           yres                              240
INT           region_min_x                      -2147483648
INT           region_min_y                      -2147483648
INT           region_max_x                      -2147483648
INT           region_max_y                      -2147483648
FLOAT         pixel_aspect_ratio                1
ENUM          fis_filter                        none
FLOAT         fis_filter_width                  3
INT           bucket_size                       64
ENUM          bucket_scanning                   spiral
VECTOR2[]     buckets                           (empty)
BOOL          ignore_textures                   false
BOOL          ignore_shaders                    false
BOOL          ignore_atmosphere                 false
BOOL          ignore_lights                     false
BOOL          ignore_shadows                    false
BOOL          ignore_subdivision                false
BOOL          ignore_displacement               false
BOOL          ignore_bump                       false
BOOL          ignore_motion                     false
BOOL          ignore_motion_blur                false
BOOL          ignore_dof                        false
BOOL          ignore_smoothing                  false
BOOL          ignore_sss                        false
BOOL          ignore_operators                  false
BOOL          ignore_imagers                    false
STRING[]      ignore_list                       (empty)
INT           auto_transparency_depth           10
INT           texture_max_open_files            0
FLOAT         texture_max_memory_MB             4096
BOOL          texture_per_file_stats            false
STRING        texture_searchpath                
BOOL          texture_automip                   true
INT           texture_autotile                  0
BOOL          texture_accept_untiled            true
BOOL          texture_accept_unmipped           true
BOOL          texture_use_existing_tx           true
BOOL          texture_auto_generate_tx          true
STRING        texture_auto_tx_path              
INT           texture_failure_retries           0
BOOL          texture_conservative_lookups      true
FLOAT         texture_max_sharpen               1.5
NODE          camera                            (null)
NODE          subdiv_dicing_camera              (null)
BOOL          subdiv_frustum_culling            false
FLOAT         subdiv_frustum_padding            0
NODE          background                        (null)
BYTE          background_visibility             255
NODE          atmosphere                        (null)
NODE          shader_override                   (null)
NODE          color_manager                     (null)
NODE          operator                          (null)
FLOAT         meters_per_unit                   1
STRING        scene_units_name                  
FLOAT         indirect_specular_blur            1
FLOAT         luminaire_bias                    1e-06
FLOAT         low_light_threshold               0.001
BOOL          skip_background_atmosphere        false
BOOL          sss_use_autobump                  false
BYTE          max_subdivisions                  255
INT           curves_rr_start_depth             0
BOOL          curves_rr_aggressive              true
FLOAT         reference_time                    0
FLOAT         frame                             0
FLOAT         fps                               24
STRING        osl_includepath                   
STRING        procedural_searchpath             
STRING        plugin_searchpath                 
BOOL          procedural_auto_instancing        true
BOOL          enable_procedural_cache           true
BOOL          parallel_node_init                true
BOOL          enable_new_quad_light_sampler     true
BOOL          enable_new_point_light_sampler    true
BOOL          enable_progressive_render         false
BOOL          enable_adaptive_sampling          false
BOOL          enable_dependency_graph           false
BOOL          enable_microfacet_multiscatter    true
BOOL          enable_deprecated_hair_absorp...  false
BOOL          dielectric_priorities             true
BOOL          enable_fast_ipr                   true
BOOL          force_non_progressive_sampling    false
FLOAT         imager_overhead_target_percent    1
INT           GI_diffuse_depth                  0
INT           GI_specular_depth                 0
INT           GI_transmission_depth             2
INT           GI_volume_depth                   0
INT           GI_total_depth                    10
INT           GI_diffuse_samples                2
INT           GI_specular_samples               2
INT           GI_transmission_samples           2
INT           GI_sss_samples                    2
INT           GI_volume_samples                 2
ENUM          render_device                     CPU
ENUM          render_device_fallback            error
STRING        gpu_default_names                 *
INT           gpu_default_min_memory_MB         512
INT           gpu_max_texture_resolution        0
BOOL          gpu_sparse_textures               true
INT           min_optix_denoiser_sample         0
STRING        name                              

Mais on va essayer d’aller plus loin et de récupérer les valeurs par défaut de tous les nœuds d’Arnold. :reflexionIntense:

Le code

Comme d’habitude, je vous donne le script en brut et je l’explique après :

from __future__ import (absolute_import,
                        division,
                        print_function,
                        unicode_literals)

import re
import subprocess


class ANodeDef(object):
    def __init__(self, name, type, rest=None):
        self.name = name
        self.type = type
        self.rest = rest
        self.attrs = []

    def __repr__(self):
        return "{}('{}', '{}')".format(self.__class__.__name__,
                                       self.name,
                                       self.type)


class AAttrDef(object):
    def __init__(self, name, type, default):
        self.name = name
        self.type = type
        self.default = default

    def __repr__(self):
        return "{}('{}', '{}', '{}')".format(self.__class__.__name__,
                                             self.name,
                                             self.type,
                                             self.default)


# Print Arnold version.
process = subprocess.Popen(['kick', '--help'],
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT)
out, err = process.communicate()

first_line = out.split("\n")[0]

print(first_line)

# Get node list.
process = subprocess.Popen(['kick', '-nodes'],
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT)
out, err = process.communicate()
node_line_re = re.compile(r"\s(?P<name>\w+)\s+(?P<type>\w+)(?P<rest>.*)")
node_defs = []

for line in out.split("\n"):

    match_grp = node_line_re.match(line)

    if not match_grp:
        continue

    name = match_grp.group('name')
    type_ = match_grp.group('type')
    rest = match_grp.group('rest')
    node = ANodeDef(name, type_, rest)

    node_defs.append(node)

# For each node, get its default parameters.
attr_line_re = re.compile(r"(?P<type>\w+)\s+(?P<name>\w+)\s+(?P<default>.*)")

for node_def in node_defs:

    process = subprocess.Popen(['kick', '-info', node_def.name],
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
    out, err = process.communicate()

    param_start = False

    for line in out.split("\n"):

        if line.startswith("----"):
            param_start = True
            continue

        if not param_start:
            continue

        match_grp = attr_line_re.match(line)

        if not match_grp:
            continue

        type_ = match_grp.group('type')
        name = match_grp.group('name')
        default = match_grp.group('default')

        attr = AAttrDef(name, type_, default)
        node_def.attrs.append(attr)

# Print final output.
for node_def in sorted(node_defs, key=lambda n: n.name):

    print("{} ({})".format(node_def.name, node_def.type))

    for attr in sorted(node_def.attrs, key=lambda a: a.name):

        print("  {} {} {}".format(attr.name, attr.type, attr.default))

Et voilà, du pâté. :enerve:

Vous pouvez copier-coller ça et voir ce que ça donne. Mais nous savons tous que ce qui anime vos vies, c’est le besoin de comprendre les choses, pas vrai ? :baffed:

Explication

Alors c’est parti !

from __future__ import (absolute_import,
                        division,
                        print_function,
                        unicode_literals)

Houdini étant en pleine transition vers Python 3, ce bloc permet de garder un peu de cohérence entre Python 2 et 3.

Ensuite nous avons deux classes :


class ANodeDef(object):
    def __init__(self, name, type, rest=None):
        self.name = name
        self.type = type
        self.rest = rest
        self.attrs = []

    def __repr__(self):
        return "{}('{}', '{}')".format(self.__class__.__name__,
                                       self.name,
                                       self.type)


class AAttrDef(object):
    def __init__(self, name, type, default):
        self.name = name
        self.type = type
        self.default = default

    def __repr__(self):
        return "{}('{}', '{}', '{}')".format(self.__class__.__name__,
                                             self.name,
                                             self.type,
                                             self.default)

Ces deux classes sont des dataclass (classes de données). L’idée étant de stocker des données dans des objets spécifiques pour éviter d’utiliser des dictionnaires ou des namedtuple (mais vous pouvez utiliser l’un et l’autre si vous êtes pressé).

J’implémente souvent les __repr__ sur mes dataclass pour visualiser rapidement les objets dans le debugger ou via des print().

Ça nous donnera, grosso modo, une hiérarchie sous la forme :

ANodeDef
  name: alembic
  type: shape
  rest: (procedural)
  attrs: [
    AAttrDef
      name: invert_normals
      type: BOOL
      default: false
    AAttrDef
      name: ray_bias
      type: FLOAT
      default: 1e-06
    ...
...

Beaucoup de prise de tête pour rien, me rétorqueriez-vous, « Utilise des dicos ! Ça va plus vite, le code c’est quand même plus beau quand ça fait un max de choses en une seule ligne, gnagnagna… ».

Dico ou classes de données, nous sommes différents

Ne perdez pas votre temps en débats stériles, vous avez déjà perdu, alors avançons :

# Print Arnold version.
process = subprocess.Popen(['kick', '--help'],
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT)
out, err = process.communicate()

first_line = out.split("\n")[0]

print(first_line)

Cette partie est la plus surement la plus simple à comprendre : On lance kick --help et on affiche uniquement la première ligne, celle qui affiche la version :

Arnold 7.1.4.1 [c989b21f] linux x86_64 clang-10.0.1 oiio-2.4.1 osl-1.12.0 vdb-7.1.1 adlsdk-7.4.2.47 clmhub-3.1.1.43 rlm-14.2.5 optix-6.6.0 2022/11/29 11:24:49

La suite :

# Get node list.
process = subprocess.Popen(['kick', '-nodes'],
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT)
out, err = process.communicate()
node_line_re = re.compile(r"\s(?P<name>\w+)\s+(?P<type>\w+)(?P<rest>.*)")
node_defs = []

for line in out.split("\n"):

    match_grp = node_line_re.match(line)

    if not match_grp:
        continue

    name = match_grp.group('name')
    type_ = match_grp.group('type')
    rest = match_grp.group('rest')
    node = ANodeDef(name, type_, rest)

    node_defs.append(node)

Ça commence à devenir intéressant. On lance (via subprocess.Popen()) la commande kick -nodes qui va nous lister tous les nœuds disponibles, et on interprète chaque ligne pour en extraire les informations qu’on stocke dans un objet ANodeDef qu’on ajoute à la liste globale node_defs.

Chaque ligne ressemblera à :

 node                             type information_supplémentaires

Par exemple :

...
 add                              shader
 alembic                          shape (procedural)
 complex_ior                      shader          [deprecated]
...

L’expression régulière node_line_re s’occupe d’attraper le contenu de chaque ligne :

   \s        (?P<name>\w+)         \s+               (?P<type>\w+)          (?P<rest>.*)
1 espace|un mot appelé name|1 ou plusieurs espaces|un mot appelé type|le reste, optionnel appelé rest

Une fois qu’on a cette liste de nœuds, on va lancer la ligne de commande kick -info pour chaque nœud et attraper le résultat, c’est ce que fait le bloc de code suivant :

# For each node, get its default parameters.
attr_line_re = re.compile(r"(?P<type>\w+)\s+(?P<name>\w+)\s+(?P<default>.*)")

for node_def in node_defs:

    process = subprocess.Popen(['kick', '-info', node_def.name],
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
    out, err = process.communicate()

    param_start = False

    for line in out.split("\n"):

        if line.startswith("----"):
            param_start = True
            continue

        if not param_start:
            continue

        match_grp = attr_line_re.match(line)

        if not match_grp:
            continue

        type_ = match_grp.group('type')
        name = match_grp.group('name')
        default = match_grp.group('default')

        attr = AAttrDef(name, type_, default)
        node_def.attrs.append(attr)

Le code est plus verbeux, mais on va expliquer tout ça. La sortie de la commande kick -info ressemble à ça :

Type          Name                              Default
------------  --------------------------------  --------------------------------
BYTE          visibility                        255
BYTE          sidedness                         255
BOOL          receive_shadows                   true
BOOL          self_shadows                      true

On a donc besoin d’une expression régulière que je ne détaille pas, car elle est assez similaire à la précédente. :redface:

La seule subtilité c’est qu’on attend (via la variable param_start) d’avoir passé la ligne commençant par ---- avant de commencer à parser chaque paramètre.

Après tous les if passé, on récupère les valeurs de l’expression régulière, on fabrique un objet AAttrDef et on l’ajoute à la liste des attributs du nœud qu’on inspecte. :reflechi:

Et maintenant qu’on a notre petite hiérarchie, on affiche le tout en triant par nom :

# Print final output.
for node_def in sorted(node_defs, key=lambda n: n.name):

    print("{} ({})".format(node_def.name, node_def.type))

    for attr in sorted(node_def.attrs, key=lambda a: a.name):

        print("  {} {} {}".format(attr.name, attr.type, attr.default))

Cela nous donne ça :

options (options)
  AA_adaptive_threshold FLOAT 0.015
  AA_sample_clamp FLOAT 1e+30
  AA_sample_clamp_affects_aovs BOOL false
  etc.

À vous de l’afficher comme vous l’entendez ! :banaeyouhou:

Conclusion

Python, c’est bien, il faut faire du Python, faites du Python.

:marioCours:

Objectif Helios, 5D et « bip » de confirmation de mise au point

samedi 21 mai 2022 à 15:30

Canon 5D Mark III et objectif Helios-44Je fais ce billet pour partager mes galères dans mes recherches sur le moyen de faire fonctionner le « bip » de confirmation de mise au point de mon boitier 5D Mark III avec un objectif Helios 44. On parlera de l’Helios, mais il est possible que les problèmes soulevés et les solutions (et les erreurs aussi…) puisse vous intéresser.

Si vous êtes hermétique aux termes liés au matériel photo, passez votre chemin. Les autres, bienvenues chez vous !

TL ; PL :

Résumé

Mon premier reflex a été un Canon 550D (en 2010), avec un Sigma 17-70 mm f/2.8-4 DC Macro OS HSM, « DC » étant la gamme Sigma dédiée au APS-C.

Ensuite j’ai eu un Sigma 50 mm f/1.4 EX DG HSM, « DG » étant la gamme Sigma dédiée aux full frames, soit un équivalent à un 75 mm (50 × 1.5). Il était loin d’être parfait (plutôt cheap, même), mais comme je me focalise sur les portraits sa profondeur de champ me convenait. Ça été mon objectif principal pendant longtemps.

Depuis j’ai opté pour un 5D Mark III en occasion, par curiosité vis-à-vis du full frame. Combiné à mon Sigma 50 mm f/1.4, et à un 35 mm f/1.8 Canon pour compléter, c’était parfait.

Je m’arrête là, mais ça vous donne une idée du matériel de base. :sourit:

Pourquoi un objectif vintage ?

Comparé à pas mal de choses, la qualité optique d’un objectif ne vieillit pas aussi vite (voir, pas du tout). Ce qui change c’est les boîtiers, les montures, le fait d’avoir l’auto focus embarqué, etc. Cela veut dire qu’il est possible d’avoir des objectifs avec une très bonne optique, pour pas trop cher (comparé à certains objectifs modernes qui peuvent atteindre le millier d’euros…).

À cela s’ajoute une vision personnelle donc totalement subjective : Les objectifs actuels sont assez bruts. Ils visent à rendre correctement la couleur, la lumière et à éviter tous les problèmes récurent distorsion, aberration chromatique, etc. Ils renvoient une image forte en détails, avec tout dedans, pour laisser de la marge lors du développement.

Les objectifs plus anciens sont imparfaits, même les très haut de gamme embarquent avec eux les défauts de leur époque. Quand on cherche un objectif vintage, on cherche ça.

Après pas mal de recherche j’hésitais entre le Jupiter 8, le Helios 44 et le Takumar SMC.

Jupiter 8

À ma grosse surprise, une personne cherchait à se débarrasser d’un Jupiter à 30 km de chez moi. Pas de frais de port, je le prends !

C’est un Jupiter 8 KMZ 50 mm f/2 monture M39 :

Objectif Jupiter 8 KMZ

Comme la monture de mon 5D est de type Canon EF, J’achète un adaptateur M39 vers EF basique.

Tiens tiens, il est très petit dis donc… Je branche l’ensemble et bizarre… Tout est en macro…

Je vais découvrir qu’une monture ne fait pas tout, et que quand on commence à regarder les objectifs anciens, il faut garder un œil sur pas mal de choses.

Ici, c’est le flange qui posait problème. Le flange, c’est la distance entre le capteur (la pellicule) et la monture de l’objectif (le « tirage mécanique », en français).

Il y a énormément suivant les montures, et parfois des variantes pour la même monture…

La monture Canon EF a une distance de 44 mm. Pour le M39, c’est compliqué, mais en gros, pour le Jupiter c’est 28.8 mm. Ça veut dire que pour que mon objectif Jupiter fonctionne sur mon 5D, il faudrait techniquement que j’enfonce l’objectif dans le boîtier. Donc c’est mort pour les montures EF (j’essaierai un jour, si j’ai un mirrorless, qui ont des distances plus courtes).

Bon, j’ai fait quelques tests en macro histoire de, mais autant j’aime les portraits, autant ceux des fourmis de mon jardin en vintage, ce n’était pas le plan… :aupoil:

Helios 44.

Ducoup je me rabats sur mon second choix et commande un Helios-44 58мм f/2 M39 Zenit blanc, 8 lames.

M39 aussi, j’ai vérifié le flange : 45.2 mm. Je ne me ferais plus avoir.

Notez que les Helios, il y en a énormément. Je ne refais pas l’historique : Objectif créé en URSS, peut-être le plus massivement produit de l’histoire. Donc à moins de passer du temps à chercher, il est peu probable que vous tombiez deux fois sur le même.

Il arrive, et c’est la révélation : Il est génial ! Ses seuls défauts sont une bague un peu molle et de ne pas pouvoir faire de mise au point à l’infini (je ne suis pas sûr de comprendre pourquoi), mais comme je ne fais que des portraits avec…

Objectif Helios 44

En parlant de portrait, le bokeh tourbillonnant (swirly bokeh) de l’objectif donne a cachet immédiat. Honnêtement, pour le prix, je ne peux que vous conseiller d’en acheter un (après avoir lu ce billet :hehe: ). On les trouve facilement sur ebay.

Nous disions plus haut que le problème des anciens objectifs est leur manque de matériel embarqué, à savoir, pas d’auto focus. C’est logiquement le cas avec le Helios.

À pleine ouverture, sa profondeur de champ est très faible. Mon Sigma 50 mm m’avait habitué à la sanction sans appel d’une mise au point mal faite à pleine ouverture, mais ce dernier est équipé d’un auto focus qui diminuait énormément les ratés.

Le Helios n’ayant rien du tout, la bonne méthode semble être de faire la meilleure mise au point possible puis de mitrailler en faisant une légère rotation de la bague, ou de passer par la mise au point via l’écran LCD, qui permet de faire un zoom à l’intérieur de l’image et de focaliser sur un élément en particulier, mais ça rendait les prises très longues. :neutral:

Comme je passe toutes mes photos en RAW, ça veut dire que je passe énormément de temps à isoler les quelques bonnes prises.

Déambulation

Les mois passent, et il devient mon objectif principal, toujours branché sur mon boîtier. Je suis resté longtemps en mise au point manuelle (alternant entre le viseur et l’écran LCD), mais le nombre de prises ratées devenait de plus en plus frustrant et je commençais à me dire que non, il fallait que je fasse quelques choses pour rendre la prise de vue plus agréable et moins aléatoire.

Et là on tombe un peu dans la zone grise : Il n’y a pas énormément d’information, car chaque combinaison boîtier/objectif est différente.

La première solution qui m’a été proposé a été de mettre un stigmomètre. Il s’agit d’une sorte de lame de verre qui amplifie la profondeur de champs dans le viseur. C’était ce qu’on utilisait « avant » les autofocus. Le seul site qui semble en vendre de façon sérieuse est Focusing Screen. Ils ont pas mal de choix. Presque séduit, je regarde les instructions d’installation, mais l’idée d’aller jouer avec un tournevis et une pince à épiler à quelques cm du capteur m’intéressait moyennement. Sans compter que cette installation est définitive et risquait de me gêner lors de l’utilisation d’objectifs avec autofocus intégré. L’autre problème de tels stigmomètres étant une diminution non négligeable de la luminosité du viseur. Sans compter le prix (plusieurs centaines d’euros).

Je commençais à perdre espoir, mais lors de mes dernières recherche je comprends qu’il y a bien un mécanisme de vérification du focus dans le boitier de l’appareil photo, mais que la seule et unique raison pour laquelle il ne fonctionne pas avec les objectifs vintage, c’est parce que l’adaptateur ne répond pas… En gros il faut juste que l’adaptateur « réponde » au boitier afin que ce dernier soit capable de proposer un petit « bip » lorsque la mise au point est bonne. Et pour ça, il faut que l’adaptateur soit équipé d’une puce spéciale. Et il n’y a aucun moyen de forcer l’utilisation de l’autofocus sans ça. Putain, mais Canon, quoi !!! :injures:

Au fil de mes recherches, je finis par trouver le Graal : Fotodiox propose des adaptateurs avec une puce intégrée, qui se fait passer pour un objectif. :laClasse:

Et là, je tombe dans le piège « montures Helios » qui fait que je vais royalement me planter sur le modèle à prendre (et aussi la raison originale de ce billet). Explication :

Après avoir eut mon Helios, toutes les informations que je trouvais faisaient référence à la monture M42. J’en suis arrivé à intérioriser que j’avais un M42, parce qu’une fois l’adaptateur placé, le fait que j’avais pris une monture exotique (M39 alors que quasiment tous les Helios sont en M42) m’était totalement sortie de la tête.

Je fais cette première erreur en regardant le modèle dédié au M42. Ou plutôt, « les » modèles… En effet, la monture M42 est disponible en 2 versions, suivant votre objectif (si vous cherchez « fotodiox M42 v1 or v2 » vous tomberez sur une petite image).

Sans rentrer dans les détails, l’arrière des objectifs dépends des fabricants et certains ont placé la goupille d’ouverture à différents endroits. Un exemple ici et . Bref, je passe énormément de temps à déterminer le type qu’il me fait et fais mon choix, le V1, celui qui est conseillé par le site « pour les Helios » et qu’on voit sur certaines images (je vois un Helios monté dessus, je me dis que c’est ce modèle en oubliant totalement la spécificité du mien).

Ce bordel sans nom entre V1 et V2 est la raison pour laquelle j’ai perdu de vue la question de la monture. Je commande donc ce modèle. L’adaptateur arrive, j’approche mon objectif et je comprends mon erreur : Helios première génération = M39…

T’es un champion, Dorian… Change rien. :slow_clap:

À titre informatif (et histoire d’ajouter un peu de sel), voici le détail du prix :

L’adaptateur : $24.95
Frais de port : $27.81
Taxes : $10.55
Total : $63.31 (60,29 €)

À cela c’est ajouté 13 € de douane… :baffed:

Petite inspiration triste, je relativise et me dit que la monture M42 c’est la monture d’un Takumar, que j’aurais peut-être un jour… :siffle:

Finalement, je refais une commande : M39 vers Canon EF, même tarif… Par contre pas de V1/V2, forcément, c’est plus simple :

En bas à gauche, la bague conversion M39 de Fotodiox, en haut en droite la bague de conversion M39 standard.

L’adaptateur arrive, je l’installe sur mon boîtier avec mon objectif, j’enclenche à demi-course, commence à tourner la bague de mise au point et joie ! L’appareil émet un petit « bip » quand la mise au point est faite ! J’enclenche, ça fonctionne nickel !

Je commence à mitrailler comme un môme, sous l’air décontenancé de mes proches…

Adaptateur Fotodiox

Je n’ai pas grands chose à ajouter si ce n’est que ça fonctionne bien. Certains ont toutefois eut des problèmes, mais c’est dû à comment est configuré l’appareil. Je suis toujours en « Manuel » et toujours sur un seul collimateur d’auto focus (central). Ça se change en appuyant sur la touche des collimateurs, puis la touche « M-Fn ».

Ça et le fait de configurer certaines options internes (AF, page 3) :

En gros, sachez que si vous n’entendez pas le bip, il est probable que vous n’ayez pas configuré correctement votre boitier.

Et la bague M42 ? Et le Jupiter ?

Après avoir hésité pendant plusieurs mois, une offre intéressante tombe et je finis par prendre un objectif (Pentax Super Multi Coated Takumar 50 mm F/1.4 M42). Il arrive, je le branche, je fais la mise au point, et j’entends le bip de confirmation de focus. J’enclenche et ça fonctionne impeccablement :

L’optique est bonne (jaunie, car il s’agit de verre de thorium qui jauni sous sa propre radioactivité, mais passé sous UV quelques heures). L’objectif est magnifique, taillé dans la masse (comme le Helios). En ouverture 1.4, ça ne pardonne pas.

Avec tout ça, je ne suis pas près d’acheter du nouveau matériel.

Résumé technique

Helios + bague M39 : Fonctionne impec !
Takumar + bague M42 (type 1) : Fonctionne impec !

En espérant que ce billet vous évitera de perdre du temps.

:marioCours: