J'ai développé un traceur de rayons qui utilise un modèle d'éclairage phong / blinn phong standard. Maintenant, je le modifie pour prendre en charge le rendu physique, donc j'implémente différents modèles BRDF. En ce moment, je me concentre sur les modèles Oren-Nayar et Torrance-Sparrow. Chacun d'eux est basé sur des coordonnées sphériques utilisées pour exprimer la direction incidente wi et la direction wo sortante de la lumière.
Ma question est la suivante: de quelle manière la bonne convertit-elle wi et wo des coordonnées cartésiennes en coordonnées sphériques?
J'applique la formule standard indiquée ici https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions mais je ne suis pas sûr de faire la bonne chose, car mon vecteur n'est pas avec une queue à l'origine du système de coordonnées cartésiennes, mais sont centrées sur le point d'intersection du rayon avec l'objet.
Ici vous pouvez trouver mon implémentation actuelle:
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
Quelqu'un peut-il m'aider à expliquer la bonne façon de convertir le vecteur wi et wo de la carte cartésienne en coordonnées sphériques?
MISE À JOUR
Je copie ici la partie pertinente du code:
calcul de coordonnées sphériques
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;
}
MISE À JOUR 2
Après quelques recherches, j'ai trouvé cette implémentation de Oren-Nayar BRDF .
Dans l'implémentation ci-dessus, thêta pour wi et wo est obtenu simplement en faisant arccos (wo.dotProduct (Normal)) et arccos (wi.dotProduct (Normal)). Cela me semble raisonnable, car nous pouvons utiliser la normale du point d'intersection comme direction zénithale pour notre système de coordonnées sphériques et faire le calcul. Le calcul de gamma = cos (phi_wi - phi_wo) fait une sorte de projection de wi et wo sur ce qu'il appelle "l'espace tangent". En supposant que tout est correct dans cette implémentation, puis-je simplement utiliser les formules | View - Normal x (View.dotProduct (Normal)) | et | Light - Normal x (Light.dotProduct (Normal)) | obtenir la coordonnée phi (au lieu d'utiliser arctan ("quelque chose"))?