Deferred Shading in Callisto
Callisto ENGINE
Deferred Shading in Callisto
INTRODUCCIÓN............................................................................. .................................4 SINGLE PASS – MULTIPLE LIGHTS.....................................................................................4 MULTIPLE PASSES – MULTIPLE LIGHTS................................................................................5 DEFERRED SHADING................................................................................................ .....6 HISTORIA........................................................................................... ...................7 G-BUFFER........................................................................................................ ...........9 MULTIPLE RENDER TARGETS....................................................................................... .....9 POSITION............................................................................. ..................................13 NORMAL........................................................................................ ........................13 ALBEDO......................................................................................................... ........15 GBUFFER PASS............................................................................................. ..............17 VERTEX SHADER........................................................................................ ...............17 PIXEL SHADER.............................................................................. ...........................18 SHADING PASS.................................................................................................. ..........19 VERTEX SHADER........................................................................................ ...............20 PIXEL SHADER.............................................................................. ...........................21 COMPOSICIÓN FINAL...................................................................................................23 OPTIMIZACIONES..........................................................................................................24 DISEÑO DEL G-BUFFER.................................................................................... ...........24 EMPAQUETADO DE ATRIBUTOS................................................................................... ...26
Deferred Shading in Callisto
Tabla de contenido
36
Deferred Shading in Callisto
EYE-SPACE NORMALS......................................................................................... .....26 POSITION FROM DEPTH............................................................................................28 VOLÚMENES DE LUZ.................................................................................................... 32 FASE DE ACTUALIZACIÓN..........................................................................................32 FASE DE RENDERIZADO.............................................................................. ..............35
35
Introducción El cálculo de iluminación de una escena es un aspecto que ha evolucionado de una manera espectacular gracias al incesante progreso del hardware gráfico. Hace algunos años la iluminación de un escenario consistía en el cálculo por vértice de 1-2 luces que solían proyectar sombras planas y se complementaban mediante el uso de lightmaps. Por el contrario los juegos actuales hacen uso de todo el potencial gráfico donde cada objeto en escena es iluminado simultáneamente por varias luces, dando lugar a una maraña de complejos efectos de sombreado a nivel de pixel todo ello en tiempo real. (Ilustración 1 Neverwinter Nights (2002) VS Mass Effect (2008) )
I LUST RACIÓN 1 - N EVERWINTER N IGHT S (2002)
VS
M ASS E FFECT (2008)
Estas técnicas se engloban con el sobrenombre de forward shading y cada una de ellas conlleva ciertos inconvenientes que se describen a continuación.
Single Pass – Multiple Lights La primera opción de iluminación en tiempo real consiste en utilizar una única pasada con el fin de calcular la aportación final de todas las luces sobre un mismo objeto.
Deferred Shading in Callisto
Se puede afirmar que son dos las opciones especialmente utilizadas a la hora de realizar todos los cálculos de iluminación en tiempo real para una escena: ejecutar una única pasada y calcular todas las luces en la misma (single passmultiple lights) ó realizar el cálculo en varias pasadas (multiple pass-multiple lights).
36
El siguiente seudocódigo ilustra el enunciado anterior: for each object do for each light do in a single shader framebuffer = brdf(object,light);
Se trata de la aproximación más simple de las dos, por lo que posee algunas limitaciones e inconvenientes: o
Ya que la iluminación es computada por objeto, puede llegar a ocurrir que se realice un proceso de renderizado y sombreado de la superficie correspondiente a un objeto y que posteriormente sea cubierta por la superficie correspondiente a otro, por lo que se habrá realizado una computación innecesaria.
o
Otro problema se traduce en la cantidad de combinaciones resultantes al intentar recrear en una única plantilla los distintos tipos de luces/materiales resultantes. Como todo el proceso de sombreado se realiza en un solo shader, que posee una cantidad limitada de instrucciones, esta técnica solamente es viable para un número reducido de luces.
o
Por último, su integración con sombras es bastante complicada y la utilización de shadow maps puede llegar a consumir bastante VRAM.
Multiple Passes – Multiple Lights En ésta segunda opción todos los cálculos son realizados por fuente de iluminación, de tal forma que en cada pasada, para cada luz existente en escena, todos los objetos influenciados por la misma son sombreados.
Deferred Shading in Callisto
Como en el ejemplo anterior se proporciona el siguiente seudocódigo:
35
for each light do for each object affected by light do framebuffer += brdf(object,light);
Asimismo se detectan algunos problemas o limitaciones: o
Como en el caso anterior, se realizan cálculos innecesarios por redibujado de pixeles.
o
En cada pasada que se envía el mismo objeto (ya que puede ser iluminado por varias luces a la vez), éste es procesado nuevamente por el vertex shader por lo que se generan una y otra vez las mismas transformaciones. De la misma forma, en cada una de las pasadas se vuelven a aplicar los filtros anisotrópicos.
o
Por último, esta técnica produce un elevado batching (número de draw calls o llamadas a dibujado) que en el peor de los casos es del orden O(num_lights * num_objects).
Deferred Shading Para representar un efecto complejo, se requerirán varias pasadas para computar el color final de cada pixel. Si se utilizan las dos técnicas descritas anteriormente, se repetirá el envío de la geometría una vez por cada pasada necesaria. A diferencia de las técnicas anteriores en las que se envía la geometría e inmediatamente se aplican los shaders correspondientes, la técnica denominada deferred shading consiste en enviar el grueso de geometría de la escena únicamente una vez, almacenando en dicha pasada todos los atributos de iluminación (posición, normales, color, etc.) en memoria de video local (G-Buffer) de tal forma que se puedan usar en las siguientes pasadas. En estas últimas pasadas mencionadas se creará un rectángulo alineado en pantalla en el que, mediante un postproceso en 2D donde se utilizan los atributos del G-Buffer como entrada, se calculará el color resultante para cada pixel.
El hecho de reducir toda esa tasa de transferencia de vértices que antes se procesaba en cada pasada y ahora solamente es enviada en la correspondiente a la creación del G-Buffer, podrá servir para incrementar la cantidad de polígonos en la escena aumentando el realismo de la misma sin comprometer al rendimiento. Tampoco existirá un sobre redibujado de pixeles (solo en la pasada en la que se rellena el G-Buffer) ya que el color de cada pixel es calculado solamente una vez. El siguiente seudocódigo ilustra de forma clara la naturaleza de la técnica: for each ob jec tdo render lighting properties of object to G-Buffer; for each l i gh tdo framebuffer += brdf (G-buffer,light);
Deferred Shading in Callisto
De esta forma, la gran ventaja de esta técnica respecto a las comentadas con anterioridad radica en el hecho de que se reduce sobremanera la complejidad computacional, siendo en el peor de los casos del orden de O(num_lights + num_objects).
36
Historia La técnica fue inicialmente introducida en 1988 por Michael Deering et al en el SIGGRAPH. En dicho trabajo, aparte de no mencionar en ningún caso el término “deferred”, los autores proponían un sistema VLSI (Very Large Scale Integration ) donde un pipeline procesaba la geometría mientras otro pipeline se encargaba de aplicar Phong shading, con varias fuentes emisoras de luz, sobre dicha geometría. Posteriormente al estudio inicial de Deering et al, el siguiente trabajo con relevancia dentro del ámbito de la técnica deferred shading corresponde al realizado por Saito and Takahashi en 1990. Los autores proponen una técnica de renderizado que generaba imágenes 3D y las mejoraba utilizando líneas, patrones, discontinuidades, bordes, obtenidos a partir de las diferentes propiedades de la geometría (normales, profundidad, etc.) almacenadas en buffers de geometría (G-Buffers). Otro estudio relacionado con la técnica fue el realizado por Ellsworth en 1991, que investigó arquitecturas paralelas y algoritmos para síntesis en tiempo real de imágenes en alta calidad usando deferred shading.
I LUST RACIÓN 2 – R ENDER ING OF 3 D - SHAPES (S AITO AND T AKAHASHI )
Poco más tarde, en 1992, el grupo de investigación de gráficos por computador UNC propuso la arquitectura PixelFlow para la generación de imágenes a alta velocidad. En dicho artículo Molnar et al usaban deferred shading para reducir los cálculos realizados en los complejos modelos de sombreado utilizados en su arquitectura.
Deferred Shading in Callisto
Despues de los artículos mencionados han existido otras investigaciones y desarrollos que han utilizado la técnica deferred shading ( Nicolas Thibieroz, Shawn Hargreaves, Randima Fernando, Ariel Brunetto, Frank Pluig entre otros).
35
Recientemente, Oles Shishkovtsov ha escrito un capítulo en el libro “GPU Gems 2” en el que describe los detalles de la técnica deferred shading usada en el juego S.T.A.L.K.E.R. Aún más reciente es el artículo escrito por Rusty Koonce en el libro “GPU Gems 3”,
continuación obligada al escrito por Shishkovtsov. Mientras que éste último cubre los aspectos fundamentales de la técnica, el artículo de Koonce enfatiza los problemas, técnicas y soluciones encontradas mientras se trabajaba en la realización del motor de renderizado del juego “Tabula Rasa”, basado en la técnica deferred shading. I LUSTRACIÓN 3 – S.T.A.L.K.E.R. S HADOW
OF
C HERNOBYL
Para finalizar existen recientes técnicas (Light Indexed Deferred Lighting [Damien Trebilco 2007] y Light Pre-Pass Rendering, [Wolfgang Engel 2008] ) basadas en deferred shading que intentan solucionar varios problemas derivados de su naturaleza, como es el caso de transparencias, AA y poder gestionar varios BRDF’s en la escena. En ellas se utiliza el G-Buffer para almacenar otro tipo de información y utilizarla en una pasada posterior usando forward rendering.
Deferred Shading in Callisto
I LUSTRACIÓN 4 - T ABUL A R ASA
36
G-Buffer En contraposición al comportamiento de cualquier sistema basado en forward rendering, en uno basado en deferred shading, tanto la iluminación como los demás efectos no son calculados en la misma pasada en la que se envía y procesa la geometría de la escena. Existirá en este caso una primera pasada en la que la geometría es procesada y todos los atributos asociados a la misma como posición, normales, albedo, etc., son almacenados en varias texturas formando un buffer auxiliar denominado GBuffer. Dichas texturas serán utilizadas en sucesivas pasadas para realizar todos los cálculos correspondientes al color de cada pixel sin necesidad de volver a enviar de nuevo la totalidad de geometría de la escena. Para almacenar atributos como la posición, donde es necesario un elevado rango de representación, se hará casi obligatorio el uso de texturas de punto flotante. Aunque existen diferentes métodos de empaquetado para almacenar en texturas de menor precisión, la mayoría de los sistemas actuales soportan este tipo de texturas de punto flotante.
Multiple Render Targets Anteriormente (DirectX 8) solo se podía escribir en una sola textura (render target) utilizando un máximo de 32 bits consistentes en 4 componentes de color (8 bits por componente).
Deferred Shading in Callisto
RT
35
R
G
B
A
RT
Por lo tanto si se opta por utilizar un único render target, se deberán realizar múltiples pasadas con el fin de almacenar todos los atributos seleccionados: position, normals, albedo, etc., por lo que habría que enviar en cada pasada toda la geometría resultando una técnica casi ineficiente, cercana al comportamiento y rendimiento de un forward renderer. DirectX 9 aportó una nueva característica denominada Multiple Render Targets (MRT) en la que se permitía utilizar hasta cuatro diferentes render targets en los que escribir en una única pasada, y aumentando la precisión anterior de 32 bits a 512 bits (varía respecto al dispositivo utilizado).
De esta forma se podrían empaquetar los atributos necesarios del G-Buffer en MRTs, organizándolos de forma inteligente, de tal manera que se consiguiesen escribir en una sola pasada y en el menor número de texturas posibles.
o
Deben poseer el mismo tamaño. Esto no es del todo cierto, ya que se podrá utilizar una profundidad de bits diferente si el dispositivo soporta D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS. Prácticamente todas las tarjetas del mercado soportan dicha característica.
o
Se pueden mezclar RTs con diferente número de canales, es decir, se podría realizar la siguiente configuración de MRTs:
RT0 : G16R16F
RT1 : A8R8G8B8
RT2 : R32F
o
Dithering, alpha testing, fogging, blending, o masking solamente podrán ser utilizados si el dispositivo tiene marcado el bit D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING.
o
No soportan MSAA (MultiSample Anti-Aliasing) por hardware. En DirectX 10, como se comentará más adelante, se solventa éste problema. Mientras tanto en DirectX 9 se deberán utilizar otros métodos más intrusivos para obtener una aproximación al AA.
El uso de todos estos MRTs da como resultado la utilización de gran cantidad de memoria VRAM así como de ancho de banda del dispositivo. Cuando se diseña un sistema basado en deferred shading habrá que tener especial cuidado en seleccionar qué valores son almacenados en el G-Buffer y sobre todo de qué forma serán almacenados para reducir tanto VRAM como ancho de banda. Existen varias técnicas para optimizar el empaquetado y diseño del G-Buffer, todas ellas se comentarán en posteriores apartados (Ver Optimizaciones). Un posible diseño del G-Buffer, bastante simple y sin optimizar, correspondería al que se muestra a continuación:
RT0
POSITION.X
POSITION.Y
G16R16F
Deferred Shading in Callisto
En general los MRTs poseen las siguientes limitaciones o características:
36 RT1 RT3 RT2
POSITION.Z COLOR.R LIBRE
NORMAL.X COLOR.G
LIBRE NORMAL.Y COLOR.B
NORMAL.Z
G16R16F
A2RGB1 0 LIBRE
RGBA8
En total serían 128 bits por pixel. A una resolución de 1024x768 correspondería a un total de 12 MB de memoria VRAM. Éste no sería el principal inconveniente dada la gran cantidad de memoria que proporciona el hardware actual. La mayor desventaja y la que proporciona un mayor riesgo de producir bottlenecks en el rendimiento de la técnica corresponde al ancho de banda requerido en cada fotograma aunque, como ocurre en el caso anterior, este inconveniente queda mitigado gracias a la potencia que proporcionan los dispositivos 3D actuales.
Deferred Shading in Callisto
La siguiente tabla representa un cálculo aproximado del ancho de banda necesario, es decir, la cantidad de memoria que es necesario transferir por el bus del dispositivo en cada fotograma, para un escenario particular de 1024x768 y diferentes opciones de bpp.
35
32 bpp
64 bpp
128 bpp
nMRTs = 2
14 GB/seg
18 GB/seg
26 GB/seg
nMRTs = 3
16 GB/seg
22 GB/seg
34 GB/seg
nMRTs = 4
18 GB/seg
26 GB/seg
42 GB/seg
Si estos valores son comparados con un listado de características correspondientes a tarjetas gráficas de la serie 8 de NVIDIA, podemos comprobar cómo, para dispositivos de gama media/baja dentro de la serie, se hace patente las limitaciones impuestas por el ancho de banda a utilizar.
Deferred Shading in Callisto
A continuación se describirá el proceso de creación de cada una de las superficies de almacenamiento que forman el G-Buffer final, siempre siguiendo el ejemplo de configuración anterior donde los atributos que se mapean corresponden a position, normals y diffuse albedo.
36
Position Corresponde a la posición del pixel y puede estar representada en diferentes sistemas de coordenadas, aunque las más lógicas corresponden a view/world space. En el ejemplo se han utilizado coordenadas en view space, por lo que ha sido necesario multiplicar cada vértice en el vertex shader por la matriz de transformación correspondiente. Ya en el pixel shader se almacenará cada coordenada (x,y,z) en la RT correspondiente. Las siguientes imágenes muestran las texturas correspondientes a RT0 y RT1, en las que se han mapeado (position.x, position.y) y (position.z) respectivamente:
I LUST RACIÓN 5 - RT0 ( POS . X ,
POS . Y )
Y
RT1 ( POS . Z )
Aunque la textura en la que se empaqueta la coordenada z del pixel presente un color constante, al tratarse de una textura de punto flotante estará almacenando un mayor rango de valores que el aparentemente representado.
Deferred Shading in Callisto
Normal
35
Se trata del vector normal correspondiente a cada pixel almacenado en view space. Como es costumbre, se utilizarán mapas de vectores normales definidos en tangent space (ó texture space), que no viene a ser más que otro sistema de coordenadas en el que se indica la orientación de la superficie de la textura en cada vértice.
A partir de dicho sistema de coordenadas se podrán realizar técnicas como bump mapping ó incluso parallax mapping/relief mapping que serán comentadas en apartados posteriores. En la ilustración 6 se puede observar un ejemplo de mapa de normales en tangent space en el que se representan los detalles que se van a añadir al conjunto de polígonos con el fin de dar el aspecto final de una pared de ladrillos. I LUSTRACIÓN 6 - M APA T ANGENT S PACE
Volviendo al caso de ejemplo que se expone en el presente apartado, ya que el sistema utilizado corresponde a coordenadas en view space, habrá que obtener los vectores normales en dicho sistema. DE NORM AL ES EN
Para ello habrá que formar la matriz tangencial utilizando los vectores tangent, binormal y normal que a su vez estarán en view space.
Por último habrá que multiplicar el vector normal obtenido de samplear el mapa de normales por la matriz tangencial anterior, resultando el vector normal en el sistema de coordenadas deseado:
Posteriormente, el vector resultante de la ecuación anterior será normalizado para no perder precisión en los cálculos posteriores.
La siguiente imagen muestra la RT2 correspondiente al G-Buffer, en la que se ha mapeado N (normal.x, normal.y, normal.z) en los canales RGB, dejando libre el canal Alfa:
Deferred Shading in Callisto
De la misma manera se suelen utilizar texturas de formato coma flotante (16F) para almacenar con suficiente exactitud el valor obtenido, aunque es más económico y la mayoría de las ocasiones resulta suficiente disponer de una textura con formato entero de 32 bits (A2RGB10). De esta forma los canales RGB, los tres de 10 bits cada uno, se utilizarán para almacenar las coordenadas (x,y,z) del vector normal, quedando el canal A libre.
36
I LUSTRACIÓN 7 - RT2 ( VIEW
SPACE NORMAL S )
Albedo El color de cada pixel correspondiente a la componente difusa (color propio del objeto) es tomado de la textura denominada comúnmente albedo map asociada al modelo. Como cada componente de color RGBA se puede representar en un rango 0-255, se utilizará una textura de 32 bits, con 8 bits por cada canal.
Deferred Shading in Callisto
La siguiente imagen muestra la RT3 correspondiente al G-Buffer, en la que se ha mapeado D (diffuse.x, diffuse.y, diffuse.z) en los canales RGB, dejando libre el canal Alfa:
35
I LUSTRACIÓN 8 - RT3 ( AL BEDO
MAP )
De la misma forma se podrían almacenar otro tipo de atributos en los canales no utilizados, como mapas que definan la componente especular (specular power / specular intensity).
Deferred Shading in Callisto
Una vez definida la composición y estructura del G-Buffer de forma teórica, se procederá a mostrar en el siguiente apartado de una forma absolutamente práctica el proceso de creación de cada una de las texturas que forman el FAT Buffer.
36
GBuffer Pass La primera pasada se encargará de almacenar los atributos seleccionados (position, normal, albedo) en cada una de las textura que forman el G-Buffer.
Vertex Shader El vertex shader es realmente simple. Aparte de transformar la posición para expresarla en clip space (AKA screen space), se propagan otra serie de vectores:
o
Se transforma el vector posición en view space que corresponderá al valor almacenado en la RT0/RT1.
o
Normal, binormal, tangent se transforman de object space a view space mediante la matriz de transformación correspondiente. Estos vectores, como se comentó anteriormente, formarán la matriz utilizada en la creación del vector normal (en view space) almacenado en RT2.
o
Se propaga sin ningún tipo de transformación las coordenadas del albedo map, que se escribirán en la RT3.
VS_OUTPUT_GBUFFERPASS VS_GBUFFER( VS_INPUT_GBUFFERPASS IN ) { VS_OUTPUT_GBUFFERPASS Out;
Deferred Shading in Callisto
Out.Pos = mul(IN.pos, g_mWorldViewProjection);
35
float4 pp = mul( IN.pos, g_mWorldView ); Out.WorldPos = pp; Out.interPos = mul(IN.pos, g_mWorldView).xyz; Out.normal = mul(IN.normal, (float3x3)g_mWorldView); Out.binormal = mul(IN.binormal, (float3x3)g_mWorldView); Out.tangent = mul(IN.tangent, (float3x3)g_mWorldView); Out.texCoord = IN.texcoord; return Out;
}
Pixel Shader Ya en el pixel shader, se generarán los colores que se van a propagar a los MRTs configurados. Para obtener el color de la componente difusa albedo (Out.Color) simplemente habrá que samplear la textura correspondiente a partir de las coordenadas propagadas desde el vertex shader. El vector position se propagará sin ningún tipo de cálculo posterior hacia los RTs correspondientes: Out.PosXY y Out.PosZ. En cuanto a las normales, habrá que samplear el vector de la textura correspondiente y transformarlo a view space utilizando la matriz (objToTangentSpace). El resultado se propagará hacia el RT Out.Normal.
PS_OUTPUT_GBUFFERPASS PS_GBUFFER( VS_OUTPUT_GBUFFERPASS IN ) { PS_OUTPUT_GBUFFERPASS Out; float3 base = tex2D(Textura2D0, IN.texCoord); float3 bump = tex2D(Textura2D1, IN.texCoord) * 2 - 1; bump = normalize(bump); float3x3 objToTangentSpace = float3x3( IN.tangent, IN.binormal, IN.normal ); Out.PosXY Out.PosZ
= float4(IN.interPos.x, IN.interPos.y, 0, 1); = float4(IN.interPos.z, 0, 0, 1);
float3 normal = normalize( mul( bump, objToTangentSpace ) ); //view space bump texture space ( [-1;+1] [0;1] ) normal = normal * 0.5 + 0.5; Out.Normal = float4(normal.x, normal.y, normal.z, 1); Out.Color = float4(base, 1); return Out;
Deferred Shading in Callisto
}
36
Shading Pass Como se había comentado en anteriores apartados, el G-Buffer generado se utilizará en ésta pasada para realizar el cálculo de iluminación sin necesidad de enviar de nuevo todo el grueso de geometría de la escena. Simplemente será necesario un rectángulo alineado en pantalla de tal forma que cada uno de los pixeles de su superficie presente su homólogo en todas las texturas del G-Buffer. Para ello hay que tener en cuenta que los sistemas de coordenadas correspondientes a pixels y texels en Direct3D 9 difieren en la situación de su origen de coordenadas. El primero considera el centro del pixel como el origen del sistema de coordenadas:
Deferred Shading in Callisto
I LUSTRACIÓN 9 - P IXEL C OORDINATE S YSTEM
35
Mientras que el segundo considera como origen de coordenadas la esquina superior izquierda del pixel:
I LUSTRACIÓN 10 - T EXEL C OORDINATE S YSTEM
Por lo tanto para alinear correctamente texels con pixels habrá que estipular un desplazamiento proporcional al tamaño del texel, como se representa en la siguiente ilustración:
RECTÁNGULO ALINEADO EN PANTALLA
W
Vertex Shader El vertex shader es tan simple como propagar la posición de los vértices que forman el rectángulo alineado en pantalla y realizar la corrección de las coordenadas de textura indicada en el apartado anterior.
Deferred Shading in Callisto
H
36
VS_OUTPUT_SCREENQUAD VS_RenderScreenQuad( VS_INPUT_SCREENQUAD IN ) { VS_OUTPUT_SCREENQUAD OUT; OUT.Position = IN.Position; OUT.TC0 = IN.TC0 + vTexelSize; OUT.EyeScreenRay = IN.FrustumFar; return OUT; }
La variable vTexelSize se ha definido como un float2 de la siguiente forma: float2 vTexelSize = float2(1.0f / (2.0f * screenWidth), 1.0f / (2.0f * screenHeight));
Pixel Shader Por último en el pixel shader se realizarán los cálculos necesarios para computar la iluminación de cada pixel en pantalla.
Deferred Shading in Callisto
La ejecución del pixel shader es individual para cada fuente de luz por lo que se realizará tantas veces como fuentes de luz existan. El resultando de cada iteración se irá acumulando en el framebuffer mediante transparencia aditiva (additive blending).
35
float4 PS_RenderLight(VS_OUTPUT_LIGHT IN) : COLOR0 { half3 pos = float3(0,0,0); pos.xy = tex2D(GBufferTexture1, IN.PosProj).xy; pos.z = tex2D(GBufferTexture2, IN.PosProj).x; half3 eyeVec = camPos - pos; half3 lightVec = (1.0 / PointLightRange) * (PointLightPosition - pos); half3 normal; // normales en texture space [0,1] -> convertir a [-1,+1] normal.xyz = tex2D(GBufferTexture4, IN.PosProj).xyz * 2 - 1; half4 base = tex2D(GBufferTexture3, IN.PosProj); half light_l = length(lightVec); half atten = tex1D(AttenuationMap,light_l).r; half diffuse = 0.0f; half specular = 0.0f; //ambient half3 lighting = 0.1 * base; diffuse = saturate(dot(normalize(lightVec), normal)); specular = pow(saturate(dot(reflect(normalize(-eyeVec), normal), normalize(lightVec))), 16); lighting = PointLightColor * (diffuse * base + 0.7 * specular) * atten; return half4(lighting, 1.0f); }
En el ejemplo se utiliza Phong Shading como modelo de iluminación teniendo en cuenta una distancia de atenuación para cada fuente de luz. Esta distancia de atenuación es obtenida de una textura en función de la posición de la fuente de luz y del rango máximo de iluminación de la misma.
En resumen, en el pixel shader se realizan las siguientes acciones: o
Se recuperan los atributos almacenados anteriormente en el G-Buffer.
o
Se calcula la atenuación de la luz basada en la posición actual y en el rango máximo de iluminación.
Deferred Shading in Callisto
La textura de atenuación es similar a la siguiente:
36
o
Se realiza el cálculo de iluminación correspondiente al pixel que, a grandes rasgos, se puede definir como:
En el pixel shader solamente se realiza el cálculo correspondiente a La componente
.
se deberá calcular en una pasada diferente y
añadir posteriormente al resultado final.
Composición final
Deferred Shading in Callisto
El resultado final generado como salida del pixel shader anterior, para una escena iluminada por 11 fuentes de luz diferentes, es similar al que presenta la siguiente figura:
35
I LUSTRACIÓN 11 - R ESULTADO
FINAL
Optimizaciones El caso de ejemplo presentado en el apartado anterior se puede considerar el acercamiento más simple a la técnica deferred shading, tanto por el número de atributos almacenados en el G-Buffer como por el hecho de que no está optimizado de ninguna manera. A continuación se describen una serie de optimizaciones aplicables a lo largo del ciclo de vida de la técnica, desde la creación del G-Buffer hasta el cálculo de la iluminación, con el fin de mejorar el rendimiento final.
Diseño del G-Buffer Si se decidiesen almacenar otros atributos en el G-Buffer (specular power, specular intensity, occlusion terms, motion vectors, etc.), el número de canales libres en los RTs no serían suficientes. Tampoco existiría la posibilidad de añadir nuevos RTs, ya que solo se pueden utilizar simultáneamente los cuatro ya presentes (esto para DX9, en DX10 es aumentado hasta 8). Por lo tanto, variar el tamaño de cada uno de los RTs sería una de las opciones a contemplar. El inconveniente vendría en modo de penalización en cuanto a memoria VRAM y ancho de banda que se verían aumentados de forma prohibitiva para algunos dispositivos.
o
Position, 3 x FP16
o
Normals, 3 x FP16
o
Diffuse Albedo, 3 x I8
o
Specular Power, 1 x I8
o
Specular Intensity, 1 x I8
o
Motion Vectors, 2 x I8
o
Material ID, 1 x I8
En total sumarian 160 bits, 32 bits mayor que el límite que proporciona el diseño del G-Buffer anterior en el que cada RT era de 32 bits.
Deferred Shading in Callisto
Supongamos que se desean almacenar los siguientes atributos en el G-Buffer:
36
Si se aumenta el tamaño de cada RT a 64 bits se obtiene la cifra de 192 bits para 3 RTs. Se habría conseguido disminuir el número de texturas en memoria (menor gasto de VRAM) pero el ancho de banda, que es el que produce mayor riesgo de bottlenecks, se habría aumentado. Las optimizaciones que se comentan a continuación consiguen reducir el número de bits utilizados para almacenar los atributos, reduciendo el ancho de banda utilizado en cada fotograma.
Deferred Shading in Callisto
Hay que considerar que toda esta reducción de espacio conlleva un coste computacional adicional, por lo que será conveniente analizar detenidamente que solución merece ser aceptada como la más apropiada en cada caso.
35
Empaquetado de atributos A la hora de almacenar ciertos atributos se puede reducir el tamaño utilizado, siempre teniendo en cuenta que a menor tamaño de almacenamiento, menor precisión a la hora de realizar los cálculos de iluminación pertinentes y por lo tanto menor calidad final. Reducir el número de bits para almacenar un atributo se solía utilizar para conseguir un G-Buffer de RTs enteros en dispositivos que no soportaban texturas en coma flotante. Actualmente, la mayoría de los dispositivos presentes en el mercado (si no todos) soportan este tipo de texturas, por lo que la acción de empaquetar a menor precisión simplemente tiene como objetivo conseguir mejorar el rendimiento global de la técnica. Las siguientes funciones HLSL realizan un proceso de empaquetado/desempaquetado de FP16 I8:
half2 PackFloat16( half depth ) { depth /= 4; half Integer = floor(depth); half fraction= frac(depth); return half2( Integer/256, fraction); } half UnpackFloat16( half2 depth ) { const half2 unpack = {1024.0f, 4.0f}; return dot(unpack, depth); }
Si se usan vectores unitarios como normales, se puede llegar a calcular un componente a partir de los otros dos utilizando la siguiente ecuación:
Como se puede observar el componente calculado puede ser positivo o negativo. Sin embargo, si todo el cálculo de la iluminación es realizado en view space, en casi todas las ocasiones los polígonos presentarán un valor positivo o negativo en
Deferred Shading in Callisto
Eye-Space Normals
36
su componente Z, dependiendo del modo de representación utilizado (OpenGL positivo, Direct3D negativo). Solamente en contadas ocasiones, en las que el FOV es exageradamente amplio, el valor de la componente Z puede cambiar de signo.
La siguiente figura ilustra lo expuesto:
N +
V
EYE
Deferred Shading in Callisto
Para evitar en estos casos la incorrecta iluminación producida por el cambio de signo, se puede reservar un bit de uno de los dos canales que albergan las componentes x e y para almacenar el signo correspondiente. El RT quedaría así:
35
o
CANAL R (I8/FP16) 8-16 bits para almacenar normal.x
o
CANAL G (I8/FP16) 7-15 bits para almacenar normal.y, 1 bit para almacenar el signo
La optimización expuesta reduce en un canal el tamaño final para almacenar el vector normal, a costa de un coste computacional adicional. Se podría evitar este coste computacional utilizando una textura en la que cada texel albergase el valor de la componente z. De esta forma al samplear la textura con los valores u y v correspondientes a las componentes x e y de la normal, se obtendría la componente z.
Esta otra solución plantea el inconveniente de que se aumenta el coste del ancho de banda utilizado por la aplicación, que es lo que se planteaba en un principio reducir para mejorar el rendimiento final de la técnica. Por último, a la hora de almacenar los valores se pueden utilizar los métodos de empaquetado expuestos en el apartado anterior, indicando de nuevo que, en el caso de las normales necesarias para el cálculo de iluminación y otras técnicas como bump mapping, la pérdida de precisión se transforma en una pérdida de calidad visual notable. La siguiente tabla, sacada de Shishkovtsov[GPU GEMS 2], presenta todas las posibles opciones de configuración del RT que alberga las normales:
A8R8G8B 8
A2R10G10B10
All
Radeon 9500 or better
One mad
One mad
Hardware Support Deferring Cost
R16FG16F
A16R16G16B1 6F
Geforce FX or better
Geforce FX or better
Radeon 9500 or better
Radeon 9500 or better
None
None
One mov One mad
One mad
One dp2a
One nrm
One nrm
One rsq
Decoding Cost Sampling and Storage Quality Free Components
One rcp 32 bits
32 bits
32 bits
Poor
Good for rough surfaces
Excellent
1
1 (2 bits, very low precision)
0
64 bits Excellent 1
Si recordamos el diseño del G-Buffer del caso de ejemplo, para almacenar el vector position se han utilizado 3 canales de 16 bits cada uno (48 bits en total). El tamaño por componente establecido puede ser suficiente cuando se utiliza un sistema de coordenadas en view space, pero en world space las distancias pueden hacerse tan grandes que sea necesario utilizar precisiones de 32 bits, por lo que trastocarían la estructura del G-Buffer de forma considerable, así como se aumentarían los dichosos ancho de banda y VRAM que tan de cabeza traen en la fase de diseño.
Deferred Shading in Callisto
Position from Depth
36
La solución consiste en almacenar en el G-Buffer solamente la componente z del vector position en view space, normalizada entre el rango [0.0, distancia al plano más alejado del view frustum]. De esta forma, en vez de utilizar 48 bits para almacenar las tres componentes del vector position, se utilizarán 32 bits para almacenar la componente LESD (linear eye-space depth). En fase de creación del G-Buffer, más concretamente en el Pixel Shader, la componente LESD se calcula y propaga posteriormente de la siguiente manera:
… float fDepth = IN.ViewPos.z / frustumCoord.z; Out.Pos = float4(fDepth, 0, 0, 1); …
La RT resultante tendrá el siguiente aspecto:
Deferred Shading in Callisto
I LUSTRACIÓN 12 - L INEAR E YE -S PACE D EPTH
35
Para obtener las componentes x e y a partir del LESD se procederá como se comenta a continuación. Cuando se envía el rectángulo alineado en pantalla hacia el vertex shader, aparte de la información relativa a posición y coordenadas de textura, se pasará un vector adicional correspondiente a una de las esquinas del plano más alejado del view frustum. La esquina seleccionada del view frustum se corresponderá con la esquina del rectángulo alineado como indica la siguiente imagen:
FAR FRUSTUM PLANE
RECTÁNGULO ALINEADO EN PANTALLA
Al propagar el vector
desde el vertex shader hacia el pixel shader, se verá
interpolado de tal forma que se corresponderá con el pixel a calcular:
FAR FRUSTUM PLANE
Por último para recuperar el vector position original, bastará con multiplicar el vector
por la profundidad obtenida del G-Buffer como se muestra a
continuación:
Deferred Shading in Callisto
RECTÁNGULO ALINEADO EN PANTALLA
36
El siguiente bloque de código corresponde a los cambios realizados tanto en el vertex shader como en el pixel shader correspondientes a la segunda pasada (cálculo de iluminación):
//VERTEX SHADER
… //FrustumFar esquina correspondiente al plano más alejado del view frustum
OUT.EyeScreenRay = IN.FrustumFar; … //PIXEL SHADER
… //Se obtiene la profundidad correspondiente al pixel
float pDepth = tex2D(GBufferTexture1, IN.TC0).r; //Vp = Pfp x Pdepth
pos =
(IN.EyeScreenRay * pDepth);
…
Gracias a ésta optimización se ha reducido el número de bits utilizados en la construcción del G-Buffer final del ejemplo, así como el número de RTs de almacenamiento. La estructura resultante es similar a la siguiente: o
RT0 Depth : R32F (32 bits, nada libre)
o
RT1 Normals (x,y) + sign bit : G16R16F (32 bits, nada libre)
o
RT2 Albedo : A8R8G8B8 (32 bits, 1 canal de 8 bits libre)
En total 96 bits organizados en 3 RTs, dando lugar a un diseño de G-Buffer de lo más optimizado posible.
Deferred Shading in Callisto
Si al final se adopta la estructura propuesta en el apartado Diseño del G-Buffer se necesitaría solamente un RT más cuya composición sería la siguiente:
35
o
RT3 Specular Intensity (8 bits), Motion Vectors (16 bits), Material Id (8 bits) : A8R8G8B8 (32 bits, nada libre)
Y el canal libre de RT2 albergaría el atributo resultante, Specular Power, de 8 bits. Se obtendría pues un diseño muy completo de G-Buffer compuesto por un gran número de atributos, en solo 4 RTs y con un total de 128 bits por pixel, margen adecuado para evitar un gasto desorbitado de VRAM o el consumo peligroso de ancho de banda de sistema.
Volúmenes de luz Se ha visto como optimizando el diseño del GBuffer se consigue reducir el ancho de banda total en cada fotograma. Una vez generado el G-Buffer, éste es utilizado en cada pasada correspondiente al cálculo de iluminación para computar el color de cada uno de los pixeles de la pantalla. Ya que cada fuente de luz posee una atenuación determinada, existirán pixeles en pantalla que no estarán influenciados por ninguna luz, por lo tanto el cálculo realizado para los mismos se transforma en una pérdida de rendimiento bastante acusada. De esta forma, en vez de aplicar el pixel shader para todos los pixeles en pantalla, se aplicará a todos aquellos que estén influenciados al menos por un volumen de luz.
o
Point Lights, que transmiten luz con la misma intensidad en todas direcciones. Su volumen de luz corresponde a una esfera.
o
Spot Lights, que transmiten luz en una dirección fija determinada mediante un cono, que a su vez representa su volumen.
o
Directional lights, cuya luz se transmite por la escena en una misma dirección. Como ejemplo se puede considerar la luz del sol, que no sufre de atenuación y cuyo volumen es un rectángulo que cubre la pantalla entera.
Una vez descritos los diferentes volúmenes de luz, se pueden definir dos fases diferentes a la hora de establecer el proceso de optimización en el periodo de iluminación: fase de actualización y fase de renderizado.
Fase de Actualización En la fase de actualización se intenta reducir el número de fuentes de luz que afectan a la escena, aplicando uno a uno los siguientes enunciados:
Deferred Shading in Callisto
Existen varios tipos de fuente de luz, cada uno de ellos se puede asociar con una primitiva que representa su volumen final:
36
Deferred Shading in Callisto
35
Se descartarán las luces cuya influencia no sea apreciable una vez aplicados los algoritmos de visibilidad y oclusión apropiados. Los volúmenes de luz que queden completamente ocultos por otros elementos de la escena o que estén fuera del view frustum no entrarán a formar parte del cálculo de iluminación.
Se proyectarán los volúmenes de luz en pantalla (screen space).
Todas aquellas fuentes de luz que afecten a una misma región de pixeles se podrán combinar de tal forma que la fuente resultante proporcione una intensidad que simule la existencia de las fuentes originales.
De la misma forma, las fuentes de luz cuyo volumen proyectado sea tan pequeño que solamente influencia a pocos pixeles (bien sea porque la
fuente de luz sea pequeña o porque se encuentre alejada), podrá ser descartada.
Por último, se puede definir un número máximo predefinido de fuentes de luz que pueden afectar al mismo tiempo a cada pixel, escogiendo las más grandes, intensas y cercanas. De esta forma si el frame rate obtenido en el fotograma es bastante elevado, se puede elevar el número de fuentes de luz que pueden afectar a cada pixel. Por el contrario, si el frame rate es bajo, se puede reducir este número en beneficio del rendimiento.
Deferred Shading in Callisto
La siguiente imagen muestra un claro ejemplo de los volúmenes de luz calculados en la fase de actualización, que serán utilizados individualmente en la siguiente fase para aplicar las diferentes optimizaciones existentes.
36
Fase de Renderizado Se pueden definir dos grupos en los que clasificar los efectos especiales y de iluminación: fuentes globales y fuentes locales. Las fuentes globales afectarán a todos los pixeles, por lo que implica que los efectos se ejecutarán procesando la escena con el típico rectángulo en pantalla. Ejemplos de fuentes globales son las luces que iluminan todo el “mundo virtual” como la del sol, luces que se encuentran demasiado tan cerca de la cámara que la contienen dentro de su volumen ó efectos que se aplican a pantalla completa, como Depth of Field, fog, etc.
Deferred Shading in Callisto
Ya que en la técnica deferred shading el coste de procesamiento es directamente proporcional al número de píxeles afectados, las fuentes globales son las más costosas de todas. Afortunadamente no se suelen usar en cantidades que reduzcan de forma alarmante el rendimiento del sistema.
35
Las fuentes locales, al contrario de las anteriores, solamente afectan a regiones específicas de la escena. El beneficio en cuanto a las fuentes globales es evidente: solamente se procesarán los píxeles de pantalla que sean afectados al menos por una región. Existen diferentes métodos para determinar los pixeles que deberán ser procesados: Scissor Test, Stencil Cull, Z-Cull.
Scissor Test
La técnica consiste en utilizar el volumen que engloba la fuente de luz, calculado en la fase de actualización, para generar a partir del mismo un rectángulo que, una vez proyectado en pantalla, contendrá los pixeles sobre los que se realizarán los cálculos de iluminación pertinentes. Obviamente esta técnica es solamente adecuada para fuentes de luz del tipo point light o spot light ya que las directional light no tienen una posición en el espacio, por lo que se considera que afectan a toda la pantalla. El siguiente bloque de código [NVIDIA 2004], solamente aplicable a point lights, se encarga de calcular el rectángulo a partir de la posición de la fuente de luz y de su distancia de atenuación. position, const float range) in world space bbox3D[0].y = position.y + range; bbox3D[1].y = position.y + range; bbox3D[2].y = position.y - range; bbox3D[3].y = position.y - range; bbox3D[4].y = position.y + range; bbox3D[5].y = position.y + range; bbox3D[6].y = position.y - range; bbox3D[7].y = position.y - range;
Deferred Shading in Callisto
RECT DetermineClipRect(const D3DXVECTOR3& { //compute 3D bounding box of light D3DXVECTOR3 bbox3D[8]; bbox3D[0].x = position.x - range; bbox3D[0].z = position.z - range; bbox3D[1].x = position.x + range; bbox3D[1].z = position.z - range; bbox3D[2].x = position.x - range; bbox3D[2].z = position.z - range; bbox3D[3].x = position.x + range; bbox3D[3].z = position.z - range; bbox3D[4].x = position.x - range; bbox3D[4].z = position.z + range; bbox3D[5].x = position.x + range; bbox3D[5].z = position.z + range; bbox3D[6].x = position.x - range; bbox3D[6].z = position.z + range; bbox3D[7].x = position.x + range; bbox3D[7].z = position.z + range;
36
//project coordinates D3DXMATRIX viewProjMat = m_View * m_Projection; D3DXVECTOR2 projBox[8]; for (int i = 0; i < 8; ++i) { D3DXVECTOR4 projPoint; D3DXVec3Transform(&projPoint, &bbox3D[i], &viewProjMat); projBox[i].x = projPoint.x / projPoint.w; projBox[i].y = projPoint.y / projPoint.w; //clip to extents if (projBox[i].x < -1.0f) projBox[i].x = -1.0f; else if (projBox[i].x > 1.0f) projBox[i].x = 1.0f; if (projBox[i].y < -1.0f) projBox[i].y = -1.0f; else if (projBox[i].y > 1.0f) projBox[i].y = 1.0f;
}
//go to pixel coordinates projBox[i].x = ((projBox[i].x + 1.0f) / 2.0f) * iScreenWidth; projBox[i].y = ((-projBox[i].y + 1.0f) / 2.0f) * iScreenHeight;
//compute 2D unsigned int unsigned int unsigned int unsigned int
bounding box of projected coordinates minX = 0xFFFFFFFF; maxX = 0x00000000; minY = 0xFFFFFFFF; maxY = 0x00000000;
for (int i = 0; i < 8; ++i) { unsigned int x = static_cast(projBox[i].x); unsigned int y = static_cast(projBox[i].y); if (x < minX) minX = x; if (x > maxX) maxX = x; if (y < minY) minY = y; if (y > maxY) maxY = y; } RECT bbox2D; bbox2D.top = minY; bbox2D.bottom = maxY; bbox2D.left = minX; bbox2D.right = maxX; }
return bbox2D;
Deferred Shading in Callisto
En Direct3D se activará el scissor test de la siguiente manera:
35
... pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); ... RECT rect = DetermineClipRect(light.Position, light.Range); pd3dDevice->SetScissorRect(&rect);
La siguiente imagen ilustra el rectángulo resultante proyectado en pantalla:
En el caso de spot lights habrá que proceder de forma distinta ya que el volumen de un cono difícilmente se pude aproximar mediante una esfera. En este caso se utilizarán bounding boxes alineados hacia la dirección de la luz. De la misma forma que en el caso anterior, el bounding box será proyectado en pantalla con el fin de obtener el rectángulo deseado.
o
Crear un bounding box que encierre el cono de la forma más ajustada posible.
o
Proyectar las esquinas del bounding box al plano de pantalla.
o
Determinar a partir de la proyección las esquinas absolutas, es decir, la esquina superior izquierda, la esquina superior derecha, etc.
o
Si una de las esquinas se encuentra detrás del plano significará que la cámara se encuentra dentro del volumen, por lo que la luz se aplicará a toda la pantalla.
o
Utilizar las esquinas para formar el rectángulo y posteriormente pasárselo a Direct3D como en el caso anterior.
Deferred Shading in Callisto
El algoritmo para calcular el rectángulo final será similar al siguiente:
36
Z-Cull La aproximación más sencilla y en la que se obtienen buenos resultados a favor de un aceptable rendimiento consiste en proyectar el volumen de luz y utilizar el Z-Buffer para determinar el conjunto de pixeles final en los que se va a realizar el cómputo de iluminación. El algoritmo es el siguiente:
o
Si la cámara se encuentra dentro del volumen de luz, habrá que proyectar las caras traseras (backfaces) del volumen de luz. En ese caso se utilizará ZFunc= Greater para indicar en qué pixeles se realizará el cálculo de iluminación.
GREATE VIEW
Deferred Shading in Callisto
o
35
Si la cámara corta el plano más alejado del view frustum, habrá que proyectar las caras delanteras (frontfaces) del volumen de luz. En ese caso se utilizará ZFunc= Less para indicar en qué pixeles se realizará el cálculo de iluminación.
L
VIEW
o
En los demás casos se puede utilizar el procedimiento del primer punto, proyectando las caras traseras (backfaces) del volumen de luz y utilizando ZFunc= GreaterEqual para indicar en qué pixeles se realizará el cálculo de iluminación.
GREATE
La siguiente imagen muestra la totalidad de pixeles que se procesarían en el pixel shader al no efectuar Z-Culling. Nótese que, aunque se eliminan del cálculo los pixeles que no se encuentran dentro de la proyección del volumen de luz, todavía se están calculando pixeles correspondientes a fragmentos que no se encuentran físicamente dentro del volumen.
Deferred Shading in Callisto
VIEW
36
Deferred Shading in Callisto
Cuando se activa el Z-Culling, todos los pixeles que fallen el depth test establecido anteriormente no entrarán en el cómputo del pixel shader, por lo que el rendimiento se verá favorecido. En la siguiente imagen se puede comprobar la amplia región de pixeles dentro de la proyección del volumen de luz que se ha excluido del cálculo de iluminación.
35
Pero el Z-Cull realizado no excluye todos los pixeles dentro del volumen de luz. Su principal inconveniente es que todos los pixeles cuyo índice Z es menor al de los backfaces de la esfera son procesados por el pixel shader. En la imagen anterior coincide que no se encuentra ningún elemento delante de la esfera por lo que el Z-Cull trabajará de la forma deseada todos esos casos, pero en la siguiente imagen se muestra como, aunque la columna se encuentra fuera del volumen de luz, los pixeles que se encuentran dentro de la proyección del volumen serán igualmente calculados.
I LUSTRACIÓN 13 - I NCONVENIENT E Z-C ULL
Stencil Cull
Se trata de un algoritmo que utiliza dos pasadas por cada fuente de luz:
o
La primera pasada no afecta mucho al rendimiento global ya que se desactiva la escritura de color en el buffer. Se tendrán que proporcionar los siguientes estados de renderizado:
Depth-Func = LESS
Deferred Shading in Callisto
El siguiente método para descartar cálculos de iluminación innecesarios en pixeles que no están siendo afectados por ninguna fuente de luz se denomina Stencil Culling. A diferencia del anterior, este sí que excluye todos los pixeles fuera del volumen de luz.
36
Stencil Func = ALWAYS Stencil Pass = REPLACE (con el valor X) Todos los demás operandos (Stencil Fail, Stencil Pass) a KEEP CullMode = (Front Faces)
En la imagen se pueden ver las caras (back-faces en esta pasada) del volumen de luz que pasan el depth test. El paralelepípedo P posee un z-index menor por lo que en la zona que corta a la proyección del volumen (marcada con las líneas rojas) no se pasa el depth test. Todos los pixeles englobados entre las líneas azul-verdes serían calculados, justo como ocurre en el método anterior Z-Cull correspondiente a la Ilustración 13.
P
VIEW
Deferred Shading in Callisto
o
35
La segunda pasada se realiza con el shader de iluminación. Se tendrán que proporcionar los siguientes estados de renderizado:
Depth-Func = GREATEREQUAL Stencil Func = EQUAL Stencil Ref = X Todos los demás operandos (Stencil Fail, Stencil Pass) a KEEP CullMode = (Back Faces)
De esta forma, solo a las caras que en la primera iteración pasaron el stencil test (marcadas con un 1 en el mismo) se filtrarán a partir del segundo test. La siguiente imagen muestra, una vez pasado el segundo depth test y filtrar por el stencil buffer, la región final en la que se realizará el cálculo de iluminación (marcada por líneas verdes y rojas):
P
VIEW
Como inconvenientes, se pueden destacar dos de baja importancia:
Deferred Shading in Callisto
Como se puede observar en la siguiente captura de pantalla, la columna no se procesará por el pixel shader ya que no se encuentra dentro del volumen de luz.
36
o
El cambio continuo de RenderStates por fuente de luz, por lo que no sería posible procesar todas en una sola pasada.
Deferred Shading in Callisto
La existencia de dos pasadas por fuente de luz, aunque en la primera se desactive la escritura de color, baja el rendimiento. Sin embargo, en la mayoría de los casos es con la que mejor rendimiento se obtiene.
35