Standart phong / blinn phong aydınlatma modelini kullanan bir ışın izleyici geliştirdim. Şimdi fiziksel olarak oluşturmayı destekleyecek şekilde değiştiriyorum, bu yüzden çeşitli BRDF modelleri uyguluyorum. Şu anda Oren-Nayar ve Torrance-Sparrow modeline odaklandım. Bunların her biri, olay wi ve giden ışık yönünü ifade etmek için kullanılan küresel koordinatlara dayanmaktadır.
Benim sorum şu: doğru olan ne wi ve wo'yu kartezyen koordinattan küresel koordinatlara dönüştürüyor?
Burada bildirilen standart formülü uyguluyorum https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions ama doğru şeyi yaptığımdan emin değilim, çünkü vektörüm kökeninde kuyruklu değil kartezyen koordinat sistemi, ancak nesne ile ışının kesişme noktası üzerinde merkezlenir.
Burada mevcut uygulamamı bulabilirsiniz:
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
Herkes wi ve wo vektörü kartezyen küresel koordinat dönüştürmek için doğru yolu açıklamak bana yardımcı olabilir?
GÜNCELLEME
Kodun ilgili kısmını buraya kopyalarım:
küresel koordinat hesabı
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;
}
Ören 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;
}
GÜNCELLEME 2
Bazı araştırmalardan sonra Oren-Nayar BRDF'nin bu uygulamasını buldum .
Yukarıdaki uygulamada wi ve wo için teta sadece arccos (wo.dotProduct (Normal)) ve arccos (wi.dotProduct (Normal)) yapılarak elde edilir. Bu benim için makul görünüyor, çünkü kavşak noktasının normalini küresel koordinat sistemimiz için zirve yönü olarak kullanabilir ve hesaplamayı yapabiliriz. Gama = cos (phi_wi - phi_wo) hesaplaması, “teğet boşluk” olarak adlandırdığı şeye bir tür wi ve wo projeksiyonu yapar. Bu uygulamada her şeyin doğru olduğunu varsayarsak, sadece formülleri kullanabilir miyim | View - Normal x (View.dotProduct (Normal)) | ve | Işık - Normal x (Light.dotProduct (Normal)) | phi koordinatını elde etmek için (arctan kullanmak yerine ("bir şey"))?