Trabalho de Fundamentos da Computação Gráfica Melhorias para o Ray Tracing 1. Níveis de pré-visualização 2. Paralelização com Buckets (na CPU) 3. Anti-aliasing (Supersampling) 4. Passes a. Mapa de Profundidade b. Oclusão de Ambiente c. Composição dos passes 5. Conclusão 6. Referências Aarão Marins 1021747 1. Níveis de pré-visualização Uma vez que o passe de oclusão de ambiente está sendo calculado junto com o ray tracing, o tempo de render é maior, portanto foi ajustou-se o algoritmo de exibição para permitir ao usuário pré-visualizar como a cena está ficando, simplesmente fazendo o cálculos de pixels intercalados, de acordo com uma variável de resolução, que vai convergendo para a resolução final da imagem. Figura 1 - Dois níveis de pré-visualização e o resultado final É importante ressaltar que a cada vez que a resolução converge, os pixels já calculados são reaproveitados para a nova exibição, logo, não aumentam o tempo de cálculos da respectiva resolução. Se o usuário escolher ver dois níveis de resolução, na primeira imagem exibida ele verá um pixel a cada conjunto de 4x4 pixels, já na segunda imagem verá um pixel a cada conjunto de 2x2 pixels, e enfim verá a terceira com todos os pixels. 2. Paralelização com Buckets (na CPU) Como os raios no ray tracing utilizado são independentes entre si, utilizou-se o OpenMP para fazer a paralelização dos mesmo na CPU. A fim de trabalhar com espaços determinados na memória e obter uma visualização mais "reveladora" para o usuário, foi implementada uma estrutura de buckets, que neste caso, são conjuntos quadrados de pixels com tamanho pré-definidos. Figura 2 - Representação gráfica de uma execução do programa com buckets de 60x60 pixels Uma vez que todo o espaço de pixels está mapeado nos n buckets, uma lista que contém esses n buckets é distribuída por todas as threads disponíveis para o programa: t. Logo cada processador começa o processor de ray tracing com n/t buckets que lhe foram atribuídos. Para não perder o controle do programa durante a execução, antes de iniciar o raytracing, uma thread é criada para executar esse código em paralelo, enquanto a thread principal (que é a única que se comunica com o OpenGL) continua na iteração da glutIdle(), graças a isso é possível visualizar os buckets na medida em que terminam de ser processados. 3. Anti-aliasing (Supersampling) Pensando na melhoria da qualidade da imagem final, implementou-se uma forma simples (e cara) de fazer o anti-aliasing chamada Supersampling. Basicamente é uma média onde ao invés de apenas um, vários raios são mandados para o cálculo de um pixel. Existem várias formas de escolher quais serão estes raios, a forma escolhida para esse trabalho foi o Jittered, onde um pixel é divido em sub-pixels, e dentro de cada sub-pixel é lançado um raio aleatório. O resultado final é obtido então, a partir da média entre todos os resultados obtidos, como pode ser visto na Figura 3. Figura 3 - Exemplo de como funciona o refinamento das bordas usando 4 raios por pixel 4. Passes Este trabalho aproveita o mesmo algoritmo de traçado de raios para calcular além das informações de cor (com reflexão) do algoritmo base, um passe que guarda o mapa de profundidade e um para guardar o mapa de oclusão de ambiente. No algoritmo base retirou-se o cálculo de sombras para melhor visualização da oclusão de ambiente. Figura 4 - Ray tracing inicial (sem as sombras) a. Mapa de Profundidade O mapa de profundidade é obtido simplesmente com uma proporção entre a distância do olho até o ponto aonde o raio atirado bate em relação à distância de alcance máxima do olho. Figura 5 - Resultado do passe do mapa de profundidade b. Oclusão de Ambiente O mapa de oclusão de ambiente é um passe que ajuda a adicionar à imagem um toque de realismo, considerando a atenuação da luz nos objetos devido a oclusão. Figura 6 - Resultado do passe de oclusão de ambiente Para cada ponto da superfície encontrado no algoritmo do traçados de raios, são gerados diversos raios aleatórios na semi-esfera orientada na direção da normal desse ponto. A atenuação da oclusão se dá em função do raio escolhido para essa esfera. A oclusão é obtida calculando-se a soma da divisão todas as distâncias onde houve oclusão (cada distância é dividida pelo raio da semi-esfera). Essa soma então é dividida pelo número total de raios traçados, obtendo-se o valor da oclusão para o determinado ponto. c. Composição dos Passes A imagem final é uma composição do passe inicial (ray tracing sem sombra), sendo formada pela multiplicação do mesmo com o mapa de profundidade, e este resultado então é multiplicado pelo mapa de oclusão de ambiente, obtendo a imagem final do algoritmo. Um exemplo de composição final pode ser visto na figura 7. Figura 7 - Composição final das imagens 5. Conclusão Foi possível notar que o tempo de render aumenta consideravelmente quando a oclusão de ambiente está ativa. Uma vez que para que os resultados ficassem satisfatórios, era necessário traçar cem novos raios aleatórios para calcular a oclusão, e isto para cada ponto de superfície encontrado no ray tracing. Em contrapartida a paralelização usando buckets garantiu uma aceleração bem significante para o algoritmo, e, principalmente, se tornando uma forma do usuário já ter uma idéia de como está ficando a imagem final antes do término de todo o processamento. 6. Referências - Supersampling http://en.wikipedia.org/wiki/Supersampling http://www.everything2.com/index.pl?node_id=1028947 - GPU Gems - Chapter 17. Ambient Occlusion http://http.developer.nvidia.com/GPUGems/gpugems_ch17.html - Assorted Notes about Ambient Occlusion by Greg Coombe http://www.cs.unc.edu/~coombe/research/ao/