Ich versuche, meinen eigenen Gradient Domain Path Tracer zu implementieren, indem ich dem Code desjenigen folge, der ihn bereits implementiert hat:
https://gist.github.com/BachiLi/4f5c6e5a4fef5773dab1
Ich habe es bereits geschafft, verschiedene Schritte zu durchlaufen, aber ich wollte etwas mehr tun. Ich habe den Code in der Referenz durch die Implementierung der nächsten Ereignisschätzung erweitert und hier sind einige Ergebnisse.
Normal Path Tracer Bild:
Ergebnisbild der Verlaufsdomäne:
Die Ergebnisse sind bereits gut. Aber wie gesagt, ich wollte etwas mehr. Also habe ich Next Event Estimation implementiert und hier ist das Ergebnis des grundlegenden Path Tracers:
Hier ist mein Code:
private Vector3 SampleWithNEE( Ray ray )
{
// prepare
Vector3 T = (1,1,1), E = (0,0,0), NL = (0,-1,0);
int depth = 0;
// random walk
while (depth++ < MAXDEPTH)
{
// find nearest ray/scene intersection
Scene.Intersect( ray );
if (ray.objIdx == -1) break; //if there is no intersection
Vector3 I = ray.O + ray.t * ray.D; //go to the Hit Point on the scene
Material material = scene.GetMaterial( ray.objIdx, I );
if (material.emissive) //case of a light
{
E += material.diffuse;
break;
}
// next event estimation
Vector3 BRDF = material.diffuse * 1 / PI;
float f = RTTools.RandomFloat();
Vector3 L = Scene.RandomPointOnLight() - I;
float dist = L.Length();
L = Vector3.Normalize( L );
float NLdotL = Math.Abs( Vector3.Dot( NL, -L ) );
float NdotL = Vector3.Dot( ray.N, L );
if (NdotL > 0)
{
Ray r = new Ray( I + L * EPSILON, L, dist - 2 * EPSILON ); //make it a tiny bit shorter otherwise I risk to hit my starting and destination point
Scene.Intersect( r );
if (r.objIdx == -1) //no occlusion towards the light
{
float solidAngle= (nldotl * light.getArea()) / (dist * dist);
E += T * (NdotL) * solidAngle * BRDF * light.emission;
}
}
// sample random direction on hemisphere
Vector3 R = DiffuseReflectionCosWeighted( ray.N );
float hemi_PDF = Vector3.Dot( R, ray.N ) / PI;
T *= (Vector3.Dot( R, ray.N ) / hemiPDF) * BRDF;
ray = new Ray( I + R * EPSILON, R, 1e34f );
}
return E;
}
Die Dinge funktionieren wirklich gut und die Ergebnisse werden mit dem Bild oben gezeigt. Noch etwas: Ich habe nur diffuse Oberflächen in meiner Szene.
Das Problem ist nun, dass ich bei dieser Methode zwei Arten von PDFs verwende:
- Eine davon wird durch zufälliges Abtasten des Lichts in der direkten Beleuchtung der nächsten Ereignisschätzung gegeben. Tatsächlich ist SolidAngle unser PDF oder besser 1 / PDF.
- Das zweite PDF wird von DiffuseReflectionCosWeighted verwendet, wodurch ein PDF mit CosTheta / PI erstellt wird .
Bisher ist alles in Ordnung und für alle Implementierungsdetails können Sie sich nur meinen Code ansehen, aber Probleme treten mit meinem Gradient Domain Path Tracer auf. In der Tat benötige ich dort, wie auch in dem oben von Tzu-Mao Li implementierten Referenzlink, die endgültige Wahrscheinlichkeitsdichte eines ganzen Pfades , um das endgültige Gradientenbild zu berechnen. Wie habe ich es für den Fall ohne Next Event Estimation (NEE) berechnet? In diesem Fall (da ich nur diffuse Oberflächen habe) ist diese Wahrscheinlichkeit das Produkt von CosTheta / PI bei jedem Sprung in der Szene. Alles ist in Ordnung und das resultierende Verlaufsbild ist oben dargestellt.
Wenn ich NEE verwende, funktionieren die Dinge stattdessen nicht mehr, da sich die Wahrscheinlichkeitsdichte meines gesamten Pfads ändert und ich nicht verstehe, wie es ist. Das resultierende Gradientendomänenbild mit der Schätzung des nächsten Ereignisses lautet:
Ich muss verstehen, wie man die endgültige Dichtewahrscheinlichkeit eines Pfades berechnet. Kannst du mir dabei helfen? Danke im Voraus!