Como o Anti Aliasing é implementado no Ray Tracing?

12

Depois de ler alguns artigos on-line, posso dizer com confiança que não tenho noção de como o Anti-Aliasing funciona quando o Ray Tracing .

Tudo o que entendo é que um único pixel / raio é dividido em 4 sub-pixels e 4 raios em vez de 1 .

Alguém poderia explicar como isso é feito (de preferência com código)?

Arjan Singh
fonte
2
Posso apenas sugerir que você veja "supersampling" en.wikipedia.org/wiki/Supersampling e talvez também en.wikipedia.org/wiki/Distributed_ray_tracing ?
Simon F
2
Também posso recomendar a leitura deste capítulo do PBRT pbrt.org/chapters/pbrt_chapter7.pdf e a leitura deste artigo lgdv.cs.fau.de/get/785 (que explica uma técnica diferente daquela implementada no pbrt).
Tom van Bussel
1
foreach pixel : p{acc = 0; foreach subsample : s { acc+=sample_scene(s);} store(p, acc);}
catraca aberração

Respostas:

11

Eu acho que é seguro dizer que existem duas maneiras diferentes de fazer AA no raytracing:

1: se você tiver a imagem final e a imagem em profundidade, é possível aplicar quase todas as técnicas existentes usadas em jogos (FXAA, etc.). Essas trabalham diretamente na imagem final e não estão relacionadas ao raytracing.

2: o segundo método é levar em consideração vários raios para cada pixel e calcular a média do resultado. Para uma versão muito simples, pense assim:

  • você renderiza primeiro uma imagem de tamanho 1024x1024, um raio para cada pixel (por exemplo)
  • após a renderização, você redimensiona a imagem para 512x512 (cada 4 pixels são redimensionados em um) e pode observar que as bordas são mais suaves. Dessa forma, você usou efetivamente 4 raios para cada pixel na imagem final do tamanho de 512x512.

Existem outras variações nesse método. Por exemplo, você pode adaptar o número de amostras de pixels que estão exatamente na borda da geometria, o que significa que, para alguns pixels, você terá apenas 4 amostras e para outras 16.

Verifique os links nos comentários acima.

Raxvan
fonte
Então, basicamente, eu renderizo uma imagem para um tamanho grande e, ao salvá-la em uma imagem, reduzi-la para um tamanho menor? Isso parece bastante simples :)! Esse é o método de super amostragem?
Arjan Singh
1
@ Arjan Singh sim é en.wikipedia.org/wiki/Supersampling , mas este é o mais lento de todos eles, raytracing permite que você facilmente fazer supersampling adaptativa, que pode realizar muito melhor
Raxvan
12

Raxvan está completamente certo de que as técnicas "tradicionais" de anti-aliasing funcionarão no raytracing, incluindo aquelas que usam informações como profundidade para executar o antialiasing. Você pode até fazer anti-aliasing temporal no traçado de raios, por exemplo.

Julien expandiu o segundo item de Raxvan, que era uma explicação da super amostragem, e mostrou como você realmente faria isso, mencionando também que você pode aleatoriamente a localização das amostras dentro do pixel, mas então você está inserindo o país de processamento de sinal, que é muito mais profundo, e definitivamente é!

NN

Se você fizer isso, ainda poderá obter o aliasing. É melhor do que NÃO fazê-lo, porque você está aumentando sua taxa de amostragem; portanto, será capaz de lidar com dados de frequência mais alta (também conhecidos como detalhes menores), mas ainda pode causar aliases.

N

Quando você usa apenas números aleatórios "regulares" como os obtidos com rand () ou std :: uniform_int_distribution, isso é chamado de "ruído branco" porque contém todas as frequências, como a luz branca é composta por todas as outras cores (frequências ) de luz.

O uso de ruído branco para aleatorizar as amostras em um pixel tem o problema de que algumas vezes suas amostras se agrupam. Por exemplo, se você calcula a média de 100 amostras em um pixel, mas TODAS elas acabam no canto superior esquerdo do pixel, você não obtém QUALQUER informação sobre as outras partes do pixel, portanto, a cor final do pixel resultante faltam informações sobre qual cor deve ser.

Uma abordagem melhor é usar algo chamado ruído azul, que contém apenas componentes de alta frequência (como a luz azul é a luz de alta frequência).

O benefício do ruído azul é que você obtém uma cobertura uniforme sobre o pixel, como em uma grade de amostragem uniforme, mas ainda obtém alguma aleatoriedade, que transforma o aliasing em ruído e oferece uma imagem com melhor aparência.

Infelizmente, o ruído azul pode ser muito caro para calcular, e os melhores métodos parecem todos patenteados (que diabos ?!), mas uma maneira de fazer isso, inventada pela pixar (e patenteada também, eu acho, mas não 100% certa) é criar uma grade uniforme de pontos de amostra e, em seguida, deslocar aleatoriamente cada ponto de amostra em uma pequena quantidade - como uma quantidade aleatória entre mais ou menos metade da largura e altura da grade de amostragem. Dessa forma, você obtém uma espécie de amostragem de ruído azul por muito barato.

Observe que esta é uma forma de amostragem estratificada e a amostragem de disco de poisson também é uma forma de gerar ruído azul: https://www.jasondavies.com/poisson-disc/

Se você estiver interessado em ir mais fundo, provavelmente também desejará conferir esta pergunta e responder!

Qual é o raciocínio fundamental para o anti-aliasing usando várias amostras aleatórias em um pixel?

Por fim, esse material está começando a se desviar para o domínio do rastreamento de caminhos de monte carlo, que é o método comum para fazer o rastreamento de raios fotorrealista. se você estiver interessado em aprender mais sobre isso, leia isso!

http://blog.demofox.org/2016/09/21/path-tracing-getting-started-with-diffuse-and-emissive/

Alan Wolfe
fonte
7

Vamos supor um loop principal do raytracing bastante típico:

struct Ray
{
    vec3 origin;
    vec3 direction;
};

RGBColor* image = CreateImageBuffer(width, height);

for (int j=0; j < height; ++i)
{
    for (int i=0; i < width; ++i)
    {
        float x = 2.0 * (float)i / (float)max(width, height) - 1.0;
        float y = 2.0 * (float)j / (float)max(width, height) - 1.0;

        vec3 dir = normalize(vec3(x, y, -tanHalfFov));
        Ray r = { cameraPosition, dir };

        image[width * j + i] = ComputeColor(r);
    }
}

Uma possível modificação para fazer 4 amostras do MSAA seria:

float jitterMatrix[4 * 2] = {
    -1.0/4.0,  3.0/4.0,
     3.0/4.0,  1.0/3.0,
    -3.0/4.0, -1.0/4.0,
     1.0/4.0, -3.0/4.0,
};

for (int j=0; j < height; ++i)
{
    for (int i=0; i < width; ++i)
    {
        // Init the pixel to 100% black (no light).
        image[width * j + i] = RGBColor(0.0);

        // Accumulate light for N samples.
        for (int sample = 0; sample < 4; ++sample)
        {
            float x = 2.0 * (i + jitterMatrix[2*sample]) / (float)max(width, height) - 1.0;
            float y = 2.0 * (i + jitterMatrix[2*sample+1]) / (float)max(width, height) - 1.0;

            vec3 dir = normalize(vec3(x, y, -tanHalfFov) + jitter);
            Ray r = { cameraPosition, dir };

            image[width * j + i] += ComputeColor(r);
        }

        // Get the average.
        image[width * j + i] /= 4.0;
    }
}

Outra possibilidade é fazer um jitter aleatório (em vez da matriz baseada acima), mas logo você entra no campo do processamento de sinais e é preciso muita leitura para saber como escolher uma boa função de ruído.

A idéia permanece a mesma: considere o pixel como uma pequena área quadrada e, em vez de disparar apenas um raio que passa pelo centro do pixel, atire em muitos raios cobrindo toda a área do pixel. Quanto mais densa a distribuição de raios, melhor o sinal que você recebe.

PS: Eu escrevi o código acima em tempo real, então esperaria alguns erros nele. Destina-se apenas a mostrar a ideia básica.

Julien Guertault
fonte
Ótima resposta! Quais seriam os benefícios de usar esse método em oposição ao método @Raxvan usado? Obterei os mesmos resultados renderizando em um tamanho grande e diminuindo para um tamanho menor?
Arjan Singh
Fundamentalmente, com o traçado de raios, você não precisa renderizar uma imagem maior e reduzi-la. Isso significa que você tem muito mais flexibilidade: pode ter muitas amostras, pode variar o número de amostras dependendo da região e, simplesmente, não precisa adicionar a etapa de nova escala.
Julien Guertault
2
No tópico de tremulação, esse acaba sendo um tópico bastante complexo. Aqui está um grande papel analisar a state-of-the-art de alguns anos atrás graphics.pixar.com/library/MultiJitteredSampling/paper.pdf
Mikkel Gjoel
O exemplo de código acima usa um MSAA de 4 exemplos. Se eu quisesse fazer o MSAA 8x, como seria a matriz? O que eu precisaria mudar na matriz de jitter mostrada acima?
Arjan Singh