ฉันพัฒนาเครื่องติดตามรังสีที่ใช้โมเดลไฟพงษ์ / บลินมาตรฐาน ตอนนี้ฉันกำลังแก้ไขเพื่อสนับสนุนการเรนเดอร์ตามร่างกายดังนั้นฉันจึงใช้โมเดล BRDF ต่างๆ ในขณะนี้ฉันมุ่งเน้นโมเดลของ Oren-Nayar และ Torrance-Sparrow แต่ละอันขึ้นอยู่กับพิกัดทรงกลมที่ใช้ในการแสดงเหตุการณ์ที่เกิดขึ้นและทิศทางแสง wo ขาออก
คำถามของฉันคือวิธีใดที่เหมาะสมที่จะแปลง wi และ wo จากพิกัดคาร์ทีเซียนไปเป็นพิกัดกลม
ฉันกำลังใช้สูตรมาตรฐานที่รายงานที่นี่https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversionsแต่ฉันไม่แน่ใจว่าฉันทำสิ่งที่ถูกต้องเพราะเวกเตอร์ของฉันไม่ได้อยู่ที่ต้นกำเนิดของ ระบบพิกัดคาร์ทีเซียน แต่มีศูนย์กลางอยู่ที่จุดตัดของรังสีกับวัตถุ
ที่นี่คุณสามารถค้นหาการใช้งานปัจจุบันของฉัน:
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
ใครสามารถช่วยฉันอธิบายวิธีที่ถูกต้องในการแปลงเวกเตอร์ wi และ wo จากคาร์ทีเซียนไปเป็นพิกัดกลม
UPDATE
ฉันคัดลอกส่วนที่เกี่ยวข้องของรหัสที่นี่:
การคำนวณพิกัดทรงกลม
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;
}
โอเรนายาร์
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-กระจอก
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;
}
อัพเดท 2
หลังจากการค้นหาบางอย่างฉันพบการใช้งาน Oren-Nayar BRDFนี้
ในการดำเนินการดังกล่าวข้างต้น theta สำหรับ wi และ wo นั้นได้รับเพียงแค่ทำ arccos (wo.dotProduct (ปกติ)) และ arccos (wi.dotProduct (ปกติ)) สิ่งนี้ดูสมเหตุสมผลสำหรับฉันเนื่องจากเราสามารถใช้จุดปกติของจุดตัดเป็นทิศทางที่ดีที่สุดสำหรับระบบพิกัดทรงกลมของเราและทำการคำนวณ การคำนวณแกมม่า = cos (phi_wi - phi_wo) ทำการประมาณการแบบ wi และ wo ในสิ่งที่เรียกว่า "พื้นที่สัมผัสกัน" สมมติว่าทุกอย่างถูกต้องในการนำไปใช้นี้ฉันสามารถใช้สูตร | ดู - ปกติ x (View.dotProduct (ปกติ)) | และ | แสง - ปกติ x (Light.dotProduct (ปกติ)) การรับพิกัด phi (แทนที่จะใช้ arctan ("อะไร"))?