Wie wird Anti-Aliasing in Ray Tracing implementiert?

12

Nachdem ich ein paar Artikel online gelesen habe, kann ich mit Sicherheit sagen, dass ich keine Ahnung habe, wie Anti-Aliasing beim Ray Tracing funktioniert .

Ich verstehe nur, dass ein einzelnes Pixel / Strahl in 4 Unterpixel und 4 Strahlen anstatt in 1 aufgeteilt ist .

Könnte jemand bitte erklären, wie das gemacht wird (vorzugsweise mit Code)?

Arjan Singh
quelle
2
Kann ich nur vorschlagen, dass Sie sich "supersampling" anschauen, en.wikipedia.org/wiki/Supersampling und vielleicht auch en.wikipedia.org/wiki/Distributed_ray_tracing ?
Simon F
2
Ich kann auch empfehlen dieses Kapitel PBRT Lesen pbrt.org/chapters/pbrt_chapter7.pdf und Lesen dieses Papier lgdv.cs.fau.de/get/785 (die eine andere Technik als die in PBRT implementiert erklärt).
Tom van Bussel
1
foreach pixel : p{acc = 0; foreach subsample : s { acc+=sample_scene(s);} store(p, acc);}
Ratschenfreak

Antworten:

11

Ich glaube, man kann mit Sicherheit sagen, dass es beim Raytracing zwei verschiedene Möglichkeiten gibt, AA durchzuführen:

1: Wenn Sie das endgültige Bild und das Tiefenbild haben, können Sie fast alle vorhandenen Techniken anwenden, die in Spielen verwendet werden (FXAA usw.). Diese wirken direkt auf das endgültige Bild und haben nichts mit Raytracing zu tun

2: Die zweite Methode besteht darin, mehrere Strahlen für jedes Pixel zu berücksichtigen und dann das Ergebnis zu mitteln. Für eine sehr einfache Version denken Sie daran wie folgt:

  • Sie rendern zuerst ein Bild der Größe 1024x1024, zum Beispiel einen Strahl pro Pixel.
  • Nach dem Rendern skalieren Sie das Bild auf 512 x 512 (jeweils 4 Pixel werden zu einem Pixel zusammengerechnet) und stellen fest, dass die Kanten glatter sind. Auf diese Weise haben Sie effektiv 4 Strahlen für jedes Pixel im endgültigen Bild mit einer Größe von 512 x 512 verwendet.

Es gibt andere Variationen dieser Methode. Sie können beispielsweise die Anzahl der Abtastwerte für Pixel anpassen, die sich direkt am Rand der Geometrie befinden. Dies bedeutet, dass Sie für einige Pixel nur 4 Abtastwerte und für andere 16 haben.

Überprüfen Sie die Links in den Kommentaren oben.

Raxvan
quelle
Im Grunde genommen rendere ich ein Bild auf eine große Größe und verkleinere es, wenn ich es auf ein Bild speichere? Das scheint ganz einfach zu sein :)! Ist dies die Super-Sampling-Methode?
Arjan Singh
1
@Arjan Singh Ja, es ist de.wikipedia.org/wiki/Supersampling , aber dies ist das langsamste von allen. Mit Raytracing können Sie einfach adaptives Supersampling durchführen, das viel bessere
Ergebnisse erzielen
12

Raxvan hat vollkommen Recht, dass "traditionelle" Anti-Aliasing-Techniken beim Raytracing funktionieren, einschließlich solcher, die Informationen wie beispielsweise die Tiefe für das Antialiasing verwenden. Sie können zum Beispiel auch temporäres Anti-Aliasing beim Ray-Tracing durchführen.

Julien erweiterte Raxvans zweiten Punkt, der eine Erklärung für Super-Sampling war, und zeigte, wie Sie das tatsächlich tun würden, und erwähnte auch, dass Sie die Position der Samples innerhalb des Pixels zufällig bestimmen können, aber dann das Land der Signalverarbeitung eingeben, was sehr viel ist tiefer, und es ist definitiv!

NN

Wenn Sie das tun, können Sie dennoch Aliasing erhalten. Es ist besser, als es NICHT zu tun, da Sie Ihre Abtastrate erhöhen, um Daten mit höherer Frequenz (auch bekannt als kleinere Details) verarbeiten zu können, aber es kann trotzdem Aliasing verursachen.

N

Wenn Sie nur "reguläre" Zufallszahlen verwenden, wie Sie sie von rand () oder std :: uniform_int_distribution erhalten, wird dies als "weißes Rauschen" bezeichnet, da es alle Frequenzen enthält, beispielsweise wie weißes Licht aus allen anderen Farben (Frequenzen) besteht ) des Lichts.

Die Verwendung von weißem Rauschen zum Randomisieren der Samples innerhalb eines Pixels hat das Problem, dass Ihre Samples manchmal zusammenklumpen. Wenn Sie beispielsweise einen Durchschnitt von 100 Samples in einem Pixel bilden, die sich jedoch ALLE in der oberen linken Ecke des Pixels befinden, erhalten Sie keine Informationen zu den anderen Teilen des Pixels, sodass die endgültige Pixelfarbe erhalten wird Es fehlen Angaben dazu, welche Farbe es haben soll.

Ein besserer Ansatz ist die Verwendung von sogenanntem blauem Rauschen, das nur hochfrequente Komponenten enthält (z. B. wie blaues Licht hochfrequentes Licht ist).

Der Vorteil des blauen Rauschens ist, dass Sie eine gleichmäßige Abdeckung des Pixels erhalten, wie dies bei einem einheitlichen Abtastraster der Fall ist. Sie erhalten jedoch immer noch eine gewisse Zufälligkeit, die Aliasing in Rauschen umwandelt und Ihnen ein besser aussehendes Bild verleiht.

Leider kann das Berechnen von blauem Rauschen sehr kostspielig sein, und die besten Methoden scheinen alle patentiert zu sein (was zum Teufel ?!), aber eine Möglichkeit, dies zu tun, wurde von pixar erfunden (und ich denke auch patentiert, aber nicht 100% sicher). Soll ein gleichmäßiges Raster von Abtastpunkten erstellt werden, versetzen Sie jeden Abtastpunkt zufällig um einen kleinen Betrag - wie einen zufälligen Betrag zwischen plus oder minus der Hälfte der Breite und Höhe des Abtastrasters. Auf diese Weise erhalten Sie eine Art Sampling für blaues Rauschen für ziemlich billig.

Beachten Sie, dass dies eine Form der geschichteten Abtastung ist, und dass die Poisson-Disk-Abtastung auch eine Form davon ist, wodurch auch blaues Rauschen erzeugt wird: https://www.jasondavies.com/poisson-disc/

Wenn Sie daran interessiert sind, tiefer zu gehen, werden Sie wahrscheinlich auch diese Frage und Antwort prüfen wollen!

Was ist die fundamentale Begründung für Anti-Aliasing mit mehreren Zufallsstichproben innerhalb eines Pixels?

Schließlich beginnt dieses Zeug, in den Bereich der Pfadverfolgung von Monte Carlo abzuweichen, die die übliche Methode für die fotorealistische Strahlverfolgung ist. Wenn Sie mehr darüber erfahren möchten, lesen Sie es bitte!

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

Alan Wolfe
quelle
7

Nehmen wir eine ziemlich typische Raytracing-Hauptschleife an:

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

Eine mögliche Modifikation für 4 MSAA-Stichproben wäre:

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

Eine andere Möglichkeit besteht darin, einen zufälligen Jitter zu erzeugen (anstelle des obigen matrixbasierten Jitters), aber Sie betreten bald den Bereich der Signalverarbeitung und müssen viel lesen, um zu wissen, wie Sie eine gute Rauschfunktion auswählen.

Die Idee bleibt jedoch dieselbe: Betrachten Sie das Pixel als einen winzigen quadratischen Bereich, und anstatt nur einen Strahl durch die Mitte des Pixels zu schießen, schießen Sie viele Strahlen, die den gesamten Pixelbereich abdecken. Je dichter die Strahlenverteilung ist, desto besser ist das Signal.

PS: Ich habe den obigen Code im laufenden Betrieb geschrieben, daher würde ich ein paar Fehler darin erwarten. Es soll nur die Grundidee zeigen.

Julien Guertault
quelle
Gute Antwort! Was wären die Vorteile dieser Methode gegenüber der verwendeten Methode @Raxvan? Erhalte ich die gleichen Ergebnisse, wenn ich auf eine große Größe rendere und dann auf eine kleinere Größe verkleinere?
Arjan Singh
Grundsätzlich müssen Sie mit Ray Tracing kein größeres Bild rendern, als es zu verkleinern. Das heißt, Sie haben viel mehr Flexibilität: Sie können viele Samples haben, Sie können die Anzahl der Samples abhängig von der Region variieren und Sie müssen einfach den Rescale-Schritt nicht hinzufügen.
Julien Guertault
2
Beim Thema Jittering stellt sich heraus, dass dies ein ziemlich komplexes Thema ist. Hier ist ein großartiger
Artikel
Das obige Codebeispiel verwendet eine MSAA mit 4 Beispielen. Wenn ich 8x MSAA ausführen möchte, wie würde die Matrix dann aussehen? Was muss ich in der oben gezeigten Jittermatrix ändern?
Arjan Singh