Shaders para o BRL-CAD

Como eu já havia dito anteriormente, o BRL-CAD é um software de modelagem através de geometria construtiva sólida. Essa modelagem consiste em, dado um conjunto qualquer de primitivas básicas como cubo, esfera, cilindo, etc., construir objetos mais complexos baseando-se em operadores tais como união, interseção e diferença.

A vantagem desse tipo de modelagem é que a descrição do objeto fica bem compacta, bastando guardar os parâmetros das primitivas e as operações feitas sobre elas. A desvantagem é que os objetos que podem ser construídos dessa forma são bem limitados.


Exemplo de objeto construído a partir de cones, esferas e cubos.

Renderizando um objeto no BRL-CAD

Uma das ferramentas do BRL-CAD é chamada de mged. Ela consiste de um terminal e uma janela de visualização. Nesse terminal, o usuário pode digitar comandos para criação de objetos primitivos, realizar operações entre objetos, etc. Os objetos criados podem ser visualizados na outra janela. A figura abaixo destaca o wireframe de uma taça criada através de comandos no terminal.

Screenshot da interface do programa mged.

Depois de modelado, é possível renderizar a cena através de raytracing. A figura abaixo é resultado da renderização com uma textura padrão, o phong shader (modela a textura de plástico).

Objeto renderizado usando o shader de plástico.

Pela própria interface mged dá para atribuir shaders para o objeto. Alguns deles são: plástico, espelho, vidro, xadrez, nuvem, luz, etc. Pelo pouco que experimentei esses shaders não são muito realistas (ou eu não estou usando direito :P). Abaixo segue uma imagem aplicando o shader de vidro à taça, sobre um plano laranja, com uma fonte de luz e com background preto.

Teste com shader de vidro.

Estudando os shaders do BRL-CAD

Agora que já aprendi como testar um shader visualmente, é hora de estudar o código de um deles. O mais simples é o null shader, simplesmente porque ele não faz nada! Ele serve para termos uma ideia das interfaces que devem ser implementadas. A maior parte do BRL-CAD, incluindo seus shaders, é escrita em C.

struct mfuncs null_mfuncs[] = {
    {MF_MAGIC, "null", 0, MFI_HIT, 0,
     sh_null_setup, sh_null_render,
     sh_null_print, sh_null_free },

    {MF_MAGIC, "invisible", 0, MFI_HIT, 0,
     sh_null_setup, sh_null_render,
     sh_null_print, sh_null_free },    

    {0, (char *)0, 0, 0, 0, 0, 0, 0, 0}
};

Cada shader deve preencher um array de estruturas como as do código acima, e este array deve ser terminado pela estrutura nula. Cada estrutura dessa representa uma interface para o programa principal. O segundo parâmetro (preenchido com “null” e “invisible” no exemplo) representa o nome pelo qual o shader será referenciado.

A função sh_null_setup será chamada no começo do programa e deve inicializar o shader; A função sh_null_render é chamada cada vez que um raio do raytracer bater em uma superfície com esse shader e deve preencher a cor no ponto de interseção; A função sh_null_print é chamada sempre que um erro acontece e deve imprimir uma mensagem de debug; Finalmente, sh_null_free libera eventual memória alocada.

Se fosse em C++, poderíamos definir uma classe base para mfuncs e cada elemento do array acima poderia ser uma classe derivada dela. Note como sh_null_setup é bem o que um construtor costuma fazer e sh_null_free faz as vezes do destrutor.

Partindo para um shader não trivial, fui olhar o código do Toon Shader que gera imagens não foto-realistas, com aparência de desenho animado, conforme mostra a figura abaixo:


Teste com o shader ‘toon’

Para entender seu funcionamento, temos que falar sobre a lei dos cossenos de Lambert:

Quanto maior o ângulo entre a luz e a normal no ponto sendo avaliado, menor intensidade de luz difusa será refletida. Isso pode ser notado pelos gradientes na imagem da taça de plástico. O que o toon shader faz é discretizar esse gradiente, dividindo-o em buckets. Note que na figura da taça com shader toon, é possível ver as divisões entre as diferentes tonalidades de cinza.

Desenvolvendo um novo shader para o BRL-CAD

Minha tarefa era desenvolver um shader do tipo polka dot ou do tipo zebra. Optei pelo polka dot, cuja textura lembra aqueles vestidos de bolinha:

Pesquisando um pouco sobre como implementar tal shader, descobri um escrito para o RenderMan. Nele há duas variáveis s e t que não foram passadas como parâmetro, então imagino que sejam globais.

Apesar dos nomes s e t, pareceu ter relação com UV mapping . Eu já havia ouvido falar disso quando mexia com o Blender. A ideia é mapear a superfície de um objeto 3D em um plano 2D. Um exemplo clássico desse mapeamento é a projeção do globo terrestre em mapas 2D.

Da mesma forma que mapas, há diversas maneiras de se fazer o mapeamento e nenhuma delas é necessariamente melhor. No BRL-CAD o mapeamento é feito de maneira automática pelo sistema de shaders. Um shader já existente é a da textura Xadrez. Renderizei a imagem da taça com esse shader:


Teste com shader xadrez.

O sistema de shaders do BRL-CAD fornece as coordenas u e v correspondente à projeção no plano. O que eu fiz foi substituir essas cordenadas pelo s e t do shader do RenderMan. O resultado ficou o seguinte:

Teste com shader ‘polka dot’.

A shader não ficou nada bom. As bolinhas estão distorcidas. Porém, meus mentores do projeto disseram que é o suficiente. O importante era que eu aprendesse o básico da criação dos shaders. Como acessar as variáveis globais, como compilar, etc.

Próximos Passos

Conversando com os mentores, decidimos que o próximo passo será desenvolver um shader para interfacear com o sistema de shaders do OSL. Creio que com conhecimento adquirido estudando o raytracer usando OSL e desenvolvendo um shader para o BRL-CAD, dá para realizar essa tarefa.

Os comentários estão fechados.

%d bloggers like this: