¿Cómo se implementa Anti Aliasing en Ray Tracing?

13

Después de leer algunos artículos en línea, puedo decir con confianza que no tengo idea de cómo funciona el Anti-Aliasing cuando Ray Tracing .

Todo lo que entiendo es que un solo píxel / rayo se divide en 4 subpíxeles y 4 rayos en lugar de 1 .

¿Alguien podría explicar cómo se hace esto (preferiblemente con código)?

Arjan Singh
fuente
2
¿Puedo sugerirle que mire "supersampling" en.wikipedia.org/wiki/Supersampling y quizás también en.wikipedia.org/wiki/Distributed_ray_tracing ?
Simon F
2
También puedo recomendar leer este capítulo de PBRT pbrt.org/chapters/pbrt_chapter7.pdf y leer este documento lgdv.cs.fau.de/get/785 (que explica una técnica diferente a la implementada en pbrt).
Tom van Bussel el
1
foreach pixel : p{acc = 0; foreach subsample : s { acc+=sample_scene(s);} store(p, acc);}
Ratchet freak

Respuestas:

12

Creo que es seguro decir que hay dos formas diferentes de hacer AA en el trazado de rayos:

1: si tiene la imagen final y la imagen de profundidad, es posible aplicar casi todas las técnicas existentes que se utilizan en los juegos (FXAA, etc.) que funcionan directamente en la imagen final y no están relacionadas con el trazado de rayos

2: el segundo método es tener en cuenta múltiples rayos para cada píxel y luego promediar el resultado. Para una versión muy simple, piense así:

  • primero representa una imagen de tamaño 1024x1024, un rayo por cada píxel (por ejemplo)
  • después de renderizar, escala la imagen a 512x512 (cada 4 píxeles se vuelven a formar uno) y puede notar que los bordes son más suaves. De esta manera, ha utilizado efectivamente 4 rayos para cada píxel en la imagen final de tamaño 512x512.

Hay otras variaciones en este método. Por ejemplo, puede adaptar el número de muestras para píxeles que están justo en el borde de la geometría, lo que significa que para algunos píxeles tendrá solo 4 muestras y para otros 16.

Verifique los enlaces en los comentarios anteriores.

Raxvan
fuente
¿Entonces básicamente renderizo una imagen a un tamaño grande y cuando la guardo en una imagen, la reduzco a un tamaño menor? Eso parece bastante simple :)! ¿Es este el método de supermuestreo?
Arjan Singh
1
@Arjan Singh sí, es en.wikipedia.org/wiki/Supersampling , pero este es el más lento de todos, el trazado de rayos le permite realizar fácilmente supermuestreo adaptativo, que puede funcionar mucho mejor
Raxvan
13

Raxvan tiene toda la razón en que las técnicas antisolapamiento "tradicionales" funcionarán en el trazado de rayos, incluidas las que usan información como la profundidad para hacer antialiasing. Incluso podría hacer un anti aliasing temporal en el trazado de rayos, por ejemplo.

Julien amplió el segundo elemento de Raxvan, que era una explicación de supermuestreo, y mostró cómo lo haría realmente, también mencionó que puede aleatorizar la ubicación de las muestras dentro del píxel, pero luego ingresa al país de procesamiento de señal, que es mucho más profundo, y definitivamente lo es!

NN

Si haces eso, aún puedes obtener alias. Es mejor que NO hacerlo, ya que está aumentando su frecuencia de muestreo, por lo que podrá manejar datos de frecuencia más alta (también conocidos como detalles más pequeños), pero aún puede causar aliasing.

N

Cuando usa números aleatorios "regulares" como los que obtendría de rand () o std :: uniform_int_distribution, eso se llama "ruido blanco" porque contiene todas las frecuencias, como la forma en que la luz blanca se compone de todos los demás colores (frecuencias ) de luz.

El uso de ruido blanco para aleatorizar las muestras dentro de un píxel tiene el problema de que a veces sus muestras se agruparán. Por ejemplo, si promedia 100 muestras en un píxel, pero TODAS terminan en la esquina superior izquierda del píxel, no obtendrá NINGUNA información sobre las otras partes del píxel, por lo que su color de píxel final resultante faltará información sobre el color que debe ser.

Un mejor enfoque es usar algo llamado ruido azul que solo contiene componentes de alta frecuencia (como la luz azul es luz de alta frecuencia).

El beneficio del ruido azul es que obtienes una cobertura uniforme sobre el píxel, como lo obtienes con una cuadrícula de muestreo uniforme, pero aún obtienes algo de aleatoriedad, lo que convierte el alias en ruido y te da una mejor imagen.

Desafortunadamente, el ruido azul puede ser muy costoso de calcular, y todos los mejores métodos parecen estar patentados (¿qué diablos? es hacer una cuadrícula uniforme de puntos de muestra, luego compensar aleatoriamente cada punto de muestra una pequeña cantidad, como una cantidad aleatoria entre más o menos la mitad del ancho y la altura de la cuadrícula de muestreo. De esta manera, obtienes una especie de muestreo de ruido azul por bastante barato.

Tenga en cuenta que esta es una forma de muestreo estratificado, y el muestreo de disco de Poisson también es una forma de eso, que también es una forma de generar ruido azul: https://www.jasondavies.com/poisson-disc/

Si estás interesado en profundizar, ¡probablemente también quieras consultar esta pregunta y respuesta!

¿Cuál es el razonamiento fundamental para el suavizado usando múltiples muestras aleatorias dentro de un píxel?

Por último, este material está comenzando a desviarse en el ámbito del trazado de ruta monte carlo, que es el método común para hacer trazado de rayos fotorrealista. Si estás interesado en aprender más sobre eso, ¡lee esto!

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

Alan Wolfe
fuente
7

Supongamos un bucle principal de trazado de rayos 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);
    }
}

Una posible modificación para hacer 4 muestras de MSAA sería:

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

Otra posibilidad es hacer una fluctuación aleatoria (en lugar de la matriz basada en la anterior), pero pronto ingresa al ámbito del procesamiento de señales y se necesita mucha lectura para saber cómo elegir una buena función de ruido.

Sin embargo, la idea sigue siendo la misma: considere que el píxel representa un área cuadrada pequeña, y en lugar de disparar solo un rayo que pasa por el centro del píxel, dispare muchos rayos que cubran el área completa del píxel. Cuanto más densa es la distribución del rayo, mejor señal obtienes.

PD: Escribí el código anterior sobre la marcha, por lo que esperaría algunos errores. Solo pretende mostrar la idea básica.

Julien Guertault
fuente
¡Gran respuesta! ¿Cuáles serían los beneficios de usar este método en oposición al método que utilizó @Raxvan? ¿Obtendré los mismos resultados renderizando a un tamaño grande y luego reduciendo la escala a un tamaño más pequeño?
Arjan Singh
Fundamentalmente, con el trazado de rayos simplemente no necesita renderizar una imagen más grande y luego reducirla. Eso significa que tiene mucha más flexibilidad: puede tener muchas muestras, puede variar la cantidad de muestras según la región y, simplemente, no tiene que agregar el paso de reescalado.
Julien Guertault
2
Sobre el tema del jittering, este resulta ser un tema bastante complejo. Aquí hay un gran artículo que analiza el estado del arte hace unos años graphics.pixar.com/library/MultiJitteredSampling/paper.pdf
Mikkel Gjoel
El ejemplo de código anterior usa un MSAA de 4 ejemplos, si quisiera hacer 8x MSAA, ¿cómo sería la matriz entonces? ¿Qué necesitaría cambiar en la matriz de fluctuación que se muestra arriba?
Arjan Singh