problema con il campionamento dell'importanza

Aug 24 2020

Stavo cercando di fare il campionamento dell'importanza sulla superficie lambertiana. All'inizio scelgo uniformemente la direzione dalla sfera unitaria.

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;

Con 10 campioni per pixel, produce:

quindi scelgo una direzione casuale dall'emisfero unitario sopra la superficie

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;

il risultato è molto simile, tranne per il minor rumore

e poi utilizzo il metodo di campionamento dell'importanza da (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;

tuttavia il risultato è diverso:

Il colore base del muro è vec3 (0.8f, 0.8f, 0.8f) e il colore della luce della cupola è vec3 (1.0f, 1.0f, 1.0f). In alcuni tutorial l'elemento coseno è all'interno del lambertiano brdf, e alcuni sono nell'equazione di rendering e inhttp://in1weekend.blogspot.com/ "fine settimana uno" non c'è alcun elemento del coseno. Mi sono davvero incasinato con quei concetti. C'è qualcuno che mi aiuta? Grazie mille.

un altro rendering con baseColor = vec3 (1.0f, 1.0f, 1.0f) e dome color = vec3 (0.5f, 0.5f, 0.5f) (campionamento dell'importanza) il colore medio dell'immagine finale su tutti i pixel è vec3 (0.470884f , 0.470884f, 0.470884f).

10.000 campioni per pixel con campionamento uniforme dell'emisfero:

Risposte

NathanReed Aug 30 2020 at 07:35

Ci sono alcuni bug nella tua matematica. Hai già riscontrato il problema con 2π e 4π nelle funzioni di campionamento dell'emisfero e della sfera, ma anche queste linee nel campionamento dell'emisfero coseno sono sbagliate:

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

Non dovrebbe esserci un fattore 2 in questi: questo sta distorcendo la distribuzione del coseno.

Anche,

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

Questo non è sbagliato, ma non è necessario: pdfcancella il cosine, quindi sarebbe preferibile impostare pdfsolo 1 / π e lasciare il fattore del coseno disattivato. In effetti, questo annulla anche 1 / π in brdf_result, quindi potresti tralasciare entrambi i fattori pi e sbarazzartene pdfcompletamente.

Maggiori informazioni sul fattore coseno: l'intera idea di campionamento con un emisfero ponderato per il coseno è di evitare di dover avere il fattore coseno nel throughput del percorso. Fondamentalmente vuoi solo il coseno in un posto: o nella distribuzione del campionamento o nel throughput, ma non entrambi. È preferibile metterlo nella distribuzione del campionamento perché quindi la varianza nei campioni è inferiore (poiché non hanno il fattore coseno fortemente variabile nel loro throughput), quindi il rendering converge più velocemente.

Questa è anche una massima generale nel tracciamento del percorso: generalmente si desidera spostare i fattori dal throughput alla distribuzione dei raggi ogni volta che è possibile. Questa è l'idea del campionamento dell'importanza dei BRDF (sposta i fattori dal BRDF nella distribuzione dei raggi) e del campionamento della luce esplicita (sposta parti della distribuzione della luce in entrata nella distribuzione dei raggi) così come cose più avanzate come il campionamento a più importanza o la guida del percorso.

dsukrect Aug 24 2020 at 19:34

Ho appena scoperto che se uso pdf = 1.0f / (4.0f * Pi) nel campionamento della sfera unitaria o pdf = 1.0f / (2.0f * Pi) nel campionamento dell'emisfero unitario, il risultato è quasi lo stesso del campionamento dell'importanza (Ottengo lo stesso risultato anche impostando baseColor su vec3 (0.4f, 0.4f, 0.4f), la metà di vec3 (0.8f, 0.8f, 0.8f). E l'area della superficie di una sfera unitaria è solo 4.0 f * Pi (dimentico perché ho usato 2.0f * Pi prima). DEVO arrivare a due conclusioni:

1: l'importanza del campionamento è corretta!

2: sono troppo stupido !!!!!!