• Aucun résultat trouvé

CHAPOIEAU ASSASSIN

Dans le document 1891 publiées par (Page 191-196)

A implementação do modelo especificado no capítulo 3 teve em conta vários aspectos, quer ao nível da codificação quer ao nível do objectivo pretendido. Nesse sentido, a implementação foi definida de uma forma modular, com o intuito de isolar possíveis influências não desejáveis nos resultados obtidos e provocados por uma dependência forte entre os distintos componentes de software que compõem o sistema. A divisão estrutural ou funcional utilizada para implementação, e já descrita no modelo proposto, é a seguinte:

• Primitivas básicas (baixo nível) de computação gráfica; • Primitivas de alto nível não recursivas;

• Primitivas de alto nível recursivas e de gestão geral do processo de

rendering

;

• Primitivas de gestão dos contentores, recursos e comunicações entre os diversos elementos do sistema.

Esta definição ou estruturação também facilita o intuito de criar e isolar os diferentes elementos presentes entre distintas implementações bem com também facilita a comparação entre implementações, como seja o caso da implementação do

renderer

com o CPU sem HRA e a implementação do

renderer

em conjugação com HRA e outros recursos de processamento.

Numa vertente mais prática da implementação, a plataforma de desenvolvimento utilizada teve por base a linguagem C (ANSI C), com recurso ao compilador “GNU C compiler” numa plataforma Linux, nomeadamente devido à sua portabilidade e suporte em múltiplas plataformas. Para a placa gráfica foram utilizados kits de desenvolvimento providenciados pelos fabricantes das placas gráficas utilizadas (Nvidia 2008). Existem, no entanto, kits de desenvolvimento que podemos denominar de “mais avançados e genéricos” que permitem a programação directamente em linguagem C para as placas gráficas da Nvidia (Nvidia 2008).

Esta divisão modular também providenciou uma forma simples de desenvolver um

renderer

minimalista, isto é, sem recorrer necessariamente à implementação de algoritmos complexos de

renderering

, mas sem comprometer uma possível implementação com algumas opções avançadas, dado que foram implementados um conjunto alargado gama de primitivas de

rendering

, nomeadamente de baixo nível e de alto nível não recursivo. Neste sentido, é possível construir um

renderer

personalizado, bastando para isso implementar o algoritmo das primitivas de alto nível recursivas e de gestão geral do processo de

rendering

pretendido 9.

4.2.1 Dados básicos e estruturas simples

Como já tinha sido referido no capítulo 3, no campo de questões práticas, a representação dos dados, mesmo dos dados compostos ou designados de complexos, assenta num tipo de dado de base comum. Os trechos de código apresentados da Figura 33 e da Figura 34 são exemplo da representação de diversos elementos usuais na implementação de um

renderer

recorrendo a um linguagem funcional e estruturada, como o caso da linguagem C que foi utilizada nesta implementação.

typedef double Real; /* base type value */ typedef Real Coord;

typedef Real Interval[2]; typedef Coord Vector2D[2]; typedef Coord Vector3D[3]; typedef Coord Point2D[3]; typedef Coord Point3D[3]; typedef Real Color; typedef Color RGB[3];

Figura 33 – Código ilustrativo da definição de tipo de dados de base

Como é possível verificar na Figura 33, as estruturas de base representativas de dados ditos primitivos são derivados do tipo real e sobre a forma de matriz, devido essencialmente a questões de alinhamento de dados entre diversas plataformas e recursos distintos de hardware. Quando os dados são definidos sobre a forma de uma matriz, todos os elementos da matriz são contíguos, enquanto quando se recorre uma estrutura com dados homogéneos não é possível garantir o mesmo alinhamento dos referidos dados em distintas plataformas. As estruturas de dados com campos heterogéneos não implicam qualquer tipo de alinhamento, como se pode verificar pelo código apresentado na Figura 34.

typedef struct Ray {

Point3D o; /* point of origin */ Vector3D d; /* direction of the ray */ } Ray;

typedef struct Cache {

Real where; /* where ray intercept */ Vector3D normal; /* normal in interception point */ Ray *ray; /* which ray intercepted */ RGB color; /* what color there */ } Cache;

4.2.2 Programação modular

Considerando ainda o código apresentado nas Figura 33 e Figura 34, é possível verificar uma hierarquia de complexidade crescente, isto os dados de complexidade média são representados por matrizes de dados simples, as estruturas de maior complexidade do sistema são composta por estruturas heterogenias de complexidade média e possivelmente baixa.

Ainda relativamente ao aspecto modular, é possível verificar que foram declaradas como genéricos os identificadores para as estruturas de dados complexas e respectivas funções de processamento, que desta forma minimizam o conhecimento da estrutura interna de determinados tipos de dados. Os trechos de código apresentados nas Figura 35 e Figura 36, apresentam esta abordagem, processamento genérica, recorrendo a apontadores do tipo

void

para os elementos pretendido. De certa forma é a abordagem parcialmente seguida em linguagens orientadas aos objectos, mas recorrendo ao C standard.

typedef struct Object {

void *data;

int (*hit)(void *data, Ray *ray); } Shape;

Figura 35 – Código (declaração) ilustrativo da relativa independência dos dados ao alto nível

No caso do trecho de código contemplado pela Figura 36 é apresentada a primitiva (função) que adiciona uma unidade de trabalho, na realidade um item genérico, a um contentor já definido e já configurado no início do funcionamento do sistema de

rendering

.

Em termos mais descritivos o dado “data” é adicionado ao fim da lista. O código associado aos

mutexes

, no inicio e final da função, é utilizado para sincronização e controlo do contentor, pois este elemento (contentor) pode ser acedido por distinto elementos (

threads

) em funcionamento no sistema.

int AddBinNodeData(Bin bin, void *data) { int status = 0; BinNode *node; if (bin) { pthread_mutex_lock(&(bin->mutex));

if (bin->size >= bin->high) /* bin almost full */ pthread_cond_signal(&(bin->work));

if (BinFull(bin)) /* if bin full wait */ pthread_cond_wait(&(bin->empty), &(bin->mutex));

node = bin->free; /* get a node from the free list */ bin->free = node->next;

node->data = data; node->next = NO_LINK;

if (bin->first == NO_LINK) /* bin empty then last is also first */ bin->first = node;

else /* if not update the actual last */ (bin->end)->next = node; bin->end = node; (bin->size)++; status = 1; pthread_mutex_unlock(&(bin->mutex)); } return (status); }

Figura 36 – Código (função) ilustrativo da relativa independência dos dados a alto nível

Como já foi referido a abordagem utilizada, de alguma forma na linha da programação orientada aos objectos, permite uma fácil adaptação do código e dos dados representativos das estruturas para diferentes recursos, bem como a alteração da representação geométrica, por exemplo. Desta forma os módulos definidos limitam a necessidade, em grande parte, da dependência do conhecimento em pormenor da estrutura e funcionamento interno de outros módulos que possam utilizar ou com quem possam interagir.

A título de exemplo, um módulo implementado e designado por “shapes”, que combina as formas geométricas de representação standard (triângulos, cones, cilindros, cubos, polígonos,

meshs

, etc), pode ser facilmente substituído por outro

módulo, por exemplo o designado por “nurbs” (

Non Uniform Rational Basis Spline

). Neste último caso, a representação da geometria dos objectos é efectuada através de curvas e superfícies paramétricas. Para um funcionamento correcto do novo módulo, este deve implementar com a mesma interface (nomes e parâmetros das primitivas implementadas).

Ainda é possível misturar diversas representações ou criar um novo conjunto de representações, desde que estas apresentem uma interface semelhante à definida para o nível superior. Desde que implementem a mesma funcionalidade mas como base em primitivas e dados diferentes, o HRA e o

renderer

principal não impõem qualquer outro tipo de condicionalismos ou restrições.

A estrutura da representação dos dados em camadas e a reduzida dimensão das primitivas permitem, com alguma facilidade, transformar e adaptar o código, e as representações para distintos e diversos recursos e plataformas, ou para novos paradigmas no campo do

rendering

.

Num futuro desenvolvimento poder-se-á definir um sistema de inserção dinâmica de módulos, isto é, em tempo de execução, para sistemas de

rendering

em funcionamento ou produção continua (

dynamic linking

((FSF) 2008)). No sistema presentemente implementado, a inserção e remoção de módulos é feita de uma forma estática, ou seja, é necessário recompilar e ligar (

link

) ((FSF) 2008) o programa em questão sempre que sejam feitas alterações ou modificação dos módulos que o compõem.

Dans le document 1891 publiées par (Page 191-196)