Você está na página 1de 22

Efeitos Título

Inserir SonorosAqui
e
Inserir Título
Visuais para Jogos
Aqui
Efeitos Visuais para Jogos

Responsável pelo Conteúdo:


Prof. Me. André Kishimoto

Revisão Textual:
Prof. Me. Luciano Vieira Francisco
Efeitos Visuais para Jogos

Nesta unidade, trabalharemos os seguintes tópicos:


• Pipeline de Renderização;
• Graphics Processing Unit (GPU) e Shaders

Fonte: iStock/Getty Images


Objetivos
• Entender como funciona a pipeline de renderização e os conceitos de shaders;
• Escrever código em linguagem de shading para criar efeitos visuais.

Caro Aluno(a)!

Normalmente, com a correria do dia a dia, não nos organizamos e deixamos para o
último momento o acesso ao estudo, o que implicará o não aprofundamento no material
trabalhado ou, ainda, a perda dos prazos para o lançamento das atividades solicitadas.

Assim, organize seus estudos de maneira que entrem na sua rotina. Por exemplo, você
poderá escolher um dia ao longo da semana ou um determinado horário todos ou alguns
dias e determinar como o seu “momento do estudo”.

No material de cada Unidade, há videoaulas e leituras indicadas, assim como sugestões


de materiais complementares, elementos didáticos que ampliarão sua interpretação e
auxiliarão o pleno entendimento dos temas abordados.

Após o contato com o conteúdo proposto, participe dos debates mediados em fóruns de
discussão, pois estes ajudarão a verificar o quanto você absorveu do conteúdo, além de
propiciar o contato com seus colegas e tutores, o que se apresenta como rico espaço de
troca de ideias e aprendizagem.

Bons Estudos!
UNIDADE
Efeitos Visuais para Jogos

Contextualização
Na época em que os jogos digitais começaram a ser desenvolvidos, os gráficos consistiam
de simples blocos na tela. Com o avanço da tecnologia, tais blocos deram lugar a imagens
criadas por artistas e, depois, o visual do jogo começou a ser criado a partir de modelos tri-
dimensionais (3D), processados e exibidos em tempo real, enquanto o jogo era executado.

Com o passar dos anos, o processamento gráfico começou a ser realizado por hardware
dedicado – com o uso de Graphics Processing Unit (GPU) –, sendo que atualmente é possível
escrever código específico para GPU, definindo o que o processador gráfico deve fazer – seja
simplesmente exibir modelos 3D e imagens ou aplicar efeitos especiais no visual do jogo.

Os códigos que rodam no hardware gráfico são chamados de shaders. Atualmente há


ferramentas que permitem criar shaders de forma visual, sendo que tais ferramentas geram
o código final. Porém, em muitos projetos há pessoas encarregadas de escrever, reescrever
e alterar os shaders via código, a fim de que estejam adequados ao projeto. Nesse sentido,
é importante entender como o processamento gráfico é realizado e como os shaders fun-
cionam e são escritos.

6
Pipeline de Renderização
Para aplicar efeitos visuais em jogos ou aplicações com renderização em tempo real, é
importante entender o que é a pipeline de renderização – graphics rendering pipeline – ou
simplesmente pipeline.

A função principal da pipeline é gerar – renderizar – uma imagem bidimensional a partir


de uma combinação de câmera virtual, objetos tridimensionais (3D), fontes de luz, texturas e
qualquer elemento que seja usado para obter a imagem final na tela.

Antes de continuarmos, talvez você esteja se perguntando: “O que é uma pipeline?” Podemos
citar um oleoduto, uma linha de produção de fábrica ou um metrô como exemplos de pipeline
do mundo real. Pensando nesses casos, podemos observar que uma pipeline – “linha de tubu-
lação” – consiste de vários estágios e que há um fluxo – de alguma coisa, como petróleo, gás,
produto elaborado na fábrica, pessoas etc. – entre os estágios.

Na pipeline de renderização existem três estágios conceituais (Figura 1): aplicação –


application –, geometria – geometry – e rasterização – rasterizer. Essa estrutura é o núcleo
– core –, ou o “motor gráfico”, usado em aplicações com renderização em tempo real.

Aplicação Geometria Rasterização


Figura 1 – Estágios da pipeline de renderização.
Fonte: elaborada pelo professor conteudista

Pensando no fluxo que ocorre na pipeline, podemos acrescentar duas informações na


Figura 1: a entrada de dados antes da aplicação – os dados que são carregados na memória
pela aplicação – e a saída da pipeline após a rasterização, conforme a Figura 2:

Entrada
(cena 3D)

Aplicação Geometria Rasterização

Saída
(imagem)

Figura 2 – Entrada e saída da pipeline de renderização.


Fonte: elaborada pelo professor conteudista

Renderização
No fluxo apresentado na Figura 2, a saída da pipeline está indicada como uma imagem.
Ao estudarmos ou trabalharmos com computação gráfica, é comum ouvir a frase “renderi-
zar uma cena 3D” – ou os termos renderização, renderizar, render.

7
7
UNIDADE
Efeitos Visuais para Jogos

O que significa “renderizar uma cena 3D”? Fazendo uma analogia com fotos: para tirar
uma fotografia, precisamos de uma câmera, seja analógica ou digital. Com esse equipamento,
conseguimos capturar as luzes e gerar uma imagem. O processo de renderizar uma cena
3D é similar: precisamos de uma câmera virtual para decidir quais elementos da cena 3D
aparecerão na imagem.

Uma cena tridimensional é composta de geometria – polígonos, arestas, vértices –, fon-


tes de luz, propriedades – materiais – da geometria e texturas. A geometria é composta por
triângulos, sendo que os vértices possuem uma posição – x, y, z – e podem incluir – vetores
– normais e outras informações.

Já a câmera virtual é definida por seis propriedades: posição – x, y, z – no espaço; vetor


direção – para onde a câmera olha –; vetor up – o lado superior da câmera –; campo de
visão – ângulo de abertura, chamado também de Field of View (FOV) –; e os planos near e
far, conforme o esquema da Figura 3:

Figura 3 – Câmera virtual (o vetor up não consta neste esquema).


Fonte: adaptada de Akenine-Möller (2003)

A imagem renderizada pelo computador é criada a partir da geometria existente na re-


gião cinza da Figura 3, isto é, os planos near e far definem o volume de visão da câmera,
junto ao seu campo de visão e direção.

A velocidade de renderização é definida pelo estágio mais lento da pipeline e pode ser
expressa em Frames per Second (FPS), que é a quantidade de imagens renderizadas por
segundo, ou em hertz (Hz), que é a frequência de atualização da tela/display – hardware.

O estágio mais lento da pipeline é chamado de bottleneck – gargalo – e, portanto,


define a velocidade máxima de renderização.

Nos próximos tópicos veremos as descrições de cada estágio da pipeline.

Estágio de Aplicação
O estágio de aplicação é onde a lógica do jogo – ou aplicativo – ocorre e o processamen-
to é tradicionalmente realizado pela CPU, sendo que o programador tem controle total sobre
o que acontece nesse estágio. Ademais, são operações comuns nessa etapa: detecção de
colisão, técnicas de otimização, animação e simulação física.

8
Nesse estágio, os dados da cena 3D são carregados. Por exemplo, modelos tridimensionais que
existem em disco, tais como arquivos nos formatos fbx e obj, são lidos e seu conteúdo carregado
na memória RAM.

No final do estágio de aplicação, a saída de fluxo de dados que segue a pipeline é com-
posta por primitivas – vértices, arestas, triângulos. Assim, a tarefa mais importante do estágio
de aplicação – em termos de renderização/gráfico – é enviar as primitivas para o hardware
gráfico – Graphics Processing Unit (GPU).

Estágio de Geometria
O estágio de geometria é responsável por operações geométricas nos dados de entrada
– as primitivas que foram enviadas pelo estágio de aplicação – e permite realizar operações
como mover objetos e câmeras – multiplicação de matrizes –, calcular iluminação nos vér-
tices dos triângulos, realizar a projeção da cena 3D em um espaço 2D, realizar o clipping
– eliminar triângulos fora da visão – e mapear a cena para a janela onde será exibida.

O estágio de geometria possui os seguintes subestágios: transformação de modelo e visão


– model and view transform –; shading de vértices – verterx shading, sombreamento no
sentido de tonalidade –; projeção – projection –; corte – clipping –; e mapeamento na tela –
screen mapping –, conforme se vê na Figura 4:

Model and View Vertex Screen


Transform Shading Projection Clipping
Mapping

Figura 4 – Subestágios do estágio de geometria.


Fonte: elaborada pelo professor conteudista.

Transformação de Modelo
Originalmente, um objeto está no espaço – coordenada – do modelo 3D. Nesse subestágio,
o objeto é transformado ao espaço do mundo. Por exemplo, considerando uma esfera com
origem em (0, 0, 0) e escala 1, qualquer operação de translação, rotação e escala fazem a
esfera aparecer em outro lugar.

Transformação de Visão
A câmera virtual e todos os objetos são convertidos ao espaço do mundo. Em seguida, a
câmera é posicionada na origem do mundo, apontando para dentro da tela, usando o eixo Z
negativo ou o eixo Z positivo – a direção do eixo Z depende da API gráfica. O eixo X positivo
aponta para a direita e o eixo Y positivo aponta para cima. Assim, há transformação de visão,
onde a câmera e os objetos são convertidos ao espaço da câmera ou ao “espaço do olho” –
camera space ou eye space. A Figura 5 mostra o resultado da transformação de visão:

Figura 5 – Transformação de visão.


Fonte: Akenine-Möller, Haines e Hoffman (2008, p. 17)

9
9
UNIDADE
Efeitos Visuais para Jogos

Vertex Shading
A operação de vertex shading determina o efeito de uma fonte de luz na superfície
do objeto, realizando operações de cálculo de iluminação nos vértices do modelo.

Projeção
Neste subestágio, a projeção transforma o volume de visão em um cubo unitário, sendo
que há duas projeções comumente usadas em computação gráfica: ortogonal ou ortográfica –
ou ainda paralela, lado esquerdo da Figura 6 – e perspectiva – lado direito da Figura 6 –, mais
comum e que simula como vemos o mundo, isto é, o tamanho dos objetos diminui conforme
se distanciam do observador – câmera.

Figura 6 – Projeções ortográfica (à esquerda) e perspectiva (à direita)


Fonte: Akenine-Möller, Haines e Hoffman (2008, p. 18)

Corte
Com o cubo unitário criado após a projeção, o subestágio de corte – clipping – define
quais primitivas devem ser processadas. As primitivas que estão dentro do cubo não mudam;
as totalmente fora do cubo são descartadas; e as que possuem parte dentro e parte fora do
cubo são cortadas no limite deste cubo, tal como mostrado na Figura 7:

Figura 7 – Subestágio de corte (clipping)


Fonte: Akenine-Möller, Haines e Hoffman (2008, p. 20)

10
Mapeamento na Tela
O subestágio de mapeamento na tela altera o cubo unitário do subestágio de corte, apli-
cando translação e escala, a fim de que o cubo se adapte à região de renderização – uma
janela na tela ou a tela cheia –, conforme a Figura 8:

Figura 8 – Mapeamento na tela


Fonte: Akenine-Möller, Haines e Hoffman (2008, p. 20)

Nesse estágio ocorre a conversão às coordenadas no espaço de tela – screen space –,


sendo que o eixo Z define a profundidade – z-depth. As coordenadas no espaço de tela e o
z-depth são enviados ao estágio de rasterização.

A Figura 9 contém um resumo sobre o estágio de geometria, criado pelo autor, pesquisa-
dor e professor Tomas Akenine-Möller (2003):

Figura 9 – Resumo do estágio de geometria


Fonte: Akenine-Möller (2003)

Estágio de Rasterização
O estágio de rasterização é responsável por transformar a saída do estágio de geometria
em pixels na tela – ou qualquer outro dispositivo de saída final –, além de adicionar
texturas, realizar outras operações em nível de pixel e resolver a ordenação de primitivas.
O estágio de rasterização possui subestágios, vejamos: configuração de triângulo – triangle
setup –, conversão de varredura – scan conversion –, shading de pixels – pixel shading,
sombreamento no sentido de tonalidade – e combinação de buffers – memória – e saída –
merging –, conforme a Figura 10:

11
11
UNIDADE
Efeitos Visuais para Jogos

Triangle Scan Pixel Merging


Setup Conversion Shading
Figura 10 – Subestágios do estágio de rasterização
Fonte: elaborada pelo professor conteudista

Configuração de Triângulo
Neste subestágio os dados da superfície do triângulo são calculados para serem usados no
próximo subestágio de conversão de varredura – scan conversion.

Conversão de Varredura
Este subestágio é responsável por encontrar os pixels que estão dentro de um triângulo,
aresta ou vértice, por meio da conversão de varredura – scan conversion –, conhecido tam-
bém por triangle traversal.

Um fragmento é gerado por pixel e as propriedades de cada fragmento do triângulo são


geradas pela interpolação dos dados dos três vértices de tal triângulo, incluindo a profundi-
dade e tonalização – shading – da geometria.

Pixel Shading
A operação de pixel shading realiza cálculos de tonalização em nível de pixel. O resultado
é uma ou mais cores que são passadas ao próximo estágio de merging. Uma das aplicações
nesse estágio de pixel shading é a texturização de superfícies.

Combinação
No último subestágio do estágio de rasterização ocorre a combinação – merging –
do conteúdo presente na memória. Ademais, a memória conhecida por color buffer
mantém a informação de cada pixel.

Nesse estágio, combina-se a cor do fragmento resultante do pixel shading com o color
buffer. Ocorre também o cálculo de visibilidade com o algoritmo z-buffer – depth buffer.

Além das informações contidas na memória do color buffer – cor – e depth buffer – pro-
fundidade –, há outras memórias e canais que podem ser usados para capturar a informação
dos fragmentos, tais como:
· Alpha channel: canal de transparência ou opacidade;
· Stencil buffer: memória offscreen – não é exibida na tela –, usada para efeitos
especiais, tais como máscara e espelho;
· Accumulation buffer: memória empregada para efeitos especiais, tais como
motion blur, depth of field, antialiasing e soft shadows;
· Frame buffer: geralmente consiste de todos os buffers do sistema;
· Double buffer: a renderização da cena é realizada de modo offscreen – back buffer.
Quando o render offscreen é concluído, o conteúdo do back buffer é copiado ao
front buffer – conteúdo onscreen, que é exibido na tela.

A saída do estágio de rasterização são as primitivas visíveis do ponto de vista da câmera


e que devem ser exibidas – renderizadas – na tela.

12
Graphics Processing Unit (GPU) e Shaders
Atualmente, as tarefas de processamento gráfico são realizadas por hardware dedicado,
por meio da GPU – Graphics Processing Unit, ou unidade de processamento gráfico –,
diferente do que acontecia há alguns anos, quando o processamento gráfico era realizado
via software. De acordo com Akenine-Möller, Haines e Hoffman (2008), a vantagem do
hardware dedicado é a velocidade, fator crítico para a renderização em tempo real.

Ademais, GPU evoluiu; começou com uma pipeline fixa em que era possível configurar
alguns estados e propriedades dessa e atualmente é programável, sendo que ainda existe a
configuração de alguns estados e propriedades da pipeline – mas programadores definem
também como alguns estágios da pipeline devem se comportar. A Figura 11 mostra a
pipeline da Vulkan API, sendo que os retângulos em azul são os estágios programáveis,
enquanto os com contornos de linhas tracejadas são opcionais.

Vertex Specification

Vertex Shader

Tessellation

Geometry Shader

Vertex Post-Processing

Primitive Assembly

Rasterization

Fragment Shader

Per-Sample Operations

Figura 11 – Pipeline de renderização da Vulkan API

A programação da pipeline de renderização é realizada via shaders, estes que são


programas de computador escritos em linguagem de shading para calcular efeitos visuais.
Fosner (2003) define que shader é um procedimento personalizado de tonalização
e iluminação que permite ao artista motivado ou programador especificar como um
vértice ou pixel será renderizado.

Posteriormente, os programadores começaram a usar GPU para aplicar outros tipos de


cálculos e processamentos – que antes eram realizados somente na CPU –, criando o
termo GPGPU – General Purpose GPU, ou GPU de propósito geral –, além de tecnologias
como OpenCL e Nvidia Cuda.

13
13
UNIDADE
Efeitos Visuais para Jogos

Shaders são programados em linguagens parecidas com a C e depois são convertidos para
uma linguagem de máquina – vejamos um histórico resumido sobre as linguagens de shading:
· RenderMan Shading Language (RSL): criada pela Pixar em 1988, é ainda
usada na produção de filmes, porém, o render não é em tempo real;
· C for Graphics (Cg): criada pela Nvidia em 2002 junto com a Microsoft. Foi
usada para render em tempo real, mas está obsoleta – legacy. A recomendação
da própria Nvidia é usar HLSL ou GLSL;
· High-Level Shading Language (HLSL): criada pela Microsoft em 2002 junto
com o Cg – Nvidia – para render em tempo real com Direct3D;
· OpenGL Shading Language (GLSL): criada pela OpenGL ARB – Architecture
Review Board – em 2004 para render em tempo real e em multiplataforma –
suportando OpenGL, OpenGL ES e WebGL;
· Metal Shading Language: criada pela Apple em 2012 para os dispositivos da
própria empresa; é baseada em C++;
· Vulkan/SPIR-V: Vulkan é uma API gráfica multiplataforma criada pelo Khronos
Group – o mesmo consórcio responsável pelo OpenGL, GLSL e OpenCL –;
suporta shaders escritos em Standard Portable Intermediate Representation
(SPIR-V); trata-se de padrão definido em 2015.

Os shaders mais comuns são vertex shader e fragment shader – este conhecido
também como pixel shader –, embora existam outros, tais como tessellation shader
e geometry shader, conforme já mostrado na Figura 11. Cada shader tem aplicações
específicas e seguem a ordem definida pela pipeline:
· Vertex shader: trabalha com dados dos vértices – tais como posição, cor,
vetor normal e coordenada UV de textura –, convertendo os dados de entrada
– que vêm do aplicativo – do espaço do modelo ao da tela, sendo que novos
vértices não podem ser criados neste estágio;
· Tessellation shader: separado em dois tipos de shaders, tessellation control
shader – ou hull shader – e tessellation evaluation shader – ou domain shader
–, o estágio de tessellation recebe um vetor de vértices – patches –, subdividindo-
os em primitivas menores. Dessa forma, é possível definir que objetos mais
próximos da câmera tenham maior nível de detalhe – da malha – e os mais
distantes tenham menos detalhes;
· Geometry shader: este tipo pode criar primitivas a partir das que foram enviadas
no início da pipeline;
· Fragment shader: conhecido também por pixel shader, calcula a cor final e pro-
fundidade de cada fragmento – pixel – que será exibido na tela, podendo realizar
pós-processamento/aplicar filtro na imagem a ser exibida.

Shaders e Unity
Embora a Unity e outras game engines forneçam shaders escritos pela própria equipe que
desenvolve as ferramentas, é interessante entender como funciona e como escrever um shader,
seja para personalizar o visual de seu jogo, seja para entender o que acontece na parte gráfica.

14
Todo shader na Unity é escrito usando uma linguagem que a empresa estabeleceu como
ShaderLab, esta que define propriedades que são expostas no editor da Unity – para que
variáveis do shader possam ser configuradas pelo editor – e subshaders –, de modo que cada
subshader pode ser usado para GPU específicas – que possuem passes, sendo que os códigos
em linguagem de shading – Cg/HLSL, no caso da Unity – são escritos dentro de cada passe.

A Figura 12 mostra exemplos de formas de propriedades que podem ser expostas no


editor, assim como a estrutura básica de um arquivo com extensão .shader da Unity:

Figura 12 – Estrutura do ShaderLab


Fonte: elaborada pelo professor conteudista

A sintaxe de uma propriedade é <propriedade> (“Nome no editor”, <tipo>) = <valor>.


Assim, quando uma propriedade é declarada na seção Properties, uma variável com o
mesmo nome precisa ser declarada na seção SubShader – dentro de um Pass e entre as
tags CGPROGRAM e ENDCG –, conforme mostrado na Figura 13:

15
15
UNIDADE
Efeitos Visuais para Jogos

Figura 13 – Variáveis com o mesmo nome das propriedades expostas no editor


Fonte: elaborada pelo professor conteudista

Ao renderizar um objeto, cada passe – pass – é executado durante a renderização e configura


o estado de renderização – cull, ztest, blend etc. O código do shader fica entre as tags CGPRO-
GRAM e ENDCG – linhas 23 e 38 da Figura 13, respectivamente.

A Figura 14 contém um exemplo simples de vertex shader e fragment shader que converte
o modelo 3D do seu espaço de modelo para o espaço da tela – no vertex shader – e renderiza
o modelo com uma cor sólida estabelecida pelo editor da Unity – no fragment shader.

Figura 14 – Exemplo de vertex shader e fragment shader


Fonte: elaborada pelo professor conteudista

16
A linha 36 define qual é a função principal do vertex shader. Neste exemplo, é a função
chamada vert(), que está no intervalo de linhas 49 a 55. A linha 37 define a função principal
do fragment shader. Neste exemplo, é a função chamada frag(), que está no intervalo de
linhas 57 a 60.

A estrutura appdata, definida entre as linhas 39 a 42, contém os dados que vêm do jogo
e são enviados ao vertex shader – parâmetro appdata v da função vert(). Neste exemplo,
a estrutura possui apenas uma variável denominada pos, que define a posição do vértice.

A estrutura v2f, definida entre as linhas 44 a 47, contém os dados que são enviados do
vertex shader ao fragment shader – retorno da função vert() e parâmetro v2f i da função
frag(). Neste exemplo, a estrutura também possui apenas uma variável pos que indica a
posição do vértice, este convertido ao espaço da tela pelo vertex shader.

O código do vertex shader recebe um parâmetro appdata, que é preenchido com os da-
dos do jogo. Com base neste parâmetro, o código transforma o vértice – v.pos – ao espaço
da tela e retorna um v2f – que é passado ao fragment shader – que contém a posição do
vértice convertido ao espaço da tela.

A função UnityObjectToClipPos() é fornecida pela Unity, sendo que o resultado é


o mesmo que multiplicar a posição do vértice por uma matriz conhecida como MVP
– Model-View-Projection –, realizando, assim, a conversão do espaço do modelo ao
espaço da tela.

Multiplicar a posição de um vértice no espaço do modelo pela matriz MVP é realizar a sequência
descrita no resumo da Figura 9.

Por fim, o fragment shader recebe um parâmetro v2f do vertex shader – neste
exemplo, o parâmetro não é usado pelo fragment shader – e retorna um half4 :
COLOR0, contendo um valor RGBA que define a cor do pixel na tela. Neste exemplo, o
fragment shader retorna a cor definida na propriedade “My Color”, via editor da Unity.

Após escrever o código do shader e salvar com a extensão .shader, vá ao editor da


Unity e crie um novo material (menu Assets | Create | Material).

Figura 15 – Associando o novo shader a um material


Fonte: elaborada pelo professor conteudista

Aplique o novo material a um modelo 3D – clicando e arrastando o arquivo do material


sobre o modelo que já está na cena. Em seguida, configure as propriedades do shader pelo
material, vejamos:

17
17
UNIDADE
Efeitos Visuais para Jogos

Figura 16 – Configuração das propriedades do shader pelo material


Fonte: elaborada pelo professor conteudista

Para se aprofundar mais no assunto, leia a documentação da Unity, em particular, os


seguintes tópicos do manual:
• Graphics, Graphics reference, Shader reference;
• Graphics, Graphics reference, Shader reference, ShaderLab syntax – e todas as
subseções;
• Graphics, Graphics reference, Shader reference, Writing vertex and fragment
shaders, Shader semantics.

18
Material Complementar
Indicações para saber mais sobre os assuntos abordados nesta Unidade:

Sites
KHRONOS GROUP
OpenCL Overview
https://goo.gl/wWiMAK
NVIDIA
CUDA zone
https://goo.gl/PU8eXK
PIXAR
Pixar’s RenderMan
https://goo.gl/BMHxb6
ZUCCONI
A. Tutorials
https://goo.gl/rvuBvj

19
19
UNIDADE
Efeitos Visuais para Jogos

Referências
AKENINE-MÖLLER, T. Advanced computer graphics EDA 425, lp 4. 2003.
(Lecture slides).

________; HAINES, E.; HOFFMAN, N. Real-time rendering. 3rd ed. Natick: A. K.


Peters, 2008.

FOSNER, R. Real-time shader programming. San Francisco: Morgan Kaufmann, 2003

20

Você também pode gostar