Comment l'anti-aliasing est-il implémenté dans Ray Tracing?

13

Après avoir lu quelques articles en ligne, je peux dire en toute confiance que je ne sais pas comment fonctionne l'anti-aliasing lorsque Ray Tracing .

Tout ce que je comprends, c'est qu'un pixel / rayon unique est divisé en 4 sous-pixels et 4 rayons plutôt que 1 .

Quelqu'un pourrait-il expliquer comment cela se fait (de préférence avec du code)?

Arjan Singh
la source
2
Puis-je simplement vous suggérer de regarder "supersampling" en.wikipedia.org/wiki/Supersampling et peut-être aussi en.wikipedia.org/wiki/Distributed_ray_tracing ?
Simon F
2
Je peux également recommander de lire ce chapitre de PBRT pbrt.org/chapters/pbrt_chapter7.pdf et de lire cet article lgdv.cs.fau.de/get/785 (qui explique une technique différente de celle implémentée dans pbrt).
Tom van Bussel
1
foreach pixel : p{acc = 0; foreach subsample : s { acc+=sample_scene(s);} store(p, acc);}
phénomène de cliquet

Réponses:

12

Je pense qu'il est prudent de dire qu'il existe deux façons différentes de faire AA dans le raytracing:

1: si vous avez l'image finale et l'image de profondeur, il est possible d'appliquer presque toutes les techniques existantes utilisées dans les jeux (FXAA, etc.). Elles fonctionnent directement sur l'image finale et ne sont pas liées au lancer de rayons.

2: la deuxième méthode consiste à prendre en compte plusieurs rayons pour chaque pixel puis à faire la moyenne du résultat. Pour une version très simple, pensez-y comme ceci:

  • vous rendez d'abord une image de taille 1024x1024, un rayon pour chaque pixel (par exemple)
  • après le rendu, vous redimensionnez l'image à 512x512 (chaque 4 pixels sont moyennés en un) et vous pouvez remarquer que les bords sont plus lisses. De cette façon, vous avez effectivement utilisé 4 rayons pour chaque pixel dans l'image finale de taille 512x512.

Il existe d'autres variantes de cette méthode. Par exemple, vous pouvez adapter le nombre d'échantillons pour les pixels situés juste au bord de la géométrie, ce qui signifie que pour certains pixels, vous n'aurez que 4 échantillons et pour d'autres 16.

Vérifiez les liens dans les commentaires ci-dessus.

Raxvan
la source
Donc, fondamentalement, je rends une image à une grande taille et lorsque je l'enregistre dans une image, la redimensionne à une taille inférieure? Cela semble assez simple :)! Est-ce la super méthode d'échantillonnage?
Arjan Singh
1
@Arjan Singh oui c'est en.wikipedia.org/wiki/Supersampling , mais c'est le plus lent de tous, le raytracing vous permet de faire facilement un suréchantillonnage adaptatif, qui peut beaucoup mieux fonctionner
Raxvan
13

Raxvan a tout à fait raison de dire que les techniques d'anti-crénelage "traditionnelles" fonctionneront dans le lancer de rayons, y compris celles qui utilisent des informations telles que la profondeur pour faire l'anti-crénelage. Vous pouvez même faire un anti-crénelage temporel dans le lancer de rayons par exemple.

Julien a développé le deuxième élément de Raxvan, qui expliquait le super échantillonnage, et a montré comment vous le feriez réellement, en mentionnant également que vous pouvez randomiser l'emplacement des échantillons dans le pixel, mais ensuite vous entrez dans le pays de traitement du signal, ce qui est beaucoup plus profond, et c'est définitivement!

NN

Si vous faites cela, vous pouvez toujours obtenir un alias. C'est mieux que de ne PAS le faire, car vous augmentez votre taux d'échantillonnage, vous serez donc en mesure de gérer des données de fréquence plus élevée (c'est-à-dire des détails plus petits), mais cela peut toujours provoquer un alias.

N

Lorsque vous utilisez uniquement des nombres aléatoires "normaux" comme vous le feriez avec rand () ou std :: uniform_int_distribution, cela est appelé "bruit blanc" car il contient toutes les fréquences, comme la façon dont la lumière blanche est composée de toutes les autres couleurs (fréquences ) de la lumière.

L'utilisation de bruit blanc pour randomiser les échantillons dans un pixel a le problème que parfois vos échantillons s'agglutinent. Par exemple, si vous effectuez en moyenne 100 échantillons dans un pixel, mais qu'ils finissent tous par se trouver dans le coin supérieur gauche du pixel, vous n'obtiendrez AUCUNE information sur les autres parties du pixel, donc la couleur finale du pixel résultant il manquera des informations sur sa couleur.

Une meilleure approche consiste à utiliser quelque chose appelé bruit bleu qui ne contient que des composants à haute fréquence (comme la lumière bleue à haute fréquence).

L'avantage du bruit bleu est que vous obtenez une couverture uniforme sur le pixel, comme vous le faites avec une grille d'échantillonnage uniforme, mais vous obtenez toujours un caractère aléatoire, ce qui transforme l'alias en bruit et vous donne une meilleure image.

Malheureusement, le bruit bleu peut être très coûteux à calculer, et les meilleures méthodes semblent toutes brevetées (qu'est-ce que c'est que ça?!), Mais une façon de le faire, inventée par pixar (et brevetée aussi je pense mais pas à 100%) consiste à créer une grille uniforme de points d'échantillonnage, puis à décaler au hasard chaque point d'échantillonnage d'une petite quantité - comme une quantité aléatoire entre plus ou moins la moitié de la largeur et de la hauteur de la grille d'échantillonnage. De cette façon, vous obtenez une sorte d'échantillonnage de bruit bleu pour pas cher.

Notez qu'il s'agit d'une forme d'échantillonnage stratifié, et l'échantillonnage de disque de poisson est également une forme de cela, qui est également un moyen de générer du bruit bleu: https://www.jasondavies.com/poisson-disc/

Si vous êtes intéressé à approfondir, vous voudrez probablement aussi consulter cette question et répondre!

Quel est le raisonnement fondamental pour l'anti-aliasing utilisant plusieurs échantillons aléatoires dans un pixel?

Enfin, ce truc commence à s'égarer dans le domaine du traçage de chemin de Monte Carlo qui est la méthode courante pour faire un raytracing photoréaliste. si vous souhaitez en savoir plus à ce sujet, lisez ceci!

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

Alan Wolfe
la source
7

Supposons une boucle principale de lancer de rayons assez typique:

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

Une modification possible de celui-ci pour faire 4 échantillons MSAA serait:

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

Une autre possibilité est de faire une gigue aléatoire (au lieu de la matrice basée sur celle ci-dessus), mais vous entrez rapidement dans le domaine du traitement du signal et il faut beaucoup de lecture pour savoir comment choisir une bonne fonction de bruit.

L'idée reste cependant la même: considérez le pixel comme représentant une petite zone carrée, et au lieu de ne filmer qu'un seul rayon qui passe par le centre du pixel, tirez de nombreux rayons couvrant toute la zone du pixel. Plus la distribution des rayons est dense, meilleur est le signal.

PS: J'ai écrit le code ci-dessus à la volée, je m'attends donc à quelques erreurs. Il ne s'agit que de montrer l'idée de base.

Julien Guertault
la source
Très bonne réponse! Quels seraient les avantages d'utiliser cette méthode par rapport à la méthode @Raxvan utilisée? Vais-je obtenir les mêmes résultats en effectuant un rendu à une grande taille, puis en réduisant l'échelle à une taille plus petite?
Arjan Singh
Fondamentalement, avec le lancer de rayons, vous n'avez tout simplement pas besoin de rendre une image plus grande, puis de la réduire. Cela signifie que vous avez beaucoup plus de flexibilité: vous pouvez avoir beaucoup d'échantillons, vous pouvez varier le nombre d'échantillons en fonction de la région, et simplement, vous n'avez pas à ajouter l'étape de mise à l'échelle.
Julien Guertault
2
Sur le sujet de la gigue, cela s'avère être un sujet assez complexe. Voici un excellent article analysant l'état de l'art il y a quelques années graphics.pixar.com/library/MultiJitteredSampling/paper.pdf
Mikkel Gjoel
L'exemple de code ci-dessus utilise un MSAA à 4 échantillons, si je voulais faire 8x MSAA, à quoi ressemblerait la matrice alors? Que devrais-je changer dans la matrice de gigue ci-dessus?
Arjan Singh du