Hola Habr! Les presento la traducción del artículo
"Exposición local dinámica" de John Chapman.
En este artículo, presentaré un par de ideas sobre la exposición dinámica local en el renderizado HDR. Bart Wronsky ya tiene
un excelente artículo sobre este tema y le recomiendo leerlo de inmediato si aún no lo ha hecho; Las ideas aquí se basan, en mayor medida, en su artículo. Al final, incluí algunos otros excelentes enlaces.
Rango dinámico bajo / alto
En los viejos tiempos (1990), los juegos se representaban directamente en el formato
LDR (rango dinámico estrecho) mostrado (espacio gamma, 8 bits). Era simple y barato, pero, por otro lado, interfirió significativamente con la creación de una imagen verdaderamente fotorrealista.
Actualmente, especialmente con el advenimiento de
PBR (renderizado basado en la física), los juegos se representan con un rango dinámico gigantesco en el espacio lineal con mayor precisión. Con este movimiento, el verdadero problema llega al fotorrealismo: ¿cómo podemos mostrar una imagen
HDR en
LDR ?
Exposición global a automóviles
El enfoque estándar para el control de exposición automático es medir el brillo promedio (o promedio logarítmico) de la escena, opcionalmente con una función de
peso que prefiere valores cercanos al centro de la imagen. Esto se puede hacer de manera muy eficiente mediante la reducción paralela o
disminuyendo la
resolución repetidamente en el
mapa MIP del búfer de luminancia . El último enfoque tiene algunas ventajas, que discutiré en la siguiente sección.
El brillo promedio se convierte posteriormente en el valor de exposición, por ejemplo, calculando el recíproco del brillo máximo permitido de la escena:
float Lavg = exp(textureLod(txLuminance, uv, 99.0).x); float ev100 = log2(Lavg * 100.0 / 12.5); ev100 -= uExposureCompensation;
Obtenido del estándar ISO para calcular la velocidad en función de la saturación; para una explicación completa, ver (3)Dado que el brillo promedio potencial es inestable en condiciones dinámicas, generalmente se suaviza con el tiempo utilizando la función de histéresis exponencial
(2) :
Lavg = Lavg + (Lnew - Lavg) * (1.0 - exp(uDeltaTime * -uRate));
Comentario del traductorEsta función debe aplicarse en el brillo de la textura de reducción de muestreo del sombreador y solo durante el cálculo del último nivel de mip (1x1). Además se escribirá sobre esto, pero en mi opinión es fácil pasarlo por alto.
Debido a su naturaleza global, este método adolece de sombras o reflejos severos en áreas de la imagen en las que hay una desviación del brillo promedio:

Aunque esto corresponde a la capacidad del ojo para adaptarse a los cambios en los niveles de luz, el efecto general está bastante lejos de lo que realmente percibimos en el mundo real.
AE local
Si generamos un brillo medio con
disminución de muestreo , tenemos acceso a niveles de mip más bajos del
buffer de
luminancia (4) para obtener un brillo promedio local.
float Lavg = exp(textureLod(txLuminance, uv, uLuminanceLod).x;
Tenga en cuenta que para que esto funcione, la histéresis debe aplicarse solo en el último paso (al grabar un nivel de mip 1x1), de lo contrario habrá artefactos.En teoría, esta es una gran idea: cada área de la imagen puede tener una buena exposición, a la vez que contrasta con las áreas vecinas. Sin embargo, en la práctica, se obtiene un resultado desagradable:

Los más desagradables son los "halos" de bloque que se encuentran en áreas con alto contraste:

Sin embargo, se pueden suavizar filtrando previamente el
tampón de luminancia o simplemente utilizando un muestreo bicúbico:

Todavía se ve asqueroso, pero ya está mejor.
El muestreo de diferentes niveles de mipmap en
luminancia cambia el radio del halo. Este parámetro es útil para controlar la "apariencia" general del resultado, así como para minimizar el efecto de halo, aunque debido a una disminución general en el contraste (se convierte en un filtro de borde) o la pérdida de la localidad del control de exposición:

Aún así, suavizar las imágenes fantasma no es suficiente. El resultado no es en absoluto natural; parece un estilo extremo de "foto HDR", a diferencia de lo que ve una persona. Sin embargo, combinando valores globales y locales, podemos obtener lo mejor de ambos mundos:
float Llocal = exp(textureLod(txLuminance, uv, uLuminanceLod).x; float Lglobal = exp(textureLod(txLuminance, uv, 99.0).x; float L = mix(Lglobal, Llocal, uLocalExposureRatio);

Al cambiar el factor de mezcla, puede ajustar la exposición local para que, como resultado, minimice los artefactos y maximice el realismo percibido:

Relación de mezcla automática
El ajuste manual de
la relación de mezcla es normal en situaciones en las que tenemos control absoluto de la posición de la cámara, la iluminación, etc. Sin embargo, en muchos casos (por ejemplo, juegos al aire libre con un cambio dinámico de día y de noche), este nivel de control simplemente no es posible. En este caso, sería bueno generar
el factor de mezcla automáticamente.
En la imagen de abajo tenemos un amplio rango dinámico; en su mayoría valores de brillo medio-bajo y varias áreas con alta intensidad (cielo en las ventanas):

Sin exposición local, se pierde el color del cielo. En este caso, me gustaría una gran
relación de mezcla :

Ahora considere la imagen a continuación, que tiene un pequeño rango dinámico principalmente con un alto valor de brillo:

En este caso, aplicar exposición local reduce demasiado el brillo de las áreas "brillantes":

Los datos de observación insinuaron un método simple de mezclar valores
locales y
globales : si la diferencia entre el brillo promedio y máximo de la escena es mayor, entonces el coeficiente de mezcla de la exposición local debería ser mayor. La generación del
brillo máximo de la escena se puede hacer trivialmente durante el cálculo del brillo, utilizando histéresis para suavizar el resultado de la misma manera que para el valor promedio. Por lo tanto, podemos expandir el fragmento de código anterior de la siguiente manera:
float Llocal = exp(textureLod(txLuminance, uv, uLuminanceLod).x; float Lglobal = exp(textureLod(txLuminance, uv, 99.0).x;
Tenga en cuenta que uLocalExposureMax apareció en nuestra entrada para controlar el grado máximo absoluto de influencia de la exposición local. Obtuve un buen resultado uLocalExposureMax <0.3 .Código final float Llocal = exp(textureLod(txLuminance, uv, uLuminanceLod).x; float Lglobal = exp(textureLod(txLuminance, uv, 99.0).x;
Conclusión
El enfoque descrito anteriormente impone algunas limitaciones sobre cuándo es necesario medir el brillo de una escena. Por lo general, la medición se realiza inmediatamente después del paso de la iluminación para evitar la adaptación de efectos de
partículas ,
floración , etc. Sin embargo, cuando se usa el brillo local, es importante que el valor real que participa en la exposición se presente en el
mapa de luminancia . Esto significa que se debe realizar una medición de brillo inmediatamente antes de aplicar la exposición. Si esto es inaceptable, entonces la solución es generar brillo local por separado de los valores promedio y máximo.
Aunque creo que usar el brillo local y global de la escena juntos es el enfoque "correcto" para crear una imagen equilibrada y de aspecto natural, la calidad del resultado es obviamente subjetiva. El hecho de que dicho método sea adecuado para un juego en particular depende completamente del contenido y del estilo visual deseado. Me interesaría escuchar otras ideas al respecto.
Referencias
- Mapeo de tonos localizado (Bart Wronski)
- Implementación de una cámara con base física (Padraic Hennessy)
- Traslado de Frostbite a PBR (Sébastien Lagarde, et al.)
- Una mirada más cercana a Tonemapping (Matt Pettineo)
- La importancia de ser lineal (Larry Gritz, et al.)
- Técnicas avanzadas y optimización de tuberías de color HDR / VDR (Timothy Lottes)
Imágenes HDR tomadas del archivo sIBL .