UNIVERSIDADE PRESBITERIANA MACKENZIE FACULDADE DE COMPUTAÇÃO E INFORMÁTICA BACHARELADO EM CIÊNCIA DA COMPUTAÇÃO Trabalho de Graduação Interdisciplinar

Bruno Gonçalves de Jesus Douglas Dionísio Fernandez das Neves

PARALELIZAÇÃO DE ALGORITMOS DE SÍNTESE DE IMAGENS FOTO-REALISTAS

São Paulo 2006

BRUNO GONÇALVES DE JESUS DOUGLAS DIONÍSIO FERNANDEZ DAS NEVES

PARALELIZAÇÃO DE ALGORITMOS DE SÍNTESE DE IMAGENS FOTO-REALISTAS

Trabalho de conclusão de curso de Ciência da Computação da Universidade Presbiteriana Mackenzie, apresentado como requisito parcial para obtenção do Grau de Bacharel em Ciência da Computação.

ORIENTADOR: Profª Drª Denise Stringhini

São Paulo 2006

“Dê-me memória e tempo e computarei o mundo.” Jussara Maria Marins

“Dê-me um cluster e computarei n vezes mais rápido.” Bruno Gonçalves de Jesus

“You know you have been ray tracing too long when ... ... You wonder which ray tracer God used.” David Kraics

AGRADECIMENTOS

Agradecemos a todos que colaboraram direta ou indiretamente para a realização, não só desse trabalho, mas de todo o período de graduação. Podemos citar: ALVARENGA, Ricardo Kaneko; 1985 BARBOZA. Alan Luiz Silva; 1984 CHOW, Kelly Yea Ling; 1985 CINTRA, Glauber Perl Ferreira; 1967 COSTA, Daniel Veronese Silva da; 1984 OLIVEIRA, Felipe Suzuki de; 1985 STUGINSKI, Leonardo Vitório de Souza; 1981 VALE, Johann Friedrich Reber do; 1984 VICENTE, Júlio Cezar Morais; 1984 VORHEES, Jason Gordo; 1946 YATABE, Vivian Satiko; 1985

E principalmente agradecemos ao apoio e paciência das duas garotas mais lindas do mundo, que precisaram agüentar nossa ausência, sono e mau humor nos dias difíceis que se antecederam à entrega final do trabalho: PELLEGRINO, Érika Fernandes Costa; 1988 PETERMANN, Priscila; 1984

RESUMO

Desde a criação da Computação Gráfica, inúmeros pesquisadores e cientistas tentam alcançar a síntese de imagens cada vez mais realistas aproximando o mundo virtual do real. Pode-se atualmente criar paisagens com sombras, luz, água, nuvens, vento, fogo, etc. com precisão de detalhes, riqueza de cores e formas. Isso é chamado foto-realismo: uma imagem gerada por meios computacionais que torna difícil ou impossível a distinção entre o real e o criado. Essa capacidade de criação demanda por computadores cada vez mais potentes e plataformas específicas para engenharia gráfica, gerando uma infinidade de áreas de pesquisa que podem ser estudadas. O problema de desempenho desses algoritmos é resolvido de forma bastante complexa, seja aperfeiçoando o hardware com novas placas aceleradoras gráficas ou distribuindo o processamento entre vários processadores e/ou computadores diferentes (paralelização). O principal objetivo desse trabalho foi a identificação de pontos de paralelização para os algoritmos de Ray-Tracing e Radiosidade do software “POV-Ray” para a elaboração de novos algoritmos que unam o poder da computação paralela de um cluster à alta necessidade de processamento gerada pela renderização de imagens na computação gráfica, diminuindo o tempo final do processo. Com isso pretendeu-se verificar a percentagem de aceleração do processo de criação de imagens foto-realistas.

Palavras-chave: Paralelismo; 3D; Foto-realismo; POV-Ray; Computação Gráfica; Cluster.

ABSTRACT

Since the beginning of Computer Graphics countless researchers and scientists have been trying to create more and more realistic image synthesis reducing distances between the real and virtual worlds. Nowadays, it's possible to create landscapes with shadows, natural light, water, clouds, wind, fire etc. with incredible detail precision, rich in colors and shapes. This process is called photorealism: an image created by computational meanings which is difficult or impossible to distinguish the real and virtual. This ability demands for better computers and specially made platforms for graphic engineering, creating lots of new research areas that can be studied. The performance problem have been solved in complex ways, making better hardware with new accelerated graphic boards or distributing the process between different processors/computers (parallelism). The main research objective is to look for parallelism points for the Ray-Tracing algorithm and Radiosity implemented by POV-Ray creating new versions of this algorithm which will merge the power of parallel computing from clusters and the high performance need that graphic computing have, making smaller the time to complete the process. We intend to see how much speedup can be obtained in the realistic computer image synthesis. Keywords: Paralelism; 3D; Fotorealism; POV-Ray; Graphic Computing; Cluster;

SUMÁRIO
Cap. I - Introdução ...................................................................................................... 14
1.1 – Objetivos ....................................................................................................................................... 14 1.2 – Descrição dos Capítulos ................................................................................................................ 14

Cap. II - Processamento Gráfico ................................................................................ 16
2.1 – Renderização ................................................................................................................................. 17 2.2 – Iluminação Local e Global ............................................................................................................ 18 2.3 – Ray-Tracing................................................................................................................................... 19 2.4 – Radiosidade ................................................................................................................................... 21 2.5 – Photon Mapping ............................................................................................................................ 23 2.6 – Anti-aliasing .................................................................................................................................. 28 2.7 – Jittering .......................................................................................................................................... 29

Cap. III - POV-Ray ........................................................................................................ 31
3.1 – Iluminação ..................................................................................................................................... 33 3.2 – Ray-Tracing no POV-Ray ............................................................................................................. 34 3.3 – Radiosidade no POV-Ray ............................................................................................................. 35 3.4 – Photon Mapping no POV-Ray ...................................................................................................... 36 3.5 – Anti-alias no POV-Ray ................................................................................................................. 38

Cap IV - Computação Paralela ................................................................................... 39
4.1 Modelos de Computadores Paralelos ............................................................................................... 39 4.1.1 Multiprocessamento com Memória Compartilhada ................................................................. 40 4.1.2 Multicomputador via Passagem de Mensagens ........................................................................ 40 4.1.3 Memória Compartilhada Distribuída ....................................................................................... 41 4.2 – Modelos de Arquiteturas Paralelas ................................................................................................ 41 4.3 – Clusters .......................................................................................................................................... 42 4.4 – Modelos de Algoritmos Paralelos ................................................................................................. 43 4.5 – Modelos de Programação Paralela ................................................................................................ 44 4.6 – Biblioteca MPI .............................................................................................................................. 44 4.6.1 – Rotinas de Passagem de Mensagens...................................................................................... 45

Cap. V – Paralelização do POV-Ray .......................................................................... 47
5.1 – Trabalhos Relacionados ................................................................................................................ 48 5.2 – Ray-Tracing em Paralelo ............................................................................................................... 51 5.2.1 – O Crivo de Raios.................................................................................................................... 51 5.2.2 – O Crivo de Raios Mestre/Escravo ......................................................................................... 53 5.3 – Radiosidade em Paralelo. .............................................................................................................. 54 5.4 – Photon Mapping em Paralelo ........................................................................................................ 56

Cap. VI – Testes e Resultados ................................................................................... 58
6.1 – Woodbox ....................................................................................................................................... 58 6.2 – Glasschess ..................................................................................................................................... 60 6.3 – Skyvase ......................................................................................................................................... 62

Cap. VII – Conclusão................................................................................................... 65
7.1 – Trabalhos Futuros.......................................................................................................................... 65

Referências Bibliográficas ......................................................................................... 66 Anexo A - Registro Oficial de Testes do Software em Paralelo .............................. 69 Anexo B - Utilização do POV-Ray na Linha de Comando........................................ 70 Anexo C - Código da função de Ray-Tracing do POV-Ray...................................... 71 Anexo D - Código da Radiosidade no POV-Ray ....................................................... 73 Anexo E - Código da função parcial do Photon Mapping no POV-Ray .................. 75

14 - CAPÍTULO I - INTRODUÇÃO

Cap. I - Introdução
A síntese de imagens na computação é um processo demorado e que exige considerável poder de processamento da máquina utilizada. Cada vez mais os algoritmos de renderização estão sendo aperfeiçoados e hoje é possível criar imagens tão reais que ninguém teria a capacidade de diferenciar uma imagem feita no computador de uma foto. Mas isso tem conseqüências, e mesmo nas máquinas mais modernas o processamento de uma única imagem pode levar dias dependendo de sua complexidade. Essa técnica, se considerando o contexto de elaboração de um filme, apresenta a seguinte problemática: para cada segundo de filme são necessárias cerca de 25 imagens para animação fluida no modelo de vídeo PAL, se cada imagem levasse um dia sendo renderizada, cada segundo de filme poderia levar até um mês para ficar pronto. A idéia foi então utilizar um cluster de computadores para aumentar o poder de processamento de modo eficaz e de baixo custo. Os testes de desempenho realizados mostraram quanto os algoritmos podem ficar mais rápidos quando executados em paralelo, trazendo soluções para as empresas que se utilizam dessas técnicas de computação gráfica além do impacto acadêmico na área de pesquisa científica.

1.1 – Objetivos
Para atingir a proposta de tornar a síntese de imagens foto-realistas mais eficaz, o objetivo do trabalho está focado nos três principais algoritmos usados em conjunto para a geração dessas imagens: Ray-Tracing, Radiosidade e Photon Mapping. O POV-Ray foi a ferramenta escolhida para servir de base para a implementação paralela por possuir implementação seqüencial de todos os métodos citados. Além disso, o programa foi codificado em C, pode ser executado em múltiplos sistemas operacionais e possui código aberto. Sendo assim, o foco principal do trabalho foi a modificação das implementações seqüenciais desses algoritmos para execução paralela em um cluster usando GNU/Linux. Com isso feito, testes comprovam o fator de aceleração (speedup) que os algoritmos em paralelo conseguiram sobre os seqüenciais, exibindo os testes feitos em um cluster composto por oito nós.

1.2 – Descrição dos Capítulos
O trabalho começa com uma explicação geral sobre o que é processamento gráfico, mostrando as diferenças entre suas quatro principais áreas: modelagem, síntese de imagens, processamento de imagem e visão computacional. Após isso é dado um foco maior à parte de renderização, que é a área de computação gráfica mais relevante ao trabalho. São exibidos também os principais algoritmos de geração de imagens foto-realistas, explicando suas características e diferenças além dos efeitos de anti-aliasing e jittering. O capítulo três enfoca o estudo do software POV-RAY, exibindo detalhes específicos da implementação de cada um dos três algoritmos de renderização, dando base para se entender como o processo de paralelização foi feito.

CAP. 1.2 - DESCRIÇÃO

DOS

C A P Í T U L O S - 15

O próximo tópico abordado é a computação paralela. São explicadas suas principais arquiteturas, tanto de hardware quanto software, citando suas diferenças mais relevantes. Fala-se sobre clusters e bibliotecas de paralelização, especialmente a biblioteca MPI. Este estudo foi a base para a escolha do sistema operacional GNU/Linux. O capítulo seguinte fala sobre como foi feita a paralelização efetivamente, explicando os detalhes e problemas encontrados em cada um dos algoritmos. Em seguida são mostrados dados sobre os testes e resultados, incluindo gráficos e cálculos dos fatores de speedup. O último capítulo apresenta a conclusão, os pontos fortes e fracos, assim como os problemas encontrados relativos aos métodos criados durante o desenvolvimento do trabalho. Trabalhos futuros são citados visando uma continuação do projeto.

16 - C A P Í T U L O I I - P R O C E S S A M E N T O G R Á F I C O

Cap. II - Processamento Gráfico
Foto-realismo é o processo de criação de imagens que se aproximam ao máximo do mundo real (como as fotografias). As primeiras técnicas para esse propósito eram naturalmente limitadas pela tecnologia da época, pois não existiam monitores coloridos e o hardware gráfico não era potente o bastante para exibir cores. Em 1980 surgiu a primeira versão do algoritmo Ray-Tracing que permitia modelos de iluminação global criando imagens foto-realistas (Figura 1) com sombras, reflexões, transparência e refrações. Já em 1984, esse algoritmo foi estendido possibilitando efeitos de penumbra, motion blur e profundidade de campo (VAN DAM; et al, 1993).

Figura 1 - Exemplo de imagem foto-realista. Detalhando os tipos de processamento gráfico, eles dividem-se basicamente em quatro áreas (WATT, 1999): • A modelagem, que nos permite criar cenas utilizando objetos gerados em wireframe que possam ser renderizados depois. Há muitos softwares específicos para modelagem como, por exemplo, o 3D Studio Max (AUTODESK, 2006), Maya (AUTODESK, 2006), Blender (BLENDER, 2006), Lightwave (NEWTEK, 2006) e Bryce (DAZ3D, 2006). • O processamento de imagens, que parte da imagem pronta e permite alterações, como aplicação de filtros, por exemplo. Exemplos de software: Photoshop (ADOBE, 2006) e Paint Shop Pro (COREL, 2006). Exemplos de filtros: passa-baixas, atenuação de ruído, suavização. • A visão computacional, que tenta a partir de uma imagem pronta identificar objetos, gerando modelos compreensíveis ao computador. • E, por último, a computação gráfica, que é a mais relevante para esta pesquisa. Ela permite transformar os objetos criados na parte de modelagem em imagens bidimensionais (renderização).

C A P . 2 . 1 - R E N D E R I Z A Ç Ã O - 17

2.1 – Renderização
Renderizar uma imagem significa converter modelos tridimensionais em imagens bidimensionais que possam ser visualizadas e impressas (figura 2), efetuando transformações geométricas, projeções, mapeamento de texturas, iluminação, efeitos especiais e rasterização. Isso se faz a partir da geometria da cena, das informações sobre os materiais de que são feitos os objetos (cores, texturas e transparências), das condições de iluminação ambiente e da posição de observação da cena (denominada câmera virtual ou observador).

Figura 2 – Modelo wireframe e correspondente renderização Todos os objetos são compostos de wireframes, nurbs ou splines. Wireframes são conjuntos de polígonos que representam uma forma enquanto nurbs e splines são equações polinomais que representam curvas. Na figura 3, há um exemplo de wireframe com diferentes números de polígonos formando cada um deles. A primeira possui 500 triângulos, a segunda 2000 e a terceira 8000. Nota-se a melhora na qualidade final da imagem de acordo com o aumento do número de triângulos (refinamento).

Figura 3 – Exemplos de wireframes Os processos mais modernos para geração de imagens que se aproximam da realidade, podem gerar belas e complexas cenas, criando um novo conceito de arte visual. Uma das principais vantagens de se utilizar uma técnica de computação no desenvolvimento de formas de arte é que ela diminui a necessidade de habilidades técnicas especiais como pintura, desenho ou escultura. Isto dá ao usuário a chance de ser o mais criativo possível sem passar muito tempo estudando técnicas de arte.

18 - C A P Í T U L O I I - P R O C E S S A M E N T O G R Á F I C O

2.2 – Iluminação Local e Global
Toda superfície em um ambiente é iluminada por uma combinação de luzes diretas e luzes refletidas. Algoritmos de iluminação local trabalham apenas com as luzes diretas. A iluminação direta é composta pelos raios de luz que partem de uma fonte e atingem, sem obstáculos, um ponto da cena, atenuados somente por algum tipo de fumaça, poeira ou neblina. Algoritmos de iluminação global são usados para simular uma iluminação mais realista as imagens geradas por computador. A iluminação indireta é a luz que, após partir da fonte, sofre algum tipo de reflexão ou refração em um ou mais objetos da cena (WHITTED, 1980). Imagens geradas usando algoritmos de iluminação global oferecem uma aparência mais foto-realista do que imagens geradas utilizando somente algoritmos de iluminação local. Porém, o custo computacional desses algoritmos é mais alto, sendo assim mais lento gerar uma imagem com cálculos de iluminação indireta. Uma abordagem comum para calcular a iluminação global em uma cena é guardar as informações de iluminação junto com a geometria da cena. Esse dado pode então ser usado para gerar imagens de diferentes pontos de vista sem a necessidade de se calcular a iluminação novamente, contanto que nenhum objeto ou fonte de luz mude de estado (COHEN; WALLACE, 1995). Na figura 4 podemos ver um conhecido modelo chamado “Caixa de Cornell” (BATTAILE; et al, 1984). Este modelo foi introduzido em 1984 e é usado como um padrão para testes de algoritmos de iluminação global. Nesse exemplo nota-se como a iluminação global melhora o realismo de uma imagem gerada no computador. A imagem da esquerda mostra a cena gerada com a iluminação local. Nota-se que o teto não possui iluminação nenhuma, pois não há pontos de luz que o atinjam diretamente. As sombras geradas pelas esferas de vidro são de cor preta ignorando a reflexão de luz nas paredes. Na imagem da direita pode-se ver a iluminação global: agora o teto está iluminado devido às reflexões da luz nas paredes e esferas, as sombras também ficaram mais claras mostrando que há uma maior interação entre todo o conjunto de objetos.

Figura 4 – Exemplo de iluminação local e iluminação global

C A P . 2 . 3 - R A Y - T R A C I N G - 19 Os algoritmos mais utilizados para se calcular iluminação global são: Radiosidade e Photon Mapping, descritos nos sub-capítulos abaixo.

2.3 – Ray-Tracing
Atualmente, o Ray-Tracing é uma das mais populares técnicas de síntese de imagens e possui fácil implementação. Ele pode usar a representação de cenas complexas com muitos objetos e muitos efeitos diferentes. Baseado no algoritmo de Ray-Casting, desenvolvido por Arthur Appel em 1968, o princípio do Ray-Tracing é simular a geometria ótica envolvida no trajeto de feixes de luz que viajam pelo espaço da cena (COHEN; WALLACE, 1995). O algoritmo de Ray-Casting dispara um feixe de luz em cada pixel (ponto da imagem) da cena, partindo do ponto de observação, e encontra o objeto mais próximo que bloqueia esse raio (figura 5). Então, a partir do ponto onde ocorreu a colisão, para cada ponto de luz da cena é disparado um novo raio. Se este raio encontra em seu caminho um objeto que impede que ele chegue até a luz, tem-se então uma sombra para aquele ponto do objeto. Caso consiga encontrar a luz sem obstáculos, a cor do pixel é calculada baseando-se no material e intensidade do ponto de luz (APPEL, 1968).

Figura 5 – Esquema exibindo a execução do Ray-Casting Diferentemente do Ray-Casting, no Ray-Tracing, à medida que o feixe choca-se nos objetos da cena, ele sofre uma reflexão, uma refração ou ambos, perdendo força dependendo do material do objeto. O processo continua recursivamente gerando novos feixes que colidem com os outros objetos até que percam toda a força, saiam da cena ou atinjam o número limite de recursões estabelecido. Somente quando o fim da recursão acontece é que o valor da cor do pixel é calculado baseado em todas as informações recolhidas. Esse processo é chamado de iluminação local e é necessário para capturar todos os efeitos da iluminação direta que ocorre na cena (GLASSNER, 1984). Cada raio, disparado a partir de um ponto chamado de observador, pode ser descrito como um fluxo de fótons viajando por um caminho em um vácuo perfeito (sem alterações devido à atmosfera). Após o disparo, o raio interage com a cena de uma das seguintes maneiras: pode ser absorvido, refletido ou refratado. Uma superfície pode refletir todo ou parte de um raio em uma ou mais direções, pode também absorver parte do raio, resultando em uma diminuição de

20 - C A P Í T U L O I I - P R O C E S S A M E N T O G R Á F I C O sua intensidade. Se uma superfície é transparente ou translúcida, ela refrata uma porção do raio por ela mesma (às vezes até alterando a cor do raio), enquanto absorve um pouco de sua intensidade. O raio continua seu percurso sendo refletido ou refratado até que perca 100% de sua força ou atinja o número máximo de reflexões/refrações definido (POV-RAY, 2005).

Figura 6 – Esquema de renderização (Ray-Tracing) A figura 6 demonstra, na primeira imagem, os disparos de raios partindo do observador e refletindo nos objetos. Pode-se ver as reflexões ocorrendo na mesa, indicadas pela letra R. O raio que atinge a região em baixo da mesa mostra como é calculada a sombra: o feixe que partiu do observador perdeu toda sua força e um novo feixe é criado e disparado contra as luzes da cena (nesse caso apenas uma). Como no caminho do raio há a mesa, o raio não atinge a luz, mostrando que aquele ponto é um ponto de sombra (feixe indicado pela letra S). Na segunda imagem, vemos o processo completo visto pelo olho do observador. O algoritmo clássico de Ray-Tracing é dado abaixo:
Para cada pixel na imagem { Criar um raio partindo do observador Iniciar objeto_mais_próximo para nulo Disparar o raio contra o pixel Para cada objeto na cena { Se o raio atinge o objeto { objeto_mais_proximo = este_objeto } } Se objeto_mais_proximo for nulo { Preencher o pixel com a cor do fundo } Se não {

C A P . 2 . 4 - R A D I O S I D A D E - 21
Disparar um raio contra cada fonte de luz para verificar sombras Se a superfície for reflexiva { Gerar um raio de reflexão Entrar em recursão com esse raio } Se o objeto for transparente { Gerar um raio de refração Entrar em recursão com esse raio } Preencher o pixel com a cor resultante } }

2.4 – Radiosidade
Como método de renderização, a Radiosidade foi introduzida em 1984 por pesquisadores da universidade de Cornell. Antes disso a teoria da Radiosidade era usada para resolver problemas de transferência de calor desde 1950. Diferentemente do Ray-Tracing, a radiosidade não é um algoritmo de renderização e sim de iluminação global. Ele não foi largamente implementado em seu conceito original porque utilizava uma solução de matrizes muito complexas para melhorar o cálculo de radiância (BATTAILE; et al, 1984). Radiância é vista como a quantidade de radiação projetada pela energia luminosa em uma determinada região de uma cena. Ela representa a intensidade da luz em cada ponto da cena e contribui para colorir corretamente todas as suas regiões. Trabalha-se com a idéia de que a energia sendo distribuída na cena encontra o equilíbrio de luz entre os objetos. Assim que os raios disparados das fontes de luz colidem com os objetos, a informação da iluminação é guardada no próprio objeto. O algoritmo da Radiosidade divide as superfícies em regiões menores também chamadas de patches. Isso cria uma subdivisão da geometria original da cena sendo que quanto menor forem os patches por superfície, melhor será a qualidade da iluminação. Na figura 7, pode-se ver uma cena renderizada e sua correspondente divisão em patches.

Figura 7 – Cena renderizada e correspondente construção em patches

22 - C A P Í T U L O I I - P R O C E S S A M E N T O G R Á F I C O Pode-se observar na figura 8 que o patch cinza é um emissor de luz. Ele está disparando um raio com uma potência de 12 unidades (poderiam ser lúmenes, por exemplo) contra um dos patches do plano vertical. Esse raio choca-se contra o plano e é absorvido por ele, nesse caso com uma taxa de absorção de 50%. O raio é refletido e choca-se com outro patch no plano horizontal onde perde 34% de força (relativa à força gerada pela absorção anterior) e, em seu último impacto contra o plano vertical novamente, perde outros 50% e sai da cena pela direita. Na imagem da direita pode-se ver os valores de iluminação para os patches atingidos. Todo patch onde não ocorreram colisões permanece com valor de iluminação zero (SHIRLEY, 1994).

Figura 8 – Exemplo de raios sendo disparados contra os patches

Figura 9 – Transferência de luz entre os patches vizinhos O “truque” da Radiosidade é que cada patch torna-se um emissor de luz secundário emitindo partículas de luz em patches vizinhos. Então, essas superfícies vizinhas emitem partículas de luz em seus vizinhos e o processo continua até que toda a energia das partículas seja distribuída na cena como visto na figura 9 (COHEN; WALLACE, 1995). Nem toda luz é re-emitida entre as superfícies, uma parte dela é absorvida pela própria superfície de acordo com as propriedades de seu material. Esta distribuição de energia é similar à maneira como a luz é distribuída na natureza. Quando se fala em Radiosidade diz-se que a

C A P . 2 . 5 - P H O T O N - M A P P I N G - 23 iluminação de um ponto qualquer na imagem é dada pela combinação de raios luminosos recebidos direta ou indiretamente de um ponto de luz.

Figura 10 - Esquerda: cena gerada com Ray-Tracing. Direita: mesma cena com efeitos de Radiosidade. Um efeito de iluminação gerado pela Radiosidade ocorre quando, por exemplo, uma partícula azul, emitida de uma fonte de luz, choca-se contra uma superfície A e parte dessa luz é re-emitida para superfícies vizinhas, como a superfície B, essas superfícies recebem parte da informação da cor azul. Se a superfície B fosse originalmente branca, agora teria partículas da cor azul espalhando-se sobre sua cor branca. A superfície B está agora composta de partículas azuis e brancas. Esta é uma grande vantagem sobre o Ray-Tracing, porque a energia luminosa de uma superfície pode se misturar com outras superfícies. O efeito ocorrido na superfície B é conhecido como Color Bleeding, e aumenta o realismo da síntese de imagens, como pode ser visto na figura 10 (JOHN, 2003). A vantagem da Radiosidade é que ela não precisa ser recalculada caso o ponto de observação seja alterado. Como a iluminação é calculada em toda cena usando-se os patches, esses valores não mudam se apenas ocorrer uma mudança no ponto de observação. A Radiosidade só precisa ser recalculada caso a iluminação da cena seja modificada. Por esse motivo a Radiosidade é conhecida como um algoritmo independente do ponto de observação. Mesmo sendo um algoritmo de cálculo de iluminação para sombras, a Radiosidade pode ser construída de forma híbrida combinando um algoritmo de renderização (que pode ser o RayTracing, por exemplo) ao seu processo. Assim, pode-se usar a radiosidade para o cálculo das sombras usando reflexões difusas e Color Bleeding, e o outro algoritmo para o cálculo de reflexões especulares e efeitos de transparência.

2.5 – Photon Mapping
O Photon Mapping (desenvolvido em 2001 por Henrik W. Jensen) criou uma nova abordagem sobre como agrupar os feixes, não se baseando apenas na geometria da colisão com objetos. A idéia principal é mudar a representação da luz. Quando se dispara um feixe de luz e esse atinge uma superfície plana, ele é refletido com o mesmo ângulo. Quando a superfície não é plana, o feixe é refletido em diferentes ângulos podendo convergir para o mesmo ponto de um outro feixe. Essa concentração de feixes em um mesmo ponto gera um efeito luminoso chamado

24 - C A P Í T U L O I I -

PROCESSAMENTO

GRÁFICO

cáustico (figura 11) – até então inexistente no algoritmo de Ray-Tracing e Radiosidade (JENSEN, 2001).

Figura 11 – Imagem com efeitos cáusticos gerado pelo algoritmo de Photon Mapping. Um exemplo simples de geração de cáusticos é a lente de aumento (lupa) que faz com que a luz se concentre em apenas uma região. (JENSEN, 2001). Há duas formas diferentes que um cáustico pode assumir: catacáustico e diacáustico. O efeito catacáustico é provocado pela reflexão dos feixes de luz na superfície enquanto o diacáustico é provocado pela refração desses feixes (quando o raio atravessa a superfície) (JENSEN, 2001). Assim como o algoritmo de Radiosidade, o Photon Mapping é um algoritmo de iluminação global, podendo também ser implementado de forma híbrida com o Ray-Tracing, funcionando assim em duas passagens: • Na primeira, geram-se os feixes de luz saindo das fontes de luz e não do olho observador. Cada vez que um feixe se choca com um objeto ele deposita uma quantidade de energia nesse ponto em uma estrutura chamada photon-map. Na segunda passada, os feixes são disparados pelo olho de observação e as informações do photon-map são cruzadas para a geração dos cáusticos na cena.

A figura 12 demonstra o funcionamento do algoritmo de Photon Mapping. Linhas disparadas a partir de um ponto de luz no teto da cena chocam-se com o objeto de cristal. Ocorrem refrações desviando os raios de luz que acabam por se encontrar em uma mesma região formando o cáustico. O mapa de fótons é representado pelos círculos no final de cada linha, e é

C A P . 2 . 5 - P H O T O N - M A P P I N G - 25 essa informação que será usada para gerar o efeito de cáustica no processo de Ray-Tracing (GÜNTHER; WALD; SLUSALLEK, 2004).

Figura 12 – Demonstração do algoritmo de Photon Mapping. No modelo do Photon Mapping os fótons são criados a partir das fontes de luz. Essas luzes podem ser luzes típicas de computação gráfica como pontos, luzes direcionais, regiões de luz (esferas, retângulos, etc.) ou podem ser fisicamente baseadas na geometria de um objeto qualquer. Ou seja, o algoritmo suporta qualquer tipo de luz. Assim como na natureza, um grande número de fótons é tipicamente emitido de cada ponto de luz. A potência (em lúmenes, por exemplo) de uma fonte de luz é dividida entre todos os fótons emitidos sendo que cada fóton transporta uma fração da potência luminosa dessa luz. É importante notar que a potência dos fótons é proporcional ao número de fótons emitidos, e não ao número de fótons guardados no photon-map. Baseado em um único fóton não se pode dizer muito sobre a quantidade de luz que uma região recebe, pois isso é dado pela densidade dos fótons na região. (JENSEN, 2001). Quando um fóton é emitido contra a cena o algoritmo trabalha exatamente da mesma forma que o Ray-Tracing, porém o seu objetivo é guardar as regiões de acúmulos de luz. O photon-map é a representação de um mapa onde estão guardados todos os fótons da cena. Um aspecto fundamental do photon-map é que ele não é acoplado ao modelo da cena, diferentemente da técnica de Radiosidade. Isso significa que os fótons não são associados à geometria, ao invés disso são guardados em uma estrutura separada.

26 - C A P Í T U L O I I -

PROCESSAMENTO

GRÁFICO

Quando a imagem é renderizada, a estrutura do photon-map é usada para calcular a iluminação do ponto. A iluminação é calculada de acordo com a estatística baseada nos pontos mais próximos onde houve colisões de fótons. Para que o algoritmo de Photon Mapping seja praticável, a busca na estrutura de dados deve ser rápida no sentido de encontrar os pontos mais próximos em um ambiente tridimensional. Ao mesmo tempo ela deve ser compacta, já que são usados milhões de fótons (JENSEN, 2001). É descartado imediatamente o uso de estruturas simples como vetores multidimensionais e listas, já que a busca através dessas estruturas tem um custo computacional elevado. Uma estrutura simples para manter a proximidade entre grupos de fótons é uma grade tridimensional onde um cubo contendo os fótons é dividido uniformemente através dos eixos X, Y e Z em um número de sub-cubos, cada um contendo certo número de fótons. A busca por pontos vizinhos é o fato de encontrar o sub-cubo correto e examinar seus fótons, e talvez também os sub-cubos vizinhos a este. Esta estratégia é quase ótima se os dados estão uniformemente distribuídos no espaço tridimensional. Infelizmente, isso não é verdade no caso dos fótons. Importantes efeitos luminosos como os cáusticos geram concentrações muito altas de fótons em um pequeno espaço. Essa natureza não uniforme dos fótons torna a grade tridimensional também impraticável. Uma estrutura de dados melhor que resolve o problema da distribuição nãouniforme dos fótons é a kd-tree (JENSEN, 2001).

Figura 13 – Exemplo de kd-tree. A kd-tree (figura 13) é uma árvore de busca binária multidimensional onde cada nó é usado para dividir uma das dimensões do espaço tridimensional (uma kd-tree unidimensional pode ser representada com uma árvore binária). O photon-map é um conjunto de pontos tridimensionais e necessita de uma estrutura de árvore tridimensional muito próxima da BSPTree para guardar os fótons. Cada nó nessa árvore contém um fóton e ponteiros para as subárvores esquerda e direita. Todos os nós, exceto os nós folha, têm um plano ortogonal que contém os fótons e corta uma das dimensões (X, Y ou Z) em duas partes. Todos os fótons na sub-árvore da esquerda estão abaixo deste plano e todos os fótons na sub-árvore da direita estão acima do plano (SUNG; SHIRLEY, 1992).

C A P . 2 . 5 - P H O T O N - M A P P I N G - 27

Figura 14 – Exemplo de BSP-Tree. A figura 14 mostra a visualização tridimensional de uma BSP-Tree (Binary Space Partitioning Tree), em um ambiente que foi dividido duas vezes. A letra A representa o ambiente completo. A primeira divisão secciona o ambiente nas regiões B e C (esquerda e direita). A segunda divisão corta a região B ao meio formando as novas sub-regiões D e E. Ao lado é disposto como ficará a árvore dessa representação. A informação dos fótons ficaria guardada nos nós folhas C, D e E, que são os únicos que não são divididos. A complexidade média para se localizar um fóton em uma kd-tree é O(log n) onde n é o número de fótons na árvore. No pior caso a busca por um fóton pode levar O(n). Se a kd-tree for balanceada, mesmo em seu pior caso a complexidade é O(log n). Para encontrar os k vizinhos mais próximos de um ponto na kd-tree leva-se O(k+log n) (JENSEN, 2001). Outra estrutura eficiente para resolver o problema da busca pelos pontos vizinhos é o diagrama de Voronoi, composto pela triangulação dupla de Delaunay. No diagrama de Voronoi, cada nó é ligado aos vizinhos mais próximos. A busca pelos pontos mais próximos pode ser feita partindo-se de um nó (ponto) aleatório e em seguida executando uma caminhada dirigida na direção do ponto de interesse, pela seleção recursiva do próximo ponto mais próximo do interesse. Tendo encontrado o nó é executado um backtracking. O nó atual é guardado como sendo um dos vizinhos mais próximos, e o ponto seguinte mais próximo é naturalmente escolhido pela recursão. A complexidade do diagrama de Voronoi é de O(k*log n) onde k é o número de vizinhos do interesse e n é o número de regiões do diagrama (AURENHAMMER, 1991).

Figura 15 – Busca pelos vizinhos do ponto de interesse.

28 - C A P Í T U L O I I -

PROCESSAMENTO

GRÁFICO

Na figura 15 vemos um diagrama de Voronoi demonstrando uma caminhada dirigida do ponto aleatório de partida (esquerdo superior) até o ponto de interesse (direito inferior). A marcação amarela demonstra o caminho estabelecido até o momento e a marcação em roxo, o ponto atual onde está a busca. A próxima iteração do algoritmo leva ao ponto de interesse, dessa forma, executa-se um backtracking e marca-se o ponto anterior como sendo um vizinho (marcação verde). Uma nova marcação em roxo é gerada encontrando um novo vizinho que está próximo do ponto de interesse. Após nova iteração, o ponto é marcado como vizinho e um novo backtracking é realizado de forma que se encontre o último ponto vizinho ao lado do ponto de interesse. Considerando-se os requisitos de eficiência, a kd-tree é a escolha mais natural para a representação do photon-map. Além de ser uma estrutura de busca rápida pelos vizinhos, a kdtree possui uma implementação compacta, diferentemente do diagrama de Voronoi que necessita O(n²) de memória para armazenar os fótons em um espaço tridimensional, onde n é o número de fótons (JENSEN; 2001).

2.6 – Anti-aliasing
Como mostrado anteriormente, o algoritmo de Ray-Tracing dispara um raio contra cada pixel da imagem. Essa técnica pode levar ao serrilhado das imagens, visões distorcidas de linhas finas e detalhes perdidos na imagem. O anti-aliasing é uma técnica utilizada para ajudar a eliminar tais erros ou reduzir o impacto negativo que eles dão na imagem. Resumindo, o anti-aliasing faz com que a imagem pareça mais suave (POV-RAY; 2005). Na figura 16 é possível notar a diferença entre uma imagem gerada com uma técnica de Ray-Tracing sem anti-aliasing (esquerda) e uma com anti-aliasing. Na imagem à esquerda existem diversas mudanças de tonalidades bruscas. Com a aplicação de um filtro de anti-alias é feita uma suavização nos pontos onde a mudança de tonalidade é mais brusca, gerando uma imagem mais suave e próxima do real.

Figura 16 – Sem anti-alias, esquerda. Com anti-alias, direita. O algoritmo trivial de anti-aliasing funciona de maneira bastante simples: para cada pixel da imagem, é feito uma média balanceada do valor desse pixel com os seus vizinhos. O resultado final é que uma região que possuía uma brusca diferença de coloração passa a apresentar uma mudança mais suave entre cores próximas (figura 17).

C A P . 2 . 7 - J I T T E R I N G - 29 Um segundo método para se aplicar um filtro de anti-alias pode ser utilizado no algoritmo de Ray-Tracing. Como visto no capítulo 2.3, o Ray-Tracing normalmente dispara um raio no centro de cada pixel da imagem. Porém, para evitar o serrilhamento da imagem, o algoritmo pode dividir o pixel em grades de sub-pixels, passando então a disparar um raio em cada sub-pixel. No fim, é feito uma média entre esses valores, que resulta na cor final do pixel.

Figura 17 – Exemplo da aplicação de anti-aliasing. Essa técnica é chamada de super-sampling (super-amostragem) e tem como principal desvantagem o custo computacional elevado, pois para cada pixel da imagem são disparados diversos raios, e não apenas um como no Ray-Tracing original. Porém, o filtro de anti-alias não é necessário para todos os pixels da imagem. Pensando nisso, uma técnica seria disparar um raio por pixel da imagem. Se a cor do pixel difere dos seus vizinhos por pelo menos um valor prédeterminado (threshold) é usado super-sampling, disparando-se um número determinado de raios adicionais. Caso contrário o Ray-Tracing continua normalmente. O cálculo do valor de threshold é feito de acordo com a fórmula:
Diff = abs(r1-r2) + abs(g1-g2) + abs(b1-b2);

Onde r1, g1 e b1 são as cores (rgb) de um ponto e r2, g2 e b2 são as cores do outro ponto. Se essa diferença é maior do que o threshold, em ambos os pixels é feito o supersampling. Os valores rgb estão no intervalo de 0.0 a 1.0, dessa forma a maior diferença possível é 3.0. Se o threshold for 0.0 o super-sampling é feito em todos os pixels; se o threshold é 3.0 nenhum pixel recebe o processo.

2.7 – Jittering
Um dos grandes problemas quando se busca o foto-realismo é o excesso de perfeição que uma renderização pode acabar gerando. Não existem imperfeições nas imagens, todas as formas geométricas calculadas são matematicamente perfeitas. Por isso, imagens geradas usando RayTracing, apesar de perfeitas, às vezes parecem estranhas ou até surreais. Para que sejam geradas imagens mais próximas à realidade é utilizada uma técnica chamada Jittering. A idéia principal dessa técnica é utilizar um par de números aleatórios (ou informados) para evitar o disparo de raios sempre contra o centro de um pixel da imagem (figura 18). Dessa forma evita-se que ocorra o efeito de grade, e também, a perfeição absoluta dos objetos. A cada pixel, o par de números é somado de forma que o raio saia do centro e seja disparado contra outro local próximo dentro do pixel (SHIRLEY, 1994).

30 - C A P Í T U L O I I - P R O C E S S A M E N T O G R Á F I C O

Figura 18 – Demonstração da técnica de Jittering. Fatores negativos somados. A figura 19 exibe exemplos de distribuição dos raios nos pixels. O quadro à esquerda exibe os raios diretamente no centro dos pixels, essa é a forma como o Ray-Tracing executa seus disparos. O quadro central exibe uma distribuição por meio de um algoritmo aleatório, notavelmente o pior método, pois os pixels são distribuídos em qualquer lugar ocorrendo casos onde há pixels com mais de um raio traçado e outros sem nenhum raio. O terceiro quadro mostra uma distribuição dos pixels com jittering. Há apenas um raio por pixel e esse raio foi distribuído de acordo com as somas dos pares de números descrito acima.

Figura 19 – Distribuição de raios central, aleatória e com jittering.

C A P . 3 – P O V - R A Y - 31

Cap. III - POV-Ray
Conhecido como Persistence of Vision Ray-Tracer, o POV-Ray foi desenvolvido a partir do DKBTrace, um antigo programa de renderização criado em 1986 para Unix (POV-RAY, 2005). Seu projeto, iniciado em 1991, era chamado de StartLight e foi criado por um grupo de pesquisadores distribuídos pelo mundo devido ao lento desenvolvimento do DKBTrace. O programa conta com diversos recursos, dos quais se podem citar: • • • • • • • • • • Uma linguagem de programação de cenas própria; Exemplos prontos, materiais e iluminação; Imagens de alta resolução renderizadas com até 48 bits de cor. Criação de paisagens com efeitos de profundidade e anti-aliasing; Diversos tipos de câmera como, por exemplo, perspectiva e lentes olho-de-peixe; Técnicas modernas de iluminação criada por spots, luzes cilíndricas, iluminação global e luzes coloridas; Implementação de algoritmos de Radiosidade e Photon Mapping; Efeitos especiais como arco-íris, neblina e atmosfera; Saída em tela em diversos formatos de arquivos (BMP, PNG, TGA, PPM); Formas básicas (esferas, caixas, cilindros, cones, triângulos, planos) e avançadas (tori, curvas de beziér, montanhas, bolhas, textos, superfícies de revolução, prismas, polígonos, fractais, objetos paramétricos, etc.); Possibilidade de união, intersecção e subtração de formas gerando objetos de geometria construtiva sólida que podem ser reutilizados. Cores e padrões de textura para vidros, madeira, fogo, água e possibilidade de utilização de arquivos de imagem como texturas.

• •

Em sistemas operacionais Windows, o POV-Ray conta com um ambiente gráfico que permite a fácil edição de imagens com menus e elaboração de código. Adicionalmente, existe a GUI (Graphical User Interface) com todas as opções possíveis em termos de manipulação do tamanho da foto, da qualidade, do anti-alias etc. Nos sistemas Unix e suas variações não existe tal ambiente gráfico oficial, mas sim diversos programas de código aberto – pode-se citar, por exemplo, o KpovModeler (KDE, 2006) e o Y.A.P.R.M (POV-RAY, 2005). De fato, o POV-Ray não precisa de nenhuma GUI ou qualquer outra parte gráfica para executar as renderizações. Diversos argumentos ligados ao executável do programa resolvem esse problema (Anexo B) e a edição de códigos pode ser feita em qualquer editor de texto comum respeitando-se as regras da linguagem de modelagem do POV-Ray. A versão utilizada do programa foi a 3.6.1, que é a versão estável mais recente até a data deste trabalho.

32 - C A P Í T U L O I I I – P O V - R A Y

Figura 20 – Imagem renderizada no POV-Ray com radiosidade e profundidade de campo. Há mais de uma maneira de utilizar o programa para renderizar uma imagem. Variações no tempo de renderização são notáveis de acordo com a escolha da qualidade desejada. Uma imagem bem elaborada com dezenas de objetos, reflexos e pontos de luz renderizada com uma grande dimensão e anti-alias pode levar minutos ou horas, dependendo da qualidade desejada. Para demonstrar as etapas do processo realizadas pelo POV-Ray para gerar uma renderização, apresenta-se o modelo abaixo, que demonstra os passos desde seu início com a construção da cena até o final, com a saída na tela, em arquivo ou em ambos (figura 21).

Figura 21 – Modelo original do POV-Ray.

C A P . 3 . 1 – I L U M I N A Ç Ã O - 33 Todo o POV-Ray foi construído em linguagem C, usando um modelo de programação estruturada. O código conta com mais de 200 arquivos distribuídos entre declarações e definições de estruturas (arquivos .h) e funções escritas e melhoradas durante mais de 10 anos (arquivos .cpp). Apesar da extensão dos arquivos, não há classes no código do POV-Ray. Um projeto prevê a conversão de todo código para C++ mas não é o que mostra a última versão disponível para testes (3.7beta2) que mantém a mesma linha de programação.

3.1 – Iluminação
O tipo de iluminação básico feito pelo POV-Ray é a iluminação pontual. Nessa iluminação, um ponto de luz emite fótons em todas as direções uniformemente. Fontes de luz spot são fontes de luz com formato cônico que emitem fótons em uma determinada direção baseando-se no raio de abertura do cone. Fontes de luz cilíndricas emitem fótons agrupados em formato cilíndrico, diferentemente da fonte spot. Além disso, diferem de outras fontes no fato de que a distância delas para os objetos não influenciam a potência da luz que elas emitem, quando em outras fontes de luz há um raio máximo de atuação sobre a cena. Essas luzes são comumente utilizadas para simulação de holofotes e feixes de laser. Luzes retangulares ou em área são fontes de luz que emitem fótons partindo de um plano. Essas luzes são basicamente um grupo de luzes pontuais alinhadas sobre uma grade retangular. A potência que a luz chega a um objeto é dada pela soma das emissões de cada uma das luzes pontuais. Cada um dos tipos de fontes de luz é exibido na figura 22.

Figura 22 – tipos de luz existentes no POV-Ray O POV-Ray implementa uma extensão de iluminação conhecida como soft-shadows que cria sombras mais suaves aos objetos durante a renderização. A figura 23 exibe como funciona esse efeito, que pode ser alcançado pelo uso de luzes retangulares (em área). Vêem-se quatro pontos que formam a fonte retangular de luz emitindo fótons sobre um objeto circular azul no centro da imagem. As áreas onde ocorre intersecção entre os pontos emissores de luz têm a sombra suavizada.

34 - C A P Í T U L O I I I – P O V - R A Y Uma estrutura importante no POV-Ray são os grupos de luz que tornam possível a união de objetos comuns com objetos de luz. Essas luzes do grupo iluminam somente os objetos do próprio grupo em que estão contidas. Em outras palavras, essas luzes não contribuem para a iluminação global.

Figura 23 – Exemplo de soft-shadows

3.2 – Ray-Tracing no POV-Ray
O algoritmo de renderização implementado pelo POV-Ray é o Ray-Tracing. Como visto na figura 21, ele é representado pelo bloco Rendering. É nesse passo em que a imagem é de fato gerada, utilizado todos os dados vindos dos passos anteriores. Além do método descrito no capítulo sobre Ray-Tracing, o POV-Ray ainda conta com um método de renderização alternativo chamado Mosaic Preview que permite ver a imagem sendo renderizada em diversos passos (com uma qualidade inferior e posterior refinamento). Esse método trabalha da mesma forma como o Ray-Tracing, mas começa com uma granularidade de pixel maior, sendo um pixel equivalente a 8x8 pixels, por exemplo. O algoritmo faz diversas iterações reduzindo a granularidade com a divisão por dois até que por fim, com granularidade 1x1 o Ray-Tracing comum é executado. Na implementação do Ray-Tracing no POV-Ray, a imagem é guardada por linha, ou seja, assim que uma linha acaba de ser gerada, ela é mostrada na tela e é salva em arquivo, não há uma matriz para todos os pixels da imagem. O POV-Ray guarda apenas a última linha renderizada, que é usada caso a opção de anti-alias esteja ativada. Cada pixel é representado por uma estrutura de cinco números em ponto flutuante (floats): são representados respectivamente o tom de vermelho, verde, azul, os níveis de filter e transmit. Filter permite a uma cor deixar passar a luz (transparência) afetando a cor da luz saída do objeto, atuando assim como um filtro. Transmit permite especificar a transparência nãofiltrada, ou seja, a superfície fica transparente sem influenciar a cor da luz. A estrutura em C está descrita abaixo.
struct Image_Colour_Struct{ unsigned float Red, Green, Blue, Filter, Transmit; };

CAP. 3.3 – RADIOSIDADE

NO

P O V - R A Y - 35

Uma linha é composta por essa estrutura de pixel multiplicado pelo número de colunas existentes na renderização. O código do método que implementa o Ray-Tracing no POV-Ray é mostrado no Anexo C.

3.3 – Radiosidade no POV-Ray
O algoritmo da Radiosidade implementado no POV-Ray é considerado como experimental, pois alterações estão previstas em versões futuras. Esse método não funciona exatamente como a Radiosidade por transferência da radiância, ele é baseado no artigo “A Ray Tracing Solution for Diffuse Interreflection” de 1988. Ele não calcula a iluminação global da cena, simplesmente trabalha em conjunto com o Ray-Tracing adicionando um passo a mais a esse algoritmo. Dessa forma, as inter-reflexões difusas são calculadas simulando o mesmo efeito gerado pela Radiosidade desenvolvida por Goral et al (1984). Evita-se o cálculo desnecessário de iluminação em áreas que não estão no ponto de vista do observador, melhorando o desempenho em relação à Radiosidade. A iluminação de um ponto é calculada com a média interpolada entre dois outros pontos nos quais o cálculo já tenha sido feito. Caso o ponto não se encontre entre dois outros pontos já calculados, a radiância é calculada a partir de toda a iluminação indireta recebida pelo ponto na cena (CLEAR; RUBINSTEIN; WARD, 1988).

Figura 24 – Cálculo da radiância pelo método da inter-reflexão A figura 24 exibe que o cálculo da radiância nos pontos E1 e E2 já foi realizado por passos anteriores. O valor calculado de A será uma média entre os valores das radiâncias de E1 e E2. B será calculado pela radiância do ponto E2. O ponto C está fora de pontos conhecidos, portanto seu cálculo será feito de acordo com a iluminação indireta. Deve-se notar que devido à distância que o ponto B encontra-se de E2, a intensidade da radiância de B será menor de acordo com essa distância. Um ponto sobre a borda da radiância de E2, por exemplo, terá um valor de intensidade quase zero (CLEAR; RUBINSTEIN; WARD, 1988). O funcionamento desde algoritmo está baseado no método de Mosaic Preview para o cálculo da iluminação dos primeiros pontos. À medida que a granularidade diminui, temos mais pontos médios calculados, de forma que cada vez menos novos pontos tenham que ser calculados. Nota-se, nas imagens da figura 25, alguns pontos que sofreram distúrbios e estão deslocados à medida que a granularidade diminui; esse é o efeito do Jittering.

36 - C A P Í T U L O I I I – P O V - R A Y

Figura 25 – Exibição do Mosaic Preview Jittering é utilizado no algoritmo da Radiosidade para simular uma imperfeição matemática, tornando a imagem final mais real, como descrito no sub-capítulo 2.7. A Radiosidade é dependente de objetos, isso significa que apenas os objetos que contiverem as informações para o uso da Radiosidade serão utilizados para o cálculo da iluminação global. Portanto, em uma mesma cena, podem existir objetos que utilizem a técnica de radiosidade para serem gerados e outros que não utilizem. O algoritmo que gera a Radiosidade pelo método de Mosaic Preview está no Anexo D.

3.4 – Photon Mapping no POV-Ray
O algoritmo de Photon Mapping integra o POV-Ray desde 2004. Usa uma kd-tree balanceada para o armazenamento dos fótons e é o algoritmo mais pesado que compõe o programa. Da mesma forma que a Radiosidade, o algoritmo usa o Ray-Tracing para renderização da imagem após a geração do photon-map e o Photon Mapping deve ser ativado objeto por objeto. Isso significa que somente em objetos ativados para o Photon Mapping é que os fótons serão disparados. A imagem 26 mostra o efeito dos cáusticos apenas nas lentes superior e inferior, indicando que a lente central não possui o recurso ativado.

Figura 26 – Apenas objetos com o recurso ativado recebem os fótons

CAP. 3.4 – PHOTON MAPPING

NO

P O V - R A Y - 37

A construção do photon-map trabalha com seis laços seguidos que disparam raios a partir dos diferentes tipos de fontes de luz contra os objetos da cena. Primeiramente, dois laços são feitos de acordo com o número de superfícies existentes na cena. As luzes globais (que afetam toda cena) são percorridas e disparadas contra cada objeto gerando os primeiros dados sobre a iluminação. Em seguida, um novo laço é feito da mesma forma para os grupos de luz. Os próximos dois laços são uma repetição dos laços anteriores, porém agora de acordo com um número pré-estabelecido de fótons durante a construção da cena, todos esses fótons são disparados de forma distribuída entre as luzes de grupo e luzes globais. O número padrão de fótons emitidos é de 20000 (POV-RAY, 2005). Os últimos dois laços são feitos para os disparos de fótons a partir de fontes de luz cilíndricas tanto para grupos quanto para iluminação global. Ao fim de todos os laços, uma kdtree balanceada é construída para posterior utilização pelo Ray-Tracing. A estrutura que guarda cada fóton do Photon Mapping é composta por um vetor de três posições que possui a localização do fóton no espaço tridimensional da cena (SNGL_VECT), um vetor de quatro caracteres que identifica a cor (vermelha, verde e azul) e a intensidade da luz do fóton (SMALL_COLOUR), um caractere que representa uma informação de localização relativa da kd-tree e dois caracteres que representam os ângulos de direção de onde veio o fóton. Fótons podem assumir qualquer cor, já que os objetos que tem a propriedade filter alteram o modo como a luz passa por eles. Os ângulos de direção que compõem um fóton são relativos à última reflexão ou refração que a este fóton sofreu.
struct photon_struct { SNGL_VECT Loc; SMALL_COLOUR Colour; unsigned char info; signed char theta, phi; }; /* /* /* /* location */ color & intensity (flux) */ info byte for kd-tree */ incoming direction */

O photon-map é composto por uma lista de listas (matriz esparsa) de fótons (PHOTON_BLOCK) dinamicamente realocada à medida que novos fótons são adicionados a ela. O photon-map não guarda informações sobre o acúmulo de intensidade em um ponto. Por exemplo, se três fótons chocam-se contra o mesmo ponto em uma superfície, todos os três fótons devem ser guardados na kd-tree para que suas intensidades sejam utilizadas durante a passada do Ray-Tracing.
struct photon_map_struct { /* these 3 are render-thread safe - NOT pre-process thread safe */ PHOTON_BLOCK *head; // the photon map - array of blocks of photons int numBlocks; /* number of blocks in base array */ int numPhotons; /* total number of photons used */ DBL DBL DBL int }; minGatherRad; minGatherRadMult; gatherRadStep; gatherNumSteps; /* /* /* /* minimum gather radius */ minimum gather radius multiplier */ step size for gather expansion */ maximum times to perform 'gather' */

A função base, parcial, para a construção do photon-map é listada no Anexo E.

38 - C A P Í T U L O I I I – P O V - R A Y

3.5 – Anti-alias no POV-Ray
Quando este recurso está ativado, o algoritmo dispara mais de um raio em um mesmo pixel e realiza uma média para determinar a cor correta do pixel. Como visto no sub-capítulo 2.6, essa técnica é chamada super-sampling e, apesar de aperfeiçoar a aparência da imagem final, acaba por aumentar o tempo requerido para renderizar a cena, já que mais cálculos serão realizados. O POV-Ray permite a utilização de uma técnica de super-sampling para o cálculo do anti-alias de forma que o pixel é subdividido recursivamente em sub-pixels e tem sua cor recalculada até que o esse pixel fique com uma diferença de cor aceitável em relação a seus vizinhos (como descrito no sub-capítulo 2.6). O nível de aceitação da distância (threshold) é fornecido pelo usuário, podendo variar entre 0.0 e 3.0. Valores considerados baixos (menores que 0.3) receberão mais anti-alias. Sendo assim, a velocidade de renderização será menor. Por isso, é recomendável usar a técnica de antialiasing somente na versão final da imagem. O POV-Ray necessita de informações em memória das últimas duas linhas geradas, pois o anti-alias é calculado baseando-se no pixel acima e no pixel à esquerda do pixel atual.

CAP. 4.1 – MODELOS

DE

C O M P U T A D O R E S P A R A L E L O S - 39

Cap IV - Computação Paralela
Além de foto-realismo pode-se citar como exemplos de processamento intensivo: a modelagem de grandes estruturas de DNA, modelagem de estruturas químicas, previsão do tempo global e a modelagem da movimentação de corpos astronômicos no espaço. Uma solução possível para essa demanda de processamento são os sistemas paralelos, que têm como principal objetivo a melhoria de desempenho dividindo um programa com grande carga de processamento em tarefas menores que possam ser executadas sem interferência umas das outras (WILKINSON; ALLEN, 2005).

4.1 Modelos de Computadores Paralelos
Há dois tipos de paradigmas de programação paralela: por variáveis compartilhadas (memória compartilhada) e por troca de mensagens (memória distribuída): No paralelismo via memória compartilhada, todos os processadores possuem acesso ao(s) módulo(s) de memória e a programação é feita através de variáveis compartilhadas. Esse tipo de paralelização exige primitivas de controle de concorrência (ex. semáforos, monitores) para que um processador não leia/escreva dados enquanto outro estiver escrevendo/lendo. No paralelismo via passagem de mensagens com o uso de clusters de PCs (Personal Computer), há diferentes computadores ligados por uma rede de alta velocidade. Nesse modo, há memória apenas local e a programação é feita através de troca de mensagens entre os computadores exigindo primitivas de comunicação entre processos (send/receive). Esses tipos de paralelização podem ser combinados para uma obtenção de melhores resultados. Um exemplo de computador paralelo usando os dois tipos é o “NEC Earth Simulator” que contêm 640 computadores com 8 processadores cada - somando 5120 processadores (de 500Mhz cada um) em um supercomputador usado para previsão de terremotos no Japão (TOP500; 2006). Outro bom exemplo é o “IBM BlueGene/L eServer” que possui 131.072 processadores (figura 27) e foi extensamente utilizado no projeto Genoma (TOP500, 2006). Esse aglomerado de computadores trabalhando em paralelo é chamado cluster.

Figura 27 – Foto do IBM Blue Gene/L

40 – C A P Í T U L O I V – C O M P U T A Ç Ã O P A R A L E L A 4.1.1 Multiprocessamento com Memória Compartilhada Um computador convencional consiste de um processador executando um programa guardado na memória principal. Cada local dessa memória é referenciado com um número chamado endereço. Endereços começam em 0 e estendem-se até 2n-1 onde n é o número de bits no endereçamento. Do ponto de vista do programador o multiprocessamento com memória compartilhada é atrativo por causa de sua conveniência em compartilhar dados. Uma maneira natural de extensão de um computador monoprocessado seria ter múltiplos processadores conectados a múltiplos módulos de memória. Dessa forma cada processador teria acesso a qualquer módulo de memória em algo que chamamos de configuração de memória compartilhada. A conexão entre os módulos de memória e os processadores é feita via uma rede de interconexão. Em um sistema de multiprocessamento com memória compartilhada, a memória é vista como um único módulo onde existe apenas um espaço de endereçamento. A maneira com a qual o programador escreve programas para cada um dos processadores é tipicamente feita via uma biblioteca de paralelização de alto nível que contêm construções e funções especiais para a programação paralela. Um exemplo dessa técnica é a biblioteca OpenMP, um padrão internacional que consiste em um conjunto de diretivas de préprocessamento para as linguagens C e Fortran que explicitamente dividem o trabalho entre processadores usando a memória compartilhada (ALLEN; WILKINSON, 2005). De outra forma, podemos usar threads aliadas às linguagens de alto nível para gerar seqüências de código em paralelo para processadores individuais. Essas seqüências de códigos podem acessar áreas compartilhadas da memória.

4.1.2 Multicomputador via Passagem de Mensagens Uma forma alternativa ao sistema de multiprocessamento de memória compartilhada seria criar uma conexão entre computadores através de uma rede. Cada computador consiste de um processador e de uma memória local, e essa memória não é acessível por outros processadores. A rede de interconexão provê uma forma dos processadores receberem e enviarem mensagens entre si. Esses sistemas de multiprocessamento são chamados de multicomputador via passagem de mensagens ou simplesmente clusters de computadores, especialmente se eles consistem em computadores completos que poderiam operar separadamente (ALLEN; WILKINSON, 2005). Programar um multicomputador via passagem de mensagens envolve, como primeiro passo, dividir o problema em partes que devem ser executadas simultaneamente. A programação pode ser feita usando linguagens paralelas ou linguagens seqüenciais estendidas, mas comumente é usada uma biblioteca de passagem de mensagens juntamente com uma linguagem seqüencial convencional. Um problema é dividido em um número concorrente de processos que podem ser executados em computadores diferentes. Se há seis processos e seis computadores têm-se um processo executado em cada computador. Se há mais processos do que processadores, cada processador pode executar mais de um processo. O multicomputador via passagem de mensagem é fisicamente melhor escalável do que um multiprocessamento com memória compartilhada por que é mais fácil torná-lo maior simplesmente adicionando mais computadores à rede. Um multicomputador com memória

CAP. 4.2 – MODELOS

DE

A R Q U I T E T U R A S P A R A L E L A S - 41

compartilhada tem um hardware pouco escalável onde a adição de mais processadores ou módulos de memória é limitada.

4.1.3 Memória Compartilhada Distribuída O paradigma de passagem de mensagens não é tão atrativo para os programadores tanto quanto o paradigma de memória compartilhada. Trocar mensagens requer que os programadores explicitamente usem rotinas de comunicação na programação o que torna o programa suscetível a erros de transmissão e mais complexo para depuração. Programação por passagem de mensagens é geralmente comparada às linguagens de baixo nível, pois os dados não são compartilhados e devem sempre ser copiados. Isso pode ser problemático em programas que requerem múltiplas operações em uma grande quantidade de dados. No entanto, o paradigma de passagem de mensagens tem a vantagem especial de que mecanismos de sincronização não são necessários para controlar simultaneamente o acesso aos dados (cada computador tem seus próprios módulos de memória) (ALLEN; WILKINSON, 2005). Reconhecendo que o paradigma de memória compartilhada é desejável de um ponto de vista da programação, muitos pesquisadores obstinaram-se a alcançar o conceito de memória compartilhada distribuída. Nesse sistema, a memória é fisicamente distribuída, mas o endereçamento é o mesmo para todos os processadores. Para que um processador acesse dados em uma região de memória que não está em sua memória local, uma passagem de mensagens ocorre transparentemente de forma que o programa funcione como um sistema de memória comum. É importante entender que um sistema de memória distribuída sobre um sistema de passagem de mensagens não terá o mesmo desempenho de um verdadeiro sistema de memória compartilhada, já que há latências de rede e necessidade de retransmissão devido à perda de dados.

4.2 – Modelos de Arquiteturas Paralelas
Os Modelos de Arquiteturas Paralelas definem como as máquinas paralelas são formadas. São denifidas cinco tipos de arquiteturas de computadores (ALLEN, WILKINSON; 2005): • Máquinas Vetoriais (PVP, Parallel Vector Processor) – Estas máquinas têm como característica básica o fato de possuírem processadores compostos de vários pipelines vetoriais de alto poder de processamento, capazes de fornecerem alguns Gflops1 de desempenho. Estas poucas unidades processadoras estão interligadas através de chaves de alta velocidade a uma memória comum compartilhada, formando uma estrutura MIMD. Máquinas com Multiprocessadores Simétricos (SMP, Symmetric Multiprocessing) – São máquinas que possuem dois ou mais microprocessadores como unidades de processamento, interligados a uma memória compartilhada, geralmente usando barramentos de alta velocidade. Máquinas Massivamente Paralelas (MPP, Massively Parallel Processing) – Máquinas com diversos microprocessadores interligados por uma rede. Cada nó de

1

Um bilhão de instruções de ponto flutuante por segundo.

42 – C A P Í T U L O I V – C O M P U T A Ç Ã O P A R A L E L A processamento da malha pode possuir mais de um processador e essas máquinas podem contem milhares de nós. Esse tipo de máquina não possui memória compartilhada. • Multiprocessadores com Memória Compartilhada Distribuída (DSM, Distributed Shared Memory) – Estas máquinas são semelhantes às SMP possuindo um conjunto de microprocessadores interligados através de uma rede de interconexão de alta velocidade. A diferença está no fato de que a memória global compartilhada na verdade está fisicamente distribuída entre os nós; porém, para o usuário é como se ele estivesse acessando um espaço de endereçamento único. rede de custo baixo, porém de alto desempenho, interligando nós que podem possuir mais de um processador. Geralmente são utilizados computadores pessoais comuns.

• Clusters – Sob este nome estão máquinas cujo princípio básico é o emprego de uma

4.3 – Clusters
Por definição, “clusters” são pilhas de PCs de uso exclusivo para paralelismo, ligados por uma rede de alta velocidade (Gigabit Ethernet, Myrinet, SCI, Infiniband) podendo conter milhares de processadores no total. Cada nó (computador) do cluster geralmente é “sem cabeça”, ou seja, não possui teclado, mouse, monitor ou qualquer outro periférico (PACHECO, 1997). A figura 28 mostra um exemplo de cluster. Nesta figura vê-se o nó mestre à direita e 12 nós “sem cabeça” (headless) ligados por um switch (equipamento de conexão para interligar vários computadores). É chamada computação paralela trivial o tipo de aplicação onde após a divisão da carga de processamento, não há necessidade de que os processos se comuniquem para chegar a um resultado. Dessa forma, evita-se perda de tempo trocando mensagens desnecessárias entre os processos. Cada nó faz o seu trabalho e o retorna ao mestre que se encarrega de organizar os dados e exibir os resultados.

Figura 28 – Exemplo de cluster

CAP. 4.4 – MODELOS

DE

A L G O R I T M O S P A R A L E L O S - 43

São muitas as bibliotecas disponíveis para paralelização de algoritmos, dentre as quais se destacam: OpenMP (Open Multi Processing) , PVM (Parallel Virtual Machine), MPI (Message Passing Interface), HPC (High Performance Computing) e PThreads (Posix Threads) (PACHECO, 1997). Cada uma delas tem seu fim específico, neste trabalho apenas a biblioteca MPI será abordada. Esta foi escolhida porque possui código aberto e tem como proposta tornarse padrão pela comunidade de programadores e usuários.

4.4 – Modelos de Algoritmos Paralelos
Os paradigmas de algoritmos paralelos tratam de como a divisão das tarefas é feita entre os processos. Os modelos de algoritmos paralelos mais usados de acordo com Pacheco (1997) são: • Divisão-e-conquista: Tem como princípio dividir uma tarefa em diversas tarefas menores e atribuí-las a processos filhos. Os filhos processarão suas partes da tarefa em paralelo e retornarão o resultado para o processo pai, que tem a função de integrar os resultados obtidos. Esta ação de divisão e integração das tarefas deve ser executada de forma recursiva até que o processo tenha sua execução completada. Este método é um dos mais simples de ser implementado, porém tem como desvantagem a dificuldade de se obter um bom balanceamento de carga entre as tarefas. Pipeline: Neste paradigma, como o próprio nome já diz, um número de processos forma uma linha de execução. Um fluxo contínuo de dados entra no primeiro estágio da linha de execução e os processos são executados nos demais estágios complementares, de forma simultânea. Mestre/Escravo (process farm): No modelo de Mestre/Escravo, um processo mestre executa as tarefas essenciais do programa e divide o restante das tarefas entre os nós escravos. Quando um nó escravo termina de executar sua tarefa ele avisa isso ao nó mestre que lhe atribui uma nova tarefa, até que todas as tarefas do programa tenham sido executadas. Sua implementação é simples, já que o controle está centralizado no nó mestre. Porém isso gera uma desvantagem: toda a comunicação tem que passar pelo mestre. Pool de Tarefas: Neste modelo, um pool (conjunto) de tarefas é disponibilizado por uma estrutura de dados global e um determinado número de processos é criado para executar esse conjunto de tarefas. No início só existe um único pedaço de tarefa; gradativamente os processos buscam pedaços da tarefa e imediatamente passam a executá-los, espalhando o processamento. O programa paralelo termina quando o pool de tarefas fica vazio. Este tipo de modelo facilita o balanceamento da carga; por outro lado é difícil obter um acesso eficiente e homogêneo aos múltiplos processos. Fases Paralelas: Neste modelo, a aplicação consiste num número de etapas, onde cada etapa é dividida em duas fases: uma fase de computação, quando os múltiplos processos executam processamentos independentes, seguida de uma fase de interação, quando os processos executam uma ou mais operações de interação síncrona, tais como barreiras ou comunicações bloqueantes.

1

Um bilhão de instruções de ponto flutuante por segundo.

44 – C A P Í T U L O I V – C O M P U T A Ç Ã O P A R A L E L A

4.5 – Modelos de Programação Paralela
Os modelos de programação paralela definem a maneira como um programa paralelo pode ser feito. Eles são divididos em modelo de programação implícita e modelo de programação explícita. O segundo sendo dividida em modelo de passagem de mensagem, modelo de paralelismo de dados e modelo de variáveis compartilhadas (ALLEN; WILKINSON, 2005). • Paralelismo Implícito: O programador não precisa especificar o paralelismo em seu código, deixando a função de estabelecimento dos pontos de paralelização para o compilador ou para o sistema de execução do programa. O compilador tenta detectar no programa trechos que podem ser executados em paralelo, ou seja, ele faz uma análise de dependência das etapas e variáveis do código. Porém ainda existem poucos compiladores capazes de paralelizar um código de maneira eficiente, sendo essa técnica pouco utilizada quando se precisa de um grande ganho no poder de processamento de uma aplicação. Passagem de Mensagens: Um programa que utiliza passagem de mensagens consiste em ter vários processos trocando informações. Um processo pode, através da troca de mensagens, solicitar informações a outro processo, passar resultados de tarefas que tenham sido executadas por ele ou passar tarefas para um outro processo. Esse é um dos métodos de programação mais usados, principalmente em se tratando de clusters de computadores. Paralelismo de Dados: A idéia do paralelismo de dados é executar um mesmo trecho do programa utilizando dados diferentes em cada um dos nós. Variáveis Compartilhadas: Neste modelo os dados residem em um sistema de endereçamento único e compartilhado. A comunicação entre os processos é feita através da leitura e escrita de variáveis compartilhadas.

• •

4.6 – Biblioteca MPI
A MPI (Message Passing Interface) é um padrão de biblioteca para troca de mensagens com sintaxe definida. O principal objetivo da MPI é viabilizar a comunicação e aumentar o desempenho computacional de programas (PACHECO, 1997). A MPI foi designada para ter um alto desempenho em computadores paralelos e redes de clusters. Ela tem implementações gratuitas e comerciais sendo a MPI padrão uma implementação gratuita com a documentação oficial disponível na Internet. A primeira versão do MPI não possui gerenciamento dinâmico de processos e processadores (não se pode, por exemplo, alterar o número de processos no cluster depois que o programa está em execução). A versão 2, atualmente em versão beta, já possui recursos de gerenciamento dinâmico de processos e utilização de entrada/saída remotamente (é possível que dois nós escrevam em um mesmo arquivo ao mesmo tempo). A MPI funciona da seguinte forma: cada máquina recebe uma cópia do programa que quando executado inicia um processo que devolve um número de identificação (chamado rank).

C A P . 4 . 6 – B I B L I O T E C A M P I - 45 Esses processos começam a executar o programa a partir da primeira linha de comando e para que cada computador saiba qual parte fazer, é feita é a utilização de estruturas de seleção, modelo de programação SPMD (Single Program Multiple Data). Cada computador é chamado de nó e o computador que inicia a aplicação é chamado de nó mestre (PACHECO, 1997). A grande dificuldade de um projeto de paralelização de algoritmos é a detecção de pontos para paralelização no algoritmo seqüencial e o efetivo ganho de desempenho com a versão paralela, e ainda tem-se que considerar que a versão paralela deve superar problemas de sobrecarga (overhead) de comunicação entre os computadores, já que essa comunicação leva um tempo relativamente alto. Uma das grandes vantagens dessa biblioteca é que além de trabalhar com clusters ligados via rede, ela também pode trabalhar em apenas uma máquina. Isso torna possível o teste de aplicações antes do efetivo uso em clusters. Se a máquina utilizada para testes for uma máquina com dois processadores SMP, dois processos podem ser executados, um em cada processador, de forma que a simulação de um cluster de dois nós fosse alcançada. Se em um cluster com quatro máquinas SMP (com dois processadores em cada máquina) for executado um programa MPI, este poderá usar oito processos ao invés de quatro aumentando a velocidade dos cálculos e diminuindo a interferência dos envios de dados via rede já que mensagens trocadas entre os processos rodando no mesmo computador são passadas diretamente sem interferência da rede. Talvez a maior desvantagem da biblioteca MPI seja a restrição de passagem de mensagens a tipos primitivos (caracteres, números inteiros, valores booleanos). Não é possível o envio de mensagens contendo, por exemplo, estruturas ou classes de programação orientada a objetos. 4.6.1 – Rotinas de Passagem de Mensagens Em um programa MPI, a cada nó participante é atribuído um rank representado por um número inteiro que varia entre 0 a p-1, onde p é o número de processos existentes. A comunicação entre os processos pode ser feita de forma bloqueante (síncrono) ou não-bloqueante (assíncrono) (ALLEN; WILKINSON, 2005): • • Em uma comunicação bloqueante um processo espera o término de um envio ou recebimento antes de prosseguir; Em uma comunicação não-bloqueante o programa não aguarda o envio ou recebimento da mensagem e prossegue sua execução normalmente.

Comunicações via passagem de mensagem podem ser uma grande fonte de operações errôneas. Um exemplo de comunicação não segura é mostrado na figura 29. Nessa figura, o processo 0 deseja enviar uma mensagem ao processo 1, mas há outra passagem de mensagens ocorrendo ao mesmo tempo, o que pode ocasionar o recebimento da informação fora de ordem.

1

Um bilhão de instruções de ponto flutuante por segundo.

46 – C A P Í T U L O I V – C O M P U T A Ç Ã O P A R A L E L A

Figura 29 – Mensagens fora de ordem Outro grande problema decorrente da passagem de mensagens incorretamente é o deadlock, que ocorre quando um processo está esperando uma mensagem que nunca chega ou que jamais foi enviada. A figura 30 ilustra o fluxo de tempo onde ocorre uma situação de deadlock. Dois processos após trocarem mensagens entre si iniciam uma operação de receive ao mesmo tempo para esperar por mensagens. A estrutura de biblioteca MPI-1 não prevê mecanismos de prevenção a deadlocks, porém a nova versão MPI-2 conta com recursos para evitar ou abortar deadlocks dinamicamente, durante a execução do programa de forma que não seja necessário reiniciar toda a operação por causa de um problema em particular.

Figura 30 – Situação de deadlock, dois processos aguardando mensagens inexistentes.

CAP. 5 – PARALELIZAÇÃO

DO

P O V - R A Y - 47

Cap. V – Paralelização do POV-Ray
Durante a fase inicial do trabalho foi realizado um estudo sobre as técnicas de paralelização de algoritmos existentes, elegendo a melhor entre essas para construção de algoritmos executados em clusters. A biblioteca escolhida para utilização foi a MPI que além de portável, possui diversos métodos de paralelização e pode ser integrada à linguagem C/C++ (que é a linguagem na qual o POV-Ray foi escrito). A versão do MPI adotada é a LAM-MPI. LAM (Local Area Multicomputer) é uma implementação aberta do padrão MPI que foi desenvolvida há mais de duas décadas. Ela permite tanto a integração do MPI com a linguagem C quanto com a linguagem Fortran 77. A versão utilizada (7.1.2) foi a última versão lançada antes do projeto LAM-MPI mudar para o nome de OpenMPI, que ocorreu no fim de 20052. Essa implementação disponibiliza uma API (Application Programming Interface) que permite aos usuários passar mensagens entre os nós em uma aplicação paralela. Além da API padrão do MPI, a LAM inclui programas de monitoramento e depuração. Projetada especificamente para redes heterogêneas de sistemas Unix, ela executa tanto em computadores de uso pessoal como grandes supercomputadores. O padrão LAM-MPI traz o MPI-1 completo e muitas das funcionalidades do MPI-2 orientado a objetos. No presente trabalho, procura-se alcançar o modelo da figura 31, onde todos os três algoritmos (Ray-Tracing, Radiosidade e Photon Mapping) estariam com versões modificadas para execução em paralelo.

Figura 31 – Modelo alvo que o trabalho busca.

² De acordo com o site da biblioteca: www.lam-mpi.org.

C A P . 5 . 1 – T R A B A L H O S R E L A C I O N A D O S - 48 Inicialmente, o código do POV-Ray foi modificado de forma que houvesse o mínimo possível de saída em tela minimizando o impacto da troca de mensagens inúteis na execução em paralelo. Funções de gravação em arquivo e exibição em tela foram removidas dos nós escravos, sendo mantidas como uma função apenas do nó mestre.

5.1 – Trabalhos Relacionados
Uma maneira trivial de se renderizar uma cena paralelamente seria seccioná-la em pequenas faixas de forma que cada processo renderizasse uma ou mais faixas gerando um arquivo de imagem, e, ao final, uníssemos essas faixas por meio de algum processo realizado em um aplicativo processador de imagens. Isso poderia ser feito manualmente ou através de algum programa que executa operações em lote para cada processador/computador envolvido no processo. Porém essa técnica não alcança bons resultados porque mesmo que as secções sejam facilmente renderizadas, teríamos o trabalho de uni-las posteriormente. Dado um número de computadores/processadores e uma cena a ser renderizada, há algumas técnicas que podem ser aplicadas de forma a aumentar a velocidade da renderização entre os processos disponíveis. Se estivéssemos renderizando uma animação seria óbvio distribuir alguns quadros para cada processo existente de forma que todos fossem renderizados ao mesmo tempo (BOURKE, 1999). Adicionar uma sincronização ao fim do processo tornaria possível ordenar os quadros e gerar a animação. Em muitos casos, apenas um quadro pode levar um tempo significante para ser renderizado. Isso pode ocorrer por muitos fatores, tanto da cena – geometria complicada, iluminação sofisticada, aplicação de anti-aliasing ou simplesmente dimensões muito grandes da imagem – quanto do computador que está efetuando a renderização – poder de processamento, memória disponível, escalonamento de tarefas. Maneiras triviais de se resolver esse problema seriam desenvolver novas versões do algoritmo que dividissem o trabalho da renderização entre vários processos. Lembrando que a renderização é feita sobre uma matriz de pontos, podemos dividir a matriz em faixas horizontais como discutido acima, mas com a diferença de que esta abordagem não geraria pequenas imagens separadas, mas sim a imagem completa evitando o trabalho de união das secções da imagem (SANTOS, 1994). Da mesma forma que faixas horizontais, pode-se distribuir o trabalho em faixas verticais (figura 32). Essas duas formas de divisão não alcançam tanta eficiência já que uma imagem pode conter regiões vazias (sem objetos, ou superfícies de forma que sua cor seja sempre a cor de fundo definida na renderização), fazendo com que o trabalho não seja uniformemente distribuído entre os processos. Uma abordagem mais funcional seria uma divisão do trabalho em sub-grades (checkerboard) onde teríamos cada processo renderizando uma ou mais partes da matriz (figura 32). Esse último método também sofre do mesmo problema dos outros dois anunciados já que também podem ocorrer regiões vazias durante a divisão. Uma quarta e última definição exibida na última parte da figura 32 seria uma divisão dinâmica em sub-grades, baseando-se na complexidade das partes da cena. O problema desse método seria o tipo de cálculo que seria realizado, pois não é possível prever qual parte da cena

C A P . 5 . 1 – T R A B A L H O S R E L A C I O N A D O S - 49 será mais complexa que outra. Uma previsão pode ser feita de acordo com o número de objetos e seus respectivos materiais, porém, tal cálculo seria complexo e adicionaria maior tempo ao processo final ao invés de reduzi-lo.

Figura 32 – horizontal, vertical, grade e dinâmica. Um pool de tarefas poderia ser implementado de forma que um processo mestre receberia requisições de trabalho e enviaria pontos ou blocos de pontos da matriz para que cada processo escravo calculasse o valor do(s) ponto(s) recebido(s) (figura 33). Dessa forma teríamos um trabalho uniforme já que todo processo sem trabalho pediria um ponto ao mestre e trabalharia sobre esse ponto enquanto os outros processos estão trabalhando em outros pontos. Se um processo está em um ponto de difícil renderização os outros processos podem seguir adiante pegando os próximos pontos paralelamente. O problema desse método é que se estivéssemos trabalhando em um cluster ocorreria um overhead de comunicações já que cada ponto precisa ser pedido e transmitido para cada computador. Como todos os computadores pediriam os pontos para apenas um mestre teríamos também um gargalo que diminuiria a velocidade da renderização.

Figura 33 – Pool de tarefas. Um algoritmo paralelo de Ray-Tracing chamado Tachyon foi proposto por Stone (1998) como sua tese de mestrado. O algoritmo suporta MPI para clusters, threads para computadores com memória compartilhada e suporta as duas arquiteturas simultaneamente em computadores que permitam isso. A figura 34 exibe o funcionamento híbrido deste algoritmo exibindo como

50 - C A P Í T U L O V – P A R A L E L I Z A Ç Ã O

DO

POV-RAY

fica a divisão entre os pixels da cena em um cluster de três máquinas com 4 processadores cada um.

Figura 34 - Divisão híbrida de processamento, memória compartilhada e distribuída. Plachetka (1998) desenvolveu uma versão paralela do Ray-Tracing também baseada no POV-Ray para máquinas com memória compartilhada utilizando a biblioteca PVM. O uso dos processadores é balanceado de forma a dividir igualmente o trabalho de renderização. Sua aceleração (speedup) quanto ao algoritmo seqüencial é quase linear. Uma abordagem da técnica de divisão de imagem em pequenas secções de renderização para posterior união é dada por Bourke (1999). Em sua proposta, é utilizado o algoritmo de RayTracing proveniente do programa POV-Ray. Cada secção é salva em um arquivo do tipo ppm. Arquivos contendo a especificação de quais partes cada processo deve fazer são inicialmente construídos e depois a renderização é realizada (figura 35). O trabalho inclui um utilitário que une os diferentes arquivos gerados durante a renderização.

Figura 35 – Secções unidas formando imagem final.

CAP. 5.2 –RAY-TRACING

EM

P A R A L E L O - 51

Um segundo trabalho também sobre o algoritmo de Ray-Tracing do POV-Ray é proposto por Dilger (2000) e utiliza a abordagem de checkboard, dividindo a imagem em uma grade com tamanho da granularidade escolhido pelo usuário antes da renderização. Há um processo mestre que é encarregado de fazer a divisão da grade em blocos e enviar para os nós escravos. Estes devolvem os blocos renderizados ao mestre que ordena e exibe o resultado final. O trabalho foi implementado sobre a biblioteca PVM e utilizou a verão 3.1 do POV-Ray, que ainda não possuía o algoritmo de Photon Mapping. A desvantagem dessa implementação paralela é que a Radiosidade não funciona. São renderizadas sombras incorretas sobre a imagem e o processo não é completado deixando a imagem no último estado gerado pelo Mosaic Preview ao invés de finalizar com a subseqüente passada do Ray-Tracing. Verral (2000) converteu a implementação de Dilger para a biblioteca MPI testando-o sobre o sistema operacional GNU/Linux. Entretanto, a versão apresentou os mesmos problemas da versão em PVM além da necessidade da visualização gráfica obrigatória o que impede o funcionamento do programa em clusters sem modo gráfico.

5.2 – Ray-Tracing em Paralelo
Apesar de todas as teorias de que as formas de paralelização apresentadas no capítulo anterior serem eficientes meios de paralelização foi constatado que apresentam alguns problemas em certos casos de renderização, principalmente relacionados à utilização dos processadores paralelos (que a princípio deve ser uniforme). Por exemplo, uma imagem que tem a metade superior com pouco ou nenhum objeto e a metade inferior repleta de objetos não seria uniformemente renderizada, já que os nós aos quais fossem atribuídas as primeiras faixas terminariam mais rapidamente e praticamente não teriam trabalho, ao contrário dos nós aos quais fossem atribuídas as faixas da metade inferior. Dessa forma, foi descartada a hipótese de se paralelizar com faixas horizontais e verticais ou grade. 5.2.1 – O Crivo de Raios A abordagem implementada efetivamente foi a de trabalhar com uma divisão em sub-grade de mínima granularidade (divisões de 1x1 pixel). Isso garante que todos os processos trabalharam uniformemente já que todos trabalham em pontos uniformemente distribuídos. Linha-a-linha, cada processo faz o cálculo da cor de um ponto e salta o número de processos existentes no cluster para que faça o próximo.

Figura 36 – Crivo de Raios em um cluster com quatro nós. A figura 36 exibe o trabalho feito para cada nó em um cluster com quatro máquinas. Os quadrados em amarelo representam os pontos calculados pelo nó zero, os quadrados em azul pelo nó um, em vermelho pelo nó dois e em verde pelo nó três.. A cada iteração o nó zero salta

52 - C A P Í T U L O V – P A R A L E L I Z A Ç Ã O

DO

POV-RAY

três pixels e faz o cálculo de quarto em quatro pixels apenas. Em coordenadas, ele fez o cálculo dos pixels (0,y); (4,y); (8,y); (12,y), onde y representa cada linha da imagem. Este método foi batizado de “Crivo de Raios” (JESUS, NEVES; 2006). Implementada e testada, esta modificação do algoritmo de Ray-Tracing para clusters provou que uma aceleração escalável pode ser atingida. Ou seja, se dobrado o número de máquinas no cluster, o tempo final de geração da imagem tende a diminuir pela metade, como será apresentado no capítulo com os resultados da implementação. Existem dois laços que compõem o Ray-Tracing, um deles itera sobre as linhas enquanto o outro itera sobre as colunas, dessa forma, modificou-se o laço que itera sobre as colunas fazendo com que um salto relativo ao número de computadores no cluster fosse somado para o cálculo da próxima coluna ao invés de um incremento de uma unidade. Ao fim do laço que itera sobre as colunas, criou-se um mecanismo de sincronização de forma que todos os nós participantes do cluster (excetuando o nó zero) enviassem seus resultados de cálculos para o nó zero. O nó mestre por sua vez adiciona o pixel à coluna correta, exibe na tela e salva em arquivo a linha concluída. Essa etapa do processo é feita de forma síncrona. Se o nó zero ainda não terminou o processamento de sua linha atual, os outros processos que terminaram os seus cálculos previamente aguardam o nó zero para que receba suas mensagens. A figura 37 mostra o funcionamento do algoritmo em um cluster de oito máquinas com uma imagem gerada pelo método ignorando-se o trabalho de um dos nós (visto nas colunas pretas).

Figura 37 – Ignorando-se o trabalho de um dos nós. Em termos de computação paralela, o nó mestre só executa operações de receive, todos os outros nós efetuam apenas operações de send. Por exemplo, se uma imagem de 100x100 pixels estivesse sendo renderizada por quatro computadores, ocorreriam 300 sends e receives, já que os nós 1, 2 e 3 enviariam (100 vezes cada um) cada uma das parcelas de cores calculadas para o nó mestre após o fim de cada linha. A estrutura, que contém os dados sobre as cores dos pixels, enviada ao nó mestre é composta por um vetor de tamanho dado pela equação abaixo:

CAP. 5.2 –RAY-TRACING
TamVet = (colunas/numprocs)*5

EM

P A R A L E L O - 53

Onde colunas é o número de colunas informadas pelo usuário para a renderização e numprocs é o número de nós do cluster rodando a aplicação. Como descrito no capítulo 3.1, a estrutura que guarda um pixel é composta por cinco floats (tom do vermelho, verde, azul, filtro de luz e transparência) por isso multiplica-se o valor por cinco. Um problema relativo ao anti-alias foi detectado durante esta fase de paralelização do trabalho. Como o anti-aliasing é gerado juntamente com a renderização dos pixels, baseando-se na cor do pixel superior e do pixel esquerdo (que na versão seqüencial, naturalmente sempre estão disponíveis), ele não poderia ser usado no Crivo já que as máquinas não conhecem as cores de seus pixels vizinhos. Os cálculos de cores dos pixels são de fato independentes no RayTracing, porém o cálculo do anti-alias como explicado no sub-capítulo 3.5 necessita tanto do pixel superior quanto do pixel da esquerda para o cálculo do super-sampling. A figura 38 ilustra o problema exibindo que o nó dois (em vermelho) apesar de possuir a cor do pixel superior, não possui as informações sobre o pixel da esquerda, já que não foi ele quem renderizou esse pixel. Isso gera uma falha no cálculo do anti-alias, que deixa de ser calculado levando a imagem final a conter o serrilhamento independente da escolha do threshold para o anti-alias. Além disso, há sobrecarga de informação sobre o nó mestre porque além de ele ter que calcular os pixels, também é necessário que alinhe os pixels, exiba na tela e salve em arquivo toda a imagem. Quando não é utilizada saída em tela, arquivo e nem anti-alias, a sobrecarga é diminuída fazendo com que o algoritmo alcance um speedup quase linear.

Figura 38 – Ampliação da falha no processo de anti-alias. 5.2.2 – O Crivo de Raios Mestre/Escravo Para solucionar o problema do anti-aliasing e sobrecarga de operações no mestre gerado pela primeira versão do crivo, uma nova técnica foi proposta diferenciando a forma como o crivo se comportaria em relação ao nó mestre. Nesse modelo, o nó mestre não executa disparos de raios contra a cena, apenas aguarda dados dos outros nós e aplica anti-aliasing sobre eles. Essa técnica torna o uso do anti-aliasing possível, pois o nó mestre sempre recebe os dados de todos os computadores possuindo sempre a

54 - C A P Í T U L O V – P A R A L E L I Z A Ç Ã O

DO

POV-RAY

linha completa. Logo, após o recebimento de dados de uma linha por todos os processos, o nó mestre alinha os dados de forma correta e aplica o anti-aliasing. O funcionamento é correto também quando não se aplica o anti-aliasing, dessa forma não se atinge um balanceamento completo já que o nó mestre aguarda mais tempo do que trabalha sobre os pixels. Com isso, uma execução em um cluster de dois computadores funciona de forma similar à execução seqüencial já que apenas um computador efetuará os disparos enquanto o outro aguarda os resultados. Só se ganha em velocidade quando são usados três ou mais computadores. De forma similar ao crivo original, o mesmo laço de iteração sobre as colunas foi modificado, porém os nós não mais saltam o número total de computadores no cluster, mas sim o número de computadores decrescido de um, que seria o nó mestre. A figura 39 mostra como fica a mesma distribuição do método anterior com quatro computadores, não há mais quatro cores diferentes já que o nó mestre não trabalha mais no processo de disparo de raios.

Figura 39 – Crivo de Raios Mestre/Escravo em um cluster com 4 nós. Com esse novo modelo, mesmo contando com um processador a menos no cálculo da cor dos pixels, nota-se uma melhora na aceleração do processo à medida que mais máquinas são utilizadas (os testes do capítulo VI comprovam essa melhoria). Essa nova abordagem trouxe um novo problema. Quando o threshold escolhido para o anti-aliasing é muito baixo, mais pixels sofrem o processo de super-sampling. Como o nó mestre é o único que faz o anti-alias, para clusters com muitas máquinas esse método gera um gargalo de comunicação. Se um nó escravo termina uma linha enquanto o nó mestre está calculando antialiasing, este terá que esperar até o fim do processo para que sua mensagem possa ser transmitida já que esse método, assim como seu antecessor, trabalha com passagem de mensagens síncrona.

5.3 – Radiosidade em Paralelo.
Como a Radiosidade é baseada no método de Mosaic Preview para o cálculo da irradiação da luz, este método foi modificado de forma a trabalhar como o crivo de raios proposto no capítulo 5.1.1. A principal diferença entre a paralelização do Ray-Tracing e esta é que, por ser um algoritmo de iluminação global, uma passada subseqüente do Ray-Tracing é necessária para

CAP. 5.3 – RADIOSIDADE

EM

P A R A L E L O - 55

efetiva renderização utilizando os dados gerados sobre iluminação pelo método do Mosaic Preview. A primeira idéia foi a solução pelo método do crivo de raios, porém, este gerou imagens diferentes das imagens geradas pelo algoritmo seqüencial como vê-se na figura 40. Nela exibe-se a diferença entre as imagens geradas, a imagem da esquerda foi gerada no algoritmo seqüencial e a da direita no paralelo. Há ondas na imagem da versão paralela que deformam a sombra, isso prova que diferentemente do Ray-Tracing onde o disparo de raios e cálculo das cores dos pixels é independente, nesse caso todos os nós precisam das informações geradas pelos outros nós.

Figura 40 – Cálculo incorreto de sombras da Radiosidade Para medida de testes, tentou-se gerar os cálculos da Radiosidade em apenas uma máquina distribuindo os resultados para todas as outras máquinas. Os cálculos foram feitos pelo nó zero enquanto os outros nós aguardavam. A sincronização foi feita por linha onde se passavam todos os pixels calculados para os outros nós. Essa medida funcionou provando que se distribuídas as informações das sombras, os cálculos do Mosaic Preview passariam a funcionar da mesma forma que o código seqüencial. Além disso, o cálculo de sombras, apesar de paralelo, não causaria problemas à imagem final. Além das cores dos pixels gerados, uma outra estrutura composta por três floats que representa a média global de luz ambiente da cena também foi sincronizada. Para que o cálculo distribuído funcionasse da mesma forma que o seqüencial, a média da luz ambiente deveria ser igual entre os nós, porém como cada nó trabalha sobre pixels diferentes, a média é gerada separadamente. A solução para esse problema foi a sincronização dos dados das médias no nó zero que soma as médias e redistribui aos nós do cluster de forma que todos tenham os mesmos dados ao fim de cada linha do Mosaic Preview.

56 - C A P Í T U L O V – P A R A L E L I Z A Ç Ã O

DO

POV-RAY

5.4 – Photon Mapping em Paralelo
O Photon Mapping foi o algoritmo mais complexo a ser paralelizado devido à estrutura do photon-map utilizada no POV-Ray. O mesmo problema da Radiosidade é encontrado no Photon Mapping, todos os nós devem ter as informações completas sobre o Photon Mapping para posterior utilização pelo Ray-Tracing. Como dito no sub-capítulo 3.4, a estrutura do Photon Mapping é composta por uma lista de listas de fótons (matriz esparsa) onde cada fóton é composto por três floats de localização (x, y, z), uma estrutura de cor composta por quatro caracteres, mais um caractere sobre a informação da localização do fóton na kd-tree e dois caracteres que representam o ângulo de incidência do fóton. Com essa estrutura, o envio de informações utilizando MPI (que permite apenas o envio de dados primitivos como números inteiros, caracteres ou floats) tornou-se quase impossível já que para cada fóton seriam necessários quatro envios de mensagens para que o fóton pudesse ser reconstruído nas outras máquinas. Juntando-se as informações de caracteres em apenas uma mensagem, pôde-se reduzir o número de mensagens para duas por fóton. Duas mensagens por fóton não resolvem o problema, contando-se que serão disparados milhares de fótons sobre a cena, a sobrecarga de informações seria muito elevada tornando inviável tal implementação. A primeira idéia para implementação do algoritmo paralelo que evitasse essa sobrecarga de troca de informação entre os nós foi colocar cada um dos seis laços existentes no algoritmo em máquinas diferentes. A sincronização de fótons deveria ser feita ao fim dos seis laços e, em seguida, cada nó construiria sua própria kd-tree balanceada e a passada do Ray-Tracing seria feita. Essa abordagem provou ser ineficiente em teoria e não foi posta em prática primeiramente porque limitava o número de máquinas para um número fixo retirando a propriedade escalar conseguida com os algoritmos de Radiosidade e Ray-Tracing. Outro problema decorre do fato que nem sempre os outros laços são usados já que cada laço desses representa um tipo de luz e luzes em grupo, luzes cilíndricas ou retangulares nem sempre estão presentes na cena. Uma nova abordagem foi separar a iluminação da cena de forma que cada computador calculasse o disparo de fótons para um mesmo número de objetos de luz. Dessa forma, conseguiu-se uma razoável divisão de trabalho para cenas com muitos pontos de luz. Por exemplo, se em uma cena composta por doze pontos de luz fosse utilizado um cluster de duas máquinas, cada nó do cluster executaria o cálculo de seis pontos de luz emissores de fótons. Os fótons seriam sincronizados ao fim dos seis laços. Essa abordagem teve o início de sua implementação testada sem a sincronização para checar o impacto gerado pela falta de informações de fótons sobre a renderização final. Infelizmente a sincronização dos fótons não pode ser construída devido à complexidade com a qual o POV-Ray adiciona os fótons ao photon-map. Por ser um programa construído em programação estruturada com variáveis globais tornou-se muito difícil encontrar as ligações entre os arquivos de código que se referenciavam ao photon-map ou a própria kd-tree.

CAP. 5.4 – PHOTON MAPPING

EM

P A R A L E L O - 57

O projeto de paralelização do Photon Mapping não pôde ser concluído nesse estudo mas foi agendado como projeto futuro e será realizado com uma melhor inspeção sobre o código buscando melhores formas e as estruturas corretas para devida distribuição e sincronização de tarefas.

58 - C A P Í T U L O V I – T E S T E S

E

RESULTADOS

Cap. VI – Testes e Resultados
Para os testes, foi utilizado o cluster da Pós-Graduação em Engenharia Elétrica (PGEEL) do Mackenzie. Ele conta com oito nós sendo que suas especificações estão dispostas na tabela abaixo. Sete desses nós possuem dois processadores com suporte a SMP. # 0 1 2 3 4 5 6 7 Processador(es) 1 AMD Opteron 240 2 AMD Opteron 242 2 AMD Opteron 242 2 AMD Opteron 242 2 AMD Opteron 244 2 AMD Opteron 244 2 AMD Opteron 244 2 AMD Opteron 244 Cache 1 Mb 2x1 Mb 2x1 Mb 2x1 Mb 2x1 Mb 2x1 Mb 2x1 Mb 2x1 Mb Clock 1400 MHz 2x1600 MHz 2x1600 MHz 2x1600 MHz 2x1800 MHz 2x1800 MHz 2x1800 MHz 2x1800 MHz Mem. RAM 3 Gb 1 Gb 1 Gb 1 Gb 1 Gb 1 Gb 1 Gb 1 Gb Interface de Rede Ethernet Gigabit Ethernet Gigabit Ethernet Gigabit Ethernet Gigabit Ethernet Gigabit Ethernet Gigabit Ethernet Gigabit Ethernet Gigabit

Foram escolhidas três imagens de exemplo. Duas dessas imagens vêm com o POV-Ray 3.6.1: woodbox.pov e glasschess.pov. A imagem woodbox é uma imagem clássica do POV-Ray. A glasschess contém um grande número de cálculo de refrações e reflexões por pixel, sendo assim uma boa imagem para os testes. A terceira imagem (skyvase.pov) foi usada para uma comparação oficial dos algoritmos de acordo com o Official POV-Ray Benchmarking (Anexo A). Para gerar grande carga de trabalho sobre os nós, as dimensões de 4000x3000 foram escolhidas para as imagens woodbox.pov e skyvase.pov. A imagem glasschess.pov foi renderizada em 1024x768 devido a alta complexidade da cena. Foram feitos testes com os dois crivos, com e sem anti-alias, variando entre 2 e 8 nós para os algoritmos paralelos. As imagens foram renderizadas primeiramente sem anti-aliasing para o algoritmo seqüencial, Crivo de Raios e Crivo Mestre/Escravo. Depois, foram renderizadas com antialiasing somente para o algoritmo seqüencial e o Crivo Mestre/Escravo, pois o Crivo de Raios, como descrito no capítulo 5.2.1, não possui suporte a anti-aliasing. O arredondamento dos números é de três casas decimais. Para todos os testes das versões paralelas foram utilizados de dois a oito nós do cluster tendo o nó de identificação zero como inicial. Cada renderização foi executada cinco vezes e a média entre elas foi calculada. Nenhum dos testes gerou saída em arquivo ou tela, isso significa que apesar da renderização ser efetivamente realizada, os dados da imagem gerada não são salvos. Além disso, todas as mensagens impressas pelo POV-Ray no console foram suprimidas para evitar ao máximo o desperdício de tempo.

6.1 – Woodbox
A tabela da próxima página exibe os resultados dos testes com o cálculo da eficiência e speedup para cada conjunto de nós usados. O tempo do algoritmo seqüencial aparece em primeiro lugar seguido dos tempos para o Crivo Mestre/Escravo e o Crivo de Raios. A secção esquerda da tabela exibe os dados para a renderização sem anti-aliasing e a secção direita, com anti-aliasing.

C A P Í T U L O 6 . 1 – W O O D B O X - 59

Woodbox.pov 4000x3000
Sem anti-aliasing Segundos Speedup Eficiência Seqüencial 1 Crivo Mestre/Escravo 2 3 4 5 6 7 8 Crivo de Raios 2 3 4 5 6 7 8 273 Seqüencial 1 Crivo Mestre/Escravo 2 3 4 5 6 7 8 315 Com anti-aliasing Segundos Speedup Eficiência

232 117 79 59 47 40 35

1,177 2,333 3,456 4,627 5,809 6,825 7,800

0,588 0,778 0,864 0,925 0,968 0,975 0,975

231 118 82 64 57 53 50

1,364 2,669 3,841 4,922 5,526 5,943 6,300

0,682 0,890 0,960 0,984 0,921 0,849 0,788

137 94 70 55 47 41 36

1,993 2,904 3,900 4,964 5,809 6,659 7,583

0,996 0,968 0,975 0,993 0,968 0,951 0,948

Os gráficos abaixo demonstram o speedup ideal (linear) e os speedups alcançados para ambos os casos de renderização com e sem anti-aliasing. Como a necessidade de anti-aliasing nessa imagem é elevada (texturas e diversos objetos de cores diferentes), à medida que se aumenta o número de nós usando o Crivo Mestre/Escravo, nota-se uma queda no speedup devido ao gargalo no nó mestre como descrito no sub-capítulo 5.2.2 (gráfico da direita).
Speedup (Woodbox sem Anti-Alias)
9 8 7 6 Speedup 5 4 3 2 1 0 2
Speedup Linear Crivo de Raios Crivo M estre Escravo

Speedup (Woodbox com Anti-alias)
9 8 7 Speedup 6 5 4 3 2 1 0

3

4

5

6

7

8

2
Speedup Linear

3

4

5

6

7

8

Núm ero de Máquinas

Cr ivo Mest re/ Escravo

Núm ero de Máquinas

60 - C A P Í T U L O V I – T E S T E S

E

RESULTADOS

Tempo (Woodbox, sem Anti-Alias)
300 250 Segundos 200 150 100 50 0 Crivo Mestre/Escravo Crivo de Raios 1 2 3 4 5 6 7 8 Núm ero de Máquinas

Esse terceiro gráfico dispõe a aproximação ocorrida entre os métodos quando se aumenta o número de nós no cluster sem o uso de anti-aliasing. Nota-se o gargalo ocorrido pelo nó zero do Crivo de Raios, pois além de calcular os pixels ele precisa sincronizar os dados com os outros processos. Isso não ocorre com o Crivo Mestre/Escravo onde o nó zero apenas aguarda os cálculos dos outros nós.

Figura 41 – Woodbox renderizada com anti-aliasing

6.2 – Glasschess
A tabela da próxima página exibe os resultados dos testes com o cálculo da eficiência e speedup para cada conjunto de nós usados. O tempo do algoritmo seqüencial aparece em primeiro lugar seguido dos tempos para o Crivo Mestre/Escravo e o Crivo de Raios. A secção esquerda da tabela exibe os dados para a renderização sem anti-aliasing e a secção direita, com anti-aliasing.

C A P Í T U L O 6 . 2 – G L A S S C H E S S - 61 Glasschess.pov 1024x768
Sem anti-aliasing Segundos Speedup Eficiência Seqüencial 1 Crivo Mestre/Escravo 2 3 4 5 6 7 8 Crivo de Raios 2 3 4 5 6 7 8 642 Seqüencial 1 Crivo Mestre/Escravo 2 3 4 5 6 7 8 649 Com anti-aliasing Segundos Speedup Eficiência

564 286 199 139 113 104 83

1,138 2,245 3,226 4,619 5,681 6,173 7,735

0,569 0,748 0,807 0,924 0,947 0,882 0,967

564 287 199 139 113 104 83

1,151 2,261 3,261 4,669 5,743 6,240 7,819

0,575 0,754 0,815 0,934 0,957 0,891 0,977

331 215 161 129 107 92 80

1,940 2,986 3,988 4,977 6,000 6,978 8,025

0,970 0,995 0,997 0,995 1,000 0,997 1,003

Os gráficos abaixo demonstram o speedup ideal (linear) e os speedups alcançados para ambos os casos de renderização com e sem anti-aliasing. Em ambos os gráficos podemos notar que o processo de cálculo dos pixels foi tão pesado entre os nós escravos que não ocorreu gargalo para o cálculo do anti-alias pelo nó mestre. A tabela de resultados mostra que tanto para execução com e sem anti-alias o Crivo Mestre/Escravo levou o mesmo tempo. O Crivo de Raios mostrou ser altamente eficiente gerando um speedup linear na renderização da imagem.
Speedup (Glasschess sem Anti-alias)
10 8

Speedup (Glasschess com Anti-Alias)
10 8 Speedup 6 4 2 0 2 3 4 5 6 7 8

Speedup

6 4 2 0

2
Speedup Linear Crivo de Raios

3

4

5

6

7

8
Speedup Linear Crivo M estre/Escravo

Número de Máquinas

Número de máquinas

Crivo M estre/Escravo

62 - C A P Í T U L O V I – T E S T E S

E

RESULTADOS

Tempo (Glasschess sem Anti-Alias)
700 600 500 Segundos 400 300 200 100 0 1
Crivo Mest re/ Escravo Crivo de Raios

2

3

4

5

6

7

8

Núm ero de Máquinas

Esse terceiro gráfico dispõe a aproximação ocorrida entre os métodos quando se aumenta o número de nós no cluster sem o uso de anti-aliasing.

Figura 42 – Glasschess renderizado com anti-aliasing

6.3 – Skyvase
Essa imagem é utilizada oficialmente pelo POV-Ray para testes de velocidade de renderização tanto para máquinas seqüências quanto para clusters. A tabela da próxima página exibe os resultados dos testes com o cálculo da eficiência e speedup para cada conjunto de nós usados. O tempo do algoritmo seqüencial aparece em primeiro lugar seguido dos tempos para o Crivo Mestre/Escravo e o Crivo de Raios. A secção esquerda da tabela exibe os dados para a renderização sem anti-aliasing e a secção direita, com anti-aliasing.

C A P Í T U L O 6 . 3 – S K Y V A S E - 63 Skyvase.pov 4000x3000
Sem anti-aliasing Segundos Speedup Eficiência Seqüencial 1 Crivo Mestre/Escravo 2 3 4 5 6 7 8 Crivo de Raios 2 3 4 5 6 7 8 246 Seqüencial 1 Crivo Mestre/Escravo 2 3 4 5 6 7 8 252 Com anti-aliasing Segundos Speedup Eficiência

216 108 73 54 44 36 31

1,139 2,278 3,370 4,556 5,591 6,833 7,935

0,569 0,759 0,842 0,911 0,932 0,976 0,992

215 108 72 53 44 37 32

1,172 2,333 3,500 4,755 5,727 6,811 7,875

0,586 0,778 0,875 0,951 0,955 0,973 0,984

120 88 63 51 43 37 33

2,050 2,795 3,905 4,824 5,721 6,649 7,455

1,025 0,932 0,976 0,965 0,953 0,950 0,932

Os gráficos abaixo demonstram o speedup ideal (linear) e os speedups alcançados para ambos os casos de renderização com e sem anti-aliasing.
Speedup (Skyvase sem Anti-alias)
9 8 7 6 5 4 3 2 1 0 2
Speedup Linear Crivo de Raios Crivo Mest re/ Escravo

Speedup (Skyvase com Anti-alias)
9 8 7 6 5 4 3 2 1 0 2
Speedup Linear Crivo Mest re/ Escravo

Sp eed u p

3

4

5

6

7

8

Speedup

Número de Máquinas

3

4

5

6

7

8

Núm ero de Máquinas

64 - C A P Í T U L O V I – T E S T E S

E

RESULTADOS

Tempo (Skyvase sem Anti-Alias)
250 200 Segundos 150 100 50 0 1
Crivo de Raios Crivo M estre/Escravo

2

3

4

5

6

7

8

Núm ero de Máquinas

Figura 43 – Skyvase renderizado com anti-aliasing

C A P . 7 . 1 – T R A B A L H O S F U T U R O S - 65

Cap. VII – Conclusão
Neste trabalho, estudaram-se os três principais algoritmos para a síntese de imagens fotorealistas. Esses algoritmos (Ray-Tracing, Radiosidade e Photon Mapping) possuem tempos de execução elevados tornando, atualmente, impossível seu uso em aplicações de tempo real. Propôs-se a modificação desses algoritmos de forma a distribuir o processamento pesado entre os computadores de um cluster. Para os algoritmos de Ray-Tracing e Radiosidade, as versões paralelas construídas provaram um aumento de desempenho quase linear enquanto que a versão paralela do Photon Mapping não pode ser concluída devido à complexa estrutura usada pelo photon-map. O máximo de speedup foi alcançado em testes não registrados quando foram usadas todas as máquinas excetuando a de identificação zero. Sete máquinas SMP, com dois processos em cada uma, totalizando 14 processos renderizando a imagem exibida no sub-capítulo 6.1 sem antialiasing pelo Crivo de Raios. O processo todo levou 19 segundos, comparando com o tempo seqüencial de 273 segundos temos que o speedup foi de aproximadamente 14,37. A eficiência calculada é de aproximadamente 1,02. Esse é o melhor resultado obtido nos testes e demonstra uma aceleração super-linear do algoritmo dado pelo fato que menos mensagens são trocadas via rede e a sincronização dos processos é mais rápida porque dois processos são iniciados por vez ao invés de um só em cada máquina. Todo trabalho realizado encontra-se no CD anexado.

7.1 – Trabalhos Futuros
Como trabalho futuro, um melhor estudo sobre o algoritmo de Photon Mapping deve ser feito melhorando as chances de se encontrar uma forma para que a paralelização seja efetivamente funcional e escalar. Um sistema de barreira em árvore poderia ser aplicado para a sincronização linha-a-linha do crivo. Provavelmente aceleraria o processo de sincronização dos nós evitando o gargalo ocorrido no mestre. Pesquisar melhores maneiras de se distribuir o trabalho do anti-aliasing melhorando o desempenho do crivo mestre/escravo. Talvez perdendo uma nova máquina para que seja feito anti-aliasing em mais de uma máquina evitando o gargalo gerado quanto thresholds muito baixos são usados.

66 - R E F E R Ê N C I A S B I B L I O G R Á F I C A S

Referências Bibliográficas
ADOBE - “Site do software PhotoShop” - Disponível em - http://www.adobe.com/ br/products/photoshop/ - Acesso em: 04 de outubro de 2006. AURENHAMMER, Franz. (1991) Voronoi Diagrams – A Survey of a Fundamental Geometric Data Structure. New York: ACM Press. AUTODESK - “Site dos softwares 3D STUDIO MAX e MAYA” - Disponível em: http://www.autodesk.com - Acesso em: 04 de outubro de 2006. ALLEN, Michael; WILKINSON, Barry. (2005) Parallel Programming: Techniques and applications using networked workstations and parallel computers. New Jersey: Prentice Hall. BATTAILE, Bennett; GORAL, Cindy M.; GREENBERG, Donald P.; TORRANCE, Kenneth E. (1984) Modeling the interaction of light between diffuse surfaces, Baltimore: SIGGRAPH. BLENDER - “Site do software de modelagem Blender” http://www.blender.org/ - Acesso em: 04 de outubro de 2006. Disponível em:

BOURKE, Paul (2006) -. “Site demonstrando a técnica aplicada por Paul Bourke” – Disponível em: http://local.wasp.uwa.edu.au/~pbourke/rendering/parallel/ - Acesso em: 05 de Outubro de 2006. BSP-Tree – “Teoria 3D: Exemplificação da Binary Space Partitioning (BSP) Tree,” – disponível em: http://www.euclideanspace.com/threed/solidmodel/spatialdecomposition/ - Acesso em 20 de Outubro de 2006. CLEAR Robert D.; RUBINSTEIN, Francis M.; WARD, Gregory J. (1988) A Ray Tracing Solution for Diffuse Interreflection, Atlanta: SIGGRAPH. COHEN, Michael F.; WALLACE, John R. (1995) Radiosity and Realistic Image Synthesis. Boston: Academic Press Professional. COREL - “Site do software Corel Paint Shop Pro” - Disponível em: http://www.corel.com/ Acesso em: 05 de outubro de 2006. DAZ3D - “Site do software de modelagem Bryce” - Disponível em: http://www.daz3d.com/ program/bryce/ - Acesso em: 05 de outubro de 2006. DILGER – “Site demonstrando a técnica aplicada por Andrea Dilger” – Disponível em: http://members.shaw.ca/adilger/povray/pvmpov.html - Acesso em 05 de Outubro de 2006. GLASSNER, Andrew S. (1989) An Introduction to Ray-Tracing. Londres: Morgan Kaufmann. GÜNTHER, Johannes; WALD, Ingo; SLUSALLEK, Philipp. (2004) Realtime Caustics Using Distributed Photon Mapping, Saarbrücken: Eurographics Symposium on Rendering. JENSEN, Henrik W. (2001) Realistic Image synthesis using photon mapping, Stanford: Stanford University.

R E F E R Ê N C I A S B I B L I O G R Á F I C A S - 67 JESUS, Bruno G.; NEVES, Douglas D. F. (2006) Paralelização do Módulo de Ray-tracing na Ferramenta POV-Ray, Ouro Preto: WSCAD. JOHN, Marlon. (2003) Focus on Photon Mapping, Cincinnati: Premier Press. KDE - “Site da interface gráfica KDE onde se encontra o software de modelagem KPovModeller” - Disponível em: http://www.kde.org/ - Acesso em: 17 de outubro de 2006. MPI-FORUM – “Site oficial da biblioteca MPI” – Disponível em: http://www.mpi-forum.org/ Acesso em 05 de Outubro de 2006. NEWTEK - “Site do software de modelagem LightWave” http://www.newtek.com/ lightwave/ Acesso em: 04 de outubro de 2006. PACHECO, Peter S. (1997) Parallel programming with MPI. Londres: Morgan Kauffmann Publishers. PLACHETKA, Thomas. (1998) POV||Ray: Persistence of Vision Parallel Raytracer, Bratislava: Spring Conference on Computer Graphics. SANTOS, Eduardo Toledo. (1994) Avaliação do algoritmo de Ray-Tracing multicomputadores, São Paulo: Dissertação (Mestrado) Universidade de São Paulo. SHIRLEY, Peter. (1994) Hybrid Radiosity/Monte Carlo Methods, Natick: AK Peters. SHIRLEY, Peter. (2000) Realistic Ray-Tracing, Natick: AK Peters. SPATIAL DATABASES (2006) – “Site demonstrando diversas técnicas de estrutura de dados” – Disponível em: http://www.inf.udec.cl/~andrea/cursos/SDB2005/e-SDB6/index.html Acesso em 20 de Outubro de 2006. STONE, John. (1998) An Efficient Library For Parallel Ray-Tracing And Animation, Missouri: Intel Supercomputer Users Group Conference. STONE, John – “John Stone Ray-Tracer - Site demonstrando a técnica aplicada por John Stone” – Disponível em: http://jedi.ks.uiuc.edu/~johns/raytracer/ - Acesso em 05 de Outubro de 2006. SUNG, Kelvin; SHIRLEY, Peter. (1992) Ray-Tracing with the BSP-Tree. San Diego: Academic Press. TOP500 - “Site com a lista dos computadores mais rápidos do mundo” - Disponível em: http://www.top500.org/ - Acesso em 05 de outubro de 2006. VAN DAM, Andries; FEINER, Steven K.; FOLEY, James D.; HUGHES, John F.; PHILLIPS, Richard L. (1993) Introduction to Computer Graphics. New York: AddisonWesley Professional. VERRAL – “Site demonstrando a técnica aplicada por Leon Verrall” – Disponível em: http://www.verrall.demon.co.uk/mpipov/ - Acesso em 05 de Outubro de 2006. WATT, Alan H. (1999) 3D Computer Graphics, New York: Addison Wesley. em

68 - R E F E R Ê N C I A S B I B L I O G R Á F I C A S WHITTED, Turner. (1980) An improved illumination model for shaded display. New York: ACM Press.

A N E X O A - 69

Anexo A - Registro Oficial de Testes do Software em Paralelo
Pode ser encontrado em http://www.haveland.com/index.htm?povbench/index.php Infelizmente o site arredonda os números para cima, o teste foi realizado em 2.6 segundos e não 3 segundos. O teste é feito para renderização da imagem skyvase.pov como visto no subtulo 6.3. A dimensão da imagem é de 640x480, usa-se anti-alias com threshold 0.3 e não é gerada saída em arquivo ou na tela. Conquistamos o 34º lugar no ranking do site entre os mais de 2100 concorrentes.

70 - A N E X O B

Anexo B - Utilização do POV-Ray na Linha de Comando
Os argumentos para a execução do programa devem ser passados na linha de comando e antecedem um sinal "+" ou "-" que respectivamente habilita ou desabilita a opção. É imprescindível que um dos argumentos seja o arquivo a ser renderizado. As opções devem estar sem espaço com seus argumentos e mais de um argumento é separado espaço em branco. Exemplo: POV-Ray +Iarquivo.pov +Q9 +H480 +W640 Entre as opções mais importantes estão a qualidade da imagem, largura e altura, utilização ou não de anti-alias, arquivo de entrada e saída: • H e W Especificam a altura e largura da imagem a ser renderizada. Devem ser valores inteiros. • I especifica o arquivo .pov a ser renderizado.

• A qualidade da imagem é dada pelo parâmetro Q e deve estar entre 0 e 9. De 0 a 9 as opções vão melhorando a qualidade da imagem adicionando cada vez mais recursos para a renderização (sombras, reflexos, efeitos especiais). A tabela abaixo mostra as qualidades com respectiva descrição. Opções 0 e 1 Opções 2 e 3 Opção 4 Opção 5 Opções 6 e 7 Opção 8 Opção 9 Algumas cores e iluminação precária Mais cores e luz ambiente Sombras Luzes estendidas Computam texturas Computa reflexões e refrações dos raios disparados Computa Radiosidade e Photon Mapping

• Anti-aliasing (anti-serrilhamento) É ativado com a opção A. O valor padrão é 0.3 e pode ser alterado colocando-se +An.n • A opção AM altera o método com o qual o Anti-aliasing é feito e pode ter como valor 1 ou 2. • O número máximo de subdivisões é dado pela opção R. Isso é diferente do primeiro método onde o número total de super-samples é especificado.

A N E X O C - 71

Anexo C - Código da função de Ray-Tracing do POV-Ray
void Start_Tracing() { COLOUR unclippedColour; int x; int antialias_line = true; int skip_lines; int first_line; int skip_odd_lines; /* Set jitterscale. */ JitterScale = opts.JitterScale / (DBL)opts.AntialiasDepth; /* Odd/even line tracing depends on the frame number. */ skip_odd_lines = !(((opts.FrameSeq.FrameNumber % 2)==1) ^ opts.FrameSeq.Odd_Field_Flag); /* Field rendering starts on an odd or even line. */ skip_lines = (opts.FrameSeq.Field_Render_Flag) && !(opts.Options & ANTIALIAS); /* Get first line number. */ first_line = (opts.Options & ANTIALIAS)?opts.First_Line-1:opts.First_Line; /* Loop over all rows. */ for (Current_Line_Number = first_line; Current_Line_Number Current_Line_Number++) { /* Skip odd or even lines depending on the line number. */ if ((skip_lines) && ((Current_Line_Number % 2) == skip_odd_lines)) { /* Write previous line again. */ if (Current_Line_Number > opts.First_Line) { output_single_image_line_with_alpha_correction(Previous_Line, Current_Line_Number); } else { POV_WRITE_LINE (Previous_Line, Current_Line_Number) } continue; } MosaicPreviewSize = 1; Send_ProgressUpdate(PROGRESS_RENDERING); Do_Cooperate(0); /* Prune vista tree. */ Prune_Vista_Tree(Current_Line_Number); /* Precalculate whether to antialias a line. */ if (opts.FrameSeq.Field_Render_Flag) { if (Current_Line_Number >= opts.First_Line) { antialias_line = ((Current_Line_Number % 2) ^ skip_odd_lines); } else { < opts.Last_Line;

72 - A N E X O C
antialias_line = false; } } /* Loop over all columns. */ for (x = opts.First_Column; x < opts.Last_Column; x++) { /* Check for user abort. */ Check_User_Abort(false); /* Trace current pixel. */ // Debug_Info("y = %3d, x = %3d\n", Current_Line_Number, x); trace_pixel(x, Current_Line_Number, Current_Line[x], unclippedColour); /* Apply anti-aliasing. */ if ((opts.Options & ANTIALIAS) && antialias_line) { do_anti_aliasing(x, Current_Line_Number, Current_Line[x]); } /* Display pixel. */ plot_pixel(x, Current_Line_Number, Current_Line[x]); POV_ASSIGN_PIXEL_UNCLIPPED (x, Current_Line_Number, unclippedColour) POV_ASSIGN_PIXEL (x, Current_Line_Number, Current_Line [x]) } /* Write current row to disk. */ output_prev_image_line_and_advance(Current_Line_Number); } Current_Line_Number = 0; /* Write last row to disk. */ if (opts.Last_Line != opts.First_Line) { output_single_image_line_with_alpha_correction(Previous_Line,opts.Last_Line - 1); } }

A N E X O D - 73

Anexo D - Código da Radiosidade no POV-Ray
void Start_Tracing_Radiosity_Preview(int StartPixelSize, int EndPixelSize) { unsigned char Red, Green, Blue, Alpha; unsigned char *thisr = NULL, *thisg = NULL, *thisb = NULL, *thisa = NULL; unsigned char *upr = NULL, *upg = NULL, *upb = NULL, *upa = NULL; int Smooth_Preview = 0; int dx, dy, skip, tr, tg, tb, ta, lastr, lastg, lastb, lasta, ulr, urr, llr, lrr, ulg, urg, llg, lrg, ulb, urb, llb, lrb, ula, ura, lla, lra, lor, log, lob, loa, hir, hig, hib, hia, tx, ty, jitter_range, jitter_offset, offset_x, offset_y, first_pass, x, x2, y2; DBL grey, gather_grey; COLOUR Colour, avg_gather, unclippedColour; int save_use_blur; lastr = lastg = lastb = lasta = 0; opts.Real_Radiosity_Error_Bound = opts.Radiosity_Error_Bound; opts.Radiosity_Error_Bound *= opts.Radiosity_Low_Error_Factor; firstRadiosityPass = true; /* Initialize the accumulators which will allow us to set average amb Brightness */ Make_Colour(Radiosity_Gather_Total, 0.0, 0.0, 0.0); Radiosity_Gather_Total_Count = 0; /* if radiosity is on, you MUST use preview pass to get reasonable results. * 8x8 is generally a good size to use if the user didn't specify anything. */ if ( StartPixelSize == 1 ) { if (opts.radPretraceStart==0 || opts.radPretraceEnd==0) { StartPixelSize = EndPixelSize = 8; } else { /* lets use some percentages instead of the INI options!! */ StartPixelSize = max(Frame.Screen_Height,Frame.Screen_Width)*opts.radPretraceStart; EndPixelSize = max(Frame.Screen_Height,Frame.Screen_Width)*opts.radPretraceEnd; } } /* /* if if Prevent 1x1 passes - this code is very slow at 2x2 or less */ NK rad - allow down to 2x2 passes */ ( StartPixelSize < 2) StartPixelSize = 2; ( EndPixelSize < 2) EndPixelSize = 2;

/* if there is no visible output, might as well just do one pass, it's faster. * The last pass is the one which determines the values which get put into * the radiosity tree, so just do the last (end) pass. */ /* NK rad - always do what the user asks for! it WILL affect the final output if ( !(opts.Options & DISPLAY)) StartPixelSize = EndPixelSize; */ /* Finally, end size must always be less than or equal to start size */ if ( EndPixelSize > StartPixelSize ) EndPixelSize = StartPixelSize; skip = StartPixelSize; first_pass = true; while ((skip >= 2) && (skip >= EndPixelSize))

74 - A N E X O D
{ /* for each pass */ jitter_range = 3; jitter_offset = skip / 2 - 1; /* add a very small amount of jitter */

#if(ALLOW_SMOOT_RAD_PREVIEW == 1) if(skip <= 8) Smooth_Preview = 1; #endif for (Current_Line_Number = opts.First_Line; Current_Line_Number < opts.Last_Line; Current_Line_Number += skip) { MosaicPreviewSize = skip; Send_ProgressUpdate(PROGRESS_RENDERING); Do_Cooperate(0); for (x = opts.First_Column; x < opts.Last_Column; x += skip) { Check_User_Abort(false); offset_x = jitter_offset + (POV_RAND() % jitter_range); offset_y = jitter_offset + (POV_RAND() % jitter_range); /* don't use focal blur for radiosity preview! */ save_use_blur = Focal_Blur_Is_Used; Focal_Blur_Is_Used = false; trace_pixel(x + offset_x, Current_Line_Number + offset_y, Colour, unclippedColour); Focal_Blur_Is_Used = save_use_blur; extract_colors(Colour, &Red, &Green, &Blue, &Alpha, &grey); POV_ASSIGN_PIXEL_UNCLIPPED (x, Current_Line_Number, unclippedColour) Assign_Colour(Current_Line[x], Colour); POV_ASSIGN_PIXEL (x, Current_Line_Number, Colour) } /* end loop for each block horizontally in a row of blocks */ } /* end loop of rows of blocks */ if (first_pass) { /* Ensure that the average ambient value returned by compute_ambient() is about * the same as the average ambient value setting in the scene file */ if ( Radiosity_Gather_Total_Count ) { VInverseScale(avg_gather, Radiosity_Gather_Total, (DBL)Radiosity_Gather_Total_Count); gather_grey = avg_gather[pRED] + avg_gather[pGREEN] + avg_gather[pBLUE]; if ( gather_grey > 0. ) { /* NK rad 1999 commented this out - we don't want to mess with the 'brightness' setting that the user chose */ /*opts.Radiosity_Brightness = 3. / gather_grey; */ if ( ot_fd != NULL) { ot_fd->printf("B%g\n", opts.Radiosity_Brightness); } } } first_pass = 0; } skip /= 2; } /* end loop of different resolutions */… }

A N E X O E - 75

Anexo E - Código da função parcial do Photon Mapping no POV-Ray
...
// global lights photonOptions.Light_Is_Global = true; for (Light = Frame.Light_Sources; Light != NULL; Light = Light->Next_Light_Source) if (Light->Light_Type != FILL_LIGHT_SOURCE) { SearchThroughObjects(Frame.Objects, Light, true); } // light_group lights photonOptions.Light_Is_Global = false; for (Light_Group_Light = Frame.Light_Group_Lights; Light_Group_Light != NULL; Light_Group_Light = Light_Group_Light->Next) { Light = Light_Group_Light->Light; if (Light->Light_Type != FILL_LIGHT_SOURCE) { SearchThroughObjects(Frame.Objects, Light, true); } } factor = (DBL)photonCountEstimate/photonOptions.surfaceCount; factor = sqrt(factor); photonOptions.surfaceSeparation *= factor; } /* COUNT THE GLOBAL PHOTONS */ if(photonOptions.globalCount>0) { DBL factor; photonCountEstimate = 0.0; photonOptions.Light_Is_Global = true; for (Light = Frame.Light_Sources; Light != NULL; Light = Light->Next_Light_Source) if (Light->Light_Type != FILL_LIGHT_SOURCE) { ShootPhotonsAtObject(NULL, Light, true); } // light_group lights photonOptions.Light_Is_Global = false; for (Light_Group_Light = Frame.Light_Group_Lights; Light_Group_Light != NULL; Light_Group_Light = Light_Group_Light->Next) { Light = Light_Group_Light->Light; if (Light->Light_Type != FILL_LIGHT_SOURCE) { ShootPhotonsAtObject(NULL, Light, true); } } factor = (DBL)photonCountEstimate/photonOptions.globalCount; factor = sqrt(factor); photonOptions.globalSeparation *= factor; Do_Cooperate(1); } // there is a world out there that wants some attention [trf]

76 - A N E X O E
Do_Cooperate(0); /* loop through global light sources */ photonOptions.Light_Is_Global = true; for (Light = Frame.Light_Sources; Light != NULL; Light = Light->Next_Light_Source) if (Light->Light_Type != FILL_LIGHT_SOURCE) { if (Light->Light_Type == CYLINDER_SOURCE && !Light->Parallel) { Warning(0,"Cylinder lights should be parallel when used with photons."); } /* do global lighting here if it is ever implemented */ if (Test_Flag(Light, PH_TARGET_FLAG) && (photonOptions.globalCount>0)) ShootPhotonsAtObject(NULL, Light, false); /* do object-specific lighting */ SearchThroughObjects(Frame.Objects, Light, false); } // loop through light_group light sources photonOptions.Light_Is_Global = false; for (Light_Group_Light = Frame.Light_Group_Lights; Light_Group_Light != NULL; Light_Group_Light = Light_Group_Light->Next) { Light = Light_Group_Light->Light; if (Light->Light_Type == CYLINDER_SOURCE && !Light->Parallel) { Warning(0,"Cylinder lights should be parallel when used with photons."); } /* do global lighting here if it is ever implemented */ if (Test_Flag(Light, PH_TARGET_FLAG) && (photonOptions.globalCount>0)) ShootPhotonsAtObject(NULL, Light, false); /* do object-specific lighting */ SearchThroughObjects(Frame.Objects, Light, false); } /* clear this flag */ backtraceFlag = 0; /* restore saved variables */ ADC_Bailout = old_adc; Max_Trace_Level = old_mtl; /* now actually build the kd-tree by sorting the array of photons */ if (photonOptions.photonMap.numPhotons>0) { buildTree(&photonOptions.photonMap); setGatherOptions(&photonOptions.photonMap, false); } #ifdef GLOBAL_PHOTONS /* ----------- global photons ------------- */ if (photonOptions.globalPhotonMap.numPhotons>0) { buildTree(&photonOptions.globalPhotonMap); setGatherOptions(&photonOptions.globalPhotonMap, false); } #endif ...