Forme correcte du terme de géométrie GGX

9

J'essaie d'implémenter un BRDF microfacet dans mon raytracer mais je rencontre des problèmes. Beaucoup d'articles et d'articles que j'ai lus définissent le terme de géométrie partielle en fonction de la vue et des demi-vecteurs: G1 (v, h). Cependant, lors de l'implémentation, j'ai obtenu le résultat suivant:

Terme de géométrie GGX utilisant le demi-vecteur

(La rangée inférieure est diélectrique avec une rugosité de 1,0 à 0,0, la rangée supérieure est métallique avec une rugosité de 1,0 à 0,0)

Il y a un point culminant étrange sur les bords et une coupure autour de nl == 0. Je ne pouvais pas vraiment comprendre d'où cela venait. J'utilise Unity comme référence pour vérifier mes rendus, j'ai donc vérifié leur source de shader pour voir ce qu'ils utilisent et d'après ce que je peux dire, leur terme de géométrie n'est pas du tout paramétré par le demi-vecteur! J'ai donc essayé le même code mais utilisé pour macro surface normale au lieu du demi-vecteur et a obtenu le résultat suivant:

Terme de géométrie GGX utilisant la macro surface normale

À mon œil non averti, cela semble beaucoup plus proche du résultat souhaité. Mais j'ai le sentiment que ce n'est pas correct? La majorité des articles que j'ai lus utilisent le demi-vecteur mais pas tous. Y a-t-il une raison à cette différence?

J'utilise le code suivant comme terme de géométrie:

float RayTracer::GeometryGGX(const Vector3& v, const Vector3& l, const Vector3& n, const Vector3& h, float a)
{
    return G1GGX(v, h, a) * G1GGX(l, h, a);
}

float RayTracer::G1GGX(const Vector3& v, const Vector3& h, float a)
{
    float NoV = Util::Clamp01(cml::dot(v, h));
    float a2 = a * a;

    return (2.0f * NoV) / std::max(NoV + sqrt(a2 + (1.0f - a2) * NoV * NoV), 1e-7f);
}

Et pour référence, voici ma fonction de distribution normale:

float RayTracer::DistributionGGX(const Vector3& n, const Vector3& h, float alpha)
{
    float alpha2 = alpha * alpha;
    float NoH = Util::Clamp01(cml::dot(n, h));
    float denom = (NoH * NoH * (alpha2 - 1.0f)) + 1.0f;
    return alpha2 / std::max((float)PI * denom * denom, 1e-7f);
}
Erwin
la source

Réponses:

5

G1


Pour éviter toute confusion, je suppose que la version isotrope du BRDF, le modèle de microfacet Smith (par opposition au modèle à cavité en V) et la distribution de microfacet GGX.

G1

χ+(ωvωm)21+1+αo2tan2θv

et selon Walter 2007 , la formule est

χ+(ωvωgωvωm)21+1+α2tan2θv

ωmωgωvαχ+(a)a>0

ωmG1ωv

ωv

G1

float SmithMaskingFunctionGgx(
    const Vec3f &aDir,  // the direction to compute masking for (either incoming or outgoing)
    const Vec3f &aMicrofacetNormal,
    const float  aRoughnessAlpha)
{
    PG3_ASSERT_VEC3F_NORMALIZED(aDir);
    PG3_ASSERT_VEC3F_NORMALIZED(aMicrofacetNormal);
    PG3_ASSERT_FLOAT_NONNEGATIVE(aRoughnessAlpha);

    if (aMicrofacetNormal.z <= 0)
        return 0.0f;

    const float cosThetaVM = Dot(aDir, aMicrofacetNormal);
    if ((aDir.z * cosThetaVM) < 0.0f)
        return 0.0f; // up direction is below microfacet or vice versa

    const float roughnessAlphaSqr = aRoughnessAlpha * aRoughnessAlpha;
    const float tanThetaSqr = Geom::TanThetaSqr(aDir);
    const float root = std::sqrt(1.0f + roughnessAlphaSqr * tanThetaSqr);

    const float result = 2.0f / (1.0f + root);

    PG3_ASSERT_FLOAT_IN_RANGE(result, 0.0f, 1.0f);

    return result;
}
ivokabel
la source
Merci pour votre réponse. J'ai implémenté la formule que vous avez fournie et j'ai obtenu des résultats identiques aux miens (lors de l'utilisation de la macrosurface normale). Il semble donc que ce soit juste une forme différente (je l'ai obtenue de: graphicrants.blogspot.nl/2013/08/specular-brdf-reference.html ) J'ai été confus au sujet du demi-vecteur parce que le cours de mathématiques SIGGRAPH 2015 PBS énonce spécifiquement la géométrie fonction dépendante de la vue, de la lumière et des demi-vecteurs. C'est donc une erreur dans les diapositives?
Erwin
nvhv
J'ai utilisé N dot V dans ma nouvelle implémentation, ce qui m'a donné des résultats identiques à la deuxième image que j'ai publiée. Mais je ne comprends toujours pas pourquoi les diapositives du cours PBS indiquent que le vecteur à mi-chemin doit être utilisé (voir: blog.selfshadow.com/publications/s2015-shading-course/hoffman/… , diapositive 88).
Erwin
hvnvG1hv
Oui, c'était le problème. Mais ma principale question était: à quoi sert le demi-vecteur, car il apparaît dans la définition de la fonction. Pour autant que je comprends maintenant, il n'est utilisé dans la vérification que si le point H V est positif. Merci d'avoir pris le temps d'écrire les réponses.
Erwin