Comme ça manque un peu d'images, le resultat apres la correction de deux grosses erreurs et avant une petite :
Shader "par defaut" type Kajika/Kay :
Le marschner avec une petite composante diffuse :
Notez le retro-eclairage/back scattering (glints & caustics), la couleur plus naturelle (grace à l'absorption), les specs plus presents et cohérents, ainsi que le shading général plus détaillé. Les racines sont aussi naturellement plus sombres sans tricher avec une teinte plus sombre.
Plus on teste un shader, plus on est à l'aide avec ses paramètres et son fonctionnement interne.
On sait à l'avance ce qu'on doit avoir comme résultat, et on sait plus ou moins d'où vient l'erreur..
Apres avoir adapté quasiment à la perfection les maths du papier de Marschner, nous nous sommes retrouvés avec des resultats bien, mais pas comme ceux à quoi on s'attendait. Ca se joue à pas grand chose : trop peu de subs, de la refraction là où il faut pas,...
Après des tonnes de vérifs, on est arrivé à la conclusion que le Marschner, il s'était gouré en recopiant ses notes. Deux (grosses) erreurs, que je vais donner ici pour éviter l'arrachage de cheveux aux codeurs qui tomberont sur ce blog en cherchant le code de ce type de shader :
- Problème lors du calcul du fresnel
Qui determine si un rayon est renvoyé ou tranmis dans le poil, et en quelle quantité.
- Problème lors du calcul de l'absorption :
Le calcul de la longueur du trajet dans le poil est incorrecte.
Une fois ces calculs corrigés, nous voilà avec un prototype fonctionnel. Petit rajout d'une composante de diffusion - pour simuler la lumiere qui rebondit entre les poils, ou celle qui traverse un poil pour en eclairer un autre - Et voilà !
Une nouvelle série de verification, et pret pour l'étape 5, décrite à l'étape 1 : "baker" tout les chemins possibles dans une lookup table afin d'accélerer le rendu !
Petite parenthese concernant le debug, pour Darkstryder :
Il y a des petits trucs qui permettent d'évaluer tout les cas possibles en un minimum de rendu/temps.
pour une image donnée, disons que les pixels vont de 0 à 1 de gauche à droite et de bas en haut.
On a donc (0,0) en haut à gauche et (1,1) en bas à droite.
Il est possible de mapper des parametres sur un plan, qui vont varier à chaque pixel (selon les coordonnées que je viens d'expliquer)
Dans cet exemple, en Y nous faisons varier l'angle phi (le rapport entre la light et la tangente du poil) et X theta_r (l'angle de reflection).
En résumé : on tourne virtuellement la camera et la lampe autour du poil.
en rouge nous avons l'energie de la reflection (R), en vert de la transmission (TT) et en bleu le subscatter (TRT).
Ca nous permet de comparer facilement nos resultats avec ce genre de graphs dispos dans le papier (et qui sont aussi obtenus en faisant tourner de trucs autour d'autres trucs)
Pour la prochaine étape, je posterais un quicktime où l'ont fait varier deux paramètres en X et Y et un 3ème dans le temps, ce qui nous donne tout les resultats possibles dans toutes les conditions possibles.
Un moteur de rendu programmable intégre de base tout ce qu'il faut : calculs vectoriels, conditions, toute les functions d'un math.h, plus des choses déjà encodées pour des fonctions "normales" d'un moteur de rendu : boucle d'illuminances, corrections couleurs, calcul d'angle de reflection/refraction(qu'on utilisera pas ici par soucis d'optimisation), raytracing, ...
Dans le cas qui nous interesse, la transcription est relativement rapide. La premiere étape fut le developpement de procedures de resolution d'équations du 3ème degré (qui servira lors de la resolution des chemins de la lumiere dans le poil - pour le TT et le TRT).
Ca peut paraitre tout bete, mais on est justement pas en c++ : il faut recoder l'ensemble, et donc impossible de piquer directement le code d'un site ou l'autre. C'est en fait plus rapide de partir des equations de base qu'un code déjà tapé :)
La 2ème étape, et l'une des plus longues, est de savoir ce qui nous est disponible dans le moteur et quel sont leurs equivalents dans les formules.
En 3d, un cheveux, c'est une courbe. L'epaisseur n'est finallement qu'une sorte de "cheat". Pas grave, les formules estiment que le poil n'a pas d'épaisseur. Mais nous avons le plus important : le vecteur tangent du poil, et sa direction (racine vers pointe). Reste le vecteur de vue (camera), et le vecteur de la lampe. Deux des choses disponibles "de base".
Il faut néamoins vérifier que les directions sont correctes. Dans ce cas-ci, entre renderman et le papier de marschner, tout était exactement inversé (X = -X, Y=-Y, Z=-Z, vecteur de light = -vecteur de light,...). Ce qui au final donnera le meme resultat, mais par soucis de simplification du debogage, il a fallu vérifier notre répère spatial, et que toutes les operations trigonométriques donnaient le resultat voulu - c'est à dire celui du papier. Une réinversion totale sera faite à la fin quand tout sera debogé.
Pas très complexe en théorie, mais très long en pratique car cette étape se doit de fonctionner parfaitement de A à Z, il faut donc vérifier chaque petite composante :
1. - lampe et camera à 90° du poil, racine en bas : angle de reflection correct ? angle incident correct ?
D'abord calculer le resultat qu'on doit avoir, le vérifier en 3d. C'est bon, on continue.
2. camera rasante au poil, lampe à 90° du poil, racine en bas : angle de reflection correct ? angle incident correct ?
- Oui, etape 3, non, retour à l'étape 1.
Bon, vous pouvez devinez tout les cas et le nombre d'étapes de verifications :) Il faut vérifier quand la camera et la lampe sont de part et d'autre du poil,...
En sauter est possible, mais pas une bonne idée : se rendre compte au dernier moment qu'un cas est incorrect remet en cause tout le travail effectué depuis le debut.
Une fois sûrs de nos vecteurs, on peut se lancer dans le calcul de la premiere (et plus simple) composante du poil : la reflection (R).
Si la première verification est bonne, cette étape est assez rapide : corriger un - oublié par ici, une racine mal mise entre () par là,...
Il faut non seulement calculer l'angle de reflection en tenant compte de la deflection des ecailles, mais surtout la quantité de lumiere qui va etre réflechie (le reste ira dans le TT et TRT).
On peut donc résumé le calcul final à :
prendre le nombre de lampe
pour chaque lampe :
illumination totale = illumination totale + (Reflection * Quantité de reflection + Refraction * Quantité de refraction + subscattering * Quantité de subscatter) * intensité de la lampe.
ou
Ci += (NR*MR+NTT*MTT+NTRT+MTRT)*Cl
NR, MR, MTT et MTRT ont étés effectivement rapides.
NTT fut le premier à utiliser le resolveur d'équation débuggé à part et fut rapide egalement..
Une fois le papier trouvé, il faut encore le lire, mais surtout le comprendre.
D'abord, comment ces gens en sont venu à ce resultat ?
- on les engage en disant "bon les gars, on a besoin d'un modele mathématique pour faire du beau poil sur notre prochain film !".
On les enferme dans une piece et on attend.
Par où on commence ? Ben, logiquement, on regarde déjà à quoi ressemble un poil. Et surtout, on regarde comment il réagit à la lumiere.
Alors j'ai pas trouvé de photo de ce genre d'équipement (parce que je sais déjà pas comme ça s'appelle exactement), mais ça marche comme ça :
- on met un poil dans une chambre noire.
- on met une lumiere qui va tourner et changer d'intensité tout les x degrés.
- on met un capteur qui lui aussi va tourner et choper la lumiere provenant du poil.
Bon evidemment une lumiere fixe et tourner le poil et la camera suffirait, mais comme c'est piti et cassant, c'est plus simple de faire tourner tout le reste.
Ca donne, par exemple, ça :
Bon c'est un exemple simple : les courbes, c'est les mesures de lumières (R G et B).
Le petit soleil noir, c'est la lumiere emise.
le plan, c'est le poil, la racine vers la gauche, la pointe vers la droite.
phiR, c'est l'angle de vue.
Par exemple, sur le tout premier schéma, il faut se placer à +- 35° pour voir un beau speculaire.
Bon bref, ils ont fait des tas et des tas de mesure. Et les resultats leur ont dit quelque chose. Et ils se sont souvenus d'un truc qu'ils ont du étudier vers l'age de 10 ans.
Petite histoire :
vers 1630, un mec un peu poète se passionne pour les arcs-en-ciel. Il essaye de comprendre comment se forme le prisme, et analyse donc un cas simplifié de la goutte d'eau : un cylindre de verre. Il en sort pleins de resultats.
Il se trouve que ces resultats sont tres similaires à ceux obtenus dans nos cheveux.
Il en sort aussi pleins de maths. Et ces maths, on peut les réappliquer facilement.
Le mec, c'est Descartes, les formules je vous laisse chercher si ça vous interesse (tout est dans le pdf de l'article 1), et le resumé, c'est ça :
et son application dans le poil :
Pour un rayon frappant le poil, une partie est reflechie (R pour reflechi), une partie est refractée deux fois et traverse donc le poil (TT pour transmis-transmis), et une partie est refléchie dans le poil pour ressortir ailleurs. (TRT pour transmis-reflechi-transmis - ou SS pour Sub Scattering).
Grace à descartes, on a les formules et simplifications pour calculer le chemin de chaque rayon dans le cylindre (cas simplifié, je vous passe les deltaH et cie), comme le fait qu'on a soit un path ou 3 pour un point d'entrée et un angle donné (ce qui donne un ou deux speculaires selon l'angle), ou comme le fait que les rayons peuvent converger et donner des caustiques dans la poil.
C'est assez efficace : au lieu de faire varier l'angle du rayon incident, on peut plutot faire varier les angles de refractions. Et comme du coup l'angle incident reste fixe, on peut précalculer tout les cas possibles, ce qui accèlere le rendu.
Encore une fois je fais des raccourcis et je simplifie le procédé, mais c'est ça - nvdia utilise ce genre de table de pré-calcul en temps réel sur la demo de nalu). On précalcule tout les cas possibles, on entre un angle de lumiere, un angle de vue, et on a immédiatement le resultat pour chaque point/path possible.
Mais bon, on est pas dans un cylindre parfait, il faut donc rajouter la dessus l'applatissement du cheveux, et surtout les fait qu'il est constitué d'écailles (l'angle alpha du schéma - renvoye R vers la base du poil et le TRT vers la pointe par exemple)
La compréhension est une chose, mettre en pratique les maths derriere, c'est autre chose. Heureusement l'un de mes patrons est une brutasse là dedans et m'a sorti en 4 jours un pseudo-code de ce papier.
Reste l'analyse des papiers sortis après celui là : le dual scattering (on prend en compte le fait que la lumiere qui sort d'un poil va contribuer à éclairer son voisin) et autres "spherical harmonics" très recentes (2007 -plus théorie que pratique- et 2008 - utilisables).
Etape 3, traduire les maths en language compréhensible par le moteur de rendu, et en utilisant les données fournies par le soft de 3d...
Crise financière aidant, j'ai (on) a un peu de temps libre pour developper nos outils. Ayant récemment eu besoin de faire des cheveux, je me suis rendu compte que les outils dont nous disposions étaient insuffisants.
le passé
Jusqu'ici, j'avais developpé des shaders pour fourrures ou poils courts, avec un resultat plutot satisfaisant, aussi bien niveau look que temps de rendu :
(celui ci étant plutot tricky : faire tenir une projection sur des poils qui se deforment)
Ce shader, facile à éclairer, donnant un agréable rendu très doux, donne un resultat peu satisfaisant sur de longs poils !
Hors, problème :
Quand on éclaire une surface en 3d, on calcule en fait des angles. Entre la lumière, le point de vue de la camera et la normale de la surface.
Dans le cas d'une diffusion (ci dessus), C'est l'angle entre les vecteurs de la normale de la surface (N) et la lumière (L).
Cet angle (a) définit l'illumination.
Si l'angle = 0 (lumière perpendiculaire), on éclaire au maximum la surface -> illumination à 100%
Si l'angle = 90 (lumière rasante), on éclaire au minimum la surface -> illumination à 0%
(essayez en vrai avec une lampe torche et une feuille de papier : plus la lampe est perpendiculaire à la feuille, plus elle est "blanche")
Une fonction mathématique simple résout ce modèle : le cosinus.
cosinus de 0 = 1, cosinus de 90 = 0. Angle à 45° ? 70% d'illumination (cosinus de 45 = 0.7)
Si vous avez accès à un programme 3d, vous pouvez vérifier : surface blanche, lampe d'intensité 1, à 45° d'un plan : vous aurez un pixel d'une valeur de 0.7.
Dans le cas de vecteur ce n'est pas un sinus mais un produit scalaire (dot product).
Ce modele mathématique basique s'appelle un lambert. C'est le plus simple, mais on en utilise bien d'autres plus complexes..
votre rendu, avec ou sans poils ? AVEC
cas n°1 avec L1 : L1 et N1(surface) sont perpendiculaire, L1 et N2 (poils) sont parallèles.
cas n°2 avec L2 : L2 et N1(surface) sont parallèle, L2 et N2 (poils) sont perpendiculaire.
Vous aurez peut etre déjà compris le problème : on illumine selon des angles.. Hors, un poil court pousse +- selon la perpendiculaire à la normale.. Si on applique la meme formule sur le poil, là où la surface est censée etre eclairée, le poil sera noir. Et inversement.
-> Le poil devient tres dur à illuminer (des zones sombres apparaissent là où il ne faut pas,...)
Dans les papiers du siggraph 2002, une méthode utilisée pour stuart little a été présentée : copier la normale de la surface à la base du poil et fader le long du poil vers la "vraie" normale, ce qui resoud ce problème.
C'est ce que fait "mon" shader de poil court.
Plus on s'éloigne de la surface, moins on est perpendiculaire.. Et ce qui marche sur des poils courts est inoperant sur des poils longs.. D'où le besoin de supprimer ce "cheat" de normale, et de revenir à un shader de base à peine amélioré.
Je vous passe les maths derrière, mais le modèle mathématique date de 1989 (de Kajiya & Kay).
Résultat :
Suremment fort bien à l'époque, il est loin d'etre convaincant à l'heure actuelle.
Premier gros problème : les reflections. Un cheveux etre tres reflectif, ce qui donne la brillance tant vantée par les pubs pour shampoing. Gardons la diffusion telle quelle, et tweakons un peu les reflections (ou spéculaires), sans cohérence physique réelle (c'est du code "artistique" :)
Je m'étendrais pas sur le code ce shader, ca n'a pas grand interet.
C'est mieux et ça a suffit pour le moment. Mais on a besoin de quelque chose de plus "réaliste" et basé sur une réalité physique.
La recherche
Bon, il faut savoir que les modèles mathématiques utilisés en 3d (temps réel ou non) sont d'abord concus par des mathématiciens ou physiciens. Pas des infographistes.
Pour faire communiquer ces deux mondes, il y a, entre autre, le siggraph. Et du coup, des gens comme lui
(Marschner Steve, nerd plus ou moins incompréhensible)
Ca date de 2003, c'est un modèle plus avancé. Des améliorations de ce modèles ont suivi, jusqu'en 2008
Avant de commencer à lire ces indigestes pages, on recherche d'abord si quelqu'un s'est tapé le boulot de traduction en code, et on lui pique sans lui demander son avis.
Pas de bol ici, ce modèle, utilisé par ilm (depuis star wars) et weta (sur king kong) et en temps réel (demos nvidia), est bien gardé secret par les personnes qui ont passé quelques jours à décoder les formules.
A vrai dire, il est meme vendu par une boite ! http://www.gmp-vfx.com/www/worldwide/src/software2.htm
Il faudra donc se frapper la retranscription du pdf, et c'est là que ça devient passionnant.. Suite au prochain épisode :)
Est-ce que ça marche ? Oui.
Est-ce que ça marche bien ? Bof..
Je suppose que c'est tres dependant de la webcam et de l'éclairage ambiant, mais meme en mettant des conditions idéales, c'est loin d'etre précis ou agréable :
Testé sur le solitaire windows, track des doigts (index et majeur). Le curseur suis le majeur, le clic se fait en "pincant" (les zones se touchent). Dans la pratique c'est tres difficile : on a naturellement tendance à bouger le majeur vers le pouce, ce qui fait du coup bouger le curseur en dehors de la zone.
Pour selectionner une carte ça va, un bord de carte moins. Un double clic est impensable.
Du coup on passe beaucoup de temps à se concentrer sur le bon mouvement, ce qui rend le truc pas intuitif du tout. Sans parler du syndrome "position inconfortable" qui m'a fait abandonné après une partie.
Sans parler de la perte de track regulière (suremment du à l'expo auto de ma webcam que je n'arrive pas à desactiver).
Bon, je testerais sur des trucs plus simples quand j'aurais le temps, mais je doute que ça depasse l'interet de l'eye toy.