Ich versuche, die "Mount"-Szene aus Eric Haines' Standardverfahrensdatenbank (SPD) aber der Teil mit der Brechung will einfach nicht mitspielen. Ich habe alles versucht, was ich mir vorstellen kann, um das Problem zu beheben.
Dies ist mein Rendering (mit Watt's Formel):
(Quelle: <a href="http://www.philosoraptor.co.za/programming/SPD/mount_mine.png" rel="nofollow noreferrer">philosoraptor.co.za </a>)
Dies ist mein Rendering unter Verwendung der "normalen" Formel:
(Quelle: <a href="http://www.philosoraptor.co.za/programming/SPD/mount_mine_normal.png" rel="nofollow noreferrer">philosoraptor.co.za </a>)
Und das hier ist die korrekte Darstellung:
(Quelle: <a href="http://www.philosoraptor.co.za/programming/SPD/mount_haines.png" rel="nofollow noreferrer">philosoraptor.co.za </a>)
Wie Sie sehen können, gibt es nur ein paar Fehler, vor allem im Bereich der Pole der Kugeln. Das lässt mich vermuten, dass die Brechung oder ein Präzisionsfehler dafür verantwortlich ist.
Bitte beachten Sie, dass es tatsächlich 4 Kugeln in der Szene gibt, deren NFF-Definitionen ( s x_coord y_coord z_coord radius
) sind:
s -0.8 0.8 1.20821 0.17
s -0.661196 0.661196 0.930598 0.17
s -0.749194 0.98961 0.930598 0.17
s -0.98961 0.749194 0.930598 0.17
Das heißt, es gibt eine vierte Kugel hinter den offensichtlichen drei im Vordergrund. Sie ist in der Lücke zwischen diesen drei Kugeln zu sehen.
Hier ist ein Bild der vierten Kugel allein:
(Quelle: <a href="http://www.philosoraptor.co.za/programming/SPD/mount_sphere_2.png" rel="nofollow noreferrer">philosoraptor.co.za </a>)
Und hier ist ein Bild der ersten Kugel allein:
(Quelle: <a href="http://www.philosoraptor.co.za/programming/SPD/mount_sphere_1.png" rel="nofollow noreferrer">philosoraptor.co.za </a>)
Sie werden feststellen, dass viele der Merkwürdigkeiten, die sowohl in meiner Version als auch in der korrekten Version vorhanden sind, fehlen. Wir können daraus schließen, dass diese Effekte das Ergebnis von Wechselwirkungen zwischen den Kugeln sind, die Frage ist nur, welche?
Was mache ich falsch? Im Folgenden sind einige der möglichen Fehler aufgeführt, die ich bereits in Betracht gezogen habe:
- Formel für den Brechungsvektor.
Soweit ich das beurteilen kann, ist das richtig. Es ist dieselbe Formel, die von mehreren Websites verwendet wird, und ich habe die Ableitung persönlich überprüft. Ich berechne sie folgendermaßen:
double sinI2 = eta * eta * (1.0f - cosI * cosI);
~~
Vector transmit = (v * eta) + (n * (eta * cosI - sqrt(1.0f - sinI2)));
~~
transmit = transmit.normalise();
Ich habe eine alternative Formel in 3D Computer Graphics, 3rd Ed von Alan Watt gefunden. Sie liefert eine bessere Annäherung an das richtige Bild:
double etaSq = eta * eta;
double sinI2 = etaSq * (1.0f - cosI * cosI);
Vector transmit = (v * eta) + (n * (eta * cosI - (sqrt(1.0f - sinI2) / etaSq)));
transmit = transmit.normalise();
Der einzige Unterschied ist, dass ich am Ende durch eta^2 teile.
- Interne Totalreflexion.
Ich habe dies getestet, indem ich die folgende Bedingung vor dem Rest meines Kreuzungscodes verwendet habe:
if (sinI2 <= 1)
- Berechnung von eta.
Ich verwende für dieses Problem einen stapelartigen Ansatz:
/* Entering object. */
if (r.normal.dot(r.dir) < 0)
{
double eta1 = r.iorStack.back();
double eta2 = m.ior;
eta = eta1 / eta2;
r.iorStack.push_back(eta2);
}
/* Exiting object. */
else
{
double eta1 = r.iorStack.back();
r.iorStack.pop_back();
double eta2 = r.iorStack.back();
eta = eta1 / eta2;
}
Wie Sie sehen können, werden die vorherigen Objekte, die diesen Strahl enthielten, in einem Stapel gespeichert. Beim Beenden des Codes wird das aktuelle IOR vom Stapel genommen und zusammen mit dem IOR darunter zur Berechnung von Eta verwendet. Soweit ich weiß, ist dies der korrekteste Weg, dies zu tun.
Dies funktioniert bei verschachtelten Sendeobjekten. Es funktioniert jedoch nicht für überschneidend Objekte zu übertragen. Das Problem dabei ist, dass man den IOR für die Kreuzung unabhängig definieren muss, was im NFF-Dateiformat nicht möglich ist. Es ist also unklar, was die "richtige" Vorgehensweise ist.
- Verschieben des Ursprungs des neuen Strahls.
Der Ursprung des neuen Strahls muss ein wenig entlang des übertragenen Pfads verschoben werden, damit er sich nicht an der gleichen Stelle wie der vorherige schneidet.
p = r.intersection + transmit * 0.0001f;
p += transmit * 0.01f;
Ich habe versucht, diesen Wert kleiner zu machen (0.001f) und (0.0001f), aber das lässt die Kugeln massiv erscheinen. Ich schätze, diese Werte bewegen die Strahlen nicht weit genug weg vom vorherigen Schnittpunkt.
EDIT: Das Problem hier war, dass der Reflexionscode das Gleiche tat. Wenn also ein Objekt sowohl reflektierend als auch brechend ist, endet der Ursprung des Strahls an der völlig falschen Stelle.
- Anzahl der Strahlenabpraller.
Ich habe die Anzahl der Strahlenabpraller künstlich auf 4 begrenzt. Ich habe getestet, diese Grenze auf 10 zu erhöhen, aber das hat das Problem nicht gelöst.
- Normale.
Ich bin mir ziemlich sicher, dass ich die Normalen der Kugeln richtig berechne. Ich nehme den Schnittpunkt, subtrahiere den Mittelpunkt der Kugel und teile durch den Radius.