O shader OSL

Nessa última semana consegui avançar de maneira satisfatória no projeto. Primeiramente, implementei o shader sh_osl que é chamado pelo aplicativo rt e usa os serviços do sistema do OSL.

No post anterior mencionei que o sistema OSL exigia muitos raios por pixel e isso seria um problema ao usar o rt, mas descobri que uma opção de hypersampling que faz exatamente isso.

De maneira simplista, temos então que o aplicativo rt atira raios através da função rt_shootray e que chama uma callback chamada shadeview toda vez que um objeto é atingido. A função shadeview chama outra callback que está associada ao objeto e corresponde ao shader dele.

Por exemplo, se o objeto atingido tem o shader sh_glass, então esse objeto possui um ponteiro para a função glass_render. O que a função shadeview faz é chamar a função referenciada por esse ponteiro.

O que a função glass_render ou qualquer outra função xyz_render deve fazer é essencialmente retornar a cor do ponto de consulta (passado como parâmetro). Vou descrever então como implementei a função osl_render, correspondendo ao shader sh_osl.

Reflexão

Primeiramente, implementei a reflexão através de uma chamada recursiva da função rt_shootray, que atira um novo raio em uma dada direção. Essa parte foi fácil pois o código que eu tinha desenvolvido para o raytracer independente que eu havia implementado fazia do mesmo jeito.

A figura a seguir é a renderização da cena da caixa de cornell usando um shader osl de espelho para a caixa maior e um shader brl-cad de xadrez para a caixa menor.


Figura 1: Teste de integração de um shader OSL e um shader BRL-CAD

No final das contas não tive que tomar nenhum cuidado adicional ao misturar shaders OSL e BRL-CAD, ao contrário do que eu havia dito no post anterior.

Refração

Implementar a refração foi mais complicado. Primeiramente, descobri que o detector de colisões do BRL-CAD sempre calcula dois pontos de interseção para cada superfície. Um, chamado inhit, é o ponto da superfície no qual o raio bate inicialmente. O outro, chamado outhit, supõe que o raio foi atirado de dentro da superfície.

A função osl_render já é chamada com o ponto de interseção P equivalente a inhit, pois quem seta isso é o shadeview. Entretanto quando o raio é de refração (interno) eu quero que P seja o outhit.

Portanto, tive que escrever uma nova callback para tratar raios refratados. Sempre que um raio for retornado pelo OSL, verifico se ele é refratado. Para isso, basta fazer um produto escalar entre a normal e o novo raio para ver se eles apontam em direções “opostas”. Em caso positivo, mudo a callback que será chamada a próxima vez que um objeto for atingido no rt_shootray. O que essa callback faz é setar P como outhit e chamar xyz_render.


Figura 2: Teste com o shader vidro

Implementando novos shaders para o OSL

Para verificar se a interface sh_osl está computando os dados corretamente, decidi criar novos shaders que utilizam esses dados. Um exemplo é o shader xadrez, que usa as coordenadas do mapeamento uv.

Aproveitei para implementar suporte aos grupos de shaders, comentado em um post anterior, além da possibilidade de setar os parâmetros via a interface mged.

Agora é possível definir um grupo de shaders através da própria interface. O grupo de shaders é então construído a partir de shaders primitivos. Por exemplo a descrição do shader

shadername gen_color#layername#c1#base#color#1.0#0.0#1.0
shadername gen_color#layername#c2#base#color#0.0#1.0#0.0
shadername checker#layername#chk#K#float#4.0
join c1#Cout#chk#Cw
join c2#Cout#chk#Cb

foi usada no caixa menor, como ilustra a imagem abaixo:


Figura 3: Teste com shader xadrez verde-magenta

A sintaxe ficou meio feia mas está funcional. A primeira linha descreve um shader de cor genérica inicializado com a cor magenta enquanto o da segunda linha possui a cor verde. O terceiro shader descrito é o shader xadrez propriamente dito, que usa a saída de dois outros shader’s para compor a cor dos quadrados.

A quarta e quinta linhas tratam de ligar a saída do primeiro e segundo shader à entrada do shader xadrez.

A vantagem de descrever shaders dessa maneira, é que se por exemplo eu quiser compor o shader xadrez com uma cor verde e com um shader de espelho é só mudar para

shadername mirror#layername#c1
shadername gen_color#layername#c2#base#color#0.0#1.0#0.0
shadername checker#layername#chk#K#float#4.0
join c1#Cout#chk#Cw
join c2#Cout#chk#Cb

Que o resultado será:


Figura 4: Teste com shader xadrez verde-espelho

Além do mais, a parede do fundo da Figura 3 usa o shader “nuvem” adaptado de um shader BRL-CAD. Não parece nem um pouco com nuvem, mas é uma textura procedural, então não dá pra fazer milagre.

Próximos passos

Preciso resolver a questão do multi-threading. Semana que vem pretendo estudar como funcionam as funções de aquisição e liberação de recursos providas pelo BRL-CAD e qual recurso do sistema OSL eu preciso garantir acesso exclusivo.

Mais pra frente gostaria de implementar um modo de visualização incremental. Atualmente se quisermos visualizar a imagem enquanto ela é renderizada, temos que esperar todas as amostragens serem feitas para cada pixel antes de vê-lo. Minha ideia é que a cada amostragem, toda a imagem seja atualizada, de forma que inicialmente vejamos um monte de pixels dispersos e conforme mais e mais amostragens sejam feitas, a imagem vá convergindo para uma sem ruídos.

Idealmente, gostaria também de implementar uma interface para fazer a composição do grupo de shaders OSL. A GUI do BRL-CAD é escrita em Tcl, linguagem que eu teria que estudar antes de mais nada. Creio que não conseguirei fazer isso antes do término do programa, mas pretendo fazer isso algum dia.

Os comentários estão fechados.

%d bloggers like this: