Radiosity - Die Lichtenergie gerät bei zu geringen Abstand aus dem Ruder
-
Nehmen wir mal an, ich würde über beide Patch-Oberflächen integrieren und somit für jeden Punkt auf Patch I zu jeden Punkt auf Patch J ein eigenen GeometryTerm berechnen. Wäre dann das r²-Problem weg? Wenn ja warum?
Die wichtigere Frage. Wie soll die Lösung aussehen?
Da ich den Algorithmus in endlicher Zeit laufen lassen will, wie soll dann der Lichtaustauschen zwischen zwei Flächen vonstatten gehen? Soll ich das Integral Numerisch in Abhängigkeit vom Verhältnis Patch-Größe zu Patchabstand in N (N berechnet sich aus diesem Verhältnis) Schritten berechnen?
-
Ich denke mal, dass das Integral für einfache Formen ein relativ einfacher geschlossener Ausdruck sein dürfte. Evtl. im Matheforum nachfragen? Meine eigenen Fähigkeiten diesbezgl. sind da etwas eingerostet.
-
Verstehe. Anstatt eine numerische Lösung sollte ich mir das Problem lieber in IntegralSchreibweise auf dem Papier Lösen und die so ermittelte Funktion dann in meine Beleuchungsfunktion einsetzen.
Mein Gedanke, warum ich 'glaube' dass das Interpolieren des Abstandwertes ok ist es folgender Grundgedanke:
Gegeben ist die Schräge 2D-Line / und ein Punkt X daneben
/---X
Wenn ich nun über die Linie entlanglaufe und den Abstand zu X berechne, dann ist er im Mittel so groß, wie der Abstand des Linien-Mittelpunktes zu X. Das was die Linie oben Näher ist, ist sie unten weiter weg.
So war mein Gedankengang zu dem Thema und warum der GeometryTerm im Mittel über die Fläche gleichgroß ist. Vielleicht ist dort einfach noch ein Denkfehler drin den ich (mal wieder) übersehen habe.
-
XMAMan schrieb:
Gegeben ist die Schräge 2D-Line / und ein Punkt X daneben
/---X
Wenn ich nun über die Linie entlanglaufe und den Abstand zu X berechne, dann ist er im Mittel so groß, wie der Abstand des Linien-Mittelpunktes zu X. Das was die Linie oben Näher ist, ist sie unten weiter weg.
Der Winkel, unter dem die Strecke vom X aus gesehen wird, bestimmt die Menge an Licht, die die Strecke von der Lichtquelle X erhält. Betrachte nun alle Punkte (den Kreis) mit dem selben Abstand wie X vom Streckenmittelpunkt. Offensichtlich, ist der betreffende Winkel in der Regel nicht gleich für alle Punkte auf dem Kreis, sondern:
- ist 0, wenn der Abstand gößer als die halbe Streckenlänge ist und wir einen Punkt nehmen, in dem der Kreis die Gerade schneidet, auf der die Strecke liegt, und größer (aber stets kleiner als 90°) für andere Punkte
- ist exakt 90°, wenn Abstand gleich halbe Streckenlänge (Satz des Thales)
- ist 180°, wenn der Abstand kleiner als die halbe Streckenlänge ist und wir einen Punkt nehmen, in dem der Kreis die Strecke liegt, und kleiner (aber stets größer als 90°) für andere Punkte.
-
Teilabdeckung ist erstmal nicht so sehr wichtig.
Die energie in der Scene wird natuerlich "steigen" wenn du es iterativ machst, da du nirgenswo energie abziehst. Du berechnest die akkumulierte energie pro patch, nicht den verteilungsfaktor, denn dafuer muestest du alle patches in ein gleichungssystem stecken und es dann z.B. mit Gaus loesen.
am anfang hast du
Energie = Lightquellen + 0.0 * Scene
und bei jeder iteration steigt die Scenenenergie
Energie = Lightquellen + 0.5 * Scene
Energie = Lightquellen + 0.9 * Scene
Energie = Lightquellen + 1.1 * Scene
usw.Die "echte" energie steigt dabei natuerlich nicht, du naeherst dich nur bei jeder iteration dem echten wert.
Ein sehr kleiner Abstand zwischen patches ist auch nicht problematisch, da mit sinkendem Abstand, auch immer weniger licht von aussen zwischen die beiden patches kommt (wenn wir erstmal Teilabdeckung usw. ignorieren).
Wenn du erstmal eine konvergierte scene bekommst, kannst du die spezialfaelle angehen.
-
rapso schrieb:
Die energie in der Scene wird natuerlich "steigen" wenn du es iterativ machst, da du nirgenswo energie abziehst.
Wenn ich es richtig verstehe, wird dem System in N Iterationsschritten N-mal Energie über die Primärlichtquellen hinzugefügt. Also teilt man vermutlich am Ende alle Werte durch N, um eine brauchbare Verteilung zu erhalten. Das setzt aber voraus, dass Sekundärlicht keine extra Energie (insbesondere nicht mit superlinearem Wachstum) erzeugt.
rapso schrieb:
Ein sehr kleiner Abstand zwischen patches ist auch nicht problematisch, da mit sinkendem Abstand, auch immer weniger licht von aussen zwischen die beiden patches kommt (wenn wir erstmal Teilabdeckung usw. ignorieren).
Hm. Das Problem ist ja, dass gegenseitige Beleuchtung abgesehen vom Farb-/Albedofaktor symmetrisch ist. Wenn Fläche eins in einem Iterationsschritt 10% mehr Energie an Fläche zwei gleicher Farbe abgibt, als sie erhalten hat, bekommt sie im folgenden Schritt nochmals 10% mehr von Fläche zwei zurück. Das führt unweigerlich zu exponentiellem Wachstum.
-
Die Energie schrumpft bei mir bei der Brdf.
In folgender Zeile wärend der Beleuchtung:
patch.OutputRadiosity = Vektor.Mult(patch.ColorOnCenterPoint, patch.InputRadiosity) * lichtdurchlassKoeffizient / (float)Math.PI;
Input ist ja erstmal nur die Summe. Output ist dann die Energie, die Reflektiert wird. Da ich ja nur 0.8 / PI in den Output lege, wird dort ja nicht alles ungefilter weiter gegebben.
Da ich ja momentan kein Lichtaustausch zwischen zwei Patches berechne, die näher als 0,01 sind, konvergiert die Szene ja bereits. Ich erhalte jetzt auch bei 20 Beleuchtungsschritten das richtige Ergebnis. Die Frage ist ja eher, wie bekomme ich diese Einschränkung mit der 0,01 weg.
private static void RadiosityStepSolidAngle(List<IPatch> patches) { foreach (var patch in patches) { float lichtdurchlassKoeffizient = 0.8f; patch.OutputRadiosity = Vektor.Mult(patch.ColorOnCenterPoint, patch.InputRadiosity) * lichtdurchlassKoeffizient / (float)Math.PI; //Beim Reflektieren der Input-Zu-Output-Energie kommt die Brdf ins Spiel if (patch.IsLightSource) patch.OutputRadiosity += new Vektor(1, 1, 1) * patch.EmissionPerPatch / patch.SurvaceArea; patch.InputRadiosity = new Vektor(0, 0, 0); } foreach (var sender in patches) { Vektor senderColor = sender.OutputRadiosity;//Das hier ist die Photonencount pro Fläche. foreach (var empfänger in sender.ViewFaktors) { empfänger.Patch.InputRadiosity += senderColor * empfänger.Faktor * sender.SurvaceArea * empfänger.Patch.SurvaceArea;//Addiere Fluxwerte } } foreach (var patch in patches) { patch.InputRadiosity /= patch.SurvaceArea; //Umrechnen von Flux in Radiosity } }
-
Evtl. reicht es ja schon, einfach den Empfangsstrom zu begrenzen.
Statt:empfänger.Patch.InputRadiosity += senderColor * empfänger.Faktor * sender.SurvaceArea * empfänger.Patch.SurvaceArea;
empfänger.Patch.InputRadiosity += senderColor * sender.SurvaceArea * min( empfänger.Faktor * empfänger.Patch.SurvaceArea, 1 );
o.ä. Der Empfänger kann ja nicht mehr Licht empfangen, als ausgesendet wird. Immer noch ein Hack, aber wenigstens keine magische Zahl mehr.
-
camper schrieb:
empfänger.Patch.InputRadiosity += senderColor * sender.SurvaceArea * min( empfänger.Faktor * empfänger.Patch.SurvaceArea, 1] );
o.ä. Der Empfänger kann ja nicht mehr Licht empfangen, als ausgesendet wird. Immer noch ein Hack, aber wenigstens keine magische Zahl mehr.
Auch 1 ist eine magic number, was ist wenn die patches 100 mal kleiner oder groesser waeren? -> Das ist nicht zielführend.
Für den Anfang, erstell eine Cornell Box, ohne inhalt, nur waende, boden, decke und ein patch oben ist das licht. dann kannst du ohne tracen trivial die formfaktoren errechnen und die sache sollte problemfrei konvergieren. (ohne jegliche magic numbers).
-
XMAMan schrieb:
Die Energie schrumpft bei mir bei der Brdf.
im ersten post schriebst du die energie wird groesser. Schumpfen sollte es nirgendwo, sonst waere es falsch.
https://www.siggraph.org/education/materials/HyperGraph/radiosity/images/slide13.jpg
-
rapso schrieb:
Auch 1 ist eine magic number, was ist wenn die patches 100 mal kleiner oder groesser waeren? -> Das ist nicht zielführend.
Die 1 hier repräsentiert einen qualitativen Unterschied, im Gegensatz zu einer willkürlichen Quantität wie 0.01 die wahrscheinlich nur zufällig in einer bestimmten Szene so passt. Ziel war dabei nur, einen möglichst einfache Ansatz zu finden, der Beleuchtung ohne Lichtquellen verhindert. Die Ursache (falscher Geometrieterm) und der richtige Lösungsansatz dazu wurden ja bereits weiter oben diskutiert.
100 mal größerer patches bedeutet eben nicht, dass das Produkt
empfänger.Faktor * empfänger.Patch.SurvaceArea größer als 1 werden kann wenn empfänger.Faktor korrekt berechnet wird. Schließlich ist der Raumwinkel, der durch eine Fläche abgedeckt werden kann, durch 2π begrenzt.
-
camper schrieb:
rapso schrieb:
Auch 1 ist eine magic number, was ist wenn die patches 100 mal kleiner oder groesser waeren? -> Das ist nicht zielführend.
Die 1 hier repräsentiert einen qualitativen Unterschied, im Gegensatz zu einer willkürlichen Quantität wie 0.01 die wahrscheinlich nur zufällig in einer bestimmten Szene so passt. Ziel war dabei nur, einen möglichst einfache Ansatz zu finden, der Beleuchtung ohne Lichtquellen verhindert. Die Ursache und der richtige Lösungsansatz dazu wurden ja bereits weiter oben diskutiert.
was passiert wenn die surfacearea 100 mal groesser skaliert waere? weiterhin 1 beibehalten oder dann min(...,100) ?
-
rapso schrieb:
was passiert wenn die surfacearea 100 mal groesser skaliert waere? weiterhin 1 beibehalten oder dann min(...,100) ?
Hab meine Antwort oben nochmal editiert, bevor ich die Frage gesehen habe. Problem ist, das empfänger.Faktor falsch berechnet wird.
-
camper schrieb:
100 mal größerer patches bedeutet eben nicht, dass das Produkt
empfänger.Faktor * empfänger.Patch.SurvaceArea größer als 1 werden kann wenn empfänger.Faktor korrekt berechnet wird. Schließlich ist der Raumwinkel, der durch eine Fläche abgedeckt werden kann, durch 2π begrenzt.dieser faktor zwischen zwei patches aendert sich nicht. Patch groesse ist nur eine granularitaet. du kannst z.b. eine wand als 1 patch nutzen oder in millionen tesselieren. Raumwinkel spielt da auch ab einer gewissen distanz zwischen patches keine roller (deswegen wird es so approximiert). 1 ist deswegen eine magic number die mal funktionieren kann, an anderer stelle ganz falsche resultate liefert.
-
camper schrieb:
rapso schrieb:
was passiert wenn die surfacearea 100 mal groesser skaliert waere? weiterhin 1 beibehalten oder dann min(...,100) ?
Hab meine Antwort oben nochmal editiert, bevor ich die Frage gesehen habe. Problem ist, das empfänger.Faktor falsch berechnet wird.
Hab ich angenommen, deswegne die 2te antwort
Es kann einiges falsch sein, deswegen mein vorschlag mit einer trivialen testszene, das kann ihm beim bug suchen helfen.
-
ach ja
if (patch.IsLightSource && patch.IsInSpotDirection(otherPatch.CenterOfGravity) == false) continue; //Der andere liegt außerhalb meiens Spotcutoff
var point = this.intersectionFinder.GetIntersectionPoint(ray, patch, 0);
if (point == null || point.IntersectedObject != otherPatch) continue; //Sichtbarkeitstestfloat lambda2 = point.Normale * (-ray.Direction);
if (lambda2 <= 0) continue;1. mach erst den lambda2 test, dann IsInSpot, dann intersection, das kann dir bis 50% rechenzeit sparen.
2. Speicher dir irgendwo als bitmaske die resultate der intersections ab.
3. n->Intersect->m ist wie m->Intersect->n, nochmal 50%
-
rapso schrieb:
XMAMan schrieb:
Die Energie schrumpft bei mir bei der Brdf.
im ersten post schriebst du die energie wird groesser. Schumpfen sollte es nirgendwo, sonst waere es falsch.
https://www.siggraph.org/education/materials/HyperGraph/radiosity/images/slide13.jpgMit schrumpfen wollte ich mich lediglich auf deine Feststellung beziehen, dass die Energie doch endlos wachsen müsste, wenn ich immer nur drauf addiere.
Ohne den Magic-Abstandfaktor von 0.01 Wächst die Gesamtenergie bei der Raumszene. Bei der Cornellbox passiert das nicht.
-
rapso schrieb:
ach ja
if (patch.IsLightSource && patch.IsInSpotDirection(otherPatch.CenterOfGravity) == false) continue; //Der andere liegt außerhalb meiens Spotcutoff
var point = this.intersectionFinder.GetIntersectionPoint(ray, patch, 0);
if (point == null || point.IntersectedObject != otherPatch) continue; //Sichtbarkeitstestfloat lambda2 = point.Normale * (-ray.Direction);
if (lambda2 <= 0) continue;1. mach erst den lambda2 test, dann IsInSpot, dann intersection, das kann dir bis 50% rechenzeit sparen.
2. Speicher dir irgendwo als bitmaske die resultate der intersections ab.
3. n->Intersect->m ist wie m->Intersect->n, nochmal 50%Ich hatte den Lambda2-Test ja ursprünglich auch davor. Dummerweise zeigen die Normalen bei manchen Objekten in die falsche Richtung. Ich müsste also entweder im Blender das jetzt korrigieren oder ich lass das durch mein Raytracer gerade stellen.
Das mit der Bitmaske stell ich mir schwer vor, das das ja eine N*N-Matrix sein muss. Ich arbeite mit mehreren Threads wärend der Formfaktor-Berechnung und kann somit nicht gleichzeitig auf ein Patch schreiben zugreifen.
Hätte ich so eine Matrix, dann wäre das Threadsicher aber sie wäre bei 10.000 Objekten (So viele Patches enthält die Szene) dann ja 10^8 * 4 Byte groß
Die letzten beiden Reihen wurden mit Radiosity berechnet. Dort verwende ich momentan diesen 0.01-Faktor bei SolidAngle.
Dort will ich diesen Faktor weggekommen, da er falsch ist.
-
XMAMan schrieb:
Ich hatte den Lambda2-Test ja ursprünglich auch davor. Dummerweise zeigen die Normalen bei manchen Objekten in die falsche Richtung. Ich müsste also entweder im Blender das jetzt korrigieren oder ich lass das durch mein Raytracer gerade stellen.
bei scenen mit richtigen normalen sind die berechnungen richtig, bei scenen mit falschen sind sie falsch? vielleicht haengt das zusammen.
du kannst die normalen beim laden selbst errechnen (brauchst ja eh nur face normalen, keine per-vertex). vergiss nicht zu normalisieren
Das mit der Bitmaske stell ich mir schwer vor, das das ja eine N*N-Matrix sein muss. Ich arbeite mit mehreren Threads wärend der Formfaktor-Berechnung und kann somit nicht gleichzeitig auf ein Patch schreiben zugreifen.
es arbeitet doch hoffentlich immer nur ein thread auf einem patch, oder? von daher kannst du auch pro patch eine zeile der matrix beschreiben.
Hätte ich so eine Matrix, dann wäre das Threadsicher aber sie wäre bei 10.000 Objekten (So viele Patches enthält die Szene) dann ja 10^8 * 4 Byte groß
10^8 / 8 (du brauchst ja nur ein bit, erstmal). 12MB oder so.
[quote]
Die letzten beiden Reihen wurden mit Radiosity berechnet. Dort verwende ich momentan diesen 0.01-Faktor bei SolidAngle.
Dort will ich diesen Faktor weggekommen, da er falsch ist.
wie schaut es ohne den faktor aus? ist es im ganzen heller, oder nur an kanten?
-
[quote="rapso"]
XMAMan schrieb:
wie schaut es ohne den faktor aus? ist es im ganzen heller, oder nur an kanten?
Es sieht nach 6 Beleuchtungsschritten so auss:
Nach 8 Beleuchtungsschritten fängt dann langsam die ganze Szene an immer heller zu werden (wie so ein glühen), bis es dann ins komplette Weiss übergeht.
Jetzt gerade für das GesamtTestBild verwende ich 20 Schritte.
Kann Blender aber auch wissen, welche seite Außen und welche innen liegt? Ich lasse mir von Blender eine Liste von Dreiecken berechnen und berechne mir die Normale dann selber. Dummerweise erzeugt es mir z.B. beim Tisch immer die Normalen falsch herrum obwohl sie laut Blender-ANzeige nach außen zeigen müssten. Deswegen habe ich dann irgendwann mal gesagt, dass ich dir mir selber zurecht drehe über den Schnittpunkttest.
Auf ein Patch arbeitet immer nur ein Thread schreibend. Wenn also jedes Patch seine eigene ViewBitmask hat, dann würde das schon gehen. Nur nur wenn ich Patch i sage, dass ich Patch j sehen kann, dann müsste ja der Thread, der dann Patch j bearbeitet dann bei i schauen und feststellen, das i bereits j sieht. Je nachdem zu welchen Zeitpunkt j diese Abfrage macht, wird er dann feststellen, dass i j sieht oder nicht. Außerdem weiß ich immer noch nicht, wie diese Bitmaske aussehen soll, so dass schreiben und lesen zugleich möglich ist. Hat jedes Patch ein Fixes Array, wo jedes Bit in dem Array dann auf alle Patches in der Szene verweist?