.. _aula-03---sistemas-gráficos:

Sistemas Gráficos
=================

Nessa aula vamos apresentar alguns recursos típicos encontrados em
sistemas gráficos usados para gerar imagens por computador. 

Componente de um sistema gráfico
--------------------------------

Um sistema gráfico computacional possui os elementos típicos de um
computador de mesa ou portátil comum, como ilustrado na
Figura :numref:`fig-SistemaGraf`, ou seja, possui dispositivos de entrada
(como teclado, mouse e joystick) e saída (como monitor, impressora e
plotter), um processador central (CPU), um processador gráfico (GPU) e
memória.

.. figure:: ./figuras/a03/sistemaGrafico.png
    :alt: Elementos de um sistema gráfico.
    :name: fig-SistemaGraf
    :width: 95.0%
    :align: center

    Elementos de um sistema gráfico.


Dispositivos de entrada e saída
-------------------------------

Os dispositivos de entrada, como teclado e mouse, são necessários para
que o usuário possa interagir com computadores usando **interfaces
gráficas** do tipo WIMP (*Windows, Icons, Menus, and Pointg*), ou seja,
interfaces formadas por janelas, ícones, menus e um cursor para
apontamento dos elementos gráficos. Outros dispositivos, como mesas
digitalizadoras e controles para jogos, são comuns em muitos aplicativos
gráficos interativos, para criação de desenhos e edição de imagens.

A saída gráfica pode ser feita por meio de impressoras e plotters, mas o
dispositivo mais comum são os monitores de vídeo por permitirem
interação em tempo real e a visualização de animações. Os monitores mais
utilizados hoje são do tipo **raster**, ou seja, que representam
informações gráficas por meio de uma matriz de pixels. Cada monitor pode
utilizar uma tecnologia distinta na sua construção e funcionamento. Os
tipos mais comuns de monitores são:

-  **Tubo de raios catódicos**: consistem em uma tela com revestimento
   de fósforo, que permite que cada pixel seja iluminado momentaneamente
   ao ser atingido por um feixe de elétrons. Um pixel está iluminado
   (branco) ou não (preto). O nível de intensidade pode ser variado para
   atingir valores de cinza arbitrários. Como o fósforo mantém sua cor
   apenas brevemente, a imagem é repetidamente digitalizada, a uma taxa
   de 25 a 30 vezes por segundo. Essa tecnologia é mais antiga e não é
   mais utilizada devido ao seu grande peso, volume e alto consumo de
   energia quando comparados aos monitores LCD e LED.

-  **Telas de cristal líquido (LCD’s)**: usa um campo elétrico para
   alterar a polarização das moléculas cristalinas em cada pixel. A luz
   que brilha através do pixel já está polarizada em alguma direção.
   Alterando a polarização do pixel, é possível variar a quantidade de
   luz que atravessa o cristal, controlando assim sua intensidade.

-  **Diodos emissores de luz (LED ou OLED)**: cada pixel é iluminado por
   um diodo emissor de luz (ou conjunto tricromático RGB
   correspondente). Como esses monitores não precisam de iluminação de
   fundo como o LCD, eles tendem a ser mais eficientes, simples de
   fabricar e mais finos (tanto que podem ser colocados sobre membranas
   flexíveis e enroláveis).

A **resolução** mais comum para monitores atualmente é chamada de "full
HD", que corresponde a 1920\ :math:`\times`\ 1080 pixels. Resoluções
cada vez maiores estão se tornando comuns também, como monitores 4K (ou
"ultra HD") por exemplo, que possuem 4 vezes mais pixels que um monitor
full HD.


Framebuffer
-----------

Em sistemas mais simples, o framebuffer armazena a imagem a ser exibida
no monitor. O **Controlador de Vídeo** é um módulo responsável para ler
a imagem do framebuffer e gerar o sinal de vídeo para o monitor.

Para exibir uma imagem RGB full HD é necessário reservar um framebuffer
com cerca de 6 MB (mega bytes). Essa quantidade de memória era
indisponível em computadores mais antigos e por isso várias alternativas
foram desenvolvidas para contornar essa limitação.

Uma forma para economizar memória em sistemas gráficos é reduzir a
profundidade das imagens utilizadas, por exemplo, utilizando uma `tabela ou palete de cores <https://en.wikipedia.org/wiki/Palette_(computing)>`__ para
imagens coloridas. Imagine, por exemplo, que nossas imagens possam ser
representadas por um conjunto de 256 cores de 24 bits cada cor. Essas
cores podem ser codificadas em uma tabela com 256 entradas, onde cada
entrada corresponde a uma cor de 24 bits. Esses 256 códigos correspondem
aos índices das linhas da tabela que precisam de 8 bits para serem
codificadas. Esse código de 1 byte pode ser colocado em cada pixel, ao
invés dos 3 bytes da cor, economizando assim 2/3 (dois terços) da
memória necessária para armazenar a imagem original com 24 bits por
pixel. Nesse caso, a imagem full HD pode ser agora representada com
cerca de 2 MB, além da memória necessária para armazenar a tabela com os
códigos das cores.

Uma alternativa capaz de reduzir ainda mais o consumo de memória é o uso
de **imagens vetoriais** ao invés de imagens raster. Imagens vetoriais
são apropriadas para representar desenhos formados por segmentos de
linha (ou simplesmente linhas), onde um segmento é representado por dois
pontos na tela do monitor. Assim, um desenho formado por centenas ou até
mesmo milhares de linhas pode ser representado de forma bem mais
compacta (em termos de memória) que uma imagem raster. Além de serem
facilmente escaláveis, esses desenhos também podem ser exibidos de forma
relativamente simples em monitores analógicos que utilizam tubo de raios
catódicos, fazendo o feixe de elétrons do tubo varrer a lista de
segmentos de forma contínua.

Observe que, para desenhar uma linha em monitores raster a partir de 2
pontos, é necessário calcular a posição de cada pixel interior ao
segmento. **Algoritmos de rasterização** servem para calcular esses
pixeis para a geração da imagem raster.

Em sistemas mais complexos, o framebuffer pode armazenar outras
informações também, como profundidade ou opacidade na representação
RGBA. Além disso, com mais memória disponível, não é mais necessário
limitar cada cor a 256 níveis por exemplo. Framebuffers modernos
permitem representar cada pixel usando 12 ou mais bits por cor,
permitindo a renderização de imagens com larga faixa dinâmica (HDR ou
*high dynamic range*).

       
Processadores e Memória
-----------------------

Em sistemas mais simples, o processador gráfico ou GPU (*Graphics
Processing Unit*) fica integrado ao processador central ou CPU (*Central
Processing Unit*). Em sistemas mais complexos o processamento gráfico
fica em um circuito ou placa dedicada, com memória própria. O
framebuffer faz parte da memória dedicada ao processamento gráfico e por
isso faz parte da memória utilizada pela GPU ou, em sistemas sem GPU,
como parte da memória do sistema.

Embora a maioria dos monitores trabalhem com frequências de 60 Hz ou
mais, a taxa de geração de imagens em animações costuma ser bem
inferior. No entanto, para manter uma sensação de continuidade, uma
animação precisa manter uma taxa de aproximadamente 15 novos quadros por
segundo (fps – *frames per second*). Abaixo de 10 fps, a sensação de
descontinuidade da animação começa a ser notada pela maioria das
pessoas. Para garantir uma boa qualidade, os projetores antigos nos
cinemas exibiam filmes a 24 fps e na TV a 30 fps.

Chamamos de **renderização** o processo de geração dos pixels (posição e
cor) no framebuffer a partir de um modelo de cena (representação
geométrica dos objetos). Muito desse processo ocorre atualmente dentro
da GPU que, para obter um alto desempenho, utilizam uma arquitetura
paralela conhecida como pipeline gráfico. A
Figura :numref:`fig-PipelineGrafico` ilustra os principais módulos de um
pipeline gráfico.


.. figure:: figuras/a03/pipelineGrafico.png
    :alt: Estágios do pipeline gráfico.
    :name: fig-PipelineGrafico
    :width: 100%
    :align: center

    Estágios do pipeline gráfico.

Para executar um comando simples como "desenhe um triângulo em 3D", o
pipeline primeiramente recebe o conjunto de vértices que definem o
triângulo. O **processador de vértices** usa os parâmetros da câmera
virtual para transformar as coordenadas dos vértices para um sistema
centrado na câmera. As transformações são representadas por matrizes e a
mudança de coordenadas envolve a multiplicação dessas matrizes.

Após a transformação de coordenadas, alguns vértices podem ficar fora do
campo visual da câmera. O módulo de **clipping** (recorte) remove esses
vértices e, caso alguma parte dos segmentos ainda sejam visíveis, esses
segmentos são redefinidos na forma de novos vértices.

Os segmentos visíveis são passados para o módulo de **rasterização**,
que usa os vértices visíveis para pintar os pixels da parte visível do
triângulo, que são chamados de **fragmentos**. Observe que a cena pode
ser composta por outros objetos que resultam em mais fragmentos. Esses
fragmentos são combinados pelo **processador de fragmentos** para
calcular a cor final de cada pixel do framebuffer.

       
Pipelines programáveis
----------------------

As placas gráficas mais antigas possuíam um pipeline rígido com poucos
recursos. As placas mais recentes permitem programar o processador de
vértices e o processador de fragmentos. Com isso podemos implementar
vários efeitos em tempo real que não era possível com as placas mais
antigas (como *bump mapping*). Chamamos de **shaders** esses programas
que são executados pela GPU.

-  **Vertex shaders**: executado pelo processador de vértices e usado
   para aplicar transformações nos vértices e nas suas propriedades;

-  **Fragment shaders**: Esse shader é usado para processar fragmentos,
   permitindo aplicar texturas e outros efeitos de iluminação e
   computações específicas para cada tipo de fragmento.

Nas placas gráficas mais antigas era também comum avaliar o desempenho
das placas pelo número de primitivas geométricas processadas por segundo
(**front end**) ou então pela taxa de bits que podiam ser movidos para o
framebuffer (**back end**). Hoje as GPUs mais poderosas podem usar ponto
flutuante em todo o pipeline, até no framebuffer, um recurso útil para
representar e manipular imagens com grande amplitude dinâmica (HDR –
*high dynamic range*). Essas GPUs são chamadas de **unified shading
engines** pois fazem o processamento dos vértices e fragmentos
concorrentemente. Hoje, esse grande poder computacional é aproveitado
por muitas aplicações não relacionadas à geração de imagens.

       
Bibliotecas gráficas
--------------------

Mesmo sendo muito eficiente, programar o pipeline para criar 30 imagens
por segundo necessárias para gerar boas animações ainda é um desafio.

Dizemos que cada redesenho faz parte de um **ciclo de atualização**
(*refresh cycle*) pois o programa deve atualizar o conteúdo da imagem. O
programa se utiliza de uma biblioteca ou API (*application programming
interface*) para se comunicar com o sistema gráfico. Existem várias APIs
diferentes usadas em sistemas gráficos modernos, cada uma fornecendo
alguns recursos distintos em relação às outras. De um modo geral, as
APIs gráficas podem ser classificadas em duas classes gerais:

-  **Modo Retido** (*retained mode*): A biblioteca mantém o estado da
   computação em suas próprias estruturas de dados internas. A cada
   ciclo de atualização, esses dados são transmitidos à GPU para
   renderização.

   -  Por conhecer o estado completo da cena, a biblioteca pode realizar
      otimizações globais automaticamente.

   -  Este método é menos adequado para conjuntos de dados que variam no
      tempo, uma vez que a representação interna dos dados precisa ser
      atualizada com frequência.

   -  Isso é funcionalmente análogo à compilação de um programa.

   -  Exemplos: Java3d, Ogre, Open Scenegraph.

-  **Modo Imediato** (*immediate mode*): O aplicativo fornece todas as
   primitivas a cada ciclo de exibição. Em outras palavras, seu programa
   transmite comandos diretamente para a GPU para serem executados.

   -  A biblioteca só pode realizar otimizações locais, pois não conhece
      o estado global. É responsabilidade do programa do usuário
      realizar otimizações globais.

   -  Isso é adequado para cenas altamente dinâmicas.

   -  Isso é funcionalmente análogo à interpretação do programa.

   -  Exemplos: OpenGL, DirectX.


OpenGL
------

O OpenGL se tornou uma API padrão largamente utilizada. Ela está
disponível em praticamente todas as plataformas e pode ser acessada
pelas mais utilizadas linguagem de programação como C/C++, Java e
Python. Para que posso funcionar em plataformas tão diferentes, ela
precisa ser genérica, em contraste por exemplo com o DirectX, que foi
desenvolvido para funcionar principalmente em sistemas da Microsoft.

Para facilitar essa generalidade e se tornar o OpenGL independente do
sistema operacional e do sistema que controla as janelas da interface, o
OpenGL não fornece recursos para entrada e saída para interação com
usuárias e usuários.

O OpenGL trabalha, em sua maior parte, no modo imediato. Isso significa
que cada chamada de função resulta no envio de um comando diretamente
para a GPU. No entanto, há alguns elementos retidos como, por exemplo,
transformações, iluminação e texturização que precisam ser configurados
previamente para que possam ser aplicadas no cálculo. Por exemplo, o
OpenGL não oferece recursos para criar uma janela, redimensionar uma
janela, determinar as coordenadas atuais do mouse ou detectar se uma
tecla do teclado foi pressionada. Para atingir esses outros objetivos, é
necessário usar um kit de ferramentas adicional. Nesse curso vamos
adotar a versão WebGL, que permite criar aplicações gráficas que fazem
uso do OpenGL a partir de qualquer navegador moderno.


Onde estamos e para onde vamos
------------------------------

Nessa aula continuamos a cobrir conceitos da computação gráfica, muitos
deles associados ao desenvolvimento histórico dos sistemas gráficos. Com
a evolução desses sistemas, temos muito mais recursos computacionais a
disposição (tanto de hardwares quanto de software) e GPUs muito
poderosas mas otimizadas para tratar de elementos geométricos básicos.

O OpenGL é uma API gráfica genérica que permite o desenvolvimento de
aplicativos gráficos para praticamente qualquer plataforma. A versão
WebGL permite usar o OpenGL dentro de um navegador. Do ponto de vista
pedagógico, isso facilita bastante o ensino pois reduz a necessidade de
instalação de ferramentas de desenvolvimento, pois necessitamos
basicamente de um editor de texto, e estimula a prática pois os
exercícios que você vai desenvolver podem ser executados em qualquer
navegador.

Na próxima aula vamos começar a introduzir alguns elementos básicos de
HTML e JavaScript para desenvolver nossas primeiras aplicações. Logo em
seguida vamos introduzir o WebGL, usando esse ambiente Web de
programação com HTML+JavaScript.

.. 
    #################################################################

Exercícios
----------

#. Considere um segmento de linha definido por dois pontos no espaço 2D e uma janela de exibição retangular. Mostre que basta redefinir um ou dois desses pontos para recortar a parte visível do segmento, ou seja, a parte do segmento interna à janela de exibição.
#. Filmes de cinema eram tipicamente filmados em películas de 35 mm com uma resolução de aproximadamente :math:`3000 \times 2000` pixels. Que implicação isso resulta quando esses filmes eram exibidos em TVs antigas? E nas modernas? 
#. Em um vídeo full HD, cada imagem possui resolução :math:`1920\times1080` pixels, com profundidade de 24 bits. Se o vídeo exibe 30 imagens por segundo, qual a frequência necessária para transmitir esse vídeo de forma serial? 


Para saber mais
---------------

Recomendamos
a seguinte leitura para complementar essas notas:

-  Capítulo 1 do livro "Interactive Computer Graphics" de Edward Angel;
   e/ou:

   -  `Capítulo 1 do Livro do David Eck <http://math.hws.edu/graphicsbook/c1/index.html>`__.


.. 

    Laboratório: Algoritmo de Bresenham para rasterizar de linhas
    -------------------------------------------------------------