Generalmente hay dos métodos para lidiar con esto. Hoy en día, se denominan renderizado directo y renderizado diferido. Hay una variación de estos dos que discutiré a continuación.
Representación hacia adelante
Renderice cada objeto una vez por cada luz que lo afecte. Esto incluye la luz ambiental. Utiliza un modo de mezcla aditiva ( glBlendFunc(GL_ONE, GL_ONE)
), por lo que las contribuciones de cada luz se suman entre sí. Como la contribución de diferentes luces es aditiva, el framebuffer eventualmente obtiene el valor
Puede obtener HDR al renderizar a un framebuffer de punto flotante. Luego, realiza una pasada final sobre la escena para reducir los valores de iluminación HDR a un rango visible; aquí también sería donde implementas bloom y otros efectos posteriores.
Una mejora de rendimiento común para esta técnica (si la escena tiene muchos objetos) es usar un "paso previo", donde se procesan todos los objetos sin dibujar nada en el buffer de cuadros de color (se usa glColorMask
para desactivar las escrituras en color). Esto solo llena el búfer de profundidad. De esta manera, si renderiza un objeto que está detrás de otro, la GPU puede omitir rápidamente esos fragmentos. Todavía tiene que ejecutar el sombreador de vértices, pero puede omitir los cálculos de sombreadores de fragmentos típicamente más caros.
Esto es más simple de codificar y más fácil de visualizar. Y en algunos equipos (principalmente GPU móviles e integrados), puede ser más eficiente que la alternativa. Pero en el hardware de gama alta, la alternativa generalmente gana para escenas con muchas luces.
Renderizado diferido
El renderizado diferido es un poco más complicado.
La ecuación de iluminación que utiliza para calcular la luz de un punto en una superficie utiliza los siguientes parámetros de superficie:
- Posición de la superficie
- Normales de superficie
- Color difuso superficial
- Color especular de superficie
- Brillo especular superficial
- Posiblemente otros parámetros de superficie (dependiendo de cuán compleja sea su ecuación de iluminación)
En el renderizado hacia adelante, estos parámetros llegan a la función de iluminación del sombreador de fragmentos al pasar directamente desde el sombreador de vértices, al extraerlos de las texturas (generalmente a través de las coordenadas de textura pasadas desde el sombreador de vértices), o generados a partir de un paño completo en el sombreador de fragmentos basado en Otros parámetros. El color difuso se puede calcular combinando un color por vértice con una textura, combinando múltiples texturas, lo que sea.
En la representación diferida, hacemos todo esto explícito. En la primera pasada, renderizamos todos los objetos. Pero no representamos colores . En cambio, representamos parámetros de superficie . Por lo tanto, cada píxel en la pantalla tiene un conjunto de parámetros de superficie. Esto se realiza mediante el procesamiento de texturas fuera de la pantalla. Una textura almacenaría el color difuso como su RGB, y posiblemente el brillo especular como el alfa. Otra textura almacenaría el color especular. Un tercero almacenaría lo normal. Y así.
La posición generalmente no se almacena. En su lugar, se reconstituye en el segundo paso por matemáticas que es demasiado complejo para entrar aquí. Baste decir que usamos el búfer de profundidad y la posición del fragmento del espacio de la pantalla como entrada para determinar la posición del espacio de la cámara del punto en una superficie.
Entonces, ahora que estas texturas contienen esencialmente toda la información de la superficie para cada píxel visible en la escena, comenzamos a representar quads de pantalla completa. Cada luz tiene un render cuádruple a pantalla completa. Tomamos muestras de las texturas de los parámetros de la superficie (y reconstituimos la posición), luego las usamos para calcular la contribución de esa luz. Esto se agrega (nuevamente glBlendFunc(GL_ONE, GL_ONE)
) a la imagen. Seguimos haciendo esto hasta que se nos acaben las luces.
HDR nuevamente es un paso posterior al proceso.
El mayor inconveniente del renderizado diferido es el antialiasing. Se requiere un poco más de trabajo para antialias correctamente.
La mayor ventaja, si su GPU tiene mucho ancho de banda de memoria, es el rendimiento. Solo renderizamos la geometría real una vez (o 1 + 1 por luz que tiene sombras, si estamos haciendo mapeo de sombras). Nosotros nunca se pierde ningún tiempo en píxeles o geometría oculta que no es visible después de esto. Todo el tiempo de iluminación se gasta en cosas que son realmente visibles.
Si su GPU no tiene mucho ancho de banda de memoria, entonces el pase de luz realmente puede comenzar a doler. Tirar de 3-5 texturas por píxel de pantalla no es divertido.
Pre-Pase Ligero
Esta es una especie de variación en el renderizado diferido que tiene compensaciones interesantes.
Al igual que en la representación diferida, representa los parámetros de su superficie en un conjunto de búferes. Sin embargo, ha abreviado los datos de superficie; los únicos datos de superficie que le interesan en este momento son el valor del búfer de profundidad (para reconstruir la posición), normal y el brillo especular.
Luego, para cada luz, calcula solo los resultados de la iluminación. Sin multiplicación con colores de superficie, nada. Solo el punto (N, L) y el término especular, completamente sin los colores de la superficie. Los términos especulares y difusos deben mantenerse en búferes separados. Los términos especulares y difusos para cada luz se resumen dentro de los dos buffers.
Luego, vuelve a renderizar la geometría, utilizando los cálculos totales de iluminación especular y difusa para hacer la combinación final con el color de la superficie, produciendo así la reflectancia general.
La ventaja aquí es que obtienes múltiples muestras (al menos, más fácil que con diferido). Hace menos renderizado por objeto que el renderizado hacia adelante. Pero lo más diferido que esto proporciona es un momento más fácil para tener diferentes ecuaciones de iluminación para diferentes superficies.
Con la representación diferida, generalmente dibuja toda la escena con el mismo sombreador por luz. Por lo tanto, cada objeto debe usar los mismos parámetros materiales. Con el paso previo de luz, puede darle a cada objeto un sombreador diferente, para que pueda hacer el paso final de iluminación por sí solo.
Esto no proporciona tanta libertad como el caso de renderizado directo. Pero aún es más rápido si tiene el ancho de banda de textura de sobra.