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)?