표준 phong / blinn phong 조명 모델을 사용하는 광선 추적기를 개발했습니다. 이제 물리 기반 렌더링을 지원하도록 수정하고 있으므로 다양한 BRDF 모델을 구현하고 있습니다. 현재 저는 Oren-Nayar 및 Torrance-Sparrow 모델에 중점을두고 있습니다. 이들 각각은 입사 wi 및 나가는 광 방향을 표현하는 데 사용되는 구형 좌표를 기반으로합니다.
내 질문은 : 올바른 방법으로 직교 좌표에서 구형 좌표로 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 벡터를 직교 좌표에서 구형 좌표로 변환하는 올바른 방법에 대한 설명을 도와 줄 수 있습니까?
최신 정보
코드의 관련 부분을 여기에 복사합니다.
구면 좌표 계산
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;
}
토 런스 참새
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 구현을 발견 했습니다 .
위의 구현에서 wi 및 wo에 대한 세타는 단순히 arccos (wo.dotProduct (Normal)) 및 arccos (wi.dotProduct (Normal))를 수행하여 얻습니다. 우리는 구형 좌표계의 천정 방향으로 교차점의 법선을 사용하고 계산을 수행 할 수 있기 때문에 나에게 합리적인 것처럼 보입니다. 감마 = cos (phi_wi-phi_wo)의 계산은 "탄젠트 스페이스"라고 불리는 것에 대해 일종의 wi 및 wo 투영을 수행합니다. 이 구현에서 모든 것이 정확하다고 가정하면 수식을 사용할 수 있습니다. | View-Normal x (View.dotProduct (Normal)) | 및 | Light-표준 x (Light.dotProduct (Normal)) | arc 좌표 ( "something") 대신 phi 좌표를 얻으려면?