Come viene implementato l'antialiasing in Ray Tracing?


13

Dopo aver letto alcuni articoli online, posso tranquillamente dire che non ho idea di come funzioni l'antialiasing quando Ray Tracing .

Tutto quello che capisco è che A Single Pixel / Ray è diviso in 4 sub-pixel e 4 raggi anziché 1 .

Qualcuno potrebbe spiegare come è fatto (preferibilmente con il codice)?


2
Posso solo suggerirti di guardare "supersampling" en.wikipedia.org/wiki/Supersampling e forse anche en.wikipedia.org/wiki/Distributed_ray_tracing ?
Simon F,

2
Posso anche raccomandare di leggere questo capitolo di PBRT pbrt.org/chapters/pbrt_chapter7.pdf e di leggere questo documento lgdv.cs.fau.de/get/785 (che spiega una tecnica diversa da quella implementata in pbrt).
Tom van Bussel,

1
foreach pixel : p{acc = 0; foreach subsample : s { acc+=sample_scene(s);} store(p, acc);}
maniaco del cricchetto,

Risposte:


12

Penso che sia sicuro dire che ci sono due modi diversi di fare AA nel raytracing:

1: se si dispone dell'immagine finale e dell'immagine di profondità è possibile applicare quasi tutte le tecniche esistenti utilizzate nei giochi (FXAA, ecc.) Che funzionano direttamente sull'immagine finale e non sono correlate al raytracing

2: il secondo metodo consiste nel prendere in considerazione più raggi per ciascun pixel e quindi fare la media del risultato. Per una versione molto semplice pensala in questo modo:

  • si esegue prima il rendering di un'immagine di dimensioni 1024x1024, un raggio per ogni pixel (ad esempio)
  • dopo il rendering, ridimensionate l'immagine su 512x512 (ogni 4 pixel sono suddivisi in uno) e noterete che i bordi sono più lisci. In questo modo hai effettivamente utilizzato 4 raggi per ogni pixel nell'immagine finale di dimensioni 512x512.

Esistono altre variazioni su questo metodo. Ad esempio, puoi adattare il numero di campioni per i pixel che si trovano proprio sul bordo della geometria, il che significa che per alcuni pixel avrai solo 4 campioni e per altri 16.

Controlla i link nei commenti sopra.


Quindi, fondamentalmente, eseguo il rendering di un'immagine di grandi dimensioni e quando la salvi su un'immagine, la ridimensiono a una dimensione inferiore? Sembra abbastanza semplice :)! È questo il metodo di supercampionamento?
Arjan Singh,

1
@Arjan Singh sì, è en.wikipedia.org/wiki/Supersampling , ma questo è il più lento di tutti, il raytracing ti consente di fare facilmente il supersampling adattivo, che può eseguire molto meglio
Raxvan

13

Raxvan ha perfettamente ragione sul fatto che le tecniche anti-aliasing "tradizionali" funzioneranno nel raytracing, comprese quelle che usano informazioni come la profondità per fare l'antialiasing. Ad esempio, potresti anche fare un anti aliasing temporale nel ray tracing.

Julien ha ampliato il secondo elemento di Raxvan, che era una spiegazione del super campionamento, e ha mostrato come lo avresti effettivamente fatto, menzionando anche che puoi randomizzare la posizione dei campioni all'interno del pixel ma poi stai entrando nel paese di elaborazione del segnale che è molto più in profondità, e lo è sicuramente!

NN

Se lo fai, puoi comunque ottenere l'aliasing. È meglio che NON farlo, perché stai aumentando la frequenza di campionamento, quindi sarai in grado di gestire dati a frequenza più alta (ovvero dettagli più piccoli), ma può comunque causare aliasing.

N

Quando usi solo numeri casuali "regolari" come quelli che ottieni da rand () o std :: uniform_int_distribution, questo è chiamato "rumore bianco" perché contiene tutte le frequenze, come il modo in cui la luce bianca è composta da tutti gli altri colori (frequenze ) di luce.

L'uso del rumore bianco per randomizzare i campioni all'interno di un pixel ha il problema che a volte i campioni si raggruppano insieme. Ad esempio, se hai una media di 100 campioni in un pixel, ma TUTTI finiscono per essere nell'angolo in alto a sinistra del pixel, non otterrai NESSUNA informazione sulle altre parti del pixel, quindi il colore finale del pixel risultante mancheranno informazioni su quale colore dovrebbe essere.

Un approccio migliore consiste nell'utilizzare qualcosa chiamato rumore blu che contiene solo componenti ad alta frequenza (come il modo in cui la luce blu è luce ad alta frequenza).

Il vantaggio del rumore blu è che si ottiene una copertura uniforme sul pixel, come si ottiene con una griglia di campionamento uniforme, ma si ottiene ancora una certa casualità, che trasforma l'aliasing in rumore e ti dà un'immagine più bella.

Sfortunatamente, il rumore blu può essere molto costoso da calcolare e tutti i migliori metodi sembrano essere brevettati (che diamine ?!), ma un modo per farlo, inventato da Pixar (e anche brevettato penso ma non sicuro al 100%) consiste nel creare una griglia uniforme di punti campione, quindi compensare casualmente ogni punto campione come una quantità casuale tra più o meno metà della larghezza e dell'altezza della griglia di campionamento. In questo modo ottieni una sorta di campionamento del rumore blu per un prezzo abbastanza basso.

Si noti che questa è una forma di campionamento stratificato e anche il campionamento del disco di Poisson è una forma di questo, che è anche un modo per generare rumore blu: https://www.jasondavies.com/poisson-disc/

Se sei interessato ad approfondire, probabilmente vorrai anche dare un'occhiata a questa domanda e rispondere!

Qual è il ragionamento fondamentale per l'antialiasing che utilizza più campioni casuali all'interno di un pixel?

Infine, questa roba sta iniziando a vagare nel regno del tracciato del percorso di Monte Carlo, che è il metodo comune per fare il raytracing fotorealistico. se sei interessato a saperne di più, dai una lettura!

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


7

Supponiamo che un loop principale di raytracing abbastanza tipico:

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);
    }
}

Una possibile modifica per eseguire 4 campioni di MSAA sarebbe:

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;
    }
}

Un'altra possibilità è quella di eseguire un jitter casuale (anziché quello basato sulla matrice sopra), ma si entra presto nel regno dell'elaborazione del segnale e ci vuole molta lettura per sapere come scegliere una buona funzione di rumore.

L'idea rimane la stessa: considera il pixel come una minuscola area quadrata e invece di riprendere un solo raggio che passa attraverso il centro del pixel, scatta molti raggi coprendo l'intera area del pixel. Più densa è la distribuzione dei raggi, migliore è il segnale che ricevi.

PS: ho scritto il codice sopra al volo, quindi mi aspetto alcuni errori. Ha solo lo scopo di mostrare l'idea di base.


Bella risposta! Quali sarebbero i vantaggi dell'utilizzo di questo metodo rispetto al metodo utilizzato da @Raxvan? Otterrò gli stessi risultati eseguendo il rendering su una dimensione grande e poi ridimensionando a una dimensione inferiore?
Arjan Singh,

Fondamentalmente, con il ray tracing non è necessario renderizzare un'immagine più grande, quindi ridimensionarla. Ciò significa che hai molta più flessibilità: puoi avere molti campioni, puoi variare il numero di campioni a seconda della regione e, semplicemente, non devi aggiungere il passaggio di riscalamento.
Julien Guertault,

2
Sul tema del jitter, questo risulta essere un argomento abbastanza complesso. Ecco un ottimo documento che analizza lo stato dell'arte alcuni anni fa graphics.pixar.com/library/MultiJitteredSampling/paper.pdf
Mikkel Gjoel

L'esempio di codice sopra usa un MSAA a 4 campioni, se volessi fare 8 MSAA come sarebbe la matrice allora? Cosa dovrei cambiare nella matrice jitter mostrata sopra?
Arjan Singh,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.