Radiosity - Die Lichtenergie gerät bei zu geringen Abstand aus dem Ruder
-
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?
-
[quote="XMAMan"]
rapso schrieb:
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.
kann es sein dass da irgendwas anderes schiefgeht. z.b. schaut der fuss vom schrank an der rechten seite ganz weiss aus, das sollte nicht sein.
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.
ich kenne mich mit blender nicht aus, aber es waere wirklich gut nur mit scenen zu testen, bei denen du dir sicher bist, dass die gut sind, um auszuschliessen dass du im code geister jagst, waehrend es nur kaputte geometrie ist.
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?
zum erstellen des arrays:
for(i=1;i<count;i++) for(j=i+1;j<count;j++) { bool hit= lambda0>0&&lambda1>0&&InSpot(..)&&Intersect(..); HitArray[i+j*count]=HitArray[j+i*count]=hit; }
beim evaluieren musst du nur noch if(HitArray...) machen, brauchst kein lambda usw. test, da das ins array eingeflossen ist.
das beispiel oben ist nur byte weise, wenn du bitweise moechtest und threadsafe, sollte jeder thread 8 patches am stueck machen, sodass jeder thread sein eigenes byte beschreibt.
-
Es klingt jetzt vielleicht etwas peinlich aber es lag wirklich nur daran, dass bei mir nicht alle Normalen nach außen gezeigt haben. Ich habe jetzt im Blender die Sache teilweise korrigiert und nun funktioniert mein Algorithmus ganz ohne die Distanz-Abfrage für ein Teil des Bildes. Das ganze Bild bekomme ich aufgrund des Normalenfehler noch nicht hin. Das blöde ist echt, dass die Blenderversion, die ich verwende bei der Normalenberechnung viele Fehler macht. Lasse ich mir die Normalen anzeigen, dann zeigen sie alle nach außen. Schaue ich bei mir im Raytracer die Normalen an, dann zeigen sie Teilweise für manche Flächen wieder in die andere Richtung. Vielleicht liegt mein Fehler ja darin, dass ich mir die Normalen selber über die Dreiecksposition berechne. Ich lass die mir mal mitausgeben und drehe dann die Dreickskoordianten entsprechend im oder gegen den Uhrzeigersinn.
Außerdem habe ich für die viewMatrix ein Zweidimmensionales enum-Array verwendet. Ich bin mir jetzt nicht sicher, ob das für Rapso so ok ist^^
private enum VisibleValue { NotSet, Visible, NotVisible }; private VisibleValue[,] visibleMatrix; this.visibleMatrix = new VisibleValue[patches.Count, patches.Count]; int index1 = this.patches.IndexOf(patch); int index2 = this.patches.IndexOf(otherPatch); if (this.visibleMatrix[index1, index2] == VisibleValue.NotVisible) continue; if (this.visibleMatrix[index1, index2] == VisibleValue.NotSet) { var point = this.intersectionFinder.GetIntersectionPoint(ray, patch, 0); VisibleValue visibleTest = VisibleValue.Visible; if (point == null || point.IntersectedObject != otherPatch) visibleTest = VisibleValue.NotVisible; //Sichtbarkeitstest this.visibleMatrix[index1, index2] = visibleTest; this.visibleMatrix[index2, index1] = visibleTest; if (visibleTest == VisibleValue.NotVisible) continue; }
Damit ist die Berechnung um 35% schneller.
@edit:
Die Ausgabe der Normalen klappt leider auch nicht. Sie zeigen trotzdem in die falsche Richtung. Ich werde es mal mit einer anderen Blenderversion probieren müssen.
-
XMAMan schrieb:
Es klingt jetzt vielleicht etwas peinlich aber es lag wirklich nur daran, dass bei mir nicht alle Normalen nach außen gezeigt haben.
Das ist nicht peinlich, es ist immer gut den grund zu finden bzw zu verstehen, bevor man magic anwendet (esseiden die zeit ist zu knapp).
Ich habe jetzt im Blender die Sache teilweise korrigiert und nun funktioniert mein Algorithmus ganz ohne die Distanz-Abfrage für ein Teil des Bildes. Das ganze Bild bekomme ich aufgrund des Normalenfehler noch nicht hin. Das blöde ist echt, dass die Blenderversion, die ich verwende bei der Normalenberechnung viele Fehler macht. Lasse ich mir die Normalen anzeigen, dann zeigen sie alle nach außen. Schaue ich bei mir im Raytracer die Normalen an, dann zeigen sie Teilweise für manche Flächen wieder in die andere Richtung. Vielleicht liegt mein Fehler ja darin, dass ich mir die Normalen selber über die Dreiecksposition berechne. Ich lass die mir mal mitausgeben und drehe dann die Dreickskoordianten entsprechend im oder gegen den Uhrzeigersinn.
Es muss nicht direkt an blender liegen, manchmal sind nur die expoerter (und manchmal importer :P) fehlerhaft. schau dir die Scene in einem anderen tool als referenz an, damit du weisst ob der bug wirklich in blender ist, oder bei dir.
Außerdem habe ich für die viewMatrix ein Zweidimmensionales enum-Array verwendet. Ich bin mir jetzt nicht sicher, ob das für Rapso so ok ist^^
private enum VisibleValue { NotSet, Visible, NotVisible }; private VisibleValue[,] visibleMatrix; this.visibleMatrix = new VisibleValue[patches.Count, patches.Count]; int index1 = this.patches.IndexOf(patch); int index2 = this.patches.IndexOf(otherPatch); if (this.visibleMatrix[index1, index2] == VisibleValue.NotVisible) continue; if (this.visibleMatrix[index1, index2] == VisibleValue.NotSet) { var point = this.intersectionFinder.GetIntersectionPoint(ray, patch, 0); VisibleValue visibleTest = VisibleValue.Visible; if (point == null || point.IntersectedObject != otherPatch) visibleTest = VisibleValue.NotVisible; //Sichtbarkeitstest this.visibleMatrix[index1, index2] = visibleTest; this.visibleMatrix[index2, index1] = visibleTest; if (visibleTest == VisibleValue.NotVisible) continue; }
Damit ist die Berechnung um 35% schneller.
Mein Punkt war, dass du diese matrix abspeichern kannst. Der test ist vermutlich 99% der laufzeitkosten und solange die geometrie sich nicht aendert, bleibt die matrix gleich, also kannst du sie von der platte laden.
Die Ausgabe der Normalen klappt leider auch nicht. Sie zeigen trotzdem in die falsche Richtung. Ich werde es mal mit einer anderen Blenderversion probieren müssen.
ist das ein bekannter blender bug? eventuell liegt es ja nicht an blender.
-
Ich muss mich doch nochmal zu dem Thema zu Wort melden^^
Das Problem lag doch nicht an den Normalen. Ich habe mit Blender hier und da einzelnen Flächen mal so und mal so gedreht mit der Hoffnung, dass nun endlich der Blender-Exporter eine Datei erzeugt, wo alle Normalen nach außen zeigen. Dadurch ist es mir dann durch Zufall gelungen genau die beiden Patches, welche ein ganz geringen Abstand zueinander haben so zu drehen, dass sie voneinander wegzeigen.
Aber...
camper hatte ganz am Anfang der Diskusion noch ein wichtigen Punkt genannt, welchen ich nun nochmal aufgegriffen habe. Er sagte, dass wenn ich zwei große Flächen nah zueinander stelle, dann ist die Formel der Geometryterm-Berechnung von den Patchmittelpunkten zu ungenau. Er hatte recht damit.
Ich habe das jetzt so gemacht, dass wenn das Verhältnis zwischen Patch-Oberfläche zu Patch-Quadratdistanz einen bestimmten (Fehler)Wert überschreitet, dann verwende ich für die FormFaktor-Berechnung eine genauere Lösung. Da ich nicht genau wußte, wie man das Integral auf dem Papier lößt, habe ich es numerisch per Monte CarloIntegration gelößt. Es funktioniert sehr gut und das Bild sieht mit dieser Funktion nun so aus:
https://picload.org/view/rwgwolgw/ausgabe3.png.html
private void AddAllViewFaktorsWithSolidAngle(IPatch patch, Random rand) { foreach (var otherPatch in this.patches) { if (patch == otherPatch) continue; //Keine Beleuchtung mit sich selbst if (otherPatch.IsLightSource) continue; //Lichtquellen dürfen nicht beleuchtet werden float distanceSqrt = (patch.CenterOfGravity - otherPatch.CenterOfGravity).QuadratBetrag(); //if (distanceSqrt <= 0.01f) continue; //Wenn der Abstand zwischen zwei Objekten zu gering ist, dann erhält man ein hohen ViewFaktor (Zahl >> 10), was dann dazu führt, dass mit jeden Beleuchtungsstep die Lichtenergie immer mehr wird Ray ray = new Ray(patch.CenterOfGravity, Vektor.Normiere(otherPatch.CenterOfGravity - patch.CenterOfGravity)); float lambda1 = patch.Normale * ray.Direction; if (lambda1 <= 0) continue; float lambda2 = otherPatch.Normale * (-ray.Direction); if (lambda2 <= 0) continue; if (patch.IsLightSource && patch.IsInSpotDirection(otherPatch.CenterOfGravity) == false) continue; //Der andere liegt außerhalb meiens Spotcutoff int index1 = this.patches.IndexOf(patch); int index2 = this.patches.IndexOf(otherPatch); if (this.visibleMatrix[index1, index2] == VisibleValue.NotVisible) continue; if (this.visibleMatrix[index1, index2] == VisibleValue.NotSet) { var point = this.intersectionFinder.GetIntersectionPoint(ray, patch, 0); VisibleValue visibleTest = VisibleValue.Visible; if (point == null || point.IntersectedObject != otherPatch) visibleTest = VisibleValue.NotVisible; //Sichtbarkeitstest this.visibleMatrix[index1, index2] = visibleTest; this.visibleMatrix[index2, index1] = visibleTest; if (visibleTest == VisibleValue.NotVisible) continue; //float lambda2 = point.Normale * (-ray.Direction); //if (lambda2 <= 0) continue; } float geometryTerm = lambda1 * lambda2 / distanceSqrt; float viewFactor = 1; //Diese Zahl soll später mal mit ein Rasterizer berechnet werden float formFaktorErrorValue = (patch.SurvaceArea + otherPatch.SurvaceArea) / distanceSqrt; float formFaktor; if (formFaktorErrorValue > 10) //Die 10 hängt von der Anzahl der Beleuchtungsschritte und der MaxSurfaceAreaPerPatch ab. Um so mehr Beleuchtungsschritte oder um so größer die MaxSurfaceAreaPerPatch ist, um so kleiner muss diese Zahl hier sein { formFaktor = GetFormfaktor(patch, otherPatch, rand); //Berechnet den Formfaktor genau, wenn eine Näherungslösung zu ungenau wird } else { formFaktor = geometryTerm * viewFactor * patch.SurvaceArea * otherPatch.SurvaceArea; //Das ist nur eine Näherungslösung } patch.AddViewFaktor(new ViewFaktor() { Patch = otherPatch, FormFaktor = formFaktor }); } } //Berechnet numerisch das Doppelintegral, wo über alle alle Punkte aus Patch1 und Patch2 gegangen wird, und jeweils der GeometryTerm für dA1 udn dA2 berechnet wird //Der FormFaktor, welcher hier beschrieben ist: https://de.wikipedia.org/wiki/Radiosity_(Computergrafik) enthält noch zusätzlich den VisibleTerm und / PI (Brdf) //Brdf gehört für mich nicht zu diesen Term, da er sich nicht direkt auf die Strecke zwischen zwei Patches bezieht sondern auf ein Patch (Pfadumknickpunkt) //VisibleTerm lasse ich aus Performancegründen weg private float GetFormfaktor(IPatch patch1, IPatch patch2, Random rand) { int sampleCount = 100; float geometryTermSum = 0; for (int i = 0; i < sampleCount; i++) { var point1 = patch1.GetRandomPointOnSurvace(rand); var point2 = patch2.GetRandomPointOnSurvace(rand); Vektor direction = point2.Position - point1.Position; float sqrtLength = direction.QuadratBetrag(); direction /= (float)Math.Sqrt(sqrtLength); float lambda1 = patch1.Normale * direction; float lambda2 = patch2.Normale * (-direction); float geometryTermEstimation = lambda1 * lambda2 / sqrtLength * patch1.SurvaceArea * patch2.SurvaceArea; geometryTermSum += geometryTermEstimation; } return geometryTermSum / sampleCount; }
Das Problem mit Blender habe ich jetzt noch nicht lösen können. Ich habe gestern die neuste Blenderversion installiert und der Wavefront(obj)-Exporter erzeugt weiterhin falsche Normalen. Das komische ist, dass er es nur für manche Objekte falsch macht. Die Lampen sind ok. Der Tisch oder der Schrank nicht. Dort muss ich die Normalen nach Innen zeigen lassen, damit sie im Export nach außen zeigen.
Ich werde versuchen mithilfe von ein globalen Beleuchtungsalgorithmus mir die 'wahren' Normalenrichtungen bestimmen zu lassen, um somit die fehlerhafte OBJ-Datei zu korrigieren.