Diffuser Shader - Normalen-Transformation?

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Diffuser Shader - Normalen-Transformation?

Beitrag von Eisflamme »

Hi,

ich habe wieder einmal ein - in euren Augen sicherlich triviales - Problem. Ich wollte jetzt Mal einen Lichtshader (zunächst per Vertex) erstellen, der einfach nur diffuses Licht nutzt. Dafür muss man ja einfach dot von dem Normalenvektor und der Lichtrichtung (direktional will ich) als Lichtintensität berechnen.

Laut einem Tutorial ( http://rbwhitaker.wikidot.com/diffuse-lighting-shader ) muss man den Normalenvektor vor der Berechnung jedoch mit der inversen (und transformierten) Worldmatrix multiplizieren. Das verstehe ich nicht ganz: Das bedeutet doch, dass der Normalenvektor sich im Worldspace befindet und dann zurück in den Objectspace gebracht wird

Aber wieso ist der Normalenvektor im Worldspace?
Sagen wir, wir gehen von einem Vertex direkt auf dem Kopf eines Modells auf, sodass der Normalenvektor (0,1,0) ist. Das ist für mich doch ein ganz eigener Space, nicht Mal Objectspace, die Position ist da schließlich nicht drin, es ist ja sowieso eine Richtung und gehört daher für mich nicht in die Überlegung von World/Objectspace. Wenn das Modell jetzt 100 nach rechtes bewegt wird (Worldspace), so ist der Normalenvektor doch trotzdem bei (0,1,0), wird also gar nicht beeinflusst von der Worldmatrix. Eine Rotation wäre schon wichtiger, weil sie das Modell umdreht, dann muss sich auch der Vektor ändern. Aber dann ist doch der Normalenvektor(Welt) != Normalenvektor(Object) * Worldmatrix, also macht doch auch die inverse Multiplikation keinen Sinn?

Und wieso Normal*WorldInverseTranspose? Ist das nicht gleich WorldInverse*Normal?

Wäre prima, wenn mir Mal wieder aus meiner Unwissenheit heraushelfen kann...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4852
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von Schrompf »

Bei der WorldInverseTranspose geht es um die Skalierungen. Es ist ja eben nicht die World -> Object Transformation, weil da ja zwei Invertierungen drin sind, nicht nur eine! Es ist einmal die Inverse der Matrix, und dann nochmal transponiert. Transponieren ist so eine Art "Inverse" für Matrizen, die nur eine Rotation darstellen. Es gibt dazu auch prima Zeichnungen im Netz, was genau da mit den Normalen passiert, aber ich habe gerade nicht die Zeit, das rauszusuchen. Damit Du erstmal weiterkommst, hier meine Empfehlung: wenn Du Deine Objekte nur unskaliert darstellst oder nur einheitlich alle drei Achsen gleichermaßen skalierst, kannst Du diesen Schritt knicken und die Normalen stattdessen mit der "normalen" ObjektZuWelt-Matrix transformieren, natürlich ohne den Translationsteil.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von Eisflamme »

Ich skaliere einiges. Aber die Hinweise helfen mir auf alle Fälle weiter, ich hätte nie gedacht, dass Normalenvektoren einen Spezialfall darstellen. :) Da dem so zu sein scheint, werde ich brav googlen und bei weiteren Fragen diese stellen, dankeschön!
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von Artificial Mind »

Das Skalieren macht nur dann ein Problem wenn du die einzelnen Achsen unterschiedlich skalierst. Wenn du nur gleichmäßig skalierst reicht es, die Normale nach dem transformieren wieder zu normieren.
klickverbot
Establishment
Beiträge: 191
Registriert: 01.03.2009, 19:22
Echter Name: David N.

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von klickverbot »

Ich habe vor ein paar Jahren mal eine kurze mathematische Herleitung dazu niedergeschrieben: http://klickverbot.at/science/3d-mathem ... matics.pdf, Seite 25 f.
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von eXile »

In der Tat, man muss mit der invertiert-transponierten Matrix multiplizieren!

Betrachten wir doch mal, was wir haben wollen. Sei \($\mathbf n$\) unser Normalenvektor für einen beliebigen Vertex \($\mathbf x$\) des Meshes. Was heißt überhaupt „Normalenvektor“? Nunja, es gibt ja immer zwei Arten von Normalenvektoren: Normalenvektor pro Face, und Normalenvektor pro Vertex. Die nachfolgende Diskussion ist für beide Arten gleich; ich nehme den Normalenvektor pro Vertex (weil man den häufiger antrifft).

Erst einmal ganz einfach: Angenommen wir transformieren unser Mesh mit einer Matrix \($\mathbf M$\). Dann gilt für den transformierten Vertex \($\mathbf x'$\) logischerweise:
\($$\mathbf x' = \mathbf{Mx}$$\)Nun zur Transformation des Normalenvektors: Die charakteristische Eigenschaft des pro-Vertex-Normalenvektors ist ja, dass der Winkel zwischen dem Normalenvektor und den benachbarten Vertices gleich bleibt. Wir sind immer noch auf einem Punkt \($\mathbf x$\) mit Normalenvektor \($\mathbf n$\), und haben die angrenzenden Vertices \($\mathbf x_1, \mathbf x_2, ..., \mathbf x_n$\). Der Normalenvektor \($\mathbf n'$\) nach der Transformation soll gerade die genannte charakteristische Eigenschaft erfüllen, d.h. es soll gelten:
\($$\mathbf n^* (\mathbf x - \mathbf x_i) = \mathbf n'^* (\mathbf M \mathbf x - \mathbf M \mathbf x_i) \quad \forall i \in [1:n]$$\)(Kurzer Einschub: Das \($\!{\ }^*$\) ist bei mir immer der Operator zum Transponieren, bzw. damit ist dann \($\mathbf a^* \mathbf b$\) das Standardskalarprodukt.)

Falls nun die Frage auftauchen sollte, warum zum Teufel ich hier \($\mathbf x - \mathbf x_i$\) geschrieben habe: Wir brauchen die relativen Koordinaten. Siehe dafür auch folgendes Bild:

Bild

Nun habe ich bereits alles beschrieben; nur haben wir noch immer nicht unser Ergebnis, d.h. wir wollen endlich wissen, wie \($\mathbf n'$\) aussieht!

Aber das ist ganz einfach. Wir betrachten obige Gleichung, und schubsen mal ein paar Variablen umher:
\($$\begin{align}\mathbf n^* (\mathbf x - \mathbf x_i) &= \mathbf n^* \mathbf \cdot \mathbf I \cdot (\mathbf x - \mathbf x_i) \quad \text{(} \mathbf I \text{ ist die Einheitsmatrix)}\\
&= \mathbf n^* \cdot \mathbf M^{-1} \mathbf M \cdot (\mathbf x - \mathbf x_i) \\
&= \big(\mathbf n^* \mathbf M^{-1}\big) \cdot \big(\mathbf M (\mathbf x - \mathbf x_i)\big) \\
&= \big((\mathbf M^{-1})^* \mathbf n\big)^* \cdot \big(\mathbf M (\mathbf x - \mathbf x_i)\big) \\
&= \big(\underbrace{(\mathbf M^{-1})^* \mathbf n}_{\mathbf n'}\big)^* \cdot \big(\mathbf M \mathbf x - \mathbf M \mathbf x_i\big) \quad \forall i \in [1:n] \\
\end{align}$$\)
Damit haben wir tatsächlich \($\mathbf n' = (\mathbf{M}^{-1})^* \mathbf n$\), d.h. die Normalenvektoren müssen mit der invertiert-transponierten Matrix transformiert werden!

(Noch einen Hinweis zur mathematischen Korrektheit: Der Vektor \($\mathbf n'$\) ist tatsächlich durch die letzte Gleichung eindeutig bestimmt worden, weil \($\mathbf x - \mathbf x_i$\) ein Erzeugendensystem des \($\mathbb{R}^3$\) ist – damit bleiben für lineare Transformationen keine anderen Möglichkeiten mehr.)

Pro-Solution:
In Graßmann/Clifford-Algebren stellt das innere Produkt, d.h. das \($\!{\ }^*$\), einen ziemlichen Spezialfall dar. Es hat keine schönen Eigenschaften, als das es mit dem äußeren Produkt eine Isomorphie bilden würde, irgendwie kommutieren würde, usw. – ist alles nicht drin. Das Wegde-Produkt isomorphiert zwar hübsch für eine lineare Funktion \($f$\):
\($$f(a \wedge b) = f(a) \wedge f(b)$$\)aber für das Kreuzprodukt (welches sich über \($\mathbf a \times \mathbf b = -(\mathbf a \wedge \mathbf b) \cdot (\mathbf e_1 \wedge \mathbf e_2 \wedge \mathbf e_3)$\) als böses inneres Produkt schreiben lässt) gilt das nicht, sondern es gilt:
\($$f(a \times b) = f^{*^{-1}}(\mathbf a) \times f^{*^{-1}}(\mathbf b) \cdot \operatorname{det}(f)$$\)Dabei ignorieren wir \($\operatorname{det}(f)$\), weil wir unseren Normalenvektor immer normalisiert haben wollen. Es muss also tatsächlich die invertiert-transponierte Funktion genommen werden.
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von Eisflamme »

Wow, vielen Dank für das ganze Material. Ich komme leider nicht so viel wie ich gerne würde dazu mir das alles genauer durchzudenken (ich muss das immer 10 Mal tun bis ich's verstehe), jedoch verstehe ich schon deutlich mehr.

eXile:
Etwas verwirren tut mich die Notation schon. Also * ist bei Dir gleichermaßen Transposition und Skalarprodukt? Denn in der Herleitung direkt unter dem Bild ist von der drittletzten zur letzten Zeile ja optisch nur eine Klammer dazugekommen, jedoch ist * hier für Transposition + Skalarprodukt gemeint, richtig?

Wieso erwähnt man die Transposition eigentlich in der Weise? Ich meine, ich muss ja nicht transponieren, wenn ich einfach von der anderen Seite multipliziere, das würde ja keinen Unterschied machen, richtig? Oder sagt man das eben, weil man immer von der gleichen Seite Matrix * Vector rechnen will?

Danke jedenfalls für die ausführliche Erklärung :) Das Paper habe ich auch schon in Abschnitten gelesen. Mir fehlen leider einige mathematische Grundlagen, aber den Kern der Idee sehe ich, denke ich.
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von eXile »

Eisflamme hat geschrieben:Etwas verwirren tut mich die Notation schon. Also * ist bei Dir gleichermaßen Transposition und Skalarprodukt? Denn in der Herleitung direkt unter dem Bild ist von der drittletzten zur letzten Zeile ja optisch nur eine Klammer dazugekommen, jedoch ist * hier für Transposition + Skalarprodukt gemeint, richtig?
Fast richtig: \($\! {\ }^*$\) ist bei mir Transposition und Standard-Skalarprodukt. Man kann sich haufenweise Skalarprodukte definieren, aber das Standard-Skalarprodukt funktioniert ja z.B. so: \($$\begin{pmatrix}a_1 \\ a_2 \\ a_3 \end{pmatrix}^* \begin{pmatrix} b_1\\ b_2 \\ b_3 \end{pmatrix} = a_1 \cdot b_1 + a_2 \cdot b_2 + a_3 \cdot b_3$$\)Ich sage jetzt einfach, dass wenn man das Standard-Skalarprodukt verwendet, kann man das auch immer als Transposition schreiben, und dann einfach die normalen Rechenregeln für das Matrixprodukt anwenden:
\($$\begin{pmatrix}a_1 \\ a_2 \\ a_3 \end{pmatrix}^* \begin{pmatrix} b_1\\ b_2 \\ b_3 \end{pmatrix} = \begin{pmatrix}a_1, a_2, a_3\end{pmatrix} \begin{pmatrix} b_1\\ b_2 \\ b_3 \end{pmatrix} = a_1 \cdot b_1 + a_2 \cdot b_2 + a_3 \cdot b_3$$\)Wie man sieht, kommt das gleiche raus.
Eisflamme hat geschrieben:Wieso erwähnt man die Transposition eigentlich in der Weise? Ich meine, ich muss ja nicht transponieren, wenn ich einfach von der anderen Seite multipliziere, das würde ja keinen Unterschied machen, richtig? Oder sagt man das eben, weil man immer von der gleichen Seite Matrix * Vector rechnen will?
Letzteres ist der Fall. ;)
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von Eisflamme »

Okay, alles verstanden, vielen Dank. :)

Wenn man jetzt Normalen bei animierten Modellen transformieren will, ist das aber hochgradig unschön, oder? Die invertierte WorldMatrix übergebe ich meinem Shader ja. Und die BoneMatrizen manipulieren die Vertices ja eigentlich bevor sie mit der WorldMatrix multipliziert werden. Wenn ich alles zu einer Matrix zusammenfasse, müsste ich also eigentlich diese invertieren und transponieren. Macht man das in der Praxis oder vernachlässigt man es einfach? Vermutlich nehme ich hier dann wirklich einfach die Abkürzung über n' = (WorldMatrix * ohne Translation) n...

Öhm, müsste ich dann bei den Bonematrizen für die Multiplikation der Normalen jeweils auch den Translationsteil weglassen oder verfälscht das die Rotation? Und ist davon auszugehen, dass es gut Performance kostet, wenn ich pro Vertex und Bone einige Matrixänderungen durchführe und dann noch multipliziere oder ist das vernachlässigbar?
Zuletzt geändert von Eisflamme am 14.05.2012, 15:04, insgesamt 1-mal geändert.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von dot »

Normalerweise hast du in deinen Bone Matritzen nur Rotation + Translation. In dem Fall kannst du die Normalen einfach mit der oberen 3x3 Matrix transformieren (der Rotationsanteil), da die orthogonal ist.
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von Eisflamme »

Ah okay. Also erst die BoneMatrizen (ohne Translation, also 3x3) draufhauen und das Ergebnis mit der inversen, transponierten Worldmatrix multiplizieren. Ist ja auch logisch, die Animation findet ja eh im ObjectSpace statt, sorry, dass ich so langsam bin. :roll: Verstanden!
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Diffuser Shader - Normalen-Transformation?

Beitrag von CodingCat »

Übrigens kannst du auch die Weltmatrix in der Regel sehr einfach invertieren, indem du einfach das Äquivalent einer Kameramatrix mit invers skalierten Achsen konstruierst. Die volle Invertierung brauchst du generell nur äußerst selten.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Antworten