Skip to content
Manoel Silva edited this page Sep 27, 2024 · 10 revisions

Introdução

Desde os anos 80, O Path Tracing é uma técnica bastante conhecida na Computação Gráfica que possui como intuito simular o comportamento de um raio de luz em uma cena e seus objetos. Essa técnica possui como principal aplicação criar imagens ou animações extremamente realistas, mas sua execução é extremamente lenta, principalmente na época de sua concepção. Com o avanço do poder computacional, já nos anos 90 havia aplicações capazes de reproduzir um algoritmo baseado em Path Tracing em computadores pessoais, como o Bryce 3D.

Captura de Tela 2023-12-18 às 22 18 35

Porém, tal técnica se mostrou bastante popular em animações 3D, através da Pixar - pioneira na área e que usaremos um algoritmo otimizado por eles-, mas ainda não era possível reproduzir em tempo real. Até a NVIDIA conseguir esse feito com um algoritmo menos custoso, o Ray Tracing, e otimizado para sua arquitetura CUDA.

Captura de Tela 2023-12-18 às 22 18 35

Ray Tracing vs Path Tracing

A diferença entre Ray Tracing e Path Tracing é bem tênue, muitos autores consideram o Path Tracing apenas uma versão aprimorada do Ray Tracing, isso se deve por conta das diferentes formas de se resolver o mesmo problema: Como simular a luz?

O Ray tracing ocorre quando raios que se dirigem para câmera (ou visão) atingem um objeto. Múltiplos raios são então projetados a partir desse ponto de origem em direção às várias fontes de luz. Esses raios podem interagir com outros objetos ou alcançar suas fontes de luz, dependendo de onde pousam. As informações são coletadas e calculadas para o ponto original do raio, e essas informações geram a imagem. Isso simula de certa forma o que a luz realmente faz.

Já o Path Tracing ocorre quando o raio atinge um objeto e depois reflete para tentar alcançar uma fonte de luz. Ele fará isso tantas vezes quantas forem necessárias até alcançar a fonte de luz. Isso é como a luz realmente se comporta; cada cálculo de reflexão fornece mais dados e, além disso, o tamanho e a forma real da fonte de luz também são relevantes. Isso representa uma simulação real da luz.

Por isso, o Path Tracing é mais realista e mais custoso. Jogos com RTX geralmente implementam o Ray Tracing, ou um Híbrido com o Path Tracing. Atualmente apenas animações e renderizadores que não são em tempo real usam o Path Tracing de forma integral (Por curiosidade, apenas alguns jogos possuem somente Path Tracing, como o Quake II RTX)

O Projeto

Implementando Path Tracing

Podemos implementar o Path Tracing como um algoritmo recursivo simples baseado em Monte Carlo. Para calcular a cor de um pixel de imagem, diversos raios são rastreados a partir do ponto de vista através desse pixel, de forma randômica. Quando um raio intersecta uma superfície, a iluminação direta das fontes de luz é calculada para o ponto de interseção (o que inclui o rastreamento de raios de sombra entre as fontes de luz e o ponto de interseção).

Além disso, um novo raio é gerado para calcular a iluminação indireta. A direção do novo raio é escolhida estocasticamente com base nas propriedades de espalhamento de luz do material da superfície: especular ou opaco, reflexivo ou refrativo. Quando tal raio atinge outra superfície, a iluminação direta é calculada lá (incluindo o rastreamento de mais raios de sombra), um novo raio é gerado e assim por diante. As cores de todos os raios em todas as profundidades contribuem para a cor do pixel do raio de origem. A recursão para quando o novo raio não atinge uma superfície, ou quando uma profundidade máxima de recursão predefinida é alcançada.

Captura de Tela 2023-12-18 às 23 47 05

O que gera a seguinte imagem:

Captura de Tela 2023-12-18 às 23 51 15

A Figura mostra imagens renderizadas com Path Tracing com 1, 16 e 256 amostras por pixel na cena da caixa apresentada. A imagem à esquerda foi renderizada rapidamente, mas possui muito ruído, gerado pelo término abrupto da recursão ou pelo não encontro do raio na superfície; as outras imagens levaram mais tempo para serem renderizadas, mas têm muito menos ruído. Apesar do ruído, um artista pode formar uma opinião sobre a iluminação e os materiais na cena mesmo com apenas algumas amostras por pixel, ou seja, após algumas iterações em uma sessão interativa.

O Raio de Luz

Para calcular o raio, usaremos uma interpolação linear, em que um raio R tem a função $𝐏(𝑡)=𝐀+𝑡𝐛$, no qual 𝐀 é a origem o raio e 𝐛 é a direção, assim teremos como saber como o raio de luz se move em um espaço 3D, através de uma câmera (viewport).

Captura de Tela 2023-12-18 às 23 51 15

Objetos

Para que o projeto funcione, precisamos de alguns objetos para renderizar, para isso vamos implementar as formas geométricas essenciais: Esfera, Quadriláteros e Triângulos. Eles serão armazenados em uma lista chamada HitList em que o raio irá fazer uma varredura para saber se acertou algum elemento.

Esfera

Para qualquer objeto, para desenhá-lo na imagem basta apenas um raio o acertar. Assim, devemos encontrar uma forma de calcular como o nosso raio intercepta uma esfera.

Para explicar como um raio no path tracing intercepta uma esfera, primeiro, é útil entender a equação da esfera e o processo básico de interseção. A equação de uma esfera em um espaço tridimensional é dada por:

$$(x - x_c)^2 + (y - y_c)^2 + (z - z_c)^2 = r^2$$

onde $(x_c, y_c, z_c)$ são as coordenadas do centro da esfera e $r$ é o raio da esfera.

A interseção entre um raio e uma esfera pode ser verificada substituindo as coordenadas do raio na equação da esfera. Um raio é geralmente definido como uma origem $(Ox, Oy, Oz)$ e uma direção normalizada $(Dx, Dy, Dz)$. Assim, a equação do raio pode ser expressa como:

$$P(t) = (Ox + t \cdot Dx, Oy + t \cdot Dy, Oz + t \cdot Dz)$$

Substituindo esta expressão na equação da esfera e resolvendo para $t$, você pode determinar os pontos de interseção, se existirem.

Resumindo em etapas:

  1. Definir o Raio: Tenha as coordenadas da origem $O$ e da direção normalizada $D$ do raio.

  2. Equação da Esfera: Utilize a equação da esfera para expressar a condição de interseção.

  3. Substituir no Sistema de Equações: Substitua as coordenadas do raio na equação da esfera e resolva para $t$.

  4. Determinar a Interseção: Se houver soluções válidas para $t$, você encontrou os pontos de interseção. Caso contrário, o raio não atinge a esfera.

Em path tracing, o processo de interseção é repetido várias vezes para simular a trajetória da luz. Quando um raio atinge uma superfície (como uma esfera), as informações sobre a interseção (ponto de interseção, normal, material, etc.) são usadas para calcular a cor da superfície naquele ponto e determinar o próximo trajeto do raio (como reflexão, refração, etc.).

Quadriláteros e Triângulos

Para determinar a interseção de um raio com um quadrilátero ou triângulo no contexto do path tracing, o processo é semelhante ao explicado para esferas, porém é um pouco mais complicado devido as particularidades dessas formas geométricas em $R_3$

Medium (Fumaça)

Um efeito legal de se ter é fumaça, para simular tal efeito é simples quando usamos técnicas probabilísticas. À medida que o raio atravessa o volume, ele pode se dispersar em qualquer ponto. Quanto mais denso o volume, maior a probabilidade disso ocorrer. A probabilidade de o raio se dispersar em uma pequena distância Δ𝐿 é dada por:

$$probabilidade = \mathbf{C} \cdot \Delta \mathbf{L}$$

onde 𝐶 é proporcional à densidade do volume. Ao resolver todas as equações diferenciais, obtemos uma distância, determinada por um número aleatório, onde ocorre a dispersão. Se essa distância estiver fora do volume, então não há "colisão". Para um volume constante, são necessárias apenas a densidade 𝐶 e a fronteira. Outro objeto, como uma esfera, pode ser usado para representar a fronteira.

Captura de Tela 2023-12-19 às 11 16 33

Materiais

Agora que possuímos os objetos, agora falta implementar como eles se interagem com o raio. Assim como na vida real, existem vários tipos de materiais que possuem propriedades luminosas diferentes - como um vidro, espelho, metal - que iremos simular, dentre eles:

Lambertiano

Apesar de seu nome estranho, ele é um tipo de material que é bem conhecido: o Fosco. Como não possui nenhuma propriedade de reflexão especial, ele é bastante fácil de implementar, após um raio atingir um objeto com esse material, basta escolher randomicamente um vetor para ser a direção do raio de "saída" seguindo uma distribuição Lambertiana. Contudo, devemos verificar se esse vetor está direcionado na superfície externa.

A distribuição lambertiana para a escolha do vetor é simples, deve-se escolher apenas um ponto em uma esfera que tem centro o ponto $P$ que o raio colidiu no objeto mais a normal $N$ neste ponto, assim temos um vetor do ponto escolhido randomicamente que parte de $P$.

Captura de Tela 2023-12-18 às 23 51 15

Metal

O metal tem uma propriedade luminosa que devemos implementar: a reflexão. Para isso, usamos o nosso conhecimento de física:

Captura de Tela 2023-12-18 às 23 51 15

Logo a fórmula para calcular a reflexão será $v - (2* (v\cdot n) *n)$. Porém temos que simular o caso do metal "fosco", que chamamos de fuzziness. Para solucionar isso, vamos criar outra esfera com o centro na ponta do vetor que calculamos anteriormente e faremos o mesmo procedimento do material lambertiano.

Captura de Tela 2023-12-18 às 23 51 15

Dielétricos

Materiais dielétricos possuem esse nome pois não conduzem eletricidade - como por exemplo o vidro, diamante e água. Tais materiais possuem outra propriedade luminosa que devemos simular: a Refração. Assim, usaremos a Lei de Snell e a Aproximação de Schlick, para vidros.

Emissores de Luz

É um material especial que força a criação de raios de luz, como lâmpadas.

Texturas

Além do Material também devemos adicionar texturas neles, no caso mais simples essa textura pode ser apenas uma cor sólida, mas podemos implementar padrões(como xadrez), imagens e ruídos como textura.

Ruído de Perlin

A função de ruído de Perlin é um método usado em computação gráfica para gerar padrões de ruído coerentes e suaves. Ela foi desenvolvida por Ken Perlin e é amplamente usada para criar efeitos realistas em texturas, terrenos, entre outros.

A função cria um padrão de ruído suave que varia de maneira natural. Isso é alcançado gerando valores pseudoaleatórios em uma grade regular e, em seguida, interpolando suavemente entre esses pontos. Cada ponto na grade tem associado a ele um vetor de gradiente, que guia a interpolação entre os pontos.

Os vetores de gradiente são obtidos através de uma tabela preenchida com números aleatórios. Cada ponto da grade tem um vetor de gradiente associado a ele, e os quatro bits de menor ordem do valor da tabela determinam qual dos 16 vetores de gradiente está associado a esse ponto.

Captura de Tela 2023-12-18 às 23 51 15

Finalmente, os valores interpolados ponderados trilinearmente são calculados para obter o valor final da função de ruído de Perlin. Essa função pode ser usada para modular diferentes propriedades, como a cor difusa de uma superfície, criando variações naturais e detalhes interessantes como um efeito de mármore.

Captura de Tela 2023-12-18 às 23 51 15

Assim usamos a função de ruído de Perlin para oferecer uma maneira eficiente de gerar padrões de ruído realistas e suaves.

Modelos 3D

Por fim, para carregar e renderizar modelos 3D usaremos um formato bem simples, o OBJ - que já está obsoleto - para sabermos a posição dos triângulos e faces em um espaço tridimensional.

OBJ

A ideia final é ler esse arquivo e colocar em uma lista todos os seus triângulos, junto com sua textura. Assim, devemos verificar o padrão do arquivo:

  1. Vértices (Vertices):

    • As coordenadas dos vértices 3D (pontos) do modelo são listadas no início do arquivo. Cada vértice é definido com a tag "v" seguida das coordenadas (x, y, z). Exemplo: v 1.0 2.0 3.0.
  2. Normais (Normals):

    • Se o modelo incluir informações de normais, elas são listadas com a tag "vn" seguida das coordenadas (x, y, z). Exemplo: vn 0.0 1.0 0.0.
  3. Coordenadas de Textura (Texture Coordinates):

    • Se houver mapeamento de texturas, as coordenadas de textura são listadas com a tag "vt" seguida das coordenadas (u, v). Exemplo: vt 0.5 0.5.
  4. Faces (Faces):

    • As faces, que representam polígonos, são definidas com a tag "f" seguida pelos índices dos vértices, coordenadas de textura e normais. Exemplo: f 1/1/1 2/2/2 3/3/3. Isso indica que a face é formada pelos vértices 1, 2 e 3, com as correspondentes coordenadas de textura e normais.

Otimizações

Com o algoritmo finalizado, é perceptível o quão lento é com cenários muito complexos. Assim, para termos um tempo razoável para renderizar, vamos atacar na maior lentidão: A checagem se um objeto foi realmente acertado por um raio. Para se ter um exemplo, leva-se 206.791s em 50spp com resolução 1280x720 para o cenário com esferas randômicas(scene 3) em Single-thread. Em paralelo fazemos em 46.492s nas mesmas configurações, agora com AABB e em paralelo fazemos em apenas 13.302s!

AABB e BVH

O algoritmo AABB, conhecido como Axis-Aligned Bounding Box, possui como ideia principal criar uma caixa que envolva completamente determinado objeto, caso um raio não acerte tal caixa também não acertará o objeto dentro da caixa. Dessa forma, com o AABB, podemos aplicar o método BVH, ou Bounding Volume Hierarchy, que agrupa os objetos em volumes delimitadores (AABB) de maneira hierárquica. Isso irá criar uma árvore binária de um volume delimitador que contém seus filhos, ou seja, agrupamos caixas em outras caixas até sobrar apenas uma.

Captura de Tela 2023-12-18 às 23 51 15

Durante um teste de colisão ou uma operação de Path Tracing, a BVH é percorrida de cima para baixo, e os nós que não intersectam com o raio ou a região em questão são descartados, reduzindo assim o número de cálculos necessários. Após implementar esse algoritmo, a renderização ficou aproximadamente 2x mais rápida, sendo muito eficiente em cenários complexos.

Captura de Tela 2023-12-18 às 23 51 15

Parallel Computing e GPU

Para agilizar as coisas, podemos usar vários núcleos de nosso processador em paralelo ao invés de apenas um núcleo. O modo como fazemos é paralelizar os raios e seus cálculos para cada pixel em uma linha e ordená-las ao montar a imagem, isso deixa o nosso código, em um computador com 8 núcleos, quase 6x mais rápido. Por esse motivo, GPUs são extremamente ágeis em Path Tracing e Ray Tracing do que CPUs, já que seu código pode ser paralelizado muito facilmente.

Resultados

Captura de Tela 2023-12-18 às 23 47 05
Captura de Tela 2023-12-18 às 23 47 05
Captura de Tela 2023-12-18 às 23 47 05

Captura de Tela 2023-12-18 às 23 47 05


Captura de Tela 2023-12-18 às 23 47 05
Captura de Tela 2023-12-18 às 23 47 05

Captura de Tela 2023-12-18 às 23 47 05


Captura de Tela 2023-12-18 às 23 47 05


O que melhorar?

  • Poderíamos aprimorar o algoritmo de Monte Carlo com Jittering (PDF)
  • Implementar um Path Tracing Bidirecional
  • Photon Mapping
  • Criar um editor de mundo
  • Implementar Modelos 3D de forma mais ampla
  • Mais materiais
  • Implementar em CUDA para renderizar em tempo real em GPU

Referências e Leituras recomendadas

  1. Ray Tracing in One Weekend. Disponível em: https://raytracing.github.io. Acesso em: [19/12/2023]. (Para mais detalhes de implementação, veja isto)

  2. Scratchapixel. Disponível em: https://www.scratchapixel.com. Acesso em: [19/12/2023].

  3. Azure from the Trenches. An Introductory Guide to AABB Tree Collision Detection. Disponível em: https://www.azurefromthetrenches.com/introductory-guide-to-aabb-tree-collision-detection/. Acesso em: [19/12/2023].

  4. Pixar. Path Traced Movies. Disponível em: https://graphics.pixar.com/library/PathTracedMovies/paper.pdf. Acesso em: [19/12/2023].

  5. Physically Based Rendering: From Theory to Implementation. Disponível em: https://www.pbrt.org. Acesso em: [19/12/2023]. (Ideal para aprender Computação Gráfica, muito mais completo que o Ray Tracing in One Weekend).

  6. YouTube - Walt Disney Animation Studios. Disney's Practical Guide to Path Tracing. Disponível em: https://www.youtube.com/watch?v=frLwRLS_ZR0. Acesso em: [19/12/2023].

  7. YouTube - LGR. Bryce 3D: Making Surreal Trapper Keeper/DnB Art on Windows 95. Disponível em: https://www.youtube.com/watch?v=EGIwcPA1_34&t=589s. Acesso em: [19/12/2023].