Dorian Fevrier's blog - Mot-clé - normalJe 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:695d9c73474c33ce3dab043823509c4bDotclearOrienter les particules sur la normale d'un mesh ou d'une surface: closestPointOnMeshurn:md5:190e37d74be06c95383bbb73607a86d32010-09-13T22:20:00+02:002013-07-26T18:13:26+02:00NarannInfographie 3D - BoulotclosestPointOnMeshfrinstancermayameshnodenormalparticule<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_tn.png" alt="oriente_particle_maya_tn.png" style="float:left; margin: 0 1em 1em 0;" title="oriente_particle_maya_tn.png, sept. 2010" height="150" width="150" />Lors de mes recherches sur les particules, j'étais face à un problème qu'on rencontre assez fréquemment:</p>
<p>Orienter des instancers en fonction de la normale du mesh sur lequel elles se trouvent... :casseTeteMur:</p>
<p>Ça semble être un bête problème mais dans Maya, ce n'est pas si évident et il faut un peut bidouiller pour pouvoir le faire.</p>
<p>Je vous propose à travers ce tuto une petite solution qui, vous allez voir, n'est pas de moi mais que je ne pouvais m'empêcher de vous faire partager.</p> <p>Une des méthodes rapides pour faire ça est de mettre une vitesse très très faible (0.001) aux particules et d'orienter la particule en fonction de sa vitesse. Franchement, ça dépanne mais ça peut poser quelques soucis plus tard....</p>
<center>:longBar:</center>
<h5>Contexte</h5>
<p>Je suis tombé sur un <a href="http://forums.cgsociety.org/showthread.php?t=789536" hreflang="en">thread CGTalk</a> d'une personne demandant justement, comment procéder.</p>
<p>La meilleur réponse fut sans conteste <a href="http://forums.cgsociety.org/showpost.php?p=6006153&postcount=4" hreflang="en">celle de YouDaftPunk</a> qui parle d'un node qui permet de faire ça: "closestPointOnMesh" :sourit:</p>
<p><a href="http://forums.cgsociety.org/showpost.php?p=6006153&postcount=4" hreflang="en"><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya001.png" alt="oriente_particle_maya001.png" style="display:block; margin:0 auto;" title="oriente_particle_maya001.png, sept. 2010" height="357" width="482" /></a></p>
<p>Il donne également une scène assez complète pour expliquer comment procéder: <a href="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/particle_normal_alignment.mb.zip" hreflang="en">particle_normal_alignment.mb.zip</a></p>
<p>Un bon conseil: Mon tuto n'étant qu'une introduction, je vous invite, à garder le billet de YouDaftPunk de coté pour y revenir plus tard, télécharger sa scène et la décortiquer. :redface:</p>
<center>:longBar:</center>
<h5>closestPointOnMesh</h5>
<p>Le fonctionnement du node closestPointOnMesh est en fait assez simple:</p>
<p>En entrée:</p>
<ul>
<li>La position d'un point (x,y,z)</li>
<li>Une surface mesh</li>
</ul>
<p>En sortie:</p>
<ul>
<li>La position d'un autre point (x,y,z), celui qui est sur la surface</li>
<li>Une orientation qui est en fait la normale de la face sur laquelle se trouve le point calculé</li>
<li>Pleins d'autres choses que je n'utiliserai pas</li>
</ul>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya004.png" alt="oriente_particle_maya004.png" style="display:block; margin:0 auto;" title="oriente_particle_maya004.png, sept. 2010" height="290" width="421" /></p>
<p>Résultat:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya002.png" alt="oriente_particle_maya002.png" style="display:block; margin:0 auto;" title="oriente_particle_maya002.png, sept. 2010" height="424" width="556" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya003.png" alt="oriente_particle_maya003.png" style="display:block; margin:0 auto;" title="oriente_particle_maya003.png, sept. 2010" height="226" width="767" /></p>
<p>Je vous donne <a href="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_scene001.ma">->la scène Maya<-</a> (bouton droit de souris, save as...).</p>
<p>Vous pouvez bouger le "locator1", vous verrez que l'autre suit en restant sur la surface! :hehe:</p>
<center>:longBar:</center>
<h5>Avec des particules</h5>
<p>Maintenant me direz vous, comment l'utiliser avec des particules? Et bien si vous avez les bases, vous allez voir que la manœuvre est un peu hasardeuse (et "perf-killer" :baffed: ) mais qu'elle a le mérite d'exister et peut rendre de fiers services!</p>
<p>Créez un mesh ayant une forme un minimum intéressante (mais pas non plus tarabiscotée, closestPointOnMesh n'est en aucun cas le messie qui vous sortira <del>de votre modé dégueulasse</del> :siffle: )</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya005.png" alt="oriente_particle_maya005.png" style="display:block; margin:0 auto;" title="oriente_particle_maya005.png, sept. 2010" height="367" width="364" /></p>
<p>Puis créez un node closestPointOnMesh auquel vous connectez le mesh:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya0051.png" alt="oriente_particle_maya0051.png" style="display:block; margin:0 auto;" title="oriente_particle_maya0051.png, sept. 2010" height="48" width="261" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya0052.png" alt="oriente_particle_maya0052.png" style="display:block; margin:0 auto;" title="oriente_particle_maya0052.png, sept. 2010" height="105" width="488" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya0053.png" alt="oriente_particle_maya0053.png" style="display:block; margin:0 auto;" title="oriente_particle_maya0053.png, sept. 2010" height="89" width="408" /></p>
<center><i>Fastoche! :dentcasse: </i></center>
<p>Créez votre "Emitter from object":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya006.png" alt="oriente_particle_maya006.png" style="display:block; margin:0 auto;" title="oriente_particle_maya006.png, sept. 2010" height="121" width="211" /></p>
<p>Passez votre "Emitter Type" en "Surface":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya007.png" alt="oriente_particle_maya007.png" style="display:block; margin:0 auto;" title="oriente_particle_maya007.png, sept. 2010" height="431" width="416" /></p>
<p>Augmentez le "Rate":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya008.png" alt="oriente_particle_maya008.png" style="display:block; margin:0 auto;" title="oriente_particle_maya008.png, sept. 2010" height="43" width="344" /></p>
<p>Et enlevez la vitesse de vos particules:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya009.png" alt="oriente_particle_maya009.png" style="display:block; margin:0 auto;" title="oriente_particle_maya009.png, sept. 2010" height="69" width="410" /></p>
<p>Lancez votre dynamique, vous devriez obtenir quelque chose comme ça:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya010.png" alt="oriente_particle_maya010.png" style="display:block; margin:0 auto;" title="oriente_particle_maya010.png, sept. 2010" height="421" width="410" /></p>
<center><i>Des particules sur toute la surface!</i></center>
<p>Créez un objet qui sera votre instancer:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya011.png" alt="oriente_particle_maya011.png" style="display:block; margin:0 auto;" title="oriente_particle_maya011.png, sept. 2010" height="349" width="388" /></p>
<center><i>Une flèche... Notez l'originalité :pasClasse: </i></center>
<p>Sélectionnez-le puis transformez-le en instancer:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya012.png" alt="oriente_particle_maya012.png" style="display:block; margin:0 auto;" title="oriente_particle_maya012.png, sept. 2010" height="252" width="221" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya013.png" alt="oriente_particle_maya013.png" style="display:block; margin:0 auto;" title="oriente_particle_maya013.png, sept. 2010" height="462" width="445" /></p>
<center><i>Essayez de ne pas regarder en haut... :hihi: </i></center>
<p>Nous allons maintenant jouer avec les expressions pour orienter nos particules à la création.</p>
<p>Dans votre système de particule, créez deux attributs de type "vecteur par particule": aimUp, aimDir.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya014.png" alt="oriente_particle_maya014.png" style="display:block; margin:0 auto;" title="oriente_particle_maya014.png, sept. 2010" height="119" width="206" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya015.png" alt="oriente_particle_maya015.png" style="display:block; margin:0 auto;" title="oriente_particle_maya015.png, sept. 2010" height="257" width="408" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya016.png" alt="oriente_particle_maya016.png" style="display:block; margin:0 auto;" title="oriente_particle_maya016.png, sept. 2010" height="160" width="223" /></p>
<p>Dans l'onglet "Instancer" de votre "particle system", settez Aim Direction sur la variable aimUp puis Aim Axis sur la variable aimDir:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya017.png" alt="oriente_particle_maya017.png" style="display:block; margin:0 auto;" title="oriente_particle_maya017.png, sept. 2010" height="229" width="300" /></p>
<p>Créez une expression à la création:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya018.png" alt="oriente_particle_maya018.png" style="display:block; margin:0 auto;" title="oriente_particle_maya018.png, sept. 2010" height="84" width="358" /></p>
<p>Voici l'expression:</p>
<pre class="mel mel"><span style="color: #666666; font-style: italic;">// on "regarde vers le haut"</span>
particleShape1.<span style="color: #202020;">aimDir</span> <span style="color: #339933;">=</span> <span style="color: #339933;"><<</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span><span style="color: #0000dd;">0</span><span style="color: #339933;">>>;</span>
<span style="color: #666666; font-style: italic;">// on donne les infos de position de la particule au node</span>
vector $p <span style="color: #339933;">=</span> particleShape1.<span style="color: #202020;">position</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> $x <span style="color: #339933;">=</span> $p.<span style="color: #202020;">x</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> $y <span style="color: #339933;">=</span> $p.<span style="color: #202020;">y</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> $z <span style="color: #339933;">=</span> $p.<span style="color: #202020;">z</span><span style="color: #339933;">;</span>
setAttr closestPointOnMesh1.<span style="color: #202020;">inPosition</span> $x $y $z<span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">// on récupère la normal qu'on donne comme information a la particule</span>
vector $normal <span style="color: #339933;">=</span> `getAttr closestPointOnMesh1.<span style="color: #202020;">normal</span>`<span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">// et on oriente la particule</span>
particleShape1.<span style="color: #202020;">aimUp</span> <span style="color: #339933;">=</span> $normal<span style="color: #339933;">;</span></pre>
<blockquote><p>Note: Certains tiqueront et diront que l'utilisation des "setAttr" et "getAttr" est à proscrire lors de l'évaluation d'une expression car elles détruisent les perfs. Ils auront raison :hehe: . Mais le soucis vient de "l'évaluation des nodes Maya" (la "computation"). Celle ci ne se fait qu'une fois l'évaluation des particules terminé.
Donc si on n'utilise pas de setAttr/getAttr, on ne force pas le node à recalculer ses valeurs, résultat, c'est comme si vous aviez évalué une particule et que toute les autres se récupères les même valeurs que la première, ça donne des choses assez drôle (qui peuvent être intéressantes dans certains cas).</p></blockquote>
<p>Remettez-vous à la frame 1 pour virer vos particules puis calculez quelques images de dynamique:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya019.png" alt="oriente_particle_maya019.png" style="display:block; margin:0 auto;" title="oriente_particle_maya019.png, sept. 2010" height="571" width="549" /></p>
<p>Et voila le travail! :banaeyouhou:</p>
<p>Voici <a href="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_scene002.ma">->la scène Maya<-</a> (bouton droit de souris, save as...).</p>
<p>A partir de là vous pouvez faire pas mal de choses.</p>
<p>Ici nous orientons les particules à la création, ce qui suppose qu'elles ne bougent pas.</p>
<p>Si c'est le cas, il faudra recréer une expression "Runtime before dynamics" qui réoriente les particules.</p>
<p>Je vous invite à regarder <a href="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/particle_normal_alignment.mb.zip" hreflang="en">la scène de YouDaftPunk </a> qui va encore plus loin que moi (pas difficile cela dis) en animant des particules le long de la surface, de manière aléatoire.</p>
<p>Si vous vous en êtes sortis avec ce tuto, vous devriez pouvoir vous débrouiller avec sa scène. :sourit:</p>
<center>:longBar:</center>
<h5>Conclusion</h5>
<p>J'espère que ce petit tuto vous aura appris quelque chose et/ou dépanné. Personnellement, c'est vraiment le genre de trucs qui se fait en trois cliques dans les autres softs 3D mais qui demande pas mal de technique dans Maya (comme d'hab, il faut bien justifier son salaire! :aupoil: ).</p>
<p>Si vous avez des questions, que je n'ai pas été assez clair ou qu'il manque une information, n'hésitez pas à laisser un commentaire.</p>
<p>A bientôt!</p>
<p>Dorian</p>
<center><i>:marioCours:</i></center>
<center>:longBar:</center>
<h5>Méthode alternative: Le Goal World Normal</h5>
<p>EDIT: Suite au commentaire de <a href="http://www.joss-vfx.com/" hreflang="fr">Joss</a>, je vous propose une méthode alternative qui est beaucoup plus rapide. :siffle:</p>
<p>Dans un premier temps, sélectionner votre géométrie:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0001.png" alt="oriente_particle_maya_second0001.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0001.png, sept. 2010" height="466" width="437" /></p>
<p>Faite un "Emit from Object" mais en sélectionnez les options:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0002.png" alt="oriente_particle_maya_second0002.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0002.png, sept. 2010" height="121" width="215" /></p>
<p>Passer "l'Emitter type" en Surface:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0003.png" alt="oriente_particle_maya_second0003.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0003.png, sept. 2010" height="104" width="307" /></p>
<p>Et surtout cochez "Need parent UV (NURBS)":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0004.png" alt="oriente_particle_maya_second0004.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0004.png, sept. 2010" height="58" width="235" /></p>
<p>Maintenant nous allons convertir la surface émettrice de particule en "Goal".</p>
<p>Sélectionnez le système de particule:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0005.png" alt="oriente_particle_maya_second0005.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0005.png, sept. 2010" height="59" width="197" /></p>
<p>Puis votre surface:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0006.png" alt="oriente_particle_maya_second0006.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0006.png, sept. 2010" height="104" width="248" /></p>
<p>Et passer tout ça en "Goal":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0007.png" alt="oriente_particle_maya_second0007.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0007.png, sept. 2010" height="212" width="205" /></p>
<p>Maintenant allez dans votre particleShape:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0008.png" alt="oriente_particle_maya_second0008.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0008.png, sept. 2010" height="42" width="125" /></p>
<p>Et créez deux attributs (goalU et goalV) de type Flaot Per Particule:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0009.png" alt="oriente_particle_maya_second0009.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0009.png, sept. 2010" height="77" width="203" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0010.png" alt="oriente_particle_maya_second0010.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0010.png, sept. 2010" height="258" width="416" /></p>
<p>Une fois cela fait, nous allons créer une expression de création de particule:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0011.png" alt="oriente_particle_maya_second0011.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0011.png, sept. 2010" height="67" width="311" /></p>
<p>Et copiez juste ça:</p>
<pre class="mel mel">goalU <span style="color: #339933;">=</span> parentU<span style="color: #339933;">;</span>
goalV <span style="color: #339933;">=</span> parentV<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0012.png" alt="oriente_particle_maya_second0012.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0012.png, sept. 2010" height="240" width="612" /></p>
<p>Puis cliquez sur création:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0013.png" alt="oriente_particle_maya_second0013.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0013.png, sept. 2010" height="78" width="180" /></p>
<p>Maintenant allez dans l'onglet "Goal Weights and Objects":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0014.png" alt="oriente_particle_maya_second0014.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0014.png, sept. 2010" height="42" width="267" /></p>
<p>Et cliquez sur "Create Goal World Normal 0 PP"</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0015.png" alt="oriente_particle_maya_second0015.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0015.png, sept. 2010" height="36" width="369" /></p>
<p>Les particules ont donc maintenant leurs valeurs de Goal, qui sont en fait les valeurs d'UV de la surface. :sourit:</p>
<p>Pour rendre le tout plus visible nous allons créer notre instancer. :joue:</p>
<p>Sélectionnez la géométrie à instancier:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0016.png" alt="oriente_particle_maya_second0016.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0016.png, sept. 2010" height="61" width="57" /></p>
<p>Puis sélectionnez votre "particle system":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0017.png" alt="oriente_particle_maya_second0017.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0017.png, sept. 2010" height="59" width="197" /></p>
<p>Créez l'instancer:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0018.png" alt="oriente_particle_maya_second0018.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0018.png, sept. 2010" height="79" width="210" /></p>
<p>Et pour finir, dans l'onglet "Instancer (Geometry Replacement)", mettez la "Aim Direction" sur "goalWorldNormal0PP":</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0019.png" alt="oriente_particle_maya_second0019.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0019.png, sept. 2010" height="207" width="235" /></p>
<p>Bougez votre timeline pour générer des particules:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0020.png" alt="oriente_particle_maya_second0020.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0020.png, sept. 2010" height="566" width="503" /></p>
<center><i>Tadaa!</i></center>
<p>Comme vous pouvez le constater, vous voyez que les instancers ne sont pas bien alignés. :mechantCrash:</p>
<p>C'est dû au fait que goalWorldMesh utilise l'axe X comme axe de référence. Mais il suffit de rajouter attribut (aimUp) et de l'orienter sur l'axe Y (qui est l'axe de ma flèche):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0021.png" alt="oriente_particle_maya_second0021.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0021.png, sept. 2010" height="78" width="168" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0022.png" alt="oriente_particle_maya_second0022.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0022.png, sept. 2010" height="263" width="336" /></p>
<p>Entrez dans l'expression de création de la particule:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0023.png" alt="oriente_particle_maya_second0023.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0023.png, sept. 2010" height="57" width="312" /></p>
<p>Ajoutez:</p>
<pre class="mel mel">particleShape1.<span style="color: #202020;">aimUp</span> <span style="color: #339933;">=</span> <span style="color: #339933;"><<</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span><span style="color: #0000dd;">0</span><span style="color: #339933;">>>;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0024.png" alt="oriente_particle_maya_second0024.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0024.png, sept. 2010" height="115" width="369" /></p>
<p>Validez puis mettez "Aim Axis" sur votre nouvel attribut: aimUp</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0025.png" alt="oriente_particle_maya_second0025.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0025.png, sept. 2010" height="265" width="238" /></p>
<p>Et voila le travail:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_second0026.png" alt="oriente_particle_maya_second0026.png" style="display:block; margin:0 auto;" title="oriente_particle_maya_second0026.png, sept. 2010" height="475" width="448" /></p>
<p>Vous remarquerez aussi que ça va beaucoup plus vite! :aupoil:</p>
<p>Voici <a href="https://www.fevrierdorian.com/blog/public/billets/2010_09_13_oriente_particle_maya/oriente_particle_maya_scene003.ma">->la scène Maya<-</a> (bouton droit de souris, save as...).</p>
<p>Un grand merci à <a href="http://www.joss-vfx.com/" hreflang="fr">Joss</a> pour cette méthode!</p>
<p>A bientôt!</p>
<p>Dorian</p>CgFX - Des shaders temps réel dans le viewport Maya! - Part 5urn:md5:d2fcde21739e21d0a3556ff57af1ffcb2010-08-28T16:21:00+02:002013-07-26T18:13:38+02:00NarannScript et codebumpcgfxfrmayanormalshader<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_tn.png" alt="cgfx_part5_tn.png" style="float:left; margin: 0 1em 1em 0;" title="cgfx_part5_tn.png, août 2010" height="150" width="150" />La voila enfin, celle que vous attendiez depuis le début, celle que je vous promet depuis maintenant quatre chapitres, celle qui finit en beauté, celle qui va filer des migraines à tout ceux qui n'ont pas fait math sup' math spé' (moi au passage :baffed: ): Le bump mapping.</p>
<p>Autant le dire tout de suite, il va falloir s'accrocher un peut pour visualiser comment ça marche. Vous allez voir qu'une fois le "truc" pigé, les choses vont vous sembler plus simple, et vous pourrez passer à d'autres choses, plus compliquées, tout seul, comme un grand! :sourit: . Allez, on y va pour cette ultime partie!</p> <p>Bon, il n'est pas vraiment utile que je remette une image de ce qu'on va obtenir... Si? Bon, ok... :youplaBoum:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_05_01_CgFX_part2/cgfx_part2_002.png" alt="cgfx_part2_002.png" style="display:block; margin:0 auto;" title="cgfx_part2_002.png, mai 2010" height="381" width="381" /></p>
<p>Fini de rire, on attaque. :grenadelauncher:</p>
<center>:longBar:</center>
<h5>Sommaire:</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#prerequis">Prérequis</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#theorie">Théorie</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#les_attributs">Les attributs</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#les_structures">Les structures</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#le_vertex_shader">Le vertex shader</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#le_pixel_shader">Le pixel shader</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#resultat">Resultat!</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#arnaque_bump_pourrave">Hey mais c'est l'arnaque ton truc! C'est quoi ce bump pourrave là?!</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#ajout_attribut_reverse_color">Ajout des attributs</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/28/CgFX-Des-shaders-temps-r%C3%A9el-dans-le-viewport-Maya%21-Part-5#conclusion">Conclusion</a></li>
</ul>
<center>:longBar:</center>
<h5>Prérequis <a name="prerequis"></a></h5>
<p>Si vous avez bien suivi le tuto, vous devriez avoir ceci:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_part4_002.png" alt="cgfx_part4_002.png" style="display:block; margin:0 auto;" title="cgfx_part4_002.png, août 2010" height="327" width="349" /></p>
<p>Vous pouvez télécharger le shader ici <a href="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_tuto_007.7z">->cgfx_tuto_007.7z<-</a></p>
<blockquote><p>Note: Si vous êtes équipé d'une carte ATI, je vous invite fortement à lire <a href="https://www.fevrierdorian.com/blog/post/2010/04/25/CgFX-Faire-du-Bump-mapping-avec-les-cartes-ATI-Radeon">ce billet</a></p></blockquote>
<p>Maintenant, le bump mapping!</p>
<center>:longBar:</center>
<h5>Théorie (anagain, anagainanagain!) :mitraille: <a name="theorie"></a></h5>
<p>Une fois n'est pas coutume, je ne trouve pas <a href="https://secure.wikimedia.org/wikipedia/fr/wiki/Placage_de_relief" hreflang="fr">la définition de wikipedia</a> très clair (un peu trop "large").</p>
<p>Dans notre cas, le bump mapping va consister à appliquer une texture de normal (normal map) sur un objet. Cette texture servira à faire varier les normales, au niveau du pixel. Je vais expliquer tout ça en détail.</p>
<p>La texture que nous allons utiliser est livrée avec le shader d'exemple de Nvidia de <a href="https://www.fevrierdorian.com/blog/post/2010/04/26/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-part-1" hreflang="fr">la partie 1</a> et s'appelle: Default_bump_normal.dds:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/Default_bump_normal.png" alt="Default_bump_normal.png" style="display:block; margin:0 auto;" title="Default_bump_normal.png, août 2010" height="256" width="512" /></p>
<p>Mais tout d'abords, quoi-qu'-c'est-qu'-ce-truc-de-normal-fack? :nevroz:</p>
<p>Comme vous le savez, chaque vertex de notre objet a une normale. Pour afficher celles de votre objet, allez dans Display/Polygons/Vertex Normals:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_001.png" alt="cgfx_part5_001.png" style="display:block; margin:0 auto;" title="cgfx_part5_001.png, août 2010" height="459" width="342" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_002.png" alt="cgfx_part5_002.png" style="display:block; margin:0 auto;" title="cgfx_part5_002.png, août 2010" height="294" width="293" /></p>
<p>Vous pouvez modifier leurs tailles via:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_003.png" alt="cgfx_part5_003.png" style="display:block; margin:0 auto;" title="cgfx_part5_003.png, août 2010" height="90" width="207" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_004.png" alt="cgfx_part5_004.png" style="display:block; margin:0 auto;" title="cgfx_part5_004.png, août 2010" height="104" width="287" /></p>
<p>Ces "traits" verts que vous voyez sont en fait des vecteurs XYZ qui indiquent la direction dans laquelle est placée les vertex. Si vous avez compris ça, c'est déjà pas mal. :sourit:</p>
<p>La question que l'on se pose naturellement c'est: "Ok, je connais la direction de chaque vertex, mais comment connaitre la direction de ce qu'il y a "entre" les vertex?</p>
<p>Et bien on interpole entre les vertex, tout simplement! :siffle:</p>
<p>Là, on rentre dans des problématiques de temps réel "pur". En effet, Pour récupérer la normale au niveau de chaque pixel, nous allons nous servir du... Vertex shader.</p>
<p>Il faut que vous sachiez qu'entre le Vertex shader et le Pixel shader se produit une étape: La rasterisation. Cette étape comporte pas mal de choses (dont on ne parlera pas ici) mais surtout, cette étape interpole, au niveau de chaque pixel, les valeurs stockées dans le Vertex shader (que ce soit de la couleur, des positions, des normales...)</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_005.png" alt="cgfx_part5_005.png" style="display:block; margin:0 auto;" title="cgfx_part5_005.png, août 2010" height="517" width="512" /></p>
Pardonnez mon minable exemple mais c'est pour visualiser...
<p>Ici, toute la sphère est bleue. Seul un vertex est rouge. Mais les valeurs numériques qui définissent les couleurs de chaque vertex (RGB) sont interpolées le long des arêtes, entre chaque vertex qui l'entoure.</p>
<p>Cas identique avec deux couleurs:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_006.png" alt="cgfx_part5_006.png" style="display:block; margin:0 auto;" title="cgfx_part5_006.png, août 2010" height="529" width="513" /></p>
<p>Pour la normale du vertex, le principe est le même. Chaque valeur de la normal (vecteur XYZ) est interpolée le long de l'arête vers la normal suivante.</p>
<p>Une fois la rasterisation terminée, vous avez des valeurs de normal (XYZ) au niveau de chaque pixel.</p>
<p>Ayez, vous commencez à saisir le truc? :sourit:</p>
<p>Et oui, c'est maintenant qu'intervient la normal map. Une normal map, c'est, comme son nom l'indique, une texture qui stocke des vecteurs de normal. Chaque pixel (couleur RGB) correspond à un axe (XYZ), ce qui donne cette teinte si particulière aux normal maps.</p>
<p>Un vecteur 0,0,0 est équivalent en RGB à 128, 128, 128. A partir de là, une normale "standard" (0,0,1, Z étant face la profondeur de la map) est 128, 128, 255. Soit un espèce de mauve dégueulasse. :pasClasse:</p>
<p>Bon, à partir d'ici vous avez compris le truc. Pour résumer, il va falloir additionner les valeurs XYZ de la normale du pixel avec la normal de la normal map.</p>
<center>:longBar:</center>
<h5>Les attributs <a name="les_attributs"></a></h5>
<p>Comme d'hab', on créé les attributs.</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//Bump</span>
<span style="color: #993333;">float</span> bumpPower
<span style="color: #339933;"><</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Bump Strength"</span><span style="color: #339933;">;</span>
string UIWidget <span style="color: #339933;">=</span> <span style="color: #ff0000;">"slider"</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIMin <span style="color: #339933;">=</span> <span style="color:#800080;">0.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIMax <span style="color: #339933;">=</span> <span style="color:#800080;">3.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIStep <span style="color: #339933;">=</span> <span style="color:#800080;">0.001</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color:#800080;">1.0</span><span style="color: #339933;">;</span></pre>
<p>Puis on créé la texture de bump.</p>
<pre class="c c">texture normalTexture
<span style="color: #339933;"><</span>
string ResourceName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"default_bump_normal.dds"</span><span style="color: #339933;">;</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Normal-Map Texture"</span><span style="color: #339933;">;</span>
string ResourceType <span style="color: #339933;">=</span> <span style="color: #ff0000;">"2D"</span><span style="color: #339933;">;</span>
<span style="color: #339933;">>;</span>
sampler2D normalSampler <span style="color: #339933;">=</span> sampler_state
<span style="color: #009900;">{</span>
Texture <span style="color: #339933;">=</span> <span style="color: #339933;"><</span>normalTexture<span style="color: #339933;">>;</span>
MinFilter <span style="color: #339933;">=</span> LinearMipMapLinear<span style="color: #339933;">;</span>
MagFilter <span style="color: #339933;">=</span> Linear<span style="color: #339933;">;</span>
WrapS <span style="color: #339933;">=</span> Repeat<span style="color: #339933;">;</span>
WrapT <span style="color: #339933;">=</span> Repeat<span style="color: #339933;">;</span>
MaxAnisotropy <span style="color: #339933;">=</span> <span style="color:#800080;">16.0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span><span style="color: #339933;">;</span></pre>
<center>:longBar:</center>
<h5>Les structures <a name="les_structures"></a></h5>
<p>Vous devez aussi modifier la structure d'entrée pour stocker les valeurs de tangents et de "binormals" (je ne sais pas exactement ce que c'est... :jdicajdirien: ).</p>
<pre class="c c"><span style="color: #993333;">struct</span> vIN
<span style="color: #009900;">{</span>
float3 Position <span style="color: #339933;">:</span> POSITION<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// La position du vertex en entrée</span>
float4 Normal <span style="color: #339933;">:</span> NORMAL<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// La normal du vertex en entrée</span>
float4 UV <span style="color: #339933;">:</span> TEXCOORD0<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// un set d'uv</span>
float4 Tangent <span style="color: #339933;">:</span> TEXCOORD1<span style="color: #339933;">;</span>
float4 Binormal <span style="color: #339933;">:</span> TEXCOORD2<span style="color: #339933;">;</span>
<span style="color: #009900;">}</span><span style="color: #339933;">;</span></pre>
<center>:longBar:</center>
<h5>Le vertex shader <a name="le_vertex_shader"></a></h5>
<p>On multiplie les matrices de tangent et binormal par la "world view matrice inversé" (qui, si je me rappelle, projette ces informations sur l'espace écran).</p>
<pre class="c c">OUT.<span style="color: #202020;">WorldTangent</span> <span style="color: #339933;">=</span> mul<span style="color: #009900;">(</span>wvMatrixIT<span style="color: #339933;">,</span> IN.<span style="color: #202020;">Tangent</span><span style="color: #009900;">)</span>.<span style="color: #202020;">xyz</span><span style="color: #339933;">;</span>
OUT.<span style="color: #202020;">WorldBinormal</span> <span style="color: #339933;">=</span> mul<span style="color: #009900;">(</span>wvMatrixIT<span style="color: #339933;">,</span> IN.<span style="color: #202020;">Binormal</span><span style="color: #009900;">)</span>.<span style="color: #202020;">xyz</span><span style="color: #339933;">;</span></pre>
<p>Ça, c'est fait! :hehe:</p>
<center>:longBar:</center>
<h5>Le pixel shader <a name="le_pixel_shader"></a></h5>
<p>C'est là que c'est le gros morceau...</p>
<p>On commence simplement par une normalisation en bon et due forme des vecteurs:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">// On normalise les vecteurs</span>
float3 Nn <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>IN.<span style="color: #202020;">WorldNormal</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
float3 pL0n <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>IN.<span style="color: #202020;">PointLight0Vec</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
float3 pL1n <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>IN.<span style="color: #202020;">PointLight1Vec</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
float3 Vn <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>IN.<span style="color: #202020;">WorldView</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
float3 Tn <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>IN.<span style="color: #202020;">WorldTangent</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
float3 Bn <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>IN.<span style="color: #202020;">WorldBinormal</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>La on a un gros paquet de vecteurs normalisés avec lesquels on va faire plein de cochonnerie... :baffed:</p>
<p>Comme dis plus haut, on va modifier la normale (le vecteur Nn) au pixel, grâce a la texture de normal map. J'ai trouvé plusieurs exemples sur le net mais ils étaient tous différents... J'ai donc pris celui qui me semblait le plus simple et qui marchait (chez moi :seSentCon: )...</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">// Modifie la normale avec le bump</span>
float3 bump <span style="color: #339933;">=</span> tex2D<span style="color: #009900;">(</span>normalSampler<span style="color: #339933;">,</span> IN.<span style="color: #202020;">UV</span><span style="color: #009900;">)</span>.<span style="color: #202020;">rgb</span><span style="color: #339933;">;</span>
bump <span style="color: #339933;">-=</span> float3<span style="color: #009900;">(</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//On décale de 0.5 les valeurs de normal</span>
half3 wNormal<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">=</span> bump.<span style="color: #202020;">x</span> <span style="color: #339933;">*</span> Tn<span style="color: #339933;">*</span>bumpPower<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">+=</span> bump.<span style="color: #202020;">y</span> <span style="color: #339933;">*</span> Bn<span style="color: #339933;">*</span>bumpPower<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">+=</span> bump.<span style="color: #202020;">z</span> <span style="color: #339933;">*</span> Nn<span style="color: #339933;">;</span>
Nn <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>wNormal<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>La première ligne:</p>
<pre class="c c">float3 bump <span style="color: #339933;">=</span> tex2D<span style="color: #009900;">(</span>normalSampler<span style="color: #339933;">,</span> IN.<span style="color: #202020;">UV</span><span style="color: #009900;">)</span>.<span style="color: #202020;">rgb</span><span style="color: #339933;">;</span></pre>
<p>On récupère la couleur de la texture au pixel.</p>
<pre class="c c">bump <span style="color: #339933;">-=</span> float3<span style="color: #009900;">(</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">//On décale de 0.5 les valeurs de normal</span></pre>
<p>Ensuite on décale les valeurs de normal de 0.5 (je ne sais pas trop pourquoi en fait... Surement car dans une normal map, un vecteur de 0 équivaut à 128,128,128).</p>
<pre class="c c">half3 wNormal<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">=</span> bump.<span style="color: #202020;">x</span> <span style="color: #339933;">*</span> Tn<span style="color: #339933;">*</span>bumpPower<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">+=</span> bump.<span style="color: #202020;">y</span> <span style="color: #339933;">*</span> Bn<span style="color: #339933;">*</span>bumpPower<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">+=</span> bump.<span style="color: #202020;">z</span> <span style="color: #339933;">*</span> Nn<span style="color: #339933;">;</span></pre>
<p>Et la c'est le pavé. J'ai pas mal galéré pour le "sortir". :pasClasse:</p>
<p>Bon, on créer un vecteur qui est la normal projetée sur l'écran (wNormal). On multiplie la composante rouge (X) par la normal de la tangente. Idem pour Y et la binormal et la normal de base (Nn). En revanche, on ne multiplie que le coefficient de bump que par la composante X et Y. Je suppose que la logique derrière est qu'il n'y aurait pas vraiment de sens à multiplier la profondeur (Z) par le coefficient de bump.</p>
<center>:longBar:</center>
<h5>Resultat! :marioCours: <a name="resultat"></a></h5>
<p>Si vous avez bien tout suivi, vous devriez obtenir quelque chose comme ça:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_007.png" alt="cgfx_part5_007.png" style="display:block; margin:0 auto;" title="cgfx_part5_007.png, août 2010" height="469" width="481" /></p>
<p>Si ce n'est pas le cas et que vous ne comprenez pas pourquoi, n'hésitez pas à me le dire, j'ai peut-être oublié quelque chose... :siffle:</p>
<p>Vous pouvez télécharger le shader ici: <a href="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_tuto_008.7z">->cgfx_tuto_008.7z<-</a></p>
<center>:longBar:</center>
<h5>Hey mais c'est l'arnaque ton truc! C'est quoi ce bump pourrave là?! <a name="arnaque_bump_pourrave"></a></h5>
<p>Bien observé! :sourit:</p>
<p>Vous aurez peut être remarqué en bougeant les pointLights (parce que bien sûr, vous avez bidouillé un peu hein? Vous attendez pas que ça vous tombe tout cuit dans le bec non plus! Les jeunes aujourd'hui... :papi: ), que le bump réagit bizarrement:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_008.png" alt="cgfx_part5_008.png" style="display:block; margin:0 auto;" title="cgfx_part5_008.png, août 2010" height="435" width="441" /></p>
<center><i>Ici j'ai mis les pointLight juste au dessus, on voit bien que le bump réagit mal.</i></center>
<p>C'est exact! En fait, il peut arriver que la composante rouge ou verte de votre normal map soit inversée (salaud!). Pour inverser la composante rouge et/ou verte, nous allons créer un attribut qui nous permettra de switcher "l'orientation" des couleurs Rouge et Verte.</p>
<center>:longBar:</center>
<h5>Ajout des attributs <a name="ajout_attribut_reverse_color"></a></h5>
<p>Et bien oui! On ajoute encore des attributs:</p>
<pre class="c c">bool bReverseRedNormal
<span style="color: #339933;"><</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Reverse Red Normal"</span><span style="color: #339933;">;</span>
string UIWidget <span style="color: #339933;">=</span> <span style="color: #ff0000;">"RadioButton"</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">false</span><span style="color: #339933;">;</span>
bool bReverseGreenNormal
<span style="color: #339933;"><</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Reverse Green Normal"</span><span style="color: #339933;">;</span>
string UIWidget <span style="color: #339933;">=</span> <span style="color: #ff0000;">"RadioButton"</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">false</span><span style="color: #339933;">;</span></pre>
<p>Et dans la partie "modification de la normale":</p>
<pre class="c c">half3 wNormal<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">=</span> bump.<span style="color: #202020;">x</span> <span style="color: #339933;">*</span> Tn <span style="color: #339933;">*</span> <span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #339933;">+</span><span style="color: #009900;">(</span><span style="color: #339933;">-</span><span style="color: #0000dd;">2</span><span style="color: #339933;">*</span>bReverseRedNormal<span style="color: #009900;">)</span><span style="color: #009900;">)</span> <span style="color: #339933;">*</span> bumpPower<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">+=</span> bump.<span style="color: #202020;">y</span> <span style="color: #339933;">*</span> Bn <span style="color: #339933;">*</span> <span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #339933;">+</span><span style="color: #009900;">(</span><span style="color: #339933;">-</span><span style="color: #0000dd;">2</span><span style="color: #339933;">*</span>bReverseGreenNormal<span style="color: #009900;">)</span><span style="color: #009900;">)</span> <span style="color: #339933;">*</span> bumpPower<span style="color: #339933;">;</span>
wNormal <span style="color: #339933;">+=</span> bump.<span style="color: #202020;">z</span> <span style="color: #339933;">*</span> Nn<span style="color: #339933;">;</span>
Nn <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>wNormal<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>Ne paniquez pas, vous allez voir que c'est en fait assez simple à comprendre. :sourit:</p>
<p>Ce que je veux c'est que:</p>
<ul>
<li>Quand bReverseRedNormal est coché (et donc, quand il est à "1"), j'ai un multiplicateur de "1".</li>
<li>Quand bReverseRedNormal est décoché (valeur à "0"), j'ai un multiplicateur de "-1".</li>
</ul>
<pre class="c c">f<span style="color: #009900;">(</span>x<span style="color: #009900;">)</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">+</span><span style="color: #009900;">(</span><span style="color: #339933;">-</span><span style="color: #0000dd;">2</span><span style="color: #339933;">*</span>x<span style="color: #009900;">)</span>
f<span style="color: #009900;">(</span><span style="color: #0000dd;">0</span><span style="color: #009900;">)</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">+</span><span style="color: #009900;">(</span><span style="color: #339933;">-</span><span style="color: #0000dd;">2</span><span style="color: #339933;">*</span><span style="color: #0000dd;">0</span><span style="color: #009900;">)</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span>
f<span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #009900;">)</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">+</span><span style="color: #009900;">(</span><span style="color: #339933;">-</span><span style="color: #0000dd;">2</span><span style="color: #339933;">*</span><span style="color: #0000dd;">1</span><span style="color: #009900;">)</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">-</span><span style="color: #0000dd;">2</span> <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span></pre>
<p>Cette petite gymnastique évite de jouer avec les "if" (quoique, ça serait peut être plus rapide... Mais pour une fois que je fais des maths). Il y avait surement moyen de faire plus simple et plus optimisé mais pour tout dire, je n'ai pas cherché (mais je vous invite à me corriger! :baffed: )</p>
<p>Grâce à ce "trick", nous pouvons contrôler l'orientation des composantes Rouge et Verte de notre normal map:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_009.png" alt="cgfx_part5_009.png" style="display:block; margin:0 auto;" title="cgfx_part5_009.png, août 2010" height="72" width="356" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_part5_010.png" alt="cgfx_part5_010.png" style="display:block; margin:0 auto;" title="cgfx_part5_010.png, août 2010" height="402" width="393" /></p>
<p>Vous pouvez télécharger le shader ici: <a href="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part5/cgfx_tuto_009.7z">->cgfx_tuto_009.7z<-</a></p>
<center>:longBar:</center>
<h5>Conclusion <a name="conclusion"></a></h5>
<p>Et voila, ce tuto sur les shaders CgFX est enfin terminé. J'espère avoir été assez clair. Que vous avez plus ou moins compris des choses et que ça vous sera utile durant vos productions. :jdicajdirien:</p>
<p>N'hésitez pas à me laisser un commentaire si j'ai foiré quelque chose (code, fichiers) ou si vous trouvez d'autres cas d'utilisation de shader CgFX (ou GLSL, HLSL, etc...) appliqué à notre métier.</p>
<p>A très bientôt pour de nouvelles aventures!</p>
<center>:marioCours:</center>