problema com a amostragem de importância
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).
Respostas
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.
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 !!!!!!