sexta-feira, 14 de abril de 2017

Atividade 5 - Implementando o Path Tracer

Depois de criar a base necessária, agora é hora de construir o verdadeiro propósito da disciplina, um Path Tracer. Path Tracing é o método de renderização de uma cena 3D mais realista da atualidade, e por isso usa um algoritmo bem pesado, chegando a demorar mais de 10 minutos para renderizar uma imagem de baixa resolução com qualidade aceitável, isso claro quando ainda não foi aplicado nenhum algoritmo de otimização, que é o nosso caso.

Para entender como implementar o algoritmo, nós precisamos estudar o comportamento da luz e dos materiais em conjunto com ela para criar uma imagem.
Uma fonte de luz emite infinitos raios em todas as direções, estes raios ao colidirem com alguma superfície são refletidos, como e para onde eles são refletidos depende de que material é feita a superfície em que ele colidiu.
Neste trabalho nós implementamos o material difuso, onde um raio de luz, ao se chocar numa superfície com essas características é refletida igualmente para todos os lados.

Diferente do algoritmo anterior, onde eram lançados raios através de um pixel da tela e na sua primeira intersecção o pixel era pintado da cor do material intersectado, para que o resultado fosse realista nós precisaríamos aplicar uma fórmula, mais conhecida como a Rendering Equation:


Que nos dá a radiância (Lo) que sai de uma superfície a partir da soma da radiância emitida (Le) e refletida. A radiância refletida é o somatório das radiâncias incidentes (Li) de todas as direções multiplicada pela refletância da superfície (fr) e o cosseno do ângulo de incidência.

Essa equação necessita do cálculo de uma integral recursiva, e para resolvê-la é utilizado a Método Monte Carlo de integração, obtendo o seguinte estimador:


Que quando é designada para materiais difusos, pode ser ainda mais simplificado.


Com a equação podemos determinar a cor dos pixels, a aplicando em cada raio gerado a partir da câmera e suas reflexões subsequentes. Um raio de luz pode ser refletido até que ele fique sem energia, por isso nós fomos aconselhados pelo professor a refletir o raio que vem da câmera 5 vezes, e assim obter mais exatidão no resultado final.

Agora vamos para a implementação propriamente dita. A primeira coisa feita foi modificar as primitivas, para que ao invés de possuir um atributo cor, possuir uma brdf e uma emitância, ambos vetores de três variáveis.

Depois disso só restou restou criar a função recursiva que calcula a integral para cada raio, o que claro não foi um trabalho fácil, gastamos muito tempo resolvendo bugs e melhorando nosso código, aqui estão algumas imagens do nosso código em desenvolvimento, com vários erros e bugs:


Mas depois de muito sofrimento nós conseguimos resolver os problemas e nosso Path Tracer agora está funcionando perfeitamente:


Nós também fizemos algumas experiências comparando o tanto e raios lançados para cada pixel da imagem, e colocamos tudo em um gif:

rpp: Raios por pixel
Como pode-se ver há uma melhora exponencial no começo, de 100 para 200 rpp, onde ainda dá para ser visto o padrão criado pela função de aleatoriedade que nós usamos, mas conforme o número de raios por pixel aumenta, mais o resultado converge para o esperado até que mesmo aumentando muito a diferença é pouca, como por exemplo de 1200 para 1700 rpp.

Com isso nós concluímos mais um post e com orgulho de dizer que agora nós temos o nosso próprio Path Tracer, a próxima etapa agora é implementar algoritmos que deixem este código mais rápido e otimizem o resultado sem comprometer o realismo. Até a próxima!