Shaders – Pipeline Programável
Introdução
Até pouco tempo atrás, pelos idos de 2003, predominava a renderização gráfica pelas GPUs através do pipeline fixo, pelo qual passa todo o dado de uma aplicação gráfica 3D que usava efetivamente uma placa de vídeo. Apesar de rápido e com a velocidade crescendo a cada dia, tal processo apresentava limitações, uma vez que não era possível ter um controle fino sobre o dado que era passado pelo pipeline, sendo que estes se sujeitavam aos algoritmos internos realizados na renderização e presentes dentro da própria GPU. Com a necessidade de realismo e desempenhos combinados crescendo sempre, motivada pela demanda dos consumidores quer sejam eles usuários comuns de aplicações gráficas, quer sejam pesquisadores científicos, muitos métodos haviam surgido para aumentar o realismo na imagem, vários deles através da renderização de multipasse. Entretanto, isto não foi o suficiente e a disputa comercial aliada aos rápidos avanços na área de computação gráfica, tiveram como conseqüência o surgimento dos shaders, ou seja, o nascimento das primeiras placas de vídeo com pipeline programável.
Destas placas a primeira placa de vídeo com alguma presença de shaders foi a Geforce 3 TI que possibilitava ao desenvolvedor programar um vertex shader. Obviamente, que como era uma tecnologia nova, ela enfrentava uma série de limitações que com o tempo foram superadas ou, minimizadas.
Seguindo o modelo criado pela Geforce 3 TI, outras placas de vídeo começaram a incorporar shaders em sua arquitetura, dando maior flexibilidade e controle do programador sobre os programas gráficos produzidos. Não demorou muito para que o DirectX 8 introduzisse suporte a shaders e para que o OpenGL 1.4 o fizesse também na forma de uma extensão padronizada pela ARB (Architeture Review Board), organização que era responsável na época pela decisão quanto aos rumos do OpenGL. Contudo, ainda que tal flexibilidade fosse possível, o código para que fosse possível manipulá-la era um tipo de Assembly, padronizado pela ARB. Porém, ainda que padronizado o assembly da placa de vídeo, como de qualquer outro hardware, é programação em baixo nível, sendo propensa a erros. Dessa dificuldade surgiram pesquisas que culminaram nas invenções das linguagens de shading de alto nível CG, HLSL, GLSL, as quais baseavam suas estruturas no C e, conseqüentemente retiravam uma grande parte da complexidade da implementação, bem como, diminui o tempo de aprendizagem da linguagem, por se basear numa linguagem amplamente conhecida e difundida, o C.
Assim desde 2003 até 2009, ano da presente publicação muitos avanços ocorreram como o aprimoramento dos pixeis e vertex shaders, a criação do shader de geometria, a unificação dos shaders nas placas de vídeo mais modernas e o amplo interesse acadêmico na GPU e o possível uso desta para uso não apenas de aplicações gráfica, mas de aplicações de propósito geral, GPGPU (General Purpose GPU).
O Pipeline Fixo
Toda a aplicação, seja ela OpenGL, seja ela DirectX, passa por um pipeline que vai desde a especificação de vértices e primitivas até a saída final dos pixels na tela. Este pipeline até a pouco tempo atrás era fixo, consistindo de diversas etapas: Especificação de vértices e primitivas, Transformações Geométricas e iluminação, Recorte (Clipping), Rasterização e Renderização.
Na parte da especificação de vértices e primitivas o programador entra com o modelo da cena, isto é, a representação do que está em cena. Tal modelo consiste de vértices, dados em coordenas 2d (z = 0) ou 3D. Além disso estes vértices são especificados dando origem a primitivos como Pontos, linhas, triângulos entre outros, que quando combinados dão origem aos demais objetos na tela como personagens, cenários, etc.
O próximo passo é o de transformação, no qual as transformações geométricas são aplicadas, podendo ser elas a rotação, translação e escalonamento, ou outras como o cisalhamento conforme o programador especifica.
Depois desta etapa ocorre a iluminação, aonde a cor é especificada por vértice. Valores intermediários ainda não são computados, sendo mais tarde interpolados entre vértices.
Em seguida, de acordo com o volume de visualização especificado pelo programador, os vértices são testados para ver se eles residem nesta área definida e, caso não, são descartados, poupando a GPU de calculá-los em passos posteriores do pipeline (Frustum Culling).
Após isso coordenadas de texturas são geradas e o objeto agora é transformado em pixels. Tal etapa é freqüentemente denominada de rasterização e seu resultado final são os pixels que vão ser desenhados na tela sendo gravados no framebuffer. Quando isto ocorre finalmente ocorre a renderização que é a representação visual do objeto na cena e, denomina-se renderização.
O Pipeline Programável (Shaders)
O pipeline programável nada mais é do que a repetição dos eventos que ocorrem no pipeline fixo, mas com um controle mais fino pela parte do programador, já que permite que este programe certos estágios do pipeline, alterando a saída que resultaria deste em sua versão fixa.
Para isto existem três tipos de shader. O vertex shader, o geometry shader e o pixel shader. Cada um deles será explicado mais adiante com suas características, a fim de expor a funcionalidade de cada um, deixando claro porquê é usado e como é usado.
Vertex Shader
O Vertex shader tem por responsabilidade substituir o processo de manipulação dos vértices presente no pipeline fixo, permitindo ao programador manipular as transformações geométricas, o posicionamento e cor dos vértices, bem como o processo de iluminação e geração de coordenas de textura na parte dos vértices.
Diante de suas responsabilidades o vertex shader é extensivamente usado, estando presente nos mais variados tipos de algoritmos aplicados ao programa em si e, portanto, tem importância fundamental no contexto do pipeline programável. Frente a esta posição ele vêm sofrendo constantes avanços, evoluindo junto e, em paralelo, aos outros shaders, à medida que o hardware evolui.
Compute Shaders
Hull Shader
Domain Shader
Geometry Shader
O Geometry Shader não tem correspondência no pipeline fixo de renderização. Sua principal função é a de criar ou remover vértices, permitindo um maior controle na geometria que passa pelo pipeline programável, aumentando ou diminuindo o nível de detalhe de acordo com o necessário, permitindo com que uma enorme variedade de efeitos sejam possíveis, como melhor interpolação de vértices com a adição de novos vértices quando necessário, renderização de silhueta, entre outros.
Apesar de ser flexível e capaz de uma série de efeitos interessantes o Geometry Shader é relativamente novo, sendo incluído na API DirectX em sua versão 10, no ano de 2006, este shader é ineficiente se comparado com os outros e deve ser usado cuidadosamente para que se tire proveito de seus benefícios e o resultado seja um shader eficiente em sua tarefa.
Pixel Shader(Fragment Shader)
Linguagens de Colorização (Shading)
Para dar mais poder de expressão e flexibilidade aos artistas e programadores as linguagens de shading foram inventadas. As primeiras delas eram offline, uma vez que para a época (1995) era proibitiva a renderização em tempo real com código de shading programável. Assim softwares como o Renderman e XXX foram inteiramente produzidas para se utilizarem renderização por software. Com o tempo foram inventadas outras linguagens para o mesmo fim até que finalmente surgissem as linguagens de shading para shaders de Placas de vídeo renderizando em tempo real.
Destas linguagens as principais hoje são a CG, a HLSL e a GLSL.
CG
Esta linguagem nasceu do trabalho em conjunto da Microsoft com a Nvidia, dai a sua semelhança com a HLSL, linguagem de shading abordada mais adiante. Ela se destaca por ter estruturas semelhantes a do C para comandos condicionais, loops, tipos de dados e funções.
Apesar de sua criadora ser a Nvidia, a mesma liberou as especificações de seu compilador, sendo tal linguagem disponível em diversas placas de vídeo e, não somente da Nvidia. Além disso, os shaders criados por ela podem ser usados tanto em programas feitos por DirectX, quanto os feitos por OpenGL.
GLSL
Linguagem de Shading do OpenGL que se encontra na sua versão 4.1 atualmente. Ela é equivalente a HLSL do DirectX, porém é livre de plataforma como a CG, sendo parte integrante do núcleo do OpenGL a partir do OpenGL 2.0..
HLSL
Linguagem de Shading que surgiu com o DirectX 9.0. A HLSL é uma linguagem parecida com CG, resultado da parceira de desenvolvimento de uma linguagem de shading entre Microsoft e NVidia. A HLSL está atualmente na sua versão 5.0, a qual acompanha o DirectX 11.
Assembly (Shading Language)
Nos primórdios do desenvolvimento dos shaders, o DirectX 8 pela parte da Microsoft e o OpenGL, através de extensões da ARB, tiveram linguagens assemblies especificadas para se programar as primeiras placas de vídeo programáveis desenvolvidas. Contudo, devido a dificuldades inerentes de uma linguagem de baixo nível como o assembly, esta linguagem caiu em desuso em favor de linguagens de alto nível como CG e GLSL.
Livros
The Cg Tutorial
Um livro produzido pela NVidia a respeito de sua linguagem Cg, abordando diversos exemplos do que pode ser feito com esta linguagem de tonalização
link: http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter01.html
GPU Gems 1
Livro escrito pela NVidia com muitos exemplos sobre o que é possível fazer com shaders
link: http://http.developer.nvidia.com/GPUGems/gpugems_part01.html
GPU Gems 2
Livro escrito pela NVidia com muitos exemplos sobre o que é possível fazer com shaders
http://http.developer.nvidia.com/GPUGems2/gpugems2_part01.html
GPU Gems 3
Livro escrito pela NVidia com muitos exemplos sobre o que é possível fazer com shaders
http://http.developer.nvidia.com/GPUGems3/gpugems3_part01.html
GPU Pro: Advanced Rendering Techniques
Fazer Descrição
link: GPU Pro na Amazon
GPU Pro 2
Fazer Descrição
link: GPU Pro 2 na Amazon