Desarrollé un rastreador de rayos que utiliza el modelo de iluminación phong / blinn phong estándar. Ahora lo estoy modificando para admitir renderizado basado físicamente, así que estoy implementando varios modelos BRDF. En este momento estoy enfocado en el modelo Oren-Nayar y Torrance-Sparrow. Cada uno de estos se basa en coordenadas esféricas utilizadas para expresar el incidente y la dirección saliente de la luz.
Mi pregunta es: ¿de qué manera es el correcto convertir wi y wo de coordenadas cartesianas a coordenadas esféricas?
Estoy aplicando la fórmula estándar que se informa aquí https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions, pero no estoy seguro de estar haciendo lo correcto, porque mi vector no tiene cola en el origen de la sistema de coordenadas cartesianas, pero se centran en el punto de intersección del rayo con el objeto.
Aquí puedes encontrar mi implementación actual:
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
¿Alguien puede ayudarme a dar una explicación de la forma correcta de convertir el vector wi y wo de coordenadas cartesianas a esféricas?
ACTUALIZAR
Copio aquí la parte relevante del código:
cálculo de coordenadas esféricas
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;
}
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;
}
ACTUALIZACIÓN 2
Después de algunas búsquedas, encontré esta implementación de Oren-Nayar BRDF .
En la implementación anterior, theta para wi y wo se obtiene simplemente haciendo arccos (wo.dotProduct (Normal)) y arccos (wi.dotProduct (Normal)). Esto me parece razonable, ya que podemos usar la normalidad del punto de intersección como la dirección cenital de nuestro sistema de coordenadas esféricas y hacer el cálculo. El cálculo de gamma = cos (phi_wi - phi_wo) hace algún tipo de proyección de wi y wo en lo que llama "espacio tangente". Suponiendo que todo sea correcto en esta implementación, ¿puedo usar las fórmulas | Ver - Normal x (View.dotProduct (Normal)) | y | Light - Normal x (Light.dotProduct (Normal)) | para obtener la coordenada phi (en lugar de usar arctan ("algo"))?
fuente
Respuestas:
En realidad, es mejor no usar coordenadas esféricas (o cualquier ángulo para el caso) para implementar BRDF, sino trabajar directamente en el sistema de coordenadas cartesianas y usar el coseno del ángulo entre vectores, que es un producto de punto simple entre vectores unitarios, como ya sabes. Esto es más robusto y eficiente.
Para Oren-Nayar, puede pensar que tiene que usar ángulos (debido al mínimo / máximo de los ángulos), pero simplemente puede implementar el BRDF directamente en el espacio cartesiano: https://fgiesen.wordpress.com/2010/10/21 / terminar-tus-derivaciones-por favor
Para los BRDF de microfaceta Torrance-Sparrow o Cook-Torrance tampoco necesita utilizar coordenadas esféricas. En estos BRDF, el ángulo se pasa a una función trigonométrica (generalmente coseno) en términos D / F / G y el denominador BRDF, por lo que puede usar identidades rectas o trigonométricas de productos de puntos sin pasar por coordenadas esféricas.
fuente
Puede especificar un sistema de coordenadas dado el N normal y otro vector. Elegiremos wi. Por lo tanto, cualquier vector que tenga la misma dirección que wi cuando se proyecta en el plano tangente tendrá un acimut de 0
Primero, proyectamos wi en el plano tangente: (suponiendo que wi ya está normalizado)
ahora, podemos hacer lo mismo con wo:
Ahora, ingenio y ingenio se encuentran tanto en un plano que es ortogonal a N como tangente al punto de intersección.
Podemos calcular el ángulo entre los dos ahora:
Que es realmente el acimut de wot con respecto al ingenio cuando se proyecta en el plano tangente.
fuente
Si conoce el punto de intersección y el punto de origen, ¿no sería solo una cuestión de restar uno del otro para obtener el resultado como si fuera del origen?
Si no cree en el resultado y desea llegar a ese punto, también puede obtener la transformación de rotación para ir de un punto a otro a través de una matriz LookAt y luego descomponerla para obtener el componente rotacional. También puede obtener un cuaternión si lo desea.
Los resultados son iguales. La prueba es un poco larga, pero no complicada, y se deja al lector.
fuente