Blinn-Phong ile son derece eğik açılarda speküler ışık sorunu

Aug 17 2020

Nesnelere çok eğik açılardan bakarken temel Blinn-Phong oluşturucumla ilgili bir sorun yaşıyorum:

Parçamın GLSL'sini aşağıda yayınlayacağım halde bunun kodumla ilgili bir sorun olduğunu sanmıyorum. Daha ziyade, nokta (Normal, Hafif) <= 0 olduğunda aydınlatmanın aynasal bileşenini sıfıra indirmenin gerekli bir sonucu gibi görünüyor. ( Herkes size bunu yapmanızı söyler.) sonlandırıcı. Kelepçenin çıkarılması başka sorunlara yol açar; artık görünür dikiş yok, ancak şimdi speküler vurgu, kürenin karanlık tarafına doğru devam ediyor.

Bunun için basit bir yol var mı, yoksa bu sadece Blinn-Phong modelinin kaçınılmaz bir dezavantajı mı?

DÜZENLE

TL; DR: Görünüşe göre bu sadece Blinn-Phong modelinin bir dezavantajı değil.

BRDF'ler üzerine biraz daha araştırma yaptım ve şu makaleyi buldum: RADIANCE için Sınırlı Albedo ve Fitting Reflectance Data ile Yeni Bir Koğuş BRDF Modeli , özellikle yüksek otlatma açıları (tam olarak benim sorunum!) Etrafında Ward modelinin eksikliğini tartışıyor ve nasıl düzeltmek için modeli değiştirdiler. Ward, anizotropik bir modeldir, ancak izotropik olması basitleştirilebilir ve bunu 22. sayfadaki uygulamaya hazır formuna yaptığınızda şunları elde edersiniz:

Bunu koduma (aşağıda güncellendi) ekledim, aaaannnnnd ... zar yok. Normal durumlarda güzel görünüyor, ancak uçta aynı arıza modlarını ve kenarın ötesinde daha da muhteşem olanları sergiliyor (Blinn-Phong'dan bile daha kötü):

Not: Her iki model de "parlak" parametresini kullanır, ancak her model için farklı anlamlar taşır. Orijinal ekran görüntüleri parlak = .8 idi, Ward için onu .1'e düşürmek zorunda kaldım.

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

Yanıtlar

4 D0SBoots Aug 20 2020 at 05:35

TL; DR: Aynasal değerinizi noktayla çarpın (normal, hafif Normal). (Ve yapmak 0 minimuma ürünü nokta kıskacı!)

Ben ve sanırım (oradaki tüm öğreticileri görmekten) birçok kişi bunu yanlış yaptı.

Kullanıyordum Çift yönlü Yansıtma dağılım fonksiyonu , doğrudan ayna yoğunluğunu hesaplamak için (AKA BRDF). Bununla birlikte, BRDF aslında bir radyometrik integrale takılması gereken bir diferansiyel miktarı tanımlar. Önemlisi, genellikle ne denir BRDF genel integrali parçası olan bir cos (θ) terimini yoksundur. Matematik için bir mideniz varsa, burada daha ayrıntılı olarak tartışılmaktadır:http://www.pbr-book.org/3ed-2018/Color_and_Radiometry/Surface_Reflection.html#TheBRDF

Sadece nokta ışık kaynakları ile uğraşırken, bütün integrali değerlendirmemize gerek yoktur; bir ışık kaynağı olarak integralin sınırı integrale gider. Ancak cos (θ) terimi hala önemlidir.

Pratikte bu, gölgelendiricinin küçük bir ayarlamaya ihtiyacı olduğu anlamına gelir. Sonuçlar:

Yukarıda sabit Blinn-Phong var. Bu, öncekiyle aynı parlaklıkta; Blinn-Phong'un bir Fresnel terimi olmadığı için, cos (θ) düzeltmesi, yoğunluğun otlatma açılarında çarpıcı biçimde düşmesine neden olur. Ama en azından süreksizlik gitti.

Bu, daha önceki 2. görüntüyle aynı parlaklığa sahip olan Ward'dır. Fresnel yansıması (kabaca, daha eğik açılarda daha yüksek yansıma) Ward'da modellenmiştir, bu nedenle bu iyi görünüyor. Ve süreksizlik yok!