Sítio do Piropo

B. Piropo

< Coluna em Fórum PCs >
Volte
08/09/2008

< Computação visual II: >
<
Rasterizando e renderizando >


Semana passada falamos sobre o Intel Developers Forum 2008 e a CVC, ou computação visual conectada, um ambiente computacional no qual artefatos de diferentes tamanhos e capacidades de processamento estariam conectados através da Internet e exibindo em suas telas imagens altamente realísticas, possibilitando usos até agora impensáveis para a computação gráfica. Comentamos ainda que este tipo de ambiente exige uma brutal capacidade de processamento, especialmente no que toca à geração de imagens. E que a Intel se dispõe a enfrentar este problema com a arquitetura Larrabee.

Pois bem: no que diz respeito à arquitetura Larrabee, antes de mais nada, é preciso deixar uma coisa bem clara: a coisa não é clara. E quanto mais eu me enfronho no labirinto de informações desencontradas sobre o Larrabee, mais me convenço que a confusão é propositalmente alimentada pela Intel por razões mais comerciais do que técnicas.

Ocorre que o assunto chamou minha atenção desde a apresentação inicial no “dia zero” do evento. Portanto, me mantive atento à programação para buscar maiores informações. Pois bem: lá pelo segundo dia havia uma palestra sobre o tema proferida pelo Dr. Larry Sailer, justamente o responsável pelo projeto Larrabee. Ou seja: Sailer era “o cara”. Evidentemente, na data aprazada, lá estava eu na primeira fila, ávido de informações. Prestei uma atenção danada. E nada. Quanto mais o homem falava, mais confuso eu ficava. Esperei então pela sessão de perguntas e respostas para ver se nela conseguiria dissipar, ainda que um pouco, as trevas de minha ignorância. E, pelo teor das perguntas, notei que não era o único a estar perplexo. E olhe que aquela sala, tirante este ignorante que vos escreve, reunia a nata dos especialistas em computação visual do IDF.

Para que vocês tenham uma idéia: durante a palestra, Sailer informara que, embora contivesse diversos núcleos, cada um deles funcionalmente idêntico aos das UCPs da Intel, o Larrabee não era uma UCP (Unidade Central de Processamento). Já durante a sessão de perguntas e respostas um dos presentes referiu-se ao Larrabee como uma UGP (Unidade Gráfica de Processamento) e foi prontamente interrompido por Sailer, que retrucou que o Larrabee “não era uma UGP”. A partir deste momento me senti tão confuso que, mesmo correndo o risco de parecer ridículo diante de tão seleta platéia, resolvi arriscar a pergunta que se impunha, se bem que um tanto deslocada no final de uma palestra justamente sobre o assunto. Pedi a palavra e lasquei: “Durante a apresentação o senhor afirmou que o Larrabee não é uma UCP. Agora afirma que não é uma UGP. Por mal que lhe pergunte, afinal o que vem a ser o Larrabee?”. Pois bem: para minha surpresa, a pergunta mereceu entusiásticos aplausos da audiência (não estou brincando, foi realmente aplaudida). Neste ponto eu percebi que não era o único dos presentes sentindo-se perdido em um labirinto. E, dada a resposta de Sailer (que comentarei adiante), percebi também que não era ali que eu iria dirimir minhas dúvidas. Resolvi então reunir todo o material disponível no IDF sobre o Larrabee e procurar as respostas em outras plagas. E, juntando tudo, tentar resumir aqui o que (eu presumo) virá a ser o Larrabee.

Voltemos então ao meu diálogo com Sailer. Minha pergunta inicial mereceu uma resposta em puro tecniquês, mencionando “novos conceitos” e coisas que tais mas da qual, espremendo bem, nada saía de aproveitável. Então voltei à carga: “Para simplificar: um computador que tenha o Larrabee ainda irá precisar de uma UCP?”. “Sim”, respondeu ele. Bem, então o bicho não era mesmo uma UCP. Isto posto, prossegui: “E se este computador precisar de grande capacidade de computação gráfica, necessitará também de uma UGP?”. “Não”, respondeu Sailer, “o Larrabee se encarregará dos gráficos”. “Então”, disse eu, “podemos considerar o Larrabee como uma UGP altamente sofisticada com grande capacidade de programação?”. Meio a contragosto Sailer aquiesceu. E ouviu-se ouvir um murmúrio de satisfação proveniente da audiência. Ou seja: embora a Intel não deseje admitir, o Larrabee será mesmo uma UGP altamente complexa e com uma imensa capacidade de programação. E será um processador “many-core”. O que, no jargão atualmente usado pelos especialistas, significa que terá no mínimo dez núcleos operando em paralelo (embora isto não seja uma nomenclatura oficial, hoje, processadores de até oito núcleos são classificados como “multi-core” e daí para cima como “many-core”). Portanto: Larrabee será, sim, uma UGP. Mas uma UGP muito peculiar.

Para entender em que exatamente consistirá esta peculiaridade, precisamos rever alguns conceitos básicos sobre computação gráfica. E computação gráfica não é a minha praia. Creio (na verdade não creio: tenho certeza) que boa parte dos leitores deste Forum conhecem muito mais sobre o assunto que este pobre engenheiro civil que vos escreve, portanto desde já me desculpo por eventuais imprecisões. Mas como não precisaremos ir muito além das noções mais elementares, talvez meu parco conhecimento seja bastante. Senão vejamos.

Primeiro, uma noção fundamental: qual a diferença entre imagens bidimensionais e tridimensionais? Note que não me refiro a objetos, mas a imagens.

Fisicamente, toda “imagem” é bidimensional, já que a tela ou o papel ou seja lá que meio usarmos para reproduzi-la tem apenas duas dimensões. Então o que chamamos de imagem tridimensional é aquela que, exibida em um meio de apenas duas dimensões, cria a impressão de que possui três. E para substancializar as diferenças, apelemos para um exemplo radical: compare as duas imagens exibidas na Figura 1.

Figura 1: imagens bidimensional e tridimensional.

Ambas foram obtidas na Wikipedia. A primeira é material de divulgação do desenho animado “Família Simpson” da 20th Century Fox. A segunda é um detalhe da obra de arte digital “Spiral Sphere and Julia” gerada em computador por Robert W. McGregor. A imagem dos Simpson é “lavada” e, embora possa exibir certa perspectiva, não mostra sombras ou detalhes. A única informação que fornece sobre que componentes estão mais próximos ou mais distantes do observador é o fato de que os primeiros encobrem os últimos. Já a segunda é cheia de brilhos, sombras e efeitos. A presença de cada objeto influencia seus vizinhos com seus reflexos. Nela, as superfícies refletoras de luz não apenas parecem de fato refleti-la pelo “brilho” que mostram, como também porque seus reflexos “iluminam” os objetos ao redor. Em imagens assim realistas, líquidos mostram aparência realmente fluida e as superfícies exibem suas texturas características. Mas, é bom lembrar, ambas são exibidas em apenas duas dimensões. A sensação que a segunda transmite de ser “tridimensional” é, portanto, apenas um efeito obtido através de técnicas muito bem apuradas. E isto vale não apenas para imagens estáticas como as mostradas na figura como também para imagens dinâmicas, como as de desenhos animados e alguns jogos tridimensionais.

E já que falamos em imagens dinâmicas, convém lembrar que tanto no cinema quanto na televisão e no computador, elas nada mais são que uma sucessão de imagens estáticas exibidas rapidamente uma após a outra em intervalos tão curtos que, em nossos olhos, a imagem seguinte se superpõe à anterior antes que esta primeira seja “apagada” da retina, dando assim a ilusão de movimento. Portanto, criar animações tridimensionais nada mais é que produzir imagens bidimensionais e projetá-las (na tela do cinema) ou renová-las (na da televisão ou computador) mais depressa do que nossa retina possa perceber. Em suma: do ponto de vista gráfico, não há diferença entre imagens estáticas e animadas. Mas do ponto de vista técnico a diferença é imensa: como cada imagem deve ser gerada “na hora”, o poder computacional necessário para gerar imagens tridimensionais em movimento é brutalmente maior do que para gerar imagens estáticas. A não ser que se esteja disposto a esperar um tempo razoável entre uma imagem e a seguinte. E este razoável aí é mera maneira de dizer: a primeira versão do programa AutoCad que rodei há quase vinte anos em meu saudoso PC trazia uma amostra de imagem tridimensional, a perspectiva do que, se bem me lembro, era uma igreja ou castelo; a imagem estava contida em um arquivo que, ao ser aberto, era “renderizado” na tela (calma que já explicamos o termo). Dava para ver cada linha sendo desenhada e, até que o desenho ficasse “pronto”, havia que esperar uns bons quinze minutos. E se você movesse o ponto de vista para “enxergar” a imagem de uma perspectiva diferente, havia que esperar outros tantos minutos para examinar a nova imagem. Já imaginou um jogo no qual, a cada movimento do ator, seja preciso esperar quinze minutos pelo novo quadro?

Agora vejamos como é possível gerar em computadores imagens “tridimensionais” como a da direita da Figura 1.

Bem, o primeiro passo é gerar um “modelo”. O modelo é uma descrição do objeto tridimensional usando uma estrutura de dados estritamente definida. Para construir o modelo divide-se o objeto, seja ele qual for, em pequenos triângulos ou polígonos e anota-se a posição exata do vértice de cada um deles. Estes elementos são denominados de “primitivas” por se tratarem de figuras planas (em geral, triângulos) que, em conjunto, geram a impressão de uma figura tridimensional.

Se exibirmos exclusivamente a representação do modelo, “desenhando” cada primitiva ao lado de suas vizinhas, teremos uma imagem estranha, transparente, que lembra um modelo físico construído com arame (e por isso recebe o nome em inglês de “wireframe”). Veja, na Figura 2 (fornecida gratuitamente como plano de fundo pelo sítio < http://www.uniquecarsandparts.com.au/ > Unique Cars and Parts), um soberbo exemplo de modelo em “wireframe” de um carro de Fórmula 1. Note os pequenos retângulos e triângulos que o formam. Cada um deles é uma “primitiva”.

Figura 2: modelo em wireframe de carro de F1.

Note que o modelo não é um desenho. A figura 2 mostra sua representação gráfica, mas o modelo propriamente dito é uma entidade abstrata, uma estrutura de dados, um imenso conjunto de coordenadas tridimensionais de cada vértice de cada uma das primitivas que o compõem. Naturalmente a imagem será tão mais realista quanto maior for o número (e menores as dimensões) de suas primitivas, os pequenos polígonos em que foi dividida.

Com o modelo em mãos, falta justamente transformá-lo em uma imagem. Para isto é preciso colorir cada pequeno polígono que forma uma primitiva no tom exato, preenchendo-o com “pixels” nas cores corretas. Mas a coisa não é tão simples assim. Pois para a imagem parecer real, ou seja, para se obter um efeito efetivamente tridimensional (falando mais simplesmente: para fazer com que uma imagem gerada em um computador se pareça com a de uma foto do objeto que representa produzida por uma câmara digital de alta resolução) falta ainda muito trabalho. É preciso preencher com pixels (pontos coloridos ou células de imagem) o intervalo entre os “arames”, como se estivéssemos “vestindo” o modelo com sua real superfície. Este procedimento chama-se “renderização” (“rendering”, em inglês) e consiste na geração da imagem propriamente dita a partir do modelo.

Originalmente a renderização consistia meramente em um procedimento chamado “rasterização”. Hoje, “rasterização” é o mais simples dos métodos de “renderização”.

A rasterização consiste em transformar uma imagem vetorial (ou seja, descrita pelas equações das linhas e superfícies que a formam) em um conjunto de pixels. A forma mais simples de fazer isto é preencher cada “primitiva” com pixels da mesma cor, gerando uma imagem conhecida por “mapa de bits”.

Mapa de bits é a forma pela qual toda e qualquer imagem é exibida na tela. Ou seja: mesmo quando trabalhamos com programas gráficos mais sofisticados, como o CorelDraw ou Fireworks, que usam internamente arquivos vetoriais para descrever a imagem, para que ela seja exibida na tela precisa ser “rasterizada”, ou seja, transformada em um mapa de bits.

Para criá-lo por meio da simples rasterização é preciso calcular o formato, em duas dimensões, de cada pequena superfície representada por um triângulo do modelo quando observada de certo ângulo de visão, e exibir esta superfície na tela na forma de um conjunto de pontos (pixels) de mesma cor. É claro que uma rasterização bem feita leva em conta a posição do foco de luz e gera as sombras correspondentes, além de “escurecer” os pontos não atingidos diretamente pelos raios de luz.

Estes cálculos são feitos em cada primitiva (ou seja, triângulo a triângulo) até que toda a imagem possa ser representada por um único mapa de bits que será exibido na tela (naturalmente as primitivas que estariam na parte “de trás” da imagem, por não serem visíveis, não precisam ser rasterizadas, a não ser que mudemos o ponto de vista para o lado oposto do objeto; se você não entendeu esta frase, leia devagar e pense um pouco; se ainda assim não entendeu, não se preocupe: eu é que não expliquei direito). Em suma: esta é a forma mais simples de renderização: tratar cada primitiva como um elemento independente das demais e “colori-la” inteira com pixels de mesma cor. E, mesmo não levando em conta um monte de fatores, como reflexos e coisas que tais (o que resulta em uma imagem meio “lavada”), dá um trabalho de cão.

Depois surgiram meios mais sofisticados de renderizar que, em vez de considerar cada primitiva como uma entidade autônoma, tratam cada um de seus vértices independentemente, primeiro calculando a cor do pixel que forma cada vértice de um triângulo elementar (primitiva) e em seguida preenchendo a superfície do triângulo com pixels cujas tonalidades são uma mistura harmônica das cores dos três vértices, permitindo que a variação de tonalidade de uma primitiva para sua vizinha seja gradual.

Mas o fato é que quanto maior a “qualidade” de uma imagem tridimensional, maior o poder computacional necessário para gerá-la. Hoje, renderizar uma imagem é um processo que, entre outros fatores, leva em conta:

iluminação: como as cores e brilhos das superfícies variam com a posição da(s) fonte(s) de luz;

textura: detalhes da superfície, como rugosidade e polimento;

sombreamento: efeito de obstrução da luz (a Figura 2, embora exibindo apenas o modelo, mostra sua sombra no piso);

reflexos: contornos de outros objetos nas superfícies refletoras de objetos polidos;

transparência e translucidez: forma pela qual a luz atravessa certos objetos;

refração: mudança da direção dos raios luminosos quando mudam de meio;

difração: capacidade da luz de se espalhar em certos meios;

profundidade de foco: diferenças de nitidez devido a diferenças nas distâncias entre o objeto e o observador...

e mais um monte de outras (quer saber mais? consulte a < http://en.wikipedia.org/wiki/Rendering_(computer_graphics) Wikipedia).

E esse é o tipo da coisa que pode envolver muita, mas muita matemática e física.

Pois pense um pouco: o “modelo” não é um desenho, mas um conjunto de coordenadas. Para renderizá-lo há que “unir” o espaço entre estas coordenadas (ou “preencher” os polígonos) com um conjunto de pixels que leve em conta todos os fatores acima mencionados (e outros, que deixei de mencionar para não complicar ainda mais a coisa). E isto é feito com base em equações de superfícies que passam pelos pontos, cujas cores e tonalidades dependem das leis físicas de transmissão e reflexão da luz, das posições relativas tridimensionais de todos os objetos e de mais um monte de leis físicas (e não apenas aquelas do campo da ótica: quando se cria um movimento, como o de uma bola “quicando”, há que calcular exatamente a aceleração do corpo que cai e a força com que ele é lançado de volta para cima conforme seu coeficiente de elasticidade). Em suma: é cálculo pracacete-abeça-oumais.

A forma mais moderna e sofisticada de se reproduzir em um meio bidimensional uma imagem que dá a impressão de ser tridimensional é uma técnica de renderização denominada “ray tracing” (ou “traçado de raios”). “Raios”, neste caso, refere-se a raios luminosos e a técnica consiste em gerar uma imagem traçando a trajetória dos raios de luz no plano da imagem. Para isto é necessário considerar a posição relativa da fonte luminosa, de cada objeto representado na imagem e do ponto de vista do observador e calcular o trajeto de raios luminosos que, provindo da fonte, se refletem nos objetos e atingem o olho do observador. A coisa complica bastante se levarmos em conta que há superfícies que refletem a luz (como partes metálicas polidas ou vidro), outras que a absorvem totalmente (como, por exemplo, um tecido preto) e outras ainda que a absorvem parcialmente e refletem a energia luminosa de forma difusa (como uma superfície “fosca”). E os raios refletidos por cada objeto poderão, por sua vez, incidir sobre os demais objetos da cena, iluminando-os ou, nos casos de superfícies refletoras, formando imagens neles refletidas que por sua vez podem se refletir em outros objetos. Complicou? Então examine detidamente a Figura 3,
obtida na < http://en.wikipedia.org/wiki/Image:Recursive_raytrace_of_a_sphere.png > Wikipedia.

Figura 3: imagem criada por “ray tracing”.

Repare, como as demais bolas se refletem tão bem na bola central e se pode distinguir a imagem de cada uma na superfície dela. Note também como a bola amarelada, situada em primeiro plano em baixo e à esquerda, aparece “desfocada” como em uma fotografia (a imagem, é bom repetir, foi inteiramente gerada em computador). Repare nas sombras, não apenas a projetada pelas bolas no piso como também as projetadas pelas imagens refletidas na bola central sobre a superfície desta bola.

Já imaginou quanta calculeira é preciso fazer para determinar o traçado de cada raio luminoso que gerou a imagem? E já pensou no brutal poder de processamento que seria necessário para criar imagens semelhantes a esta e exibi-las na tela algumas dezenas de vezes por segundo para dar a impressão de movimento?

Bem, hão de dizer alguns, mas é exatamente neste campo que o computador reluz. E eu respondo: é verdade, mas quem afirma um negócio desses não chegou nem perto de compreender o grau de complexidade que uma boa renderização exige. Pense um pouco mais sobre o assunto até a próxima coluna, quando vamos discutir do que o Larrabee é capaz.

 

B. Piropo