23. Rastreamento de Raios (Ray Tracing)

O rastreamento de raios está entre os métodos conceitualmente mais simples para sintetizar imagens altamente realistas, como a da Figura Fig. 23.1. Ao contrário dos métodos simples de renderização de polígonos usados pelo OpenGL (e WebGL), o ray tracing pode facilmente produzir sombras e pode modelar reflexões (como em espelhos) e refrações (luz passando por um meio transparente).

A técnica de ray tracing pode criar imagens fotorrealísticas.

Fig. 23.1 A técnica de ray tracing pode criar imagens fotorrealísticas. Fonte: Wikipedia.

Por ser relativamente lento, o ray tracing é normalmente usado para gerar imagens altamente realistas offline (ao invés de interativamente, como fizemos até agora usando WebGL). Por exemplo, ray tracing é usado na produção de filmes de animação de alta qualidade. Também é útil para gerar mapas de textura realistas e mapas de ambiente que podem ser usados posteriormente em programas gráficos interativos. O ray tracing também é o fundamento usado em muitas abordagens para produzir tipos complexos de sombreamento e iluminação mais realistas. Apesar de sua simplicidade conceitual, o ray tracing é um processo computacional bastante caro.

Nessa aula vamos apresentar os elementos básicos desse processo, revisitaremos o modelo de iluminação de Phong neste contexto e discutiremos alguns dos detalhes sobre a geração de raios e o tratamento de interseções de raios com objetos.

23.1. A ideia básica

Considere a nossa situação de observação em perspectiva padrão, onde existe um observador (câmera) localizado em alguma posição e, a sua frente, está o plano de formação da imagem. Sobre este plano vamos assumir que há uma grade (que pode ser uma matriz de pixels).

Queremos renderizar a cena que é visível para o observador através desta grade. Considerando um elemento arbitrário na grade, sua cor é determinada pelo raio de luz que passa por este ponto e atinge o olho do observador.

De forma mais genérica, vamos assumir que a luz viaja em raios que são emitidos da fonte de luz e atingem objetos no ambiente. Quando a luz atinge uma superfície, parte de sua energia é absorvida e parte é refletida em diferentes direções. Se o objeto for transparente, a luz também pode ser transmitida através do objeto.

A luz pode continuar a ser refletida em outros objetos. Eventualmente, alguns desses raios refletidos chegam ao olho do observador. Note que apenas esses raios são relevantes para o processo de visualização.

Se pudéssemos modelar com precisão o movimento de toda a luz em uma cena tridimensional, teoricamente poderíamos produzir renderizações muito precisas. Infelizmente, o esforço computacional necessário para uma simulação tão complexa seria proibitivamente grande. Como podemos simplificar o processo? Observe que a maioria dos raios de luz emitidos pelas fontes de luz nunca atingem nossos olhos. Consequentemente, grande parte do esforço de simulação da luz é desperdiçado. Isso sugere que, em vez de traçar os raios de luz à medida que eles saem da fonte de luz (na esperança que eventualmente atinjam o olho), invertemos o processo seguindo o caminho reverso dos raios que atingem o olho. Esta é a ideia do processo de ray tracing.

23.2. Modelo de Ray Tracing

Vamos imaginar que a grade seja uma malha fina de linhas horizontais e verticais, de modo que cada quadrado da grade corresponda a um pixel na imagem final. Disparamos raios do olho através do centro de cada quadrado da grade e traçamos o caminho reverso da luz em direção às fontes de luz.

Considere o primeiro objeto intersectado por um desses raios. Queremos saber a intensidade da luz refletida neste ponto na superfície do objeto. Isso depende de uma série de coisas, principalmente das propriedades reflexivas e da cor da superfície, além da quantidade de luz que chega a este ponto das várias fontes de luz.

A quantidade de luz que atinge este ponto da superfície é difícil de calcular com precisão. Isso ocorre porque a luz das várias fontes de luz pode ser bloqueada por outros objetos no ambiente e pode ser refletida por outros. Vimos que no modelo de iluminação local de Phong, um ponto é iluminado se o ângulo entre o vetor normal e o vetor de luz for agudo (menor que 90:math:^o). No ray tracing é comum usar uma aproximação um pouco mais global. Vamos supor que as fontes de luz são pontos. Para cada fonte de luz \(L_i\), disparamos um raio \(R_{L_i}\) do ponto da superfície para cada uma das fontes de luz (veja a Fig. 23.2 (a)). Para cada um desses raios que consegue atingir uma fonte de luz antes de ser bloqueado por outro objeto, inferimos que esse ponto é iluminado por essa fonte (como para \(L_1\) na Fig. 23.2 (a)), caso contrário, assumimos que não é iluminado , e portanto estamos na sombra do objeto bloqueador (como para \(L_2\) na Fig. 23.2 (a)).

Pontos iluminados

Você consegue imaginar uma situação em que este modelo não consiga determinar corretamente se um ponto está iluminado?

Processo de rastreamento de raios.

Fig. 23.2 Processo de rastreamento de raios. Fonte: Notas de aula do Prof. Dave Mount.

Dada a direção para a fonte de luz e a direção para o observador (olho), e a normal da superfície (que podemos calcular porque conhecemos o objeto que o raio atingiu), temos todas as informações necessárias para calcular a intensidade refletida da luz neste ponto, digamos, usando o modelo Phong e as informações sobre as propriedades de reflexão ambiente, difusa e especular do objeto. Usamos este modelo para atribuir uma cor ao pixel. Simplesmente repetimos esta operação para todos os pixels da grade para obter nossa imagem final.

Mesmo esse modelo de ray tracing simples já é melhor do que o modelo de Phong que implementamos anteriormente usando WebGL. Isso porque, por exemplo, o modelo de iluminação local de Phong não calcula sombras. O modelo global de ray tracing pode ser facilmente estendido para lidar com objetos refletivos (como espelhos e esferas brilhantes) e transparentes (bolas de vidro e gotas de chuva). Por exemplo, quando o raio atinge um objeto refletivo, computamos o raio de reflexão e o lançamos de volta ao ambiente. Invocamos o algoritmo de ray tracing recursivamente (veja a Fig. 23.2 (b)) e, quando obtemos a cor associada ao raio, basta “misturá-la” com a cor da superfície local e retornamos o resultado. O algoritmo genérico possui as seguintes funções.

  • função rayTrace(cam, W, H): Dada uma câmera cam e a resolução W:math:times`H da imagem, gera um raio :math:`R_{ij}, com origem no centro da câmera (olho), passando pelo centro de cada pixel (i, j) da imagem. Chama a função trace(R) e associa a cor retornada ao pixel. Retorna a imagem resultante.

  • função trace(R): Lança o raio \(R\) na cena. Seja \(X\) o primeiro objeto atingido e \(P\) o ponto de contato com esse objeto.

    1. Se \(X\) é refletivo, então calcule o raio de reflexão \(R_r\) de \(R\) em \(P\). Seja \(C_r = trace(R_r)\).

    2. Se \(X\) é transparente, então calcule o raio de transmissão (refração) \(R_t\) de \(R\) em \(P\). Seja \(C_t = trace(R_t)\).

    3. Para cada fonte de luz \(L\),

      1. Lance um raio \(R_L\) de \(P\) para \(L\).
      2. Se \(R_L\) não atingir nenhum objeto até atingir \(L\), então aplique o modelo de iluminação para determinar o sombreamento \(C_p\) neste ponto.
    4. Combine as cores \(C_r\) e \(C_t\) devido à reflexão e transmissão (se houver) junto com o sombreamento \(C_p\) para determinar a cor final \(C\). Retorne \(C\).

Há duas questões a serem consideradas

  • como determinar qual o objeto o raio intersecta e
  • como usar essas informações para determinar a cor refletida?

Vamos nos concentrar neste último item primeiro.

23.3. Reflexão

Vamos recordar o modelo de reflexão Phong, onde cada objeto está associado a uma cor e seus coeficientes de reflexão ambiente, difusa e especular, denotados por \(\rho_a\), \(\rho_d\) e \(\rho_s\). Para modelar a componente de reflexão, cada objeto será associado a um parâmetro adicional chamado coeficiente de reflexão, denotado \(\rho_r\). Tal como acontece com os outros coeficientes, este é um número no intervalo [0, 1]. Suponhamos que este coeficiente seja diferente de zero. Calculamos o raio de reflexão \(\vec{r}_v\) assumindo que seu ângulo com relação a normal da superfície tem o mesmo ângulo entre a normal e o vetor da vista (view vector) (que aponta para o olho). Seja \(\vec{v}\) o vetor da vista normalizado, que aponta para para o olho, ou seja, contrário ao raio de visão.

Assim, se o raio é dado por \(P + t\vec{u}\), então \(\vec{v} =\) -normalize(\(\vec{u}\)). Este é essencialmente o mesmo que o vetor de vista usado no modelo Phong, mas pode não apontar diretamente de volta para o olho devido a reflexões intermediárias. Seja \(\vec{n}\) o vetor normal da superfície apontando para fora, que assumimos também normalizado. O vetor de reflexão da vista normalizado \(\vec{r}_v\) (veja a Fig. 23.3) é derivado da seguinte forma

\(\vec{r}_v = 2\;(\vec{n} \cdot \vec{v}) \; \vec{n} \;-\;\vec{v}\).
Reflexao de um raio.

Fig. 23.3 Reflexão de um raio. Fonte: Notas de aula do Prof. Dave Mount.

Como a superfície é reflexiva, disparamos o raio que emana do ponto de contato da superfície ao longo dessa direção e aplicamos o algoritmo de ray tracing acima recursivamente. Eventualmente, quando o raio atinge um objeto não refletivo, a cor resultante é retornada. Essa cor é então fatorada no modelo Phong, como descrito mais adiante. Observe que é possível que esse processo entre em loop infinito, por exemplo, se você tiver dois espelhos um de frente para o outro. Para evitar esse loop, é comum ter uma profundidade máxima de recursão, após a qual alguma cor padrão é retornada, independentemente do objeto ser ou não refletivo.

23.4. Objetos transparentes e refração

Para modelar a refração, também chamada de transmissão, vamos usar um coeficiente de transmissão, denotado \(\rho_t\). Também precisamos associar cada superfície a dois parâmetros adicionais, os índices de refração \(\eta_i\) do lado incidente e \(\eta_t\) do lado transmitido. O índice de refração é uma propriedade física dos meios transparentes dado pela a razão entre a velocidade da luz no vácuo versus a velocidade da luz no material. A tabela abaixo ilustra alguns índices típicos de refração:

Table 23.1 Alguns índices de refração
Material Índice
Ar (vácuo) 1.0
Água 1.333
Vidro 1.5
Diamante 2.47

A lei de Snell diz que se um raio incide com ângulo \(\theta_i\) (em relação à normal da superfície), então ele será transmitido com ângulo \(\theta_t\) (relativo à normal oposta) tal que

\(\cfrac{\sin \theta_i}{\sin \theta_t} = \cfrac{\eta_t}{\eta_i}\).

Vamos calcular a direção do raio transmitido a partir disso. Como já fizemos antes, seja \(\vec{v}\) o vetor de vista normalizado, direcionado de volta ao longo do raio incidente (aponta para o olho). Seja \(\vec{t}\) o vetor unitário ao longo da direção transmitida, que desejamos calcular (veja a Fig. 23.4).

Transmissão de um raio para outro meio.

Fig. 23.4 Transmissão de um raio para outro meio. Fonte: Notas de aula do Prof. Dave Mount.

A projeção ortogonal de \(\vec{v}\) no vetor normal \(\vec{n}\) (já normalizado) é

\(\vec{m}_i \;=\; (\vec{v} \;\cdot\; \vec{n})\;\vec{n} \;=\; (\cos \theta_i) \; \vec{n}\).

Considere o vetor horizontal \(\vec{w}_i = \vec{m}_i - \vec{v}\) e seu correspondente vetor \(\vec{w}_t\), também horizontal. Como \(\vec{v}\) e \(\vec{t}\) possuem comprimento unitário temos

\(\cfrac{\eta_t}{\eta_i} \;=\; \cfrac{\sin \theta_i}{\sin \theta_t} = \cfrac{|\vec{w}_i|/|\vec{v}|}{|\vec{w}_t|/|\vec{t}|} \;=\; \cfrac{ |\vec{w}_i| }{|\vec{w}_t|}\).

Como \(\vec{w}_i\) e \(\vec{w}_t\) são paralelos (ambos são horizontais) temos

\(\vec{w}_t \;=\; \cfrac{\eta_i}{\eta_t} \vec{w}_i \;=\; \cfrac{\eta_i}{\eta_t} (\vec{m}_i - \vec{v})\).

A projeção de \(\vec{t}\) sobre \(-\vec{n}\) é \(\vec{m}_t \;=\; -(\cos \theta_t) \vec{n}\), e portanto o vector de transmissão é

\(\begin{flalign}\vec{t} & =\; \vec{w}_t + \vec{m}_t \;=\; \cfrac{\eta_i}{\eta_t} (\vec{m}_i - \vec{v}) - (\cos \theta_t)\vec{n} \;=\; \cfrac{\eta_i}{\eta_t}((\cos \theta_i)\vec{n} - \vec{v}) - (\cos \theta_t)\vec{n}\\ & =\; (\cfrac{\eta_i}{\eta_t}\;\cos \theta_i \;-\; \cos \theta_t) \vec{n} - \cfrac{\eta_i}{\eta_t} \vec{v}\end{flalign}\).

Nós já calculamos o valor de \(\cos \theta_i = (\vec{v} \cdot \vec{n})\). O \(\cos \theta_t\) pode ser derivado da lei de Snell e algumas identidades trigonométricas tal que:

\(\begin{flalign}\cos \theta_t & = \sqrt{1 - \sin^2 \theta_t} \;=\; \sqrt{1-(\cfrac{\eta_i}{\eta_t})^2 \; \sin ^2 \theta_i} \;=\; \sqrt{1-(\cfrac{\eta_i}{\eta_t})^2 \; (1 \;-\; \cos ^2 \theta_i)}\\ & = \sqrt{1-(\cfrac{\eta_i}{\eta_t})^2 \; (1 \;-\; (\vec{v} \cdot \vec{n})^2)}\end{flalign}\).

E se o termo na raiz quadrada for negativo? Isso é possível se \((\eta_i / \eta_t) \sin \theta_i > 1\). Em particular, isso só pode acontecer se \((\eta_i / \eta_t) > 1\), significando que você já está dentro de um objeto com índice de refração maior que 1. Observe que nesse caso, não podemos aplicar a lei de Snell pois é impossível encontrar \(\theta_t\) cujo seno seja maior que 1. Nessa situação, ocorre um fenômeno chamado de reflexão interna total. Ou seja, a fonte de luz não é refratada, mas é refletida de volta para dentro do objeto. (Aliás, esse fenômeno, combinado com a dispersão cromática, é uma das razões para a existência de arco-íris.) Quando isso acontece, a refração se reduz a reflexão e então definimos \(\vec{t} = \vec{r}_v\), o vetor de reflexão da vista.

Em resumo, o processo de transmissão da luz para outro meio é resolvido da seguinte forma.

  1. Calcule o ponto onde o raio intercepta a superfície. Seja \(\vec{v}\) o vetor da vista normalizado, seja \(\vec{n}\) a normal à superfície normalizada neste ponto e sejam \(\eta_i\) e \(\eta_t\) os índices de refração nos lados de entrada e saída, respectivamente.

  2. Calcule o ângulo de refração:

    \(\theta_t \;=\; \arccos \sqrt{1 - \left( \cfrac{\eta_i}{\eta_t} \right)^2 \; (1 \;-\; (\vec{v} \cdot \vec{n})^2)}\).

  3. Se o valor do termo da raiz quadrada for negativo, trate esse caso como reflexão interna, em vez de transmissão.

  4. Se o valor do termo da raiz quadrada for não negativo, calcule o vetor de transmissão:

    \(\vec{t} \;=\; \left(\cfrac{\eta_i}{\eta_t}\;\cos \theta_i \;-\; \cos \theta_t \right) \vec{n} - \cfrac{\eta_i}{\eta_t} \vec{v}\).

O raio de transmissão é emitido do ponto de contato ao longo desta direção.

23.5. Onde estamos e para onde vamos?

Nessa aula começamos a introduzir a técnica de ray tracing, que é baseada em um modelo de iluminação global. Apesar da sua aparente simplicidade, seu custo computacional ainda é elevado para ser utilizado em sistemas gráficos interativos, embora esse cenário esteja mudando rapidamente com a introdução de placas gráficas cada vez mais poderosas.

Nessa aula vimos que o comportamento local da luz, que discutimos ao introduzir o modelo de Phong, pode ser estendido para um modelo global ao considerar raios de reflexão e refração em cada ponto. O resultado desses raios é combinado ao final para a determinação da cor de cada pixel.

Na próxima aula vamos discutir como determinar a interseção de raios com objetos da cena.

23.6. Para saber mais