problem z próbkowaniem ważności

Aug 24 2020

Próbowałem pobierać próbki na lambertowskiej powierzchni, najpierw jednakowo wybieram kierunek ze sfery jednostkowej.

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;

Przy 10 próbkach na piksel daje:

następnie wybieram losowy kierunek z półkuli jednostkowej nad powierzchnią

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;

wynik jest bardzo podobny, z wyjątkiem mniejszego hałasu

a następnie używam metody próbkowania ważności z (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;

jednak wynik jest inny:

Podstawowy kolor ściany to vec3 (0,8f, 0,8f, 0,8f), a kolor światła kopuły to vec3 (1,0f, 1,0f, 1,0f). W niektórych samouczkach element cosinus znajduje się wewnątrz lambertowskiego brdf, a niektóre znajdują się w równaniu renderowania i whttp://in1weekend.blogspot.com/ „weekend one” w ogóle nie ma pozycji cosinus. Naprawdę mam problemy z tymi koncepcjami. Czy ktoś mi pomoże? wielkie dzięki.

inny rendering z baseColor = vec3 (1.0f, 1.0f, 1.0f) i kolor kopuły = vec3 (0.5f, 0.5f, 0.5f) (próbkowanie ważności) średni kolor końcowego obrazu we wszystkich pikselach to vec3 (0,470884f , 0,470884f, 0,470884f).

10000 próbek na piksel przy próbkowaniu jednorodnej półkuli:

Odpowiedzi

NathanReed Aug 30 2020 at 07:35

W twojej matematyce jest kilka błędów. Znalazłeś już problem z funkcjami próbkowania 2π i 4π w funkcji próbkowania półkuli i sfery, ale także te linie w próbkowaniu półkuli cosinusowej są błędne:

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

Nie powinno w nich być współczynnika 2: to zniekształca rozkład cosinusów.

Również,

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

To nie jest złe, ale jest niepotrzebne: pdfanuluje wartość cosine, więc byłoby lepiej ustawić pdftylko 1 / π i pozostawić współczynnik cosinus wyłączony. W rzeczywistości to również anuluje 1 / π w brdf_result, więc możesz pominąć oba te czynniki pi i pdfcałkowicie się ich pozbyć .

Więcej o współczynniku cosinus: cała idea próbkowania z półkulą ważoną cosinusem polega na tym, aby uniknąć konieczności stosowania współczynnika cosinus w przepustowości ścieżki. Zasadniczo chcesz, aby cosinus znajdował się w jednym miejscu: albo w rozkładzie próbkowania, albo w przepustowości, ale nie w obu. Lepiej jest umieścić go w rozkładzie próbkowania, ponieważ wtedy wariancja w próbkach jest mniejsza (ponieważ nie mają one silnie zmieniającego się współczynnika cosinus w ich przepustowości), więc renderowanie zbiega się szybciej.

Jest to również ogólna maksyma w śledzeniu ścieżki: generalnie chcesz przenieść czynniki z przepustowości do dystrybucji promieni, kiedy tylko jest to możliwe. To jest idea ważnego próbkowania BRDF (przenoszenie czynników z BRDF do rozkładu promieni) i jawnego próbkowania światła (przenoszenie części przychodzącego światła do rozkładu promienia), a także bardziej zaawansowanych rzeczy, takich jak próbkowanie wielu ważnych elementów lub prowadzenie ścieżki.

dsukrect Aug 24 2020 at 19:34

Właśnie stwierdziłem, że jeśli użyję pdf = 1,0f / (4,0f * Pi) w próbkowaniu jednostkowej sfery lub pdf = 1,0f / (2,0f * Pi) w próbkowaniu z półkuli jednostkowej, wynik jest prawie taki sam, jak próbkowanie ważności (Otrzymuję ten sam wynik, gdy ustawię baseColor na vec3 (0.4f, 0.4f, 0.4f), połowę vec3 (0.8f, 0.8f, 0.8f). A pole powierzchni kuli jednostkowej wynosi tylko 4,0 f * Pi (zapomniałem dlaczego wcześniej używam 2.0f * Pi) MUSZĘ dojść do dwóch wniosków:

1: próbkowanie ważności jest prawidłowe!

2: Jestem za głupi !!!!!!