Blinn-Phongでの非常に斜めの角度での鏡面反射光の問題

Aug 17 2020

非常に斜めの角度でオブジェクトを見ると、基本的なBlinn-Phongレンダラーに問題が発生します。

フラグメントのGLSLを以下に投稿しますが、これは私のコードの問題ではないと思います。むしろ、これは、dot(Normal、Light)<= 0の場合に、照明の鏡面反射成分をゼロにカットするために必要な結果のようです。https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_reflection_model#Code_samplesするように指示します。)しかし、それを行うことは、ターミネーターでこの不連続性があることを意味します。クランプを取り外すと、他の問題が発生します。目に見える継ぎ目はもうありませんが、鏡面ハイライトは球の暗い側まで続きます。

これを回避する簡単な方法はありますか、それともこれはBlinn-Phongモデルの避けられない欠点ですか?

編集

TL; DR:これはBlinn-Phongモデルの単なる欠点ではないようです。

私はBRDFについてさらに調査を行い、この論文を見つけました。 https://www.radiance-online.org/community/workshops/2010-freiburg/PDF/geisler-moroder_duer_thetmeyer_RW2010.pdf、特に高いグレージング角度(まさに私の問題です!)の周りのワードモデルの欠点と、モデルを調整して修正する方法について説明します。Wardは異方性モデルですが、等方性に簡略化できます。22ページから実装可能なフォームにそれを行うと、次のようになります。

私はこれを私のコード(以下で更新)に接続しました、aaaannnnnd ...サイコロはありません。通常の場合はきれいに見えますが、エッジで同じ障害モードを示し、エッジを超えてさらに壮観なものがいくつかあります(Blinn-Phongよりもさらに悪い):

注:どちらのモデルも「光沢のある」パラメーターを使用していますが、モデルごとに意味が異なります。元のスクリーンショットはshiny = .8でした。ワードでは、1に下げる必要がありました。

#version 150

#extension GL_ARB_conservative_depth : enable

in Frag {
    vec3 color;
    vec3 coord;
    vec3 center;
    float R;
};

out vec4 color_out;
layout (depth_greater) out float gl_FragDepth;

uniform mat4 VIEW;
uniform mat4 PROJ;

const vec3 gamma = vec3(1.0 / 2.2);
const float ambientPower = .15;
const float diffusePower = .75;

const bool PHONG = false;
const float specHardness = 60.0;
const float shiny = .1;

const bool WARD = true;

void main() {
    // Find intersection of ray (given by coord) with sphere
    vec3 eyeNormal = normalize(coord);
    float b = dot(center, eyeNormal);
    float c = b * b - (dot(center, center) - R * R);
    if (c < 0.0) {
        discard;  // Doesn't intersect sphere
    }
    vec3 point = (b - sqrt(c)) * eyeNormal;
    // Redo depth part of the projection matrix
    gl_FragDepth = (PROJ[2].z * point.z + PROJ[3].z) / -point.z;

    // Lighting begins here

    // The light dir is in world-space, unlike the others, so we have to project it to view space.
    // The direction (0, 1, 0) corresponds to the 2nd column. By the properties of the view matrix
    // (the 3x3 part is an orthogonal matrix), this is already normalized.
    vec3 lightNormal = VIEW[1].xyz;
    vec3 normal = normalize(point - center);
    float diffuse = dot(lightNormal, normal);

    float specular = 0.0;
    if (PHONG) {
        // Have to reverse sign for eyeNormal so it points out
        vec3 halfway = normalize(lightNormal - eyeNormal);
        specular = diffuse <= 0.0 ? 0.0 : pow(max(0.0, dot(halfway, normal)), specHardness);
    } else if (WARD) {
        const float PI = 3.14159265359;
        const float alpha = .15;
        const float invAlpha2 = 1 / (alpha * alpha);
        // Would move this computation to CPU and pass invAlpha2 as uniform if alpha were a parameter
        const float cFactor = invAlpha2 / PI;

        // Have to reverse sign for eyeNormal so it points out, note this is *unnormalized*
        vec3 halfway = lightNormal - eyeNormal;
        float dotP = dot(halfway, normal);
        float invDot2 = 1 / (dotP * dotP);
        float semiNormalizedInvDot = dot(halfway, halfway) * invDot2;
        // Note: You can't factor the exp(invAlpha2) part out as a constant term,
        // you'll blow out the floating-point range if you try.
        specular = cFactor * exp(invAlpha2-invAlpha2*semiNormalizedInvDot) * semiNormalizedInvDot * invDot2;
    }
    diffuse = max(0.0, diffuse);
    vec3 colorPre = (ambientPower + diffusePower * diffuse) * color
        + specular * shiny * vec3(1);

    color_out = vec4(pow(colorPre, gamma), 0);
}

回答

4 D0SBoots Aug 20 2020 at 05:35

TL; DR:スペキュラー値にdot(normal、lightNormal)を掛けます。(そして、その内積を最小0にクランプしてください!)

私と私は(そこにあるすべてのチュートリアルを見て)他の多くの人がこれをすべて間違って行っていると思います。

私は使用していました https://en.wikipedia.org/wiki/Bidirectional_reflectance_distribution_function(別名BRDF)鏡面強度を直接計算します。ただし、BRDFは実際には、放射分析積分にプラグインすることを意図した微分量を定義します。決定的に、通常と呼ばれるものBRDF全体の不可欠の一部であるCOS(θ)用語を欠いています。あなたが数学のための胃を持っているならば、それはここでより詳細に議論されます:http://www.pbr-book.org/3ed-2018/Color_and_Radiometry/Surface_Reflection.html#TheBRDF

点光源を扱っているだけの場合、積分全体を評価する必要はありません。光源が点になるときの積分の限界は被積分関数に行きます。しかし、cos(θ)項は依然として重要です。

実際には、これはシェーダーを少し調整する必要があることを意味します。結果:

上はBlinn-Phong、修正済みです。これは以前と同じ光沢があります。Blinn-Phongにはフレネル項がないため、cos(θ)修正により、かすめ角で強度が劇的に低下します。しかし、少なくとも不連続性はなくなりました。

これは固定区で、前の2番目の画像と同じ光沢があります。フレネル反射(大まかに言って、より斜めの角度でより高い反射)はワードでモデル化されているため、これは見栄えがします。そして、不連続性はありません!