Правильна форма терміна геометрії GGX


9

Я намагаюся реалізувати мікропрограму BRDF у своєму raytracer, але я стикаюся з деякими проблемами. Чимало прочитаних статей та статей визначають термін часткової геометрії як функцію перегляду та піввікторів: G1 (v, h). Однак, реалізуючи це, я отримав такий результат:

Термін геометрії GGX з використанням половинного вектора

(Нижній ряд діелектрик із шорсткістю 1,0 - 0,0, верхній ряд - металік із шорсткістю 1,0 - 0,0)

Існує дивна родзинка по краях і обріз навколо nl == 0. Я не міг реально зрозуміти, звідки це походить. Я використовую Unity як орієнтир, щоб перевірити свої рендери, тому я перевірив їх джерело шейдерів, щоб побачити, що вони використовують, і з того, що я можу сказати, їх термін геометрії взагалі не параметризований половинним вектором! Тому я спробував той самий код, але використовував макрос поверхню нормально замість половинного вектора і отримав такий результат:

Термін геометрії 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 формула неправильна.


Тільки для того, щоб уникнути плутанини, я припускаю ізотропну версію BRDF, модель мікромітки Сміта (на відміну від моделі V-порожнини) та розподіл мікрофайлів GGX.

Згідно з Heitz 2014 , термін маскування / затіненняG1 є

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

і згідно Walter 2007 , формула така

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

Де ωm - нормальний напрямок мікрофайла (вектор на півдорозі), ωg є основним (геометричним) нормальним напрямком (нормальним), ωv - вхідний або вихідний напрямок, α - параметр ізотропної шорсткості, і χ+(a) - позитивна характеристична функція, або крок функції Heaviside (дорівнює, якщо a>0 а нуль інакше).

Як ви можете помітити, половинний вектор ωm використовується лише для переконання G1дорівнює нулю, якщо геометрична конфігурація заборонена. Точніше, це гарантує, що задня поверхня мікроповерхні ніколи не видно з бокуω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;
}

Дякую за вашу відповідь Я реалізував формулу, яку ви надали, і я отримав ідентичні результати, як і власний (при використанні макроповерхні нормально). Тож здається, що це просто інша форма (я отримав її з: graphicsrants.blogspot.nl/2013/08/specular-brdf-reference.html ) Мене збентежило напіввектор, тому що математичний курс SIGGRAPH 2015 PBS спеціально визначає геометрію функція, що залежить від виду, світла та половини векторів. Так це помилка в слайдах?
Ервін

@Erwin, тепер, коли ви також подали формулу, це набагато зрозуміліше. Наступного разу зробіть це прямо на початку, це допомагає. Так, обидві версії (моя і ваша) еквівалентні, але жодна з них не використовує на півдорозі вектор для обчислення синусоїди або дотичної функції. Він використовуєnv а не hvяк ви робили у своїй реалізації - це, здається, є помилкою. Я підозрюю, що ви зробили таку ж помилку і з новою реалізацією.
ivokabel

Я використовував N dot V у своїй новій реалізації, що дало мені однакові результати для другого зображення, яке я опублікував. Але мені ще не зрозуміло, чому слайди курсу PBS зазначають, що слід використовувати напівдорогий вектор (Див.: Blog.selfshadow.com/publications/s2015-shading-course/hoffman/… , слайд 88).
Ервін

Чи правильно я розумію, що використовую hv замість nvбула проблема? Щодо використання напівдорого вектора вG1: Насправді він використовується в обох версіях, які я розмістив (я помилився, будуючи формулу LaTeX і написав геометричну нормальну в першу, я скоро це виправлю), але справа в тому, що вектор на півдорозі не є використовується для обчислення значення косинуса (тобто немає hvб / в).
ivokabel

Так, в цьому і була проблема. Але моє основне питання було: для чого використовується напівполовина, оскільки вона відображається у визначенні функції. Наскільки я зрозумів, він використовується лише в перевірці, якщо точка H V позитивна. Дякуємо, що знайшли час для написання відповідей.
Ервін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.