Wenn ich darf, schnapp' ich mir erstmal deine Renderinggleichung und schmeiß' die ganzen Variablen, die wir nicht brauchen (nämlich
\($\lambda$\) und
\($t$\)), raus:
\($$L_o(\mathbf x, \omega) = L_e(\mathbf x, \omega) + \int_\Omega f_r(\mathbf x, \omega', \omega) L_i(\mathbf x, \omega') (-\omega' \cdot \mathbf n) d \omega'$$\)(Mich persönlich stört daran das Minus beim letzten Term (die haben auf Wikipedia einfach die Richtung der Vektoren umgedreht), aber das ist mir jetzt egal; mir ist lieber, dass ich hier eure Notation verwende, anstatt hier alle meine Notation aufzuzwingen.) Der Emissionsterm
\($L_e$\) ist egal, da ein Spiegel keine Emission besitzt (jetzt kommt nicht mit venezianischen Spiegel und eine Leuchte dahinter…). Damit vereinfacht sich die
Renderinggleichung zur
Reflexionsgleichung (Emissionsterm 0):
\($$L_o(\mathbf x, \omega) = \int_\Omega f_r(\mathbf x, \omega', \omega) L_i(\mathbf x, \omega') (-\omega' \cdot \mathbf n) d \omega'$$\)Wenn man so ein Problem angeht, muss man sich fragen, was die charakteristische Eigenschaft dieser BRDF ist. Naja, die charakteristische Eigenschaft ist halt, dass die reflektierte Radianz gleich der eingehenden Radianz ist. Oder in Formeln:
\($$L_o(\mathbf x, \omega) = \int_\Omega f_{\text{mirror}}(\mathbf x, \omega', \omega) L_i(\mathbf x, \omega') (-\omega' \cdot \mathbf n) d \omega' = L_i\big(\mathbf x, R(\omega)\big)$$\)wobei das
\($R(\omega)$\) dem reflektierten Raumwinkel entspricht:
\($$R(\omega) = R(\theta, \phi) = (\theta, (\phi + \pi) \text{ mod } 2\pi)$$\)Bei einem Spiegel bleibt der Altitudewinkel
\($\theta$\) vor und nach der Reflexion gleich. Der Azimuthwinkel
\($\phi$\) ist um eine halbe Drehung verdreht. Dabei muss man noch aufpassen, dass der Winkel auch schön zwischen 0 und
\($2\pi$\) liegt, weil halt die Parametrisierung für
\($\phi$\) nur in
\($[0, 2\pi[$\) liegt.
Wenn wir nun in die obige Reflexionsgleichung die explizite Polarkoodinaten-Parametrisierung mitsamt zugehöriger Funktionaldeterminante einsetzen, kriegen wir:
\($$L_o(\mathbf x, \omega) = \int_0^{2\pi} \int_0^\pi f_{\text{mirror}}(\mathbf x, \omega', \omega) L_i(\mathbf x, \omega') \cos(\theta') \sin(\theta') \,\mathrm d\theta' \,\mathrm d\phi' = L_i\big(\mathbf x, R(\omega)\big)$$\)Das obige Minus von Wikipedia (was ich immer noch nicht mag) ist damit auch elegant im Orkus der Geschichte verschwunden, weil der Kosinus eine
gerade Funktion ist. Man kann jetzt schon mal eine Hypothese für
\($f_{\text{mirror}}$\) aufstellen: Auf jeden Fall müssen der Kosinus- und der Sinusterm verschwinden. Außerdem soll der „Peak“ der BRDF genau in Richtung
\($R(\omega)$\) gehen. Wenn man diese zwei Ideen nimmt, kann man sich mal diese BRDF anschauen:
\($$f_{\text{mirror}}(\mathbf x, \omega, \omega') = \frac{\delta(\theta' - \theta) \cdot \delta(|\phi' - \phi| - \pi)}{\cos(\theta') \sin(\theta')}$$\)Das erste Dirac-Delta erzwingt
\($\theta' = \theta$\). Das zweite Dirac-Delta erzwingt unter Beachtung von Wraparound
\($\phi' = \phi \pm \pi$\). Genau, was wir haben wollen. Durch Multiplikation können wir beide Bedingungen Und-Verknüpfen. Dann noch im Nenner durch den Kosinus- und Sinusterm teilen, und fertig ist die Laube.
Wenn ihr jetzt sagt, das geht doch für
\($\theta' = 0$\) mit eine Division durch Null voll in die Hose: Stimmt, ist aber egal, weil das nur ein einzelner Punkt (genauer: eine Nullmenge) beim Integrieren ist; das verändert also den Wert des Integrals nicht. Praxisnah ist das so wie so nicht, es sei denn, ihr bastelt mir mal eine Dirac-Verteilung im Computer.
Als „Beweis“ kann ich das ganze auch noch mal schnell durchrechnen:
\(\begin{align}L_o(\mathbf x, \omega) &= \int_0^{2\pi} \int_0^\pi f_{\text{mirror}}(\mathbf x, \omega', \omega) L_i(\mathbf x, \omega') \cos(\theta') \sin(\theta') \,\mathrm d\theta' \,\mathrm d\phi' \\
&= \int_0^{2\pi} \int_0^\pi \delta(\theta' - \theta) \cdot \delta(|\phi' - \phi| - \pi) \cdot L_i(\mathbf x, \omega') \,\mathrm d\theta' \,\mathrm d\phi' \\
&= \int_0^{2\pi} \delta(\theta' - \theta) \int_0^\pi \delta(|\phi' - \phi| - \pi) \cdot L_i(\mathbf x, \omega') \,\mathrm d\theta' \,\mathrm d\phi' \\
&= \int_0^{2\pi} \delta(\theta' - \theta) \cdot L_i\big(\mathbf x, \theta', (\phi + \pi) \text{ mod } 2\pi\big)\,\mathrm d\theta' \,\mathrm d\phi' \\[5 pt]
&= L_i(\mathbf x, \theta, (\phi + \pi) \text{ mod } 2\pi) \\[5 pt]
& = L_i(\mathbf x, R(\omega))\end{align}\)Also genau das, was wir wollten. Ich hab's zur Sicherheit auch noch mal mit Mathematica nachgerechnet. ;)
Wohlgemerkt: Damit haben wir
eine solche BRDF gefunden. Niemand sagt, dass die Formel eindeutig ist (da man hier nicht mit Funktionen, sondern mit sogenannten Distributionen rechnet, kann man die Funktionen nicht einfach punktweise vergleichen!; man muss im Falle von unbeschränkten Werten – wie bei der Dirac-Deltafunktion an der Stelle 0 – einen Grenzwertvergleich anstellen!); siehe dazu auch Zusatz 1.
Zusatz 1:
Anstelle von obiger Definition von
\($f_{\text{mirror}}$\) mit dem Kosinusterm im Nenner kann man auch folgende, äquivalente Definition nehmen:
\($$f_{\text{mirror}}(\mathbf x, \omega, \omega') = \frac{\delta\big(\!\cos(\theta') - \cos(\theta)\big) \cdot \delta(|\phi' - \phi| - \pi)}{\cos(\theta')}$$\)Wir sehen: Keine Divison durch den Sinusterm. Warum? Weil durch
die „Kettenregel“ (es ist nicht die Kettenregel für Funktionen, weil wir hier mit Distributionen arbeiten!) den Transformationssatz für Integrale (wieder: Eigentlich für Distributionen und nicht Funktionen…) beim ersten Deltaterm die Division durch den Sinusterm geschenkt bekommen!
Nachtrag: In der Formel war vorhin noch ein Fehler, ist nun gefixt.
Zusatz 2:
Die Version von MadMax funktioniert so leider nicht: Die Dirac-Verteilung nimmt als Parameter eine reelle Zahl; übergeben wurde (je nach Parametrisierung) ein zumindest zweidimensionales Tupel (nämlich
\($\omega = (\theta, \phi)$\)). D.h. nimmt man jetzt an, dass man die Winkelpaare einfach komponentenweise vergleicht, und die Dirac-Verteilungen multipliziert (so wie ich oben), dann klappt es immer noch nicht, weil eben
\($\phi$\) um
\($\pm \pi$\) rotiert werden muss.
Beim
\($\omega_{\text{reflect}}$\) kann ich aber weiterhelfen: Householder-Reflektor benutzen. Das hatte ich schon mal in diesem
Post angerissen (das
\($\mathbf{r}_\mathbf{n}$\) dort ist der Householder-Reflektor).
Zusatz 3:
Bestimmt wieder Tonnen von Fehlern eingebaut. Bin um reden Hinweis dankbar.