BRDF et coordonnées sphériques en lancer de rayons

9

J'ai développé un traceur de rayons qui utilise un modèle d'éclairage phong / blinn phong standard. Maintenant, je le modifie pour prendre en charge le rendu physique, donc j'implémente différents modèles BRDF. En ce moment, je me concentre sur les modèles Oren-Nayar et Torrance-Sparrow. Chacun d'eux est basé sur des coordonnées sphériques utilisées pour exprimer la direction incidente wi et la direction wo sortante de la lumière.

Ma question est la suivante: de quelle manière la bonne convertit-elle wi et wo des coordonnées cartésiennes en coordonnées sphériques?

J'applique la formule standard indiquée ici https://en.wikipedia.org/wiki/Spherical_coordinate_system#Coordinate_system_conversions mais je ne suis pas sûr de faire la bonne chose, car mon vecteur n'est pas avec une queue à l'origine du système de coordonnées cartésiennes, mais sont centrées sur le point d'intersection du rayon avec l'objet.

Ici vous pouvez trouver mon implémentation actuelle:

Quelqu'un peut-il m'aider à expliquer la bonne façon de convertir le vecteur wi et wo de la carte cartésienne en coordonnées sphériques?

MISE À JOUR

Je copie ici la partie pertinente du code:

calcul de coordonnées sphériques

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;
}

MISE À JOUR 2

Après quelques recherches, j'ai trouvé cette implémentation de Oren-Nayar BRDF .

Dans l'implémentation ci-dessus, thêta pour wi et wo est obtenu simplement en faisant arccos (wo.dotProduct (Normal)) et arccos (wi.dotProduct (Normal)). Cela me semble raisonnable, car nous pouvons utiliser la normale du point d'intersection comme direction zénithale pour notre système de coordonnées sphériques et faire le calcul. Le calcul de gamma = cos (phi_wi - phi_wo) fait une sorte de projection de wi et wo sur ce qu'il appelle "l'espace tangent". En supposant que tout est correct dans cette implémentation, puis-je simplement utiliser les formules | View - Normal x (View.dotProduct (Normal)) | et | Light - Normal x (Light.dotProduct (Normal)) | obtenir la coordonnée phi (au lieu d'utiliser arctan ("quelque chose"))?

Fabrizio Duroni
la source
N'importe qui pourrait m'aider?
Fabrizio Duroni
Pouvez-vous montrer l'extrait de code exact, pas l'intégralité du dépôt?
concept3d
Il semble que ce soit l'une des questions les plus mystérieuses à propos du lancer de rayons de tous les temps: D
Fabrizio Duroni
Je vous encourage à demander ici computergraphics.stackexchange.com
concept3d
Fait @ concept3d. Vous pouvez le trouver ici computergraphics.stackexchange.com/questions/1799/…
Fabrizio Duroni

Réponses:

2

C'est en fait mieux ne pas pas utiliser de coordonnées sphériques (ou aucun angle d'ailleurs) pour implémenter BRDF, mais plutôt de travailler directement dans le système de coordonnées cartésiennes et d'utiliser le cosinus de l'angle entre les vecteurs, qui est un produit scalaire simple entre les vecteurs unitaires comme vous le savez. C'est à la fois plus robuste et plus efficace.

Pour Oren-Nayar, vous pouvez penser que vous devez utiliser des angles (en raison du min / max des angles), mais vous pouvez simplement implémenter le BRDF directement dans l'espace cartésien: https://fgiesen.wordpress.com/2010/10/21 / finissez-vos-dérivations-s'il vous plaît

Pour les BRDF en microfacet Torrance-Sparrow ou Cook-Torrance, vous n'avez pas besoin non plus d'utiliser des coordonnées sphériques. Dans ces BRDF, l'angle est transmis à une fonction trigonométrique (généralement cosinus) en termes D / F / G et au dénominateur BRDF, vous pouvez donc utiliser des identités droites ou trigonométriques de produit scalaire sans passer par des coordonnées sphériques.

JarkkoL
la source
1

Vous pouvez spécifier un système de coordonnées étant donné le N normal et un autre vecteur. Nous choisirons wi. Ainsi, tout vecteur ayant la même direction que wi lorsqu'il est projeté sur le plan tangent aura un azimut de 0

Tout d'abord, nous projetons wi sur le plan tangent: (en supposant que wi est déjà normalisé)

wit = normalize(wi - N * dot(wi, N))

maintenant, nous pouvons faire de même avec wo:

wot = normalize(wo - N * dot(wo, N))

Maintenant, wit et wot se trouvent tous les deux sur un plan orthogonal à N et tangent au point d'intersection.

Nous pouvons maintenant calculer l'angle entre les deux:

azimuth = arcos ( dot(wit, wot) )

C'est vraiment l'azimut du wot par rapport à l'esprit quand il est projeté sur le plan tangent.

Gato
la source
0

Si vous connaissez le point d'intersection et le point d'origine, ne s'agirait-il pas simplement de soustraire l'un de l'autre pour obtenir le résultat comme s'il provenait de l'origine?

Si vous ne croyez pas le résultat et que vous souhaitez y arriver à long terme, vous pouvez également obtenir la transformation de rotation pour passer d'un point à un autre via une matrice LookAt, puis la décomposer pour obtenir la composante rotationnelle. Vous pouvez également en obtenir un quaternion si vous le souhaitez.

Les résultats sont égaux. La preuve est un peu longue, mais pas compliquée, et est laissée au lecteur.

Pyjama Panda
la source
Bonjour @Panda Pyjama, merci pour votre réponse, mais je ne comprends pas votre réponse. J'essaie de clarifier: si j'avais le point d'intersection et le point de vue, je peux calculer wi et wo. Ensuite, je peux utiliser la normale comme direction zénithale pour calculer, mais je ne suis pas en mesure de trouver l'autre axe nécessaire pour trouver l'angle d'azimut sur un plan orthogonal au zénith. Dans l'exemple ci-dessus, j'ai simplement appliqué les formules de conversion pour les coordonnées sphériques sur wi et wo données dans le système de coordonnées mondial, mais je ne pense pas que ce soit la bonne façon de calculer thêta et phi.
Fabrizio Duroni