GGX geometri teriminin doğru şekli


9

Işın izleyicime bir mikrofacet BRDF uygulamaya çalışıyorum ama bazı sorunlarla karşılaşıyorum. Okuduğum birçok makale ve makale, kısmi geometri terimini görünüm ve yarım vektörlerin bir fonksiyonu olarak tanımlamaktadır: G1 (v, h). Ancak, bunu uygularken aşağıdaki sonucu aldım:

GGX Yarım vektörünü kullanan geometri terimi

(Alt sıra 1.0 - 0.0 pürüzlü dielektrik, Üst sıra 1.0 - 0.0 pürüzlü metalik)

Kenarlarda garip bir vurgu ve nl == 0 civarında bir kesim var. Bunun nereden geldiğini gerçekten anlayamadım. Ben onların render ve kontrol etmek için bir referans olarak Unity kullanıyorum ne kullandıklarını görmek için gölgelendirici kaynak kontrol ve geometri terim hiç yarım vektör tarafından parametreleştirilmiş değil ne söyleyebilirim! Bu yüzden aynı kodu denedim ama yarım vektör yerine normal makro yüzeyine kullanılır ve aşağıdaki sonucu elde ettim:

Makro yüzeyini kullanan GGX geometri terimi normal

Eğitimsiz gözüme göre bu istenen sonuca çok daha yakın görünüyor. Ama bunun doğru olmadığını hissediyorum? Okuduğum makalelerin çoğunda yarım vektör kullanılıyor, ancak hepsi kullanılmıyor. Bu farkın bir nedeni var mı?

Geometri terimi olarak aşağıdaki kodu kullanıyorum:

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

Ve referans olarak, bu benim normal dağıtım fonksiyonum:

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

Yanıtlar:


5

TL; DR: formülünüz yanlış.G1


Sadece karışıklığı önlemek için, BRDF'nin izotropik versiyonunu, Smith mikrofaset modelini (V-kavite modelinin aksine) ve GGX mikrofacet dağılımını varsayıyorum.

Göre Heitz 2014 , maskeleme / gölgeleme terimi olanG1

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

ve Walter 2007'ye göre formül

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

Burada microfacet normal yönde (yarım vektör), bir , ana (geometrik) normal yön (normal) olduğunu gelen veya giden yönü, izotropik pürüzlülük parametresi ve , pozitif karakteristik işlev veya Heaviside adım işlevidir ( aksi takdirde ve sıfır ise bire eşittir ).ωmωgωvαχ+(a)a>0

Fark edebileceğiniz gibi, yarım vektör sadece geometrik konfigürasyon yasaklandığında sıfır olduğundan emin olmak için kullanılır . Daha kesin olarak, mikro yüzeyin arka yüzeyinin ön tarafındaki yönünden asla görünmemesini ve tam tersini sağlar (ikinci durum sadece kırılmalar desteklendiğinde anlamlıdır). Arama kodu bunu garanti ediyorsa, bu parametreyi açıkça atlayabilirsiniz. Muhtemelen Birlik'te bunu yapmasının nedeni budur.ωmG1ωv

Öte yandan, uygulamanız, mikrofasete göre yönünün kosinüsünü hesaplamak için yarım vektörünü kullanır ve bu da sunulan formüllerden başka bir şeyin hesaplanmasına yol açar.ωv

Herhangi bir yardım varsa, o zaman bu benim faktörü benim uygulamasıdır :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;
}

Cevabın için teşekkürler. Verdiğiniz formülü uyguladım ve kendimle aynı sonuçları aldım (normal makro yüzeyi kullanırken). Bu sadece farklı bir form gibi görünüyor (bunu aldım: graphicrants.blogspot.nl/2013/08/specular-brdf-reference.html ) SIGGRAPH 2015 PBS matematik kursu geometriyi özellikle belirttiği için yarı vektör hakkında kafam karıştı görünüm, ışık ve yarım vektörlere bağlı fonksiyon. Peki bu slaytlardaki bir hata mı?
Erwin

@Erwin, şimdi formülün kendisini de sağladığınıza göre, çok daha net. Bir dahaki sefere doğru başlangıçta, yardımcı olur. Evet, her iki sürüm de (benimki ve sizinki) eşdeğerdir, ancak ikisi de sinüs veya teğet işlevini hesaplamak için yarım vektör kullanmaz. Uygulamanızda yaptığınız gibi yerine kullanır - bu bir hata gibi görünüyor. Yeni uygulamada da aynı hatayı yaptığınızdan şüpheleniyorum. nvhv
ivokabel

Yeni uygulamamda N dot V kullandım, bu da bana gönderdiğim ikinci görüntüye aynı sonuçları verdi. Ancak PBS kurs slaytlarının neden yarım vektörünün kullanılması gerektiğini belirttiklerinden hala emin değilim (Bkz: blog.selfshadow.com/publications/s2015-shading-course/hoffman/… , Slayt 88).
Erwin

yerine kullanmanın sorun olduğunu doğru anladım mı? yarım vektörün kullanımıyla ilgili : Aslında her iki sürümde de kullanılıyor (LaTeX formülünü oluştururken bir hata yaptım ve ilkine jeomeotrik normal yazdım, yakında düzeltirim), ancak nokta, yarım vektörün kosinüs değerini hesaplamak için kullanılmadığıdır (yani, kullanılan yoktur ). hvnvG1hv
ivokabel

Evet, sorun buydu. Ama asıl sorum şuydu: İşlev tanımında göründüğü için kullanılan yarım vektörün ne olduğu. Anladığım kadarıyla, sadece H nokta V'nin pozitif olup olmadığını kontrol etmede kullanılır. Cevapları yazmak için zaman ayırdığınız için teşekkür ederiz.
Erwin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.