Bentuk yang benar dari istilah geometri GGX

9

Saya mencoba menerapkan mikrofacet BRDF dalam raytracer saya tetapi saya mengalami beberapa masalah. Banyak makalah dan artikel yang saya baca mendefinisikan istilah geometri parsial sebagai fungsi tampilan dan setengah vektor: G1 (v, h). Namun, ketika menerapkan ini saya mendapat hasil sebagai berikut:

Istilah Geometri GGX menggunakan setengah vektor

(Baris bawah adalah dielektrik dengan kekasaran 1.0 - 0.0, Baris atas adalah logam dengan kekasaran 1.0 - 0.0)

Ada highlight aneh di sekitar tepi dan cut-off di sekitar nl == 0. Saya benar-benar tidak tahu dari mana ini berasal. Saya menggunakan Unity sebagai referensi untuk memeriksa render saya, jadi saya memeriksa sumber shader mereka untuk melihat apa yang mereka gunakan dan dari apa yang saya tahu istilah geometri mereka tidak ditentukan oleh setengah vektor sama sekali! Jadi saya mencoba kode yang sama tetapi digunakan untuk permukaan makro normal, bukan setengah vektor dan mendapat hasil sebagai berikut:

Istilah geometri GGX menggunakan permukaan makro normal

Bagi mata saya yang tidak terlatih ini tampaknya jauh lebih dekat dengan hasil yang diinginkan. Tetapi saya merasa ini tidak benar? Mayoritas artikel yang saya baca menggunakan setengah vektor tetapi tidak semuanya. Apakah ada alasan untuk perbedaan ini?

Saya menggunakan kode berikut sebagai istilah geometri saya:

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

Dan untuk referensi, ini adalah fungsi distribusi normal saya:

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
sumber

Jawaban:

5

TL; DR: Formula Anda salah.G1


Untuk menghindari kebingungan, saya mengasumsikan versi isotropik dari BRDF, model microfacet Smith (sebagai lawan dari model V-cavity), dan distribusi mikrofacet GGX.

Menurut Heitz 2014 , istilah masking / shadowing adalahG1

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

dan menurut Walter 2007 , rumusnya adalah

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

Di mana adalah arah normal mikrofacet (vektor setengah jalan), adalah arah normal utama (geometris) (normal), adalah arah masuk atau keluar, adalah isotropik parameter kekasaran, dan adalah fungsi karakteristik positif, atau fungsi langkah Heaviside (sama dengan satu jika dan nol sebaliknya).ωmωgωvαχ+(Sebuah)Sebuah>0

Seperti yang Anda perhatikan, setengah vektor hanya digunakan untuk memastikan nol jika konfigurasi geometri dilarang. Lebih tepatnya, itu memastikan bahwa permukaan belakang tidak pernah terlihat dari arah di sisi depan dari makrosurface dan sebaliknya (kasus terakhir hanya berarti ketika juga refraksi didukung). Jika kode panggilan menjamin ini, maka Anda jelas dapat menghilangkan parameter ini. Mungkin itulah alasan mengapa mereka melakukannya di Unity.ωmG1ωv

Implementasi Anda, di sisi lain, menggunakan setengah vektor untuk menghitung kosinus dari arah sehubungan dengan mikrofaset, yang mengarah ke perhitungan sesuatu yang lain daripada formula yang disajikan.ωv

Jika ini bisa membantu, maka ini adalah implementasi saya dari faktor :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
sumber
Terimakasih atas balasan anda. Saya telah mengimplementasikan formula yang Anda berikan dan saya mendapatkan hasil yang identik dengan milik saya sendiri (saat menggunakan macrosurface normal). Jadi sepertinya itu hanya bentuk yang berbeda (saya mendapatkannya dari: graphicrants.blogspot.nl/2013/08/specular-brdf-reference.html ) Saya bingung tentang setengah vektor karena kursus matematika SIGGRAPH 2015 PBS khusus menyatakan geometri fungsi tergantung pada tampilan, cahaya dan vektor setengah. Jadi ini kesalahan dalam slide?
Erwin
@ Erwin, sekarang setelah kamu memberikan formula itu sendiri, itu jauh lebih jelas. Lain kali melakukannya dengan benar di awal, itu membantu. Ya, kedua versi (milik saya dan Anda) adalah setara, tetapi tidak satu pun dari mereka menggunakan vektor setengah untuk menghitung fungsi sinus atau tangen. Ini menggunakan daripada seperti yang Anda lakukan dalam implementasi Anda - yang tampaknya menjadi kesalahan. Saya menduga Anda melakukan kesalahan yang sama dengan implementasi baru juga. nvhv
ivokabel
Saya memang menggunakan N dot V dalam implementasi baru saya, yang memberi saya hasil yang identik dengan gambar kedua yang saya posting. Tapi saya masih belum jelas mengapa slide PBS menyatakan bahwa vektor setengah harus digunakan (Lihat: blog.selfshadow.com/publications/s2015-shading-course/hoffman/… , Slide 88).
Erwin
Apakah saya memahaminya dengan benar bahwa menggunakan alih-alih adalah masalahnya? Mengenai penggunaan vektor setengah jalan di : Sebenarnya ini digunakan dalam kedua versi yang saya posting (saya membuat kesalahan ketika membuat rumus LaTeX dan menulis geomeotric normal menjadi yang pertama, saya akan segera memperbaikinya), tetapi intinya adalah bahwa vektor setengah tidak digunakan untuk menghitung nilai cosinus (yaitu tidak ada digunakan). hvnvG1hv
ivokabel
Ya, itu masalahnya. Tetapi pertanyaan utama saya adalah: Untuk apa setengah vektor digunakan, karena muncul dalam definisi fungsi. Sejauh yang saya mengerti sekarang ini hanya digunakan dalam pemeriksaan jika H dot V positif. Terima kasih telah meluangkan waktu untuk menulis jawabannya.
Erwin