Hola Habr! Les presento la traducción del artículo
"Pseudo Lens Flare" de John Chapman.
Destello de lente (
destello de lente) es un artefacto fotográfico que surge de la dispersión y refracción de la luz en un sistema de lente. Aunque es un artefacto, hay muchas razones para usar el
destello de lente en los gráficos de computadora:
- aumenta el brillo percibido y el rango dinámico visible de la imagen.
- El destello de la lente a menudo se encuentra en las fotografías, por lo que su ausencia puede ser sorprendente.
- Puede desempeñar un papel importante en el estilo o el drama, o puede ser parte de la jugabilidad en los juegos (imagine el deslumbramiento cegando a un jugador)
Tradicionalmente, el
destello de lente en tiempo real se ha implementado utilizando tecnologías basadas en sprites. Aunque los sprites brindan resultados fáciles de controlar y muy realistas, deben colocarse explícitamente y requieren datos de oclusión para visualizarse correctamente. Aquí describiré un efecto de espacio de pantalla simple y relativamente barato que crea un
destello de pseudo
lente desde el búfer de color de entrada. No se basa en la física, por lo que el resultado es ligeramente diferente del fotorrealista, pero se puede usar en combinación con (o como reemplazo) para los efectos tradicionales basados en sprites.
Algoritmo
Consta de 4 etapas:
- Muestra descendente / umbral.
- Generación de elementos de destello de lente .
- Desenfoque
- Mejora / fusión con la imagen original.
1. Muestra descendente / umbral
Reducción de muestreo : optimización para reducir el costo de los pasos posteriores. Además, queremos seleccionar un subconjunto de los píxeles más brillantes en la imagen original. El uso de
scale / bias (scale / bias) proporciona una forma flexible de lograr esto:
uniform sampler2D uInputTex; uniform vec4 uScale; uniform vec4 uBias; noperspective in vec2 vTexcoord; out vec4 fResult; void main() { fResult = max(vec4(0.0), texture(uInputTex, vTexcoord) + uBias) * uScale; }
El ajuste de
escala / sesgo es la forma principal de ajustar el efecto; la mejor configuración dependerá del rango dinámico del búfer de color, así como de qué tan delgado desee ver el resultado. Debido al hecho de que la técnica es una aproximación, es más probable que la sutileza se vea mejor.
2. Generación de elementos de destello de lente.
Los elementos de
destello de la lente tienden a girar alrededor del centro de la imagen. Al simular este efecto, podemos expandir el resultado de la etapa anterior horizontal / verticalmente. Esto es fácil de hacer en la etapa de generación de elementos expandiendo las coordenadas de textura:
vec2 texcoord = -vTexcoords + vec2(1.0);
Esto no es necesario; la generación de elementos funciona bien con y sin ella. Sin embargo, el resultado de cambiar las coordenadas de textura ayuda a separar visualmente el efecto de
reflejo de la
lente de la imagen original.
Fantasmas
Los "
fantasmas " (fantasmas) son reflejos repetidos que reflejan las áreas brillantes en el búfer de color, desplegándose en relación con el centro de la imagen. El enfoque que elegí generar es obtener un vector desde el píxel actual hasta el centro de la pantalla, y luego hacer varias selecciones a lo largo de este vector.

uniform sampler2D uInputTex; uniform int uGhosts;
Tenga en cuenta que uso
fract () para asegurar que las coordenadas de textura se envuelvan; de manera equivalente, puede usar el modo de
ajuste GL_REPEAT para la textura.
Aquí está el resultado:

Puede mejorar el resultado permitiendo que solo las áreas brillantes más cercanas al centro de la imagen generen fantasmas. Podemos lograr esto agregando pesos que disminuirán desde el centro para las muestras:
vec4 result = vec4(0.0); for (int i = 0; i < uGhosts; ++i) { vec2 offset = fract(texcoord + ghostVec * float(i)); float weight = length(vec2(0.5) - offset) / length(vec2(0.5)); weight = pow(1.0 - weight, 10.0); result += texture(uInputTex, offset) * weight; }
La función de peso es lo más simple posible: lineal. La razón por la que calculamos el peso dentro del bucle es porque las áreas brillantes en el centro de la imagen de entrada pueden arrojar fantasmas a los bordes, pero las áreas brillantes en los bordes no pueden arrojar fantasmas al centro.

La mejora final es el cambio de color radial del fantasma, de acuerdo con la textura 1D:

Se aplica después del ciclo para afectar el color final del fantasma:
result *= texture(uLensColor, length(vec2(0.5) - texcoord) / length(vec2(0.5)));
HALOS (halos)
Si llevamos el vector al centro de la imagen, como en el cálculo
fantasma , pero fijamos la longitud del vector, obtenemos un efecto diferente: la imagen original se deforma radialmente:

Podemos usar esto para crear un "halo" multiplicando el peso por una muestra, limitando así la contribución de la imagen deformada a un anillo cuyo radio está controlado por
uHaloWidth :

DISTORCIÓN CROMÁTICA (distorsión de color)
Algunos destellos de lentes tienen una distorsión de color causada por variaciones en la refracción de la luz a diferentes longitudes de onda. Podemos simular esto creando una función que seleccione los canales rojo, verde y azul por separado con desplazamientos ligeramente diferentes a lo largo del vector de muestra:
vec3 textureDistorted( in sampler2D tex, in vec2 texcoord, in vec2 direction,
Se puede usar como un reemplazo directo para llamar a
texture () en la lista anterior. Calculo la
dirección y la
distorsión de la siguiente manera:
vec2 texelSize = 1.0 / vec2(textureSize(uInputTex, 0)); vec3 distortion = vec3(-texelSize.x * uDistortion, 0.0, texelSize.x * uDistortion); vec3 direction = normalize(ghostVec);
Aunque la función de búsqueda es simple, cuesta x3 muestras de la textura, aunque todas deberían ser
compatibles con la memoria caché a menos que establezca
uDistortion en algún valor gigantesco.
Con la generación de elementos, todo. Aquí está el resultado:

3. Desenfoque
Sin desenfoque, los elementos de
destello de la lente (en particular, los fantasmas) tienden a preservar la apariencia de la imagen. Al agregar desenfoque a los elementos de
destello de la
lente , debilitamos las altas frecuencias y, por lo tanto, reducimos el contraste con la imagen de entrada, lo que nos ayuda a vender el efecto.

No diré cómo hacer un desenfoque; Puede leer sobre esto en varios recursos de Internet (desenfoque gaussiano).
4. Mejora / mezcla con la imagen original
Entonces, tenemos nuestros elementos de
destello de lente , bien borrosos. ¿Cómo podemos combinarlos con la imagen fuente original? Hay varias consideraciones importantes con respecto a toda la canalización de renderizado:
- Cualquier desenfoque de movimiento posterior o profundidad de campo se debe aplicar antes de combinar con destello de lente , de modo que los elementos de destello de lente no participen en estos efectos.
- El destello de la lente debe aplicarse antes de cualquier mapeo de tonos . Esto tiene sentido físico, ya que el mapeo de tonos imita la respuesta de la película / CMOS a la luz entrante, de la cual el destello de la lente es una parte integral.
Con eso en mente, hay un par de cosas que podemos hacer en esta etapa para mejorar el resultado:
DIRECCIÓN DE LENTE
Primero, debe modificar los elementos de
destello de lente con una textura sucia en resolución completa (que se usa ampliamente en Battlefield 3):

uniform sampler2D uInputTex;
La clave de esto es la textura muy sucia en las lentes. Si el contraste es bajo,
las formas de
reflejo de la lente tienden a dominar el resultado. A medida que aumenta el contraste, los elementos de
destello de la
lente se amortiguan, lo que le da un aspecto estético diferente y también oculta algunos defectos.
DIFRACCIÓN STARBURST
Como una mejora adicional, podemos usar la textura del
estallido estelar agregándola a
la suciedad de la lente :

Como textura, el
estallido estelar no se ve muy bien. Sin embargo, podemos pasar la matriz de transformación al sombreador, lo que nos permitirá rotar / deformar el
estallido estelar en cada cuadro y obtener el efecto dinámico deseado:
uniform sampler2D uInputTex;
La
matriz de transformación
uLensStarMatrix se basa en el valor obtenido de la orientación de la cámara de la siguiente manera:
vec3 camx = cam.getViewMatrix().col(0);
Hay otras formas de obtener el valor de camrot; Lo más importante, debe cambiar continuamente cuando se gira la cámara. La matriz misma se construye de la siguiente manera:
mat3 scaleBias1 = ( 2.0f, 0.0f, -1.0f, 0.0f, 2.0f, -1.0f, 0.0f, 0.0f, 1.0f, ); mat3 rotation = ( cos(camrot), -sin(camrot), 0.0f, sin(camrot), cos(camrot), 0.0f, 0.0f, 0.0f, 1.0f ); mat3 scaleBias2 = ( 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, ); mat3 uLensStarMatrix = scaleBias2 * rotation * scaleBias1;
Las matrices de
escala y
polarización necesitan desplazamientos de origen de textura para que podamos rotar el
estallido estelar en relación con el centro de la imagen.
Conclusión
¡Así que ahora todo! Este método demuestra cómo un proceso posterior relativamente simplificado da un
destello de lente de aspecto decente. No es completamente fotorrealista, pero si se usa correctamente, puede producir excelentes resultados.

UPDEl autor también publicó
un artículo con pequeñas optimizaciones.
El código fuente se puede ver
aquí y
aquí .