problema com a amostragem de importância

Aug 24 2020

Eu estava tentando fazer uma amostragem de importância na superfície lambertiana. No início, escolho uniformemente a direção da esfera unitária.

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;

Com 10 amostras por pixel, produz:

então eu escolho a direção aleatória do hemisfério da unidade acima da superfície

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;

o resultado é muito semelhante, exceto por menos ruído

e então eu uso o método de amostragem de importância de (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;

no entanto, o resultado é diferente:

A cor base da parede é vec3 (0.8f, 0.8f, 0.8f), e a cor da luz da cúpula é vec3 (1.0f, 1.0f, 1.0f). Em alguns tutoriais, o item cosseno está dentro do brdf lambertiano, e alguns estão na equação de renderização, e emhttp://in1weekend.blogspot.com/ "fim de semana um" não há nenhum item de cosseno. Eu realmente fico confuso com esses conceitos. Alguém me ajuda? muito obrigado.

outra renderização com baseColor = vec3 (1.0f, 1.0f, 1.0f) e dome color = vec3 (0.5f, 0.5f, 0.5f) (amostragem de importância) a cor média da imagem final em todos os pixels é vec3 (0.470884f , 0,470884f, 0,470884f).

10.000 amostras por pixel com amostragem de hemisfério uniforme:

Respostas

NathanReed Aug 30 2020 at 07:35

Existem alguns bugs em sua matemática. Você já encontrou o problema com 2π e 4π nas funções de amostragem de hemisfério e esfera, mas também, essas linhas na amostragem do hemisfério cosseno estão erradas:

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

Não deve haver um fator de 2 nesses: isso está distorcendo a distribuição do cosseno.

Além disso,

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

Isso não está errado, mas é desnecessário: o pdfcancela o cosine, então seria preferível definir pdfcomo apenas 1 / π e deixar o fator cosseno desligado. Na verdade, isso também cancela o 1 / π no brdf_result, então você pode deixar ambos os fatores pi e se livrar deles pdfcompletamente.

Mais sobre o fator cosseno: toda a ideia de amostrar com um hemisfério ponderado por cosseno é evitar a necessidade de ter o fator cosseno na taxa de transferência do caminho. Basicamente, você deseja apenas o cosseno em um lugar: na distribuição de amostragem ou na taxa de transferência, mas não em ambos. É preferível colocá-lo na distribuição de amostragem porque então a variância nas amostras é menor (já que eles não têm o fator de cosseno fortemente variável em sua taxa de transferência), então a renderização converge mais rápido.

Esta também é uma máxima geral no traçado de caminho: você geralmente deseja mover fatores da taxa de transferência para a distribuição de raios sempre que possível. Essa é a ideia de amostragem de importância de BRDFs (mover fatores do BRDF para a distribuição de raios) e amostragem de luz explícita (mover partes da distribuição de luz de entrada para a distribuição de raios), bem como coisas mais avançadas como amostragem de importância múltipla ou orientação de caminho.

dsukrect Aug 24 2020 at 19:34

Acabei de descobrir que se eu usar pdf = 1.0f / (4.0f * Pi) na amostragem de esfera unitária ou pdf = 1.0f / (2.0f * Pi) na amostragem de hemisfério unitário, o resultado é quase o mesmo que a amostragem de importância (Eu também obtenho o mesmo resultado quando defino a cor base para vec3 (0.4f, 0.4f, 0.4f), a metade de vec3 (0.8f, 0.8f, 0.8f). E a área de superfície de uma esfera unitária é apenas 4,0 f * Pi (esqueci por que uso 2.0f * Pi antes). TENHO que chegar a duas conclusões:

1: a amostragem de importância um está correta!

2: Eu sou muito estúpido !!!!!!