重要度サンプリングの問題

Aug 24 2020

ランバート面で重要度サンプリングをしようとしていましたが、最初は単位球から均一に方向を選びました。

vec3 direction = camera->genDirection();
...
direction = random_in_unit_sphere();
float cosine = dotp(direction,surfaceNormal);
/*
float dotp(float val){
val = dot(val);
if(val>0.0001f) return val;
else return 0.0001f;
}
*/
vec3 brdf_result = material->baseColor/Pi;//lambertian
vec3 pdf = 1.0f/(2.0f*Pi);
throughput = throughput * brdf_result * cosine / pdf;

ピクセルあたり10サンプルの場合、次のようになります。

次に、表面上の単位半球からランダムな方向を選択します

direction = random_in_unit_hemisphere(surfaceNormal);
float cosine = dotp(direction,surfaceNormal);
vec3 brdf_result = material->baseColor/Pi;
vec3 pdf = 1.0f/(1.0f*Pi);
throughput = throughput * brdf_result * cosine / pdf;

結果は、ノイズが少ないことを除いて、非常に似ています。

次に、(からの重要度サンプリング方法を使用しますhttp://in1weekend.blogspot.com/)

    class onb {
public:
    vec3 operator[](int i)const { return axis[i]; }
    vec3 u()const { return axis[0]; }
    vec3 v()const { return axis[1]; }
    vec3 w()const { return axis[2]; }
    vec3 local(float a, float b, float c) { return a * u() + b * v() + c * w(); }
    vec3 local(const vec3& a) { return a.x * u() + a.y * v() + a.z * w(); }
    void buildFromNormal(const vec3& n) {
        axis[2] = normalize(n);
        vec3 a;
        if (std::abs(w().x) > 0.9f)
            a = vec3(0.0f, 1.0f, 0.0f);
        else
            a = vec3(1.0f, 0.0f, 0.0f);
        axis[1] = normalize(cross(w(), a));
        axis[0] = cross(w(), v());
    }


private:
    vec3 axis[3];
};
vec3 randCosDir() {
    float r1 = randFloat01();
    float r2 = randFloat01();
    float z = sqrt(1.0f - r2);
    float phi = 2.0f * Pi * r1;
    float x = cos(phi) * 2.0f * sqrt(r2);
    float y = sin(phi) * 2.0f * sqrt(r2);
    return vec3(x, y, z);
}

onb uvw;
uvw.buildFromNormal(surfaceNormal);
direction = normalize(uvw.local(randCosDir()));
float cosine = dotp(direction,surfaceNormal);
vec3 brdf_result = material->baseColor/Pi;
vec3 pdf = dotp(uvw.w(), direction)/Pi;
throughput = throughput * brdf_result * cosine / pdf;

ただし、結果は異なります。

壁のbaseColorはvec3(0.8f、0.8f、0.8f)で、ドームライトの色はvec3(1.0f、1.0f、1.0f)です。一部のチュートリアルでは、コサインアイテムはランバートbrdf内にあり、一部はレンダリング方程式に含まれています。http://in1weekend.blogspot.com/ 「週末1」コサインアイテムは一切ありません。本当にそれらのコンセプトに戸惑っています。誰か助けてくれませんか?ありがとうございます。

baseColor = vec3(1.0f、1.0f、1.0f)およびdome color = vec3(0.5f、0.5f、0.5f)(重要度サンプリング)を使用した別のレンダリングは、すべてのピクセルにわたる最終画像の平均色がvec3(0.470884f)です。 、0.470884f、0.470884f)。

均一な半球サンプリングでピクセルあたり10,000サンプル:

回答

NathanReed Aug 30 2020 at 07:35

あなたの数学にはいくつかのバグがあります。半球と球のサンプリング関数の2πと4πに問題があることがわかりましたが、余弦半球のサンプリングのこれらの線は間違っています。

float x = cos(phi) * 2.0f * sqrt(r2);
float y = sin(phi) * 2.0f * sqrt(r2);

これらには2の因数があってはなりません。これはコサイン分布を歪めています。

また、

vec3 pdf = dotp(uvw.w(), direction)/Pi;
throughput = throughput * brdf_result * cosine / pdf;

これは間違いではありませんが、不要です。をpdfキャンセルするためcosinepdf1 /πだけに設定し、コサイン係数をオフのままにしておくことをお勧めします。実際、これにより、の1 /πもキャンセルされるbrdf_resultため、これらのpi係数の両方を省略して、pdf完全に取り除くことができます。

コサインファクターの詳細:コサイン加重半球を使用したサンプリングの全体的な考え方は、パススループットにコサインファクターを含める必要がないようにすることです。基本的に、コサインは1つの場所(サンプリング分布またはスループットのいずれか)にのみ必要ですが、両方は必要ありません。サンプルの分散が低くなり(スループットに大きく変化するコサインファクターがないため)、レンダリングの収束が速くなるため、サンプリング分布に配置することをお勧めします。

これは、パストレーシングの一般的な最大値でもあります。通常、実用的な場合は常に、スループットから光線分布に係数を移動する必要があります。これは、BRDFの重要度サンプリング(BRDFから光線分布に因子を移動する)と明示的な光サンプリング(入射光分布の一部を光線分布に移動する)、および複数の重要度サンプリングやパスガイドなどのより高度なもののアイデアです。

dsukrect Aug 24 2020 at 19:34

単位球サンプリングでpdf = 1.0f /(4.0f * Pi)を使用した場合、または単位半球サンプリングでpdf = 1.0f /(2.0f * Pi)を使用した場合、結果は重要度サンプリングとほぼ同じであることがわかりました。 (baseColorをvec3(0.4f、0.4f、0.4f)、vec3(0.8f、0.8f、0.8f)の半分に設定した場合も同じ結果が得られます。また、単位球の表面積はわずか4.0です。 f * Pi(以前に2.0f * Piを使用した理由を忘れています)2つの結論に達する必要があります:

1:重要度サンプリングは正しいです!

2:私はバカすぎる!!!!!!