Ich habe einen Ray Tracer entwickelt, der ein Standardmodell für Phong / Blinn Phong-Beleuchtung verwendet. Jetzt ändere ich es, um physisch basiertes Rendering zu unterstützen, und implementiere verschiedene BRDF-Modelle. Im Moment konzentriere ich mich auf das Modell Oren-Nayar und Torrance-Sparrow. Jedes von diesen basiert auf sphärischen Koordinaten, die verwendet werden, um einfallendes wi und ausgehende wo Lichtrichtung auszudrücken.
Meine Frage ist: Welcher Weg ist der richtige, um wi und wo von kartesischer Koordinate in sphärische Koordinate umzuwandeln?
Ich wende die hier angegebene Standardformel an: https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions , bin mir aber nicht sicher, ob ich das Richtige tue, da mein Vektor nicht mit dem Schwanz am Ursprung des kartesisches Koordinatensystem, sind jedoch auf dem Schnittpunkt des Strahls mit dem Objekt zentriert.
Hier finden Sie meine aktuelle Implementierung:
https://github.com/chicio/Multispectral-Ray-tracing/tree/brdf/RayTracing/RayTracer/Objects/BRDF
https://github.com/chicio/Multispectral-Ray-tracing/blob/brdf/RayTracing/RayTracer/Math/Vector3D.cpp
Kann mir jemand helfen, eine Erklärung für die korrekte Konvertierung des wi und wo-Vektors von kartesischen in sphärische Koordinaten zu geben?
AKTUALISIEREN
Ich kopiere hier den relevanten Teil des Codes:
sphärische Koordinatenberechnung
float Vector3D::sphericalTheta() const {
float sphericalTheta = acosf(Utils::clamp(y, -1.f, 1.f));
return sphericalTheta;
}
float Vector3D::sphericalPhi() const {
float phi = atan2f(z, x);
return (phi < 0.f) ? phi + 2.f * M_PI : phi;
}
Oren Nayar
OrenNayar::OrenNayar(Spectrum<constant::spectrumSamples> reflectanceSpectrum, float degree) : reflectanceSpectrum{reflectanceSpectrum} {
float sigma = Utils::degreeToRadian(degree);
float sigmaPowerTwo = sigma * sigma;
A = 1.0f - (sigmaPowerTwo / 2.0f * (sigmaPowerTwo + 0.33f));
B = 0.45f * sigmaPowerTwo / (sigmaPowerTwo + 0.09f);
};
Spectrum<constant::spectrumSamples> OrenNayar::f(const Vector3D& wi, const Vector3D& wo, const Intersection* intersection) const {
float thetaI = wi.sphericalTheta();
float phiI = wi.sphericalPhi();
float thetaO = wo.sphericalTheta();
float phiO = wo.sphericalPhi();
float alpha = std::fmaxf(thetaI, thetaO);
float beta = std::fminf(thetaI, thetaO);
Spectrum<constant::spectrumSamples> orenNayar = reflectanceSpectrum * constant::inversePi * (A + B * std::fmaxf(0, cosf(phiI - phiO) * sinf(alpha) * tanf(beta)));
return orenNayar;
}
Torrance-Sparrow
float TorranceSparrow::G(const Vector3D& wi, const Vector3D& wo, const Vector3D& wh, const Intersection* intersection) const {
Vector3D normal = intersection->normal;
normal.normalize();
float normalDotWh = fabsf(normal.dot(wh));
float normalDotWo = fabsf(normal.dot(wo));
float normalDotWi = fabsf(normal.dot(wi));
float woDotWh = fabsf(wo.dot(wh));
float G = fminf(1.0f, std::fminf((2.0f * normalDotWh * normalDotWo)/woDotWh, (2.0f * normalDotWh * normalDotWi)/woDotWh));
return G;
}
float TorranceSparrow::D(const Vector3D& wh, const Intersection* intersection) const {
Vector3D normal = intersection->normal;
normal.normalize();
float cosThetaH = fabsf(wh.dot(normal));
float Dd = (exponent + 2) * constant::inverseTwoPi * powf(cosThetaH, exponent);
return Dd;
}
Spectrum<constant::spectrumSamples> TorranceSparrow::f(const Vector3D& wi, const Vector3D& wo, const Intersection* intersection) const {
Vector3D normal = intersection->normal;
normal.normalize();
float thetaI = wi.sphericalTheta();
float thetaO = wo.sphericalTheta();
float cosThetaO = fabsf(cosf(thetaO));
float cosThetaI = fabsf(cosf(thetaI));
if(cosThetaI == 0 || cosThetaO == 0) {
return reflectanceSpectrum * 0.0f;
}
Vector3D wh = (wi + wo);
wh.normalize();
float cosThetaH = wi.dot(wh);
float F = Fresnel::dieletricFresnel(cosThetaH, refractiveIndex);
float g = G(wi, wo, wh, intersection);
float d = D(wh, intersection);
printf("f %f g %f d %f \n", F, g, d);
printf("result %f \n", ((d * g * F) / (4.0f * cosThetaI * cosThetaO)));
Spectrum<constant::spectrumSamples> torranceSparrow = reflectanceSpectrum * ((d * g * F) / (4.0f * cosThetaI * cosThetaO));
return torranceSparrow;
}
UPDATE 2
Nach einiger Suche fand ich diese Implementierung von Oren-Nayar BRDF .
In der obigen Implementierung wird Theta für wi und wo erhalten, indem einfach arccos (wo.dotProduct (Normal)) und arccos (wi.dotProduct (Normal)) ausgeführt werden. Dies erscheint mir vernünftig, da wir die Normalen des Schnittpunkts als Zenitrichtung für unser sphärisches Koordinatensystem verwenden und die Berechnung durchführen können. Die Berechnung von gamma = cos (phi_wi - phi_wo) führt eine Art Projektion von wi und wo auf das durch, was es "Tangentenraum" nennt. Vorausgesetzt, in dieser Implementierung ist alles korrekt, kann ich einfach die Formeln | View - Normal x (View.dotProduct (Normal)) | verwenden und | Licht - Normal x (Light.dotProduct (Normal)) | um die Phi-Koordinate zu erhalten (anstatt Arctan ("etwas") zu verwenden)?
quelle
Antworten:
Es ist eigentlich besser, keine sphärischen Koordinaten (oder Winkel) für die Implementierung von BRDFs zu verwenden, sondern direkt im kartesischen Koordinatensystem zu arbeiten und den Kosinus des Winkels zwischen Vektoren zu verwenden, der bekanntlich ein einfaches Punktprodukt zwischen Einheitsvektoren ist. Dies ist sowohl robuster als auch effizienter.
Für Oren-Nayar denken Sie vielleicht, dass Sie Winkel verwenden müssen (aufgrund der minimalen / maximalen Winkel), aber Sie können die BRDF einfach direkt im kartesischen Raum implementieren: https://fgiesen.wordpress.com/2010/10/21 / Beende-deine-Ableitungen-bitte
Für Torrance-Sparrow- oder Cook-Torrance-Mikrofacetten-BRDFs müssen Sie auch keine sphärischen Koordinaten verwenden. In diesen BRDFs wird der Winkel in D / F / G-Begriffen und im BRDF-Nenner an eine trigonometrische (normalerweise Kosinus-) Funktion übergeben, sodass Sie gerade oder trigonometrische Identitäten von Punktprodukten verwenden können, ohne sphärische Koordinaten zu durchlaufen.
quelle
Sie können ein Koordinatensystem mit dem normalen N und einem anderen Vektor angeben. Wir werden wi wählen. Jeder Vektor, der die gleiche Richtung wie wi hat, wenn er auf die Tangentialebene projiziert wird, hat einen Azimut von 0
Zuerst projizieren wir wi auf die Tangentialebene: (vorausgesetzt, wi ist bereits normalisiert)
Jetzt können wir dasselbe mit wo machen:
Nun liegen Witz und Witz sowohl auf einer Ebene, die orthogonal zu N ist, als auch tangential zum Schnittpunkt.
Wir können jetzt den Winkel zwischen den beiden berechnen:
Welches ist wirklich der Azimut von Wot in Bezug auf Witz, wenn auf die Tangentialebene projiziert.
quelle
Wenn Sie den Schnittpunkt und den Ursprungspunkt kennen, geht es dann nicht nur darum, einen vom anderen zu subtrahieren, damit Sie das Ergebnis so erhalten, als wäre es vom Ursprung?
Wenn Sie dem Ergebnis nicht glauben und auf dem langen Weg dorthin gelangen möchten, können Sie die Rotationstransformation auch über eine LookAt-Matrix von einem Punkt zum anderen bringen und dann zerlegen, um die Rotationskomponente zu erhalten. Sie können auch eine Quaternion daraus erhalten, wenn Sie möchten.
Die Ergebnisse sind gleich. Der Beweis ist etwas lang, aber nicht kompliziert und bleibt dem Leser überlassen.
quelle