รูปแบบที่ถูกต้องของคำศัพท์เรขาคณิต GGX


9

ฉันกำลังพยายามนำ BRDF ของ microfacet ไปใช้ใน raytracer ของฉัน แต่ฉันพบปัญหาบางอย่าง เอกสารและบทความจำนวนมากที่ฉันได้อ่านให้นิยามคำเรขาคณิตบางส่วนเป็นฟังก์ชันของมุมมองและเวกเตอร์ครึ่งหนึ่ง: G1 (v, h) อย่างไรก็ตามเมื่อใช้งานนี้ฉันได้รับผลลัพธ์ดังต่อไปนี้:

คำเรขาคณิต GGX ใช้เวกเตอร์ครึ่งตัว

(แถวล่างเป็นฉนวนความหยาบ 1.0 - 0.0, แถวบนสุดเป็นโลหะที่มีความหยาบ 1.0 - 0.0)

มีไฮไลท์แปลก ๆ อยู่รอบ ๆ ขอบและตัดรอบ ๆ nl == 0 ฉันไม่สามารถเข้าใจได้ว่ามันมาจากไหน ฉันใช้ Unity เป็นข้อมูลอ้างอิงในการตรวจสอบการแสดงผลของฉันดังนั้นฉันจึงตรวจสอบแหล่งที่มาของ shader เพื่อดูว่าพวกเขาใช้อะไรและจากสิ่งที่ฉันสามารถบอกได้ว่าเทอมเรขาคณิตของพวกเขานั้นไม่ได้ถูกพาราเมท ดังนั้นฉันจึงลองใช้รหัสเดียวกัน แต่ใช้กับพื้นผิวมาโครปกติแทนที่จะเป็นครึ่งเวคเตอร์และได้ผลลัพธ์ดังนี้

คำเรขาคณิต GGX โดยใช้พื้นผิวมาโครปกติ

สำหรับดวงตาที่ไม่ผ่านการฝึกฝนของฉันสิ่งนี้ดูเหมือนจะใกล้เคียงกับผลลัพธ์ที่ต้องการ แต่ฉันมีความรู้สึกว่ามันไม่ถูกต้อง? บทความที่ฉันอ่านส่วนใหญ่ใช้เวกเตอร์ครึ่งตัว แต่ไม่ใช่ทั้งหมด มีเหตุผลสำหรับความแตกต่างนี้หรือไม่?

ฉันใช้รหัสต่อไปนี้เป็นคำเรขาคณิตของฉัน:

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

และสำหรับการอ้างอิงนี่คือฟังก์ชันการแจกแจงปกติของฉัน:

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

คำตอบ:


5

TL; DR: สูตรของคุณผิดG1


เพียงเพื่อหลีกเลี่ยงความสับสนฉันกำลังสมมติรุ่น isotropic ของ BRDF, Smith microfacet model (ตรงข้ามกับ V-cavity model) และการกระจาย microfacet GGX

จากHeitz 2014คำว่า masking / shadowingคือG1

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

และตามวอลเตอร์ 2007สูตรคือ

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

โดยที่เป็นทิศทางปกติของไมโครฟิล์ม (เวกเตอร์ครึ่งทาง),เป็นทิศทางปกติหลัก (เรขาคณิต) (ปกติ),คือทิศทางขาเข้าหรือขาออกคือ isotropic พารามิเตอร์ roughness และเป็นฟังก์ชันคุณลักษณะเชิงบวกหรือฟังก์ชันขั้นตอน Heaviside (เท่ากับหนึ่งถ้าและศูนย์เป็นอย่างอื่น)ωmωgωvαχ+(a)a>0

ในขณะที่คุณสามารถสังเกตเห็นได้ครึ่งเวกเตอร์ถูกใช้เพื่อให้แน่ใจว่าเป็นศูนย์หากการกำหนดค่าทางเรขาคณิตถูกห้าม แม่นยำยิ่งขึ้นทำให้แน่ใจว่าพื้นผิวด้านหลังของ microsurface ไม่สามารถมองเห็นได้จากทิศทางที่ด้านหน้าของ macrosurface และในทางกลับกัน (ในกรณีหลังนั้นมีความหมายเฉพาะเมื่อรองรับการหักเหด้วย) หากรหัสการโทรรับประกันสิ่งนี้คุณสามารถละเว้นพารามิเตอร์นี้ได้อย่างชัดเจน นั่นอาจเป็นเหตุผลว่าทำไมพวกเขาถึงทำอย่างสามัคคีω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;
}

ขอบคุณสำหรับการตอบกลับของคุณ. ฉันใช้สูตรที่คุณให้มาและได้ผลลัพธ์ที่เหมือนกันกับของฉันเอง (เมื่อใช้ macrosurface ปกติ) ดังนั้นดูเหมือนว่ามันเป็นเพียงรูปแบบที่แตกต่างกัน (ฉันได้รับมาจาก: graphicrants.blogspot.nl/2013/08/specular-brdf-reference.html ) ฉันสับสนเกี่ยวกับเวกเตอร์ครึ่งเนื่องจากหลักสูตรคณิตศาสตร์SBSGR 2015 PBS ระบุรูปร่างของเรขาคณิตโดยเฉพาะ ฟังก์ชั่นขึ้นอยู่กับมุมมองแสงและเวกเตอร์ครึ่ง นี่เป็นข้อผิดพลาดในสไลด์หรือไม่
Erwin

@Erwin ตอนนี้ที่คุณให้สูตรด้วยตัวของคุณเองมันชัดเจนกว่ามาก ครั้งต่อไปจะทำให้ถูกต้องตั้งแต่ต้นมันจะช่วย ใช่ทั้งสองแบบ (ของฉันและของคุณ) มีความคล้ายคลึงกัน แต่ทั้งสองแบบนั้นไม่ใช้เวกเตอร์ครึ่งทางสำหรับการคำนวณฟังก์ชันไซน์หรือแทนเจนต์ มันใช้nv ค่อนข้างมากกว่า hvอย่างที่คุณทำในการนำไปใช้งาน - ดูเหมือนจะเป็นความผิดพลาด ฉันสงสัยว่าคุณทำผิดพลาดเหมือนกันกับการใช้งานใหม่เช่นกัน
ivokabel

ฉันใช้ N dot V ในการนำไปใช้ใหม่ของฉันซึ่งให้ผลลัพธ์เหมือนกันกับภาพที่สองที่ฉันโพสต์ แต่ฉันยังไม่ชัดเจนว่าทำไมสไลด์ของ PBS จึงระบุว่าควรใช้เวกเตอร์ครึ่งทาง (ดูที่: blog.selfshadow.com/publications/s2015-shading-course/hoffman/ … , สไลด์ 88)
Erwin

ฉันเข้าใจถูกต้องว่าใช้ hv แทน nvเป็นปัญหาหรือไม่ เกี่ยวกับการใช้เวกเตอร์ครึ่งทางในG1: อันที่จริงมันถูกใช้ในทั้งสองเวอร์ชันที่ฉันโพสต์ (ฉันทำผิดพลาดเมื่อสร้างสูตร LaTeX และเขียน geomeotric ปกติลงในอันแรกฉันจะแก้ไขมันเร็ว ๆ นี้) แต่ประเด็นก็คือเวกเตอร์ครึ่งทางนั้นไม่ใช่ ใช้ในการคำนวณค่าโคไซน์ (เช่นไม่มี hvใช้)
ivokabel

ใช่นั่นเป็นปัญหา แต่คำถามหลักของฉันคือ: อะไรคือเวกเตอร์ครึ่งตัวที่ใช้สำหรับเนื่องจากปรากฏในนิยามฟังก์ชัน เท่าที่ฉันเข้าใจตอนนี้มันจะใช้เฉพาะในการตรวจสอบว่า H dot V เป็นบวก ขอบคุณที่สละเวลาเขียนคำตอบ
Erwin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.