Tôi đã phát triển một máy dò tia sử dụng mô hình chiếu sáng phong / phong chuẩn. Bây giờ tôi đang sửa đổi nó để hỗ trợ kết xuất dựa trên vật lý, vì vậy tôi đang triển khai các mô hình BRDF khác nhau. Hiện tại tôi đang tập trung vào mô hình Oren-Nayar và Torrance-Sparrow. Mỗi một trong số này được dựa trên tọa độ hình cầu được sử dụng để thể hiện sự cố wi và hướng ánh sáng đi ra.
Câu hỏi của tôi là: cách nào là đúng khi chuyển đổi wi và wo từ tọa độ cartesian sang tọa độ hình cầu?
Tôi đang áp dụng công thức tiêu chuẩn được báo cáo ở đây https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions nhưng tôi không chắc chắn mình đang làm đúng, vì vectơ của tôi không có đuôi ở gốc của hệ tọa độ cartesian, nhưng tập trung vào điểm giao nhau của tia với vật.
Ở đây bạn có thể tìm thấy triển khai hiện tại của tôi:
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
Ai đó có thể giúp tôi đưa ra lời giải thích về cách chính xác để chuyển đổi vectơ wi và wo từ cartesian sang tọa độ hình cầu không?
CẬP NHẬT
Tôi sao chép ở đây phần có liên quan của mã:
tính toán tọa độ hình cầu
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;
}
Chim sẻ-chim sẻ
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;
}
CẬP NHẬT 2
Sau một số tìm kiếm, tôi thấy triển khai này của Oren-Nayar BRDF .
Trong quá trình thực hiện ở trên, theta cho wi và wo thu được chỉ đơn giản là thực hiện arccos (wo.dot SẢNt (Bình thường)) và arccos (wi.dot SẢNt (Bình thường)). Điều này có vẻ hợp lý với tôi, vì chúng ta có thể sử dụng bình thường của điểm giao nhau làm hướng thiên đỉnh cho hệ tọa độ hình cầu của mình và thực hiện phép tính. Việc tính toán gamma = cos (phi_wi - phi_wo) thực hiện một số loại hình chiếu của wi và wo trên cái mà nó gọi là "không gian tiếp tuyến". Giả sử mọi thứ đều đúng trong triển khai này, tôi có thể chỉ sử dụng các công thức | Chế độ xem - Bình thường x (View.dotSản phẩm (Bình thường)) | và | Ánh sáng - Bình thường x (Light.dotSản phẩm (Bình thường)) | để có được tọa độ phi (thay vì sử dụng arctan ("cái gì đó"))?