BRDF и сферическая координата в трассировке лучей

Я разработал трассировщик лучей, который использует стандартную модель освещения фонг / блинн фонг. Сейчас я модифицирую его для поддержки физического рендеринга, поэтому я реализую различные модели BRDF. На данный момент я сосредоточен на модели Oren-Nayar и Torrance-Sparrow. Каждый из них основан на сферических координатах, используемых для выражения падающего и исходящего двух направлений света.

У меня вопрос: каким путем правильно преобразовать wi и wo из декартовой координаты в сферическую?

Я применяю стандартную формулу, указанную здесь https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions но я не уверен, что поступаю правильно, потому что мой вектор не с хвостом в начале декартовой системы координат, а центрирован в точке пересечения луча с объектом.

Здесь вы можете найти мою текущую реализацию:

Может ли кто-нибудь помочь мне дать объяснение правильного способа преобразования вектора 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;
}

Торранс-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;
}

ОБНОВЛЕНИЕ 2

После некоторых поисков я нашел эту реализацию Орен-Наярской БРДФ.

http://content.gpwiki.org/index.php/D3DBook:(Lighting)_Oren-Nayar

В приведенной выше реализации тэта для wi и wo получается просто с помощью arccos (wo.dotProduct (Normal)) и arccos (wi.dotProduct (Normal)). Мне это кажется разумным, поскольку мы можем использовать нормаль точки пересечения в качестве направления зенита для нашей сферической системы координат и выполнить расчет. Вычисление gamma = cos (phi_wi — phi_wo) делает какую-то проекцию wi и wo на то, что называется «касательным пространством». Предполагая, что в этой реализации все правильно, могу ли я просто использовать формулы | View — Normal x (View.dotProduct (Normal)) | и | Light — Normal x (Light.dotProduct (Normal)) | получить координату фи (вместо использования arctan («что-то»))?

3

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …