Anda di halaman 1dari 76

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 Ray-
Tracing, 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ão-
uniforme 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 BSP-
Tree 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 kd-
tree 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 super-
sampling. 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 Ray-
Tracing, 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ão-
filtrada, 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 kd-
tree 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; /* location */
SMALL_COLOUR Colour; /* color & intensity (flux) */
unsigned char info; /* info byte for kd-tree */
signed char theta, phi; /* 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 minGatherRad; /* minimum gather radius */


DBL minGatherRadMult; /* minimum gather radius multiplier */
DBL gatherRadStep; /* step size for gather expansion */
int gatherNumSteps; /* 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 anti-
aliasing 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.
• Clusters – Sob este nome estão máquinas cujo princípio básico é o emprego de uma
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.

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 tornar-
se 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 Ray-
Tracing 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 EM P A R A L E L O - 53
TamVet = (colunas/numprocs)*5

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 Ray-
Tracing, 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 anti-
aliasing, 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.

# Processador(es) Cache Clock Mem. RAM Interface de Rede


0 1 AMD Opteron 240 1 Mb 1400 MHz 3 Gb Ethernet Gigabit
1 2 AMD Opteron 242 2x1 Mb 2x1600 MHz 1 Gb Ethernet Gigabit
2 2 AMD Opteron 242 2x1 Mb 2x1600 MHz 1 Gb Ethernet Gigabit
3 2 AMD Opteron 242 2x1 Mb 2x1600 MHz 1 Gb Ethernet Gigabit
4 2 AMD Opteron 244 2x1 Mb 2x1800 MHz 1 Gb Ethernet Gigabit
5 2 AMD Opteron 244 2x1 Mb 2x1800 MHz 1 Gb Ethernet Gigabit
6 2 AMD Opteron 244 2x1 Mb 2x1800 MHz 1 Gb Ethernet Gigabit
7 2 AMD Opteron 244 2x1 Mb 2x1800 MHz 1 Gb 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 anti-
aliasing 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 Com anti-aliasing
Segundos Speedup Eficiência Segundos Speedup Eficiência
Seqüencial Seqüencial
1 273 1 315

Crivo Crivo
Mestre/Escravo Mestre/Escravo
2 232 1,177 0,588 2 231 1,364 0,682
3 117 2,333 0,778 3 118 2,669 0,890
4 79 3,456 0,864 4 82 3,841 0,960
5 59 4,627 0,925 5 64 4,922 0,984
6 47 5,809 0,968 6 57 5,526 0,921
7 40 6,825 0,975 7 53 5,943 0,849
8 35 7,800 0,975 8 50 6,300 0,788

Crivo de Raios
2 137 1,993 0,996
3 94 2,904 0,968
4 70 3,900 0,975
5 55 4,964 0,993
6 47 5,809 0,968
7 41 6,659 0,951
8 36 7,583 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) Speedup (Woodbox com Anti-alias)


9 9
8 8
7 7
6 6
Speedup
Speedup

5 5
4 4
3 3
2 2
1 1
0 0
2 3 4 5 6 7 8 2 3 4 5 6 7 8
Speedup Linear Speedup Linear
Crivo de Raios
Núm ero de Máquinas Núm ero de Máquinas
Cr ivo Mest re/ Escravo
Crivo M estre Escravo
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
1 2 3 4 5 6 7 8
Crivo Mestre/Escravo
Crivo de Raios 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 Com anti-aliasing
Segundos Speedup Eficiência Segundos Speedup Eficiência
Seqüencial Seqüencial
1 642 1 649

Crivo Crivo
Mestre/Escravo Mestre/Escravo
2 564 1,138 0,569 2 564 1,151 0,575
3 286 2,245 0,748 3 287 2,261 0,754
4 199 3,226 0,807 4 199 3,261 0,815
5 139 4,619 0,924 5 139 4,669 0,934
6 113 5,681 0,947 6 113 5,743 0,957
7 104 6,173 0,882 7 104 6,240 0,891
8 83 7,735 0,967 8 83 7,819 0,977

Crivo de Raios
2 331 1,940 0,970
3 215 2,986 0,995
4 161 3,988 0,997
5 129 4,977 0,995
6 107 6,000 1,000
7 92 6,978 0,997
8 80 8,025 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) Speedup (Glasschess com Anti-Alias)


10 10

8 8
Speedup
Speedup

6
6
4
4
2
2
0
0 2 3 4 5 6 7 8
2 3 4 5 6 7 8
Número de Máquinas
Speedup Linear
Speedup Linear Número de máquinas Crivo M estre/Escravo
Crivo de Raios
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

Segundos 500

400

300

200

100

0
1 2 3 4 5 6 7 8
Crivo Mest re/ Escravo
Núm ero de Máquinas
Crivo de Raios

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 Com anti-aliasing
Segundos Speedup Eficiência Segundos Speedup Eficiência
Seqüencial Seqüencial
1 246 1 252

Crivo Crivo
Mestre/Escravo Mestre/Escravo
2 216 1,139 0,569 2 215 1,172 0,586
3 108 2,278 0,759 3 108 2,333 0,778
4 73 3,370 0,842 4 72 3,500 0,875
5 54 4,556 0,911 5 53 4,755 0,951
6 44 5,591 0,932 6 44 5,727 0,955
7 36 6,833 0,976 7 37 6,811 0,973
8 31 7,935 0,992 8 32 7,875 0,984

Crivo de Raios
2 120 2,050 1,025
3 88 2,795 0,932
4 63 3,905 0,976
5 51 4,824 0,965
6 43 5,721 0,953
7 37 6,649 0,950
8 33 7,455 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) Speedup (Skyvase com Anti-alias)
9 9
8 8
7 7
6
Sp eed u p

6
Speedup

5
5
4
3 4
2 3
1 2
0 1
2 3 4 5 6 7 8 0
Speedup Linear
Número de Máquinas 2 3 4 5 6 7 8
Crivo de Raios
Crivo Mest re/ Escravo
Speedup Linear Núm ero de Máquinas
Crivo Mest re/ Escravo
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 2 3 4 5 6 7 8
Crivo de Raios
Núm ero de Máquinas
Crivo M estre/Escravo

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 foto-
realistas. 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 anti-
aliasing 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” - Disponível em:
http://www.blender.org/ - Acesso em: 04 de outubro de 2006.
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 em
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: Addison-
Wesley 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.
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 sub-
tulo 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 Algumas cores e iluminação precária
Opções 2 e 3 Mais cores e luz ambiente
Opção 4 Sombras
Opção 5 Luzes estendidas
Opções 6 e 7 Computam texturas
Opção 8 Computa reflexões e refrações dos raios disparados
Opção 9 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 < opts.Last_Line;


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
{
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;
}
}

/* Prevent 1x1 passes - this code is very slow at 2x2 or less */


/* NK rad - allow down to 2x2 passes */
if ( StartPixelSize < 2) StartPixelSize = 2;
if ( 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

...