GGXジオメトリ用語の正しい形式


9

レイトレーサーにマイクロファセットBRDFを実装しようとしていますが、いくつかの問題が発生しています。私が読んだ多くの論文や記事は、ビューと半ベクトルの関数として部分幾何学用語を定義しています: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

ここで、はマイクロファセットの法線方向(中間ベクトル)、は主(幾何学的)法線方向(法線)、は入力方向または出力方向、は等方性です。粗さパラメータであり、は正の特性関数、またはヘビサイドステップ関数(場合は1、それ以外の場合はゼロ)。ωmωgωvαχ+(a)a>0

ように、幾何学的構成が禁止されている場合、半ベクトルはがゼロであることを確認するためにのみ使用されます。より正確には、マイクロサーフェイスの背面がマクロサーフェイスの前面の方向から見えないようにし、逆も同様です(後者は、屈折もサポートされている場合にのみ意味があります)。呼び出し側のコードでこれが保証されている場合は、このパラメーターを省略できます。それがおそらく彼らがUnityでそうした理由です。ω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;
}

お返事ありがとうございます。私はあなたが提供した数式を実装し、私と同じ結果を得ました(マクロサーフェス法線を使用した場合)。だから、それは単なる別の形のようです(私はそれを入手しました:graphicrants.blogspot.nl/2013/08/specular-brdf-reference.html)SIGGRAPH 2015 PBS数学コースは特にジオメトリを述べているので、半ベクトルについて混乱しましたビュー、光ベクトル、半ベクトルに依存する関数。これはスライドのエラーですか?
アーウィン2016年

@アーウィン、数式自体も提供したので、はるかに明確になりました。次回は最初からそれを実行すると、役立ちます。はい、両方のバージョン(鉱山とあなたのもの)は同等ですが、どちらも正弦関数または正接関数の計算に中間ベクトルを使用しません。実装で行ったように、ではなく使用します。これは間違いのようです。新しい実装でも同じ間違いをしたと思います。H Vnvhv
ivokabel

新しい実装ではNドットVを使用しました。これにより、投稿した2番目の画像と同じ結果が得られました。しかし、PBSコースのスライドに、中間ベクトルを使用する必要があると記載されている理由については、まだ明確ではありません(blog.selfshadow.com/publications/s2015-shading-course/hoffman/…、スライド88を参照)。
アーウィン、

代わりにを使用することが問題だったと私は正しく理解していますか?での中間ベクトルの使用について:実際には、私が投稿した両方のバージョンで使用されています(LaTeXの式を作成するときに間違いを犯し、最初の式に地質学的法線を書き込んだので、すぐに修正します)。ポイントは、コサイン値の計算に中間ベクトルが使用されないことです(つまり、使用されません)。N V G 1時間VhvnvG1hv
ivokabel 2016年

はい、それが問題でした。しかし、私の主な質問は次のとおりでした。関数定義に表示されているため、ハーフベクトルは何に使用されたのですか。今私が理解している限り、H dot Vが正かどうかのチェックでのみ使用されます。回答を書いてくれてありがとう。
Erwin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.