Richtige Form des GGX-Geometriebegriffs

9

Ich versuche, ein Mikrofacetten-BRDF in meinem Raytracer zu implementieren, stoße jedoch auf einige Probleme. Viele der Artikel und Artikel, die ich gelesen habe, definieren den Teilgeometrie-Term als Funktion der Ansicht und der Halbvektoren: G1 (v, h). Bei der Implementierung habe ich jedoch folgendes Ergebnis erhalten:

GGX-Geometrieterm unter Verwendung des Halbvektors

(Die untere Reihe ist dielektrisch mit einer Rauheit von 1,0 bis 0,0, die obere Reihe ist metallisch mit einer Rauheit von 1,0 bis 0,0)

Es gibt ein seltsames Highlight an den Rändern und einen Cut-Off um nl == 0. Ich konnte nicht wirklich herausfinden, woher das kommt. Ich verwende Unity als Referenz, um meine Renderings zu überprüfen, also habe ich ihre Shader-Quelle überprüft, um zu sehen, was sie verwenden und was ich sagen kann, dass ihr Geometrie-Term überhaupt nicht durch den Halbvektor parametrisiert wird! Also habe ich den gleichen Code ausprobiert, aber anstelle des Halbvektors die Makrooberflächennormale verwendet und das folgende Ergebnis erhalten:

GGX-Geometriebegriff unter Verwendung der Makrooberflächennormalen

Für mein ungeübtes Auge scheint dies dem gewünschten Ergebnis viel näher zu kommen. Aber ich habe das Gefühl, das ist nicht richtig? Die meisten Artikel, die ich lese, verwenden den halben Vektor, aber nicht alle. Gibt es einen Grund für diesen Unterschied?

Ich verwende den folgenden Code als Geometriebegriff:

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

Und als Referenz ist dies meine normale Verteilungsfunktion:

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
quelle

Antworten:

5

G1


Um Verwirrung zu vermeiden, gehe ich von der isotropen Version des BRDF, dem Smith-Mikrofacettenmodell (im Gegensatz zum V-Cavity-Modell) und der GGX-Mikrofacettenverteilung aus.

G1

χ+(ωvωm)21+1+αÖ2bräunen2θv

und laut Walter 2007 lautet die Formel

χ+(ωvωGωvωm)21+1+α2bräunen2θv

ωmωGωvαχ+(ein)ein>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
quelle
Danke für Ihre Antwort. Ich habe die von Ihnen angegebene Formel implementiert und habe identische Ergebnisse wie mit meiner eigenen erzielt (bei Verwendung der normalen Makrooberfläche). Es scheint also nur eine andere Form zu sein (ich habe sie erhalten von: graphicrants.blogspot.nl/2013/08/specular-brdf-reference.html ). Ich war verwirrt über den Halbvektor, weil der PBS-Mathematikkurs SIGGRAPH 2015 speziell die Geometrie angibt Funktion abhängig von der Ansicht, Licht und Halbvektoren. Das ist also ein Fehler in den Folien?
Erwin
nvhv
Ich habe in meiner neuen Implementierung N dot V verwendet, was zu identischen Ergebnissen wie das zweite Bild führte, das ich gepostet habe. Ich bin mir jedoch immer noch nicht sicher , warum in den PBS- Kursfolien angegeben ist , dass der Halfway-Vektor verwendet werden soll (siehe: blog.selfshadow.com/publications/s2015-shading-course/hoffman/… , Folie 88).
Erwin
Verstehe ich das richtig mit hvnvG1hv
Ja, das war das Problem. Aber meine Hauptfrage war: Wofür wird der Halbvektor verwendet, da er in der Funktionsdefinition erscheint? Soweit ich jetzt verstehe, wird es nur bei der Prüfung verwendet, ob der H-Punkt V positiv ist. Vielen Dank, dass Sie sich die Zeit genommen haben, die Antworten zu schreiben.
Erwin