Deferred Shading In Callisto

  • May 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Deferred Shading In Callisto as PDF for free.

More details

  • Words: 8,102
  • Pages: 19
Deferred Shading in Callisto

Deferred Shading in Callisto

CALLISTO ENGINE

Deferred Shading in Callisto

Tabla de contenido

1

INTRODUCCIÓN ............................................................................................................... 3 SINGLE PASS – MULTIPLE LIGHTS ........................................................................................ 3 MULTIPLE PASSES – MULTIPLE LIGHTS ................................................................................. 4 DEFERRED SHADING....................................................................................................... 4 HISTORIA ................................................................................................................ 5 G-BUFFER ...................................................................................................................... 7 MULTIPLE RENDER TARGETS ............................................................................................ 7 POSITION.................................................................................................................. 10 NORMAL .................................................................................................................. 10 ALBEDO ................................................................................................................... 12 GBUFFER PASS ............................................................................................................... 13 VERTEX SHADER ......................................................................................................... 13 PIXEL SHADER ............................................................................................................ 13 SHADING PASS ............................................................................................................... 15 VERTEX SHADER ......................................................................................................... 16 PIXEL SHADER ............................................................................................................ 17 COMPOSICIÓN FINAL .................................................................................................... 18 OPTIMIZACIONES............................................................................................................ 19 DISEÑO DEL G-BUFFER.................................................................................................. 19 EMPAQUETADO DE ATRIBUTOS .................................................................................... 20

EYE-SPACE NORMALS................................................................................................ 20 POSITION FROM DEPTH ............................................................................................. 22 VOLÚMENES DE LUZ ..................................................................................................... 25 FASE DE ACTUALIZACIÓN ........................................................................................... 26 FASE DE RENDERIZADO ............................................................................................. 28

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 gráfico. Hace algunos años la iluminación ón de un escenario consistía en el cálculo por vértice de 1-22 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 real. (Ilustración 1 - Neverwinter Nights (2002) VS Mass Effect (2008) )

I L USTRACIÓN 1 - N EVER WI WINTER NTER N IGHTS (2002) VS M ASS E FFE CT (2008)

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 pass-multiple lights)) ó realizar el cálculo en varias pasadas ((multiple pass-multiple lights). 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.

Deferred Shading in Callisto

Deferred Shading in Callisto

Single Pass – Multiple Lights Light s

2

3

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. El siguiente seudocódigo ilustra el enunciado anterior: for each object do for each light do in a single shader framebuffer = brdf brdf(object,light);

Se trata de la aproximación más simple de las dos, por lo que posee algunas limitaciones e inconvenientes: o

únicamente una vez, almacenando en dicha pasada todos los atributos de iluminación ((posición, posición, normales, color, etc.) en memoria de video local (G-Buffer) Buffer) de tal forma que se puedan usar en las siguientes pasadas.

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.

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 G-Buffer como entrada, se calculará el color resultante para cada pixel. 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). 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 G-Buffer, Buffer, podrá servir para incrementar la cantidad de polígonos en la escena aumentando do 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 G-Buffer) ya que el color de cada pixel es calculado solamente una vez.

Mult iple Passes – Multiple Light s

ra de forma clara la naturaleza de la técnica: El siguiente seudocódigo ilustra for each object do render lighting properties of object to G-Buffer; for each light do framebuffer += brdf (G--buffer,light);

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. Como en el ejemplo anterior se proporciona el siguiente seudocódigo: for each light do for each object affected by light do framebuffer += brdf(object,light);

Historia La técnica fue inicialmente introducida en 1988 por Michael Deering et al en el SIGGRAPH 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 luz, sobre dicha geometría.

Asimismo se detectan algunos problemas o limitaciones:

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

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 erizado 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 (GBuffers).

Deferred Shading in Callisto

Como en el caso anterior, se realizan cálculos innecesarios por redibujado de pixeles.

Deferred Shading in Callisto

o

4

5

Otro estudio relacionado con la técnica fue el realizado por Ellsworth en 1991, que investigó nvestigó arquitecturas paralelas y algoritmos para síntesis en tiempo real de imágenes en alta calidad usando deferred shading shading.

I L USTRACIÓN 2 – R E NDE RING 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 álculos realizados en los complejos modelos de sombreado utilizados en su arquitec arquitectura.

G - Buffer

Despues de los artículos mencionados ha han existido otras investigaciones y desarrollos que han utilizado la técnica deferred shading ( Nicolas Thibieroz, Shawn Hargreaves Hargreaves, Randima Fernando, Ariel Brunetto, Frank Pluig entre otros).

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.

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.

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 G-Buffer. 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.

Aún más reciente es el artículo escrito por Rusty sty 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 bajaba en la realización del motor de renderizado del juego ““Tabula Rasa”, basado en la técnica deferred shading shading.

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.

I LUSTRACIÓN 3 – S.T.A.L.K.E.R. S HADOW OF C HE RNOBYL

Mult iple Render Targets 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 onar varios BRDF’s en la escena. En ellas se utiliza el G-Buffer Buffer para almacenar otro tipo de información y utilizarla en una pasada posterior usando forward rendering.

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).

RT

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.

I LUSTRACIÓN 4 - T AB ULA R ASA

Deferred Shading in Callisto

Deferred Shading in Callisto

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).

6

7

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. En general los MRTs poseen las siguientes limitaciones o características: 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:   

o

o

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 bpp.

RT0 : G16R16F RT1 : A8R8G8B8 RT2 : R32F

Dithering, alpha testing, fogging, blending, o masking solamente podrán ser utilizados si el dispositivo tiene marcado el bit D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING. 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.

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.

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:

RT1

POSITION.Z

POSITION.Y

LIBRE

G16R16F

G16R16F

RT2

LIBRE

NORMAL.X

NORMAL.Y

NORMAL.Z

A2RGB10

RT3

COLOR.R

COLOR.G

COLOR.B

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

POSITION.X

Deferred Shading in Callisto

RT0

8

9

A continuación se describirá el proceso de creación de cada una de las superficies de almacenamiento que forman el G-Buffer Buffer final, siempre siguiendo el ejemplo de configuración anterior donde los atributos que se mapean corresponden a position, normals y diffus diffuse albedo.

Posit io n

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.

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 space.

Para ello habrá que formar la matriz tangencial utilizando los vectores tangent, binormal y normal ormal que a su vez estarán en view 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:







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.

I L USTR ACIÓN 5 - RT0 ( POS . X , POS . Y )

Y

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 tr tres de 10 bits cada uno, se utilizarán para almacenar las coordenadas ((x,y,z) del vector normal, quedando el canal A libre.

RT1 ( POS . Z )

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:

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 representado.

Normal Se trata del vector normal correspondiente a cada pixel almacenado en view space.

I LUSTRACIÓN 6 - M APA DE NORMALES EN T ANGE NT S P ACE

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.

Deferred Shading in Callisto

A partir de dicho sistema de coordenadas se podrán realizar técnicas como bump mapping ó incluso parallax mapping/relief /relief mapping que serán comentadas en apartados posteriores.

Deferred Shading in Callisto

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.

10

11

I LUSTRACIÓN 7 - RT2 ( VIEW SPACE NORM ALS )

Albedo

GBuffer Pass

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.

La primera pasada se encargará de almacenar los atributos seleccionados (position, normal, albedo) en cada una de las textura que forman el G-Buffer.

Como cada componente de color RGBA se puede representar en un rango 00-255, 255, se utilizará una textura de 32 bits, con 8 bits por cada canal.

Verte x Shader

La siguiente imagen muestra la RT3 correspondiente al G G-Buffer, Buffer, en la que se ha mapeado D ((diffuse.x, diffuse.y, diffuse.z)) en los canales RGB, dejando libre el canal Alfa:

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; I LUSTRACIÓN 8 - RT3 ( ALBEDO MAP )

Out.Pos = mul(IN.pos, g_mWorldViewProjection); float4 pp = mul( IN.pos, g_mWorldView ); Out.WorldPos = pp;

isma forma se podrían almacenar otro tipo de atributos en los canales no utilizados, como mapas que De la misma definan la componente especular (specular specular power / specular intensity intensity).

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;

Una vez definida la composición y estructura del G 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.

return Out;

Deferred Shading in Callisto

Deferred Shading in Callisto

}

12

13

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.

Shading Pass

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.

Como se había comentado en anteriores apartados, el G-Buffer 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 G-Buffer.

PS_OUTPUT_GBUFFERPASS PS_GBUFFER( VS_OUTPUT_GBUFFERPASS IN ) { PS_OUTPUT_GBUFFERPASS Out;

Para ello hay que tener en cuenta que los sistemas de coordenadas corr correspondientes a pixels y texels en Direct3D 9 difieren en la situación de su origen de coordenadas.

float3 base = tex2D(Textura2D0, IN.texCoord); float3 bump = tex2D(Textura2D1, IN.texCoord) * 2 - 1; bump = normalize(bump);

El primero considera el centro del pixel como el origen del sistema de coordenadas:

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

I LUSTRACIÓN 9 - P I XEL C OORDINATE S YSTEM

Deferred Shading in Callisto

Deferred Shading in Callisto

Mientras que el segundo considera como origen de coordenadas la esquina superior izquierda del pixel:

14

15

I LUSTRACIÓ N 10 - T EXEL C OORDI NATE 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:

  (∆ , ∆ )







 

  

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.

  (1 + ∆ , ∆ )

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).

float4 PS_RenderLight(VS_OUTPUT_LIGHT IN) : COLOR0 {

H

RECTÁNGULO ALINEADO EN PANTALLA

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

  (∆ , 1 + ∆ )

W

half3 normal; // normales en texture space [0,1] -> convertir a [-1,+1] normal.xyz = tex2D(GBufferTexture4, IN.PosProj).xyz * 2 - 1;

  (1 + ∆ , 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;

Verte x Shader

//ambient half3 lighting = 0.1 * base;

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.

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;

VS_OUTPUT_SCREENQUAD VS_RenderScreenQuad( VS_INPUT_SCREENQUAD IN ) { VS_OUTPUT_SCREENQUAD OUT;

return half4(lighting, 1.0f); }

OUT.Position = IN.Position; OUT.TC0 = IN.TC0 + vTexelSize; OUT.EyeScreenRay = IN.FrustumFar;

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.

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

Deferred Shading in Callisto

}

Deferred Shading in Callisto

return OUT;

16

17

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. La textura de atenuación es similar a la siguiente:

En resumen, en el pixel shader se realiza realizan las siguientes acciones: o

Se recuperan los atributos almacenad almacenados anteriormente en el G-Buffer.

o

Se calcula la atenuación de la luz basada en la posición actual y eenn el rango máximo de iluminación.

o

Se realiza el cálculo de iluminación correspondiente al pixel que, a grandes rasgos, se puede definir como:

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.

!"#$  "%%(!#()) + !$*+, ) !%&%"'  !"-.(+/% + !"#$

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.

Diseño del G - Buffe r 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.

Composición final

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).

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:

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. Supongamos que se desean almacenar los siguientes atributos en el G-Buffer: o o o o o o o

Position, 3 x FP16 Normals, 3 x FP16 Diffuse Albedo, 3 x I8 Specular Power, 1 x I8 Specular Intensity, 1 x I8 Motion Vectors, 2 x I8 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

I LUSTRACIÓN 11 - R ESULTADO FINAL

Deferred Shading in Callisto

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.

18

19

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. 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.

La siguiente figura ilustra lo expuesto:

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.

N

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.

+

V

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

EYE

return half2( Integer/256, fraction); } half UnpackFloat16( half2 depth ) { const half2 unpack = {1024.0f, 4.0f};

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í:

return dot(unpack, depth); }

o o

CANAL R (I8/FP16)  8-16 bits para almacenar normal.x CANAL G (I8/FP16)  7-15 bits para almacenar normal.y, 1 bit para almacenar el signo

Eye - Space No rmals La optimización expuesta reduce en un canal el tamaño final para almacenar el vector normal, a costa de un coste computacional adicional.

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:

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.

0  ±2 − 4 − 5 Deferred Shading in Callisto

Solamente en contadas ocasiones, en las que el FOV es exageradamente amplio, el valor de la componente Z puede cambiar de signo.

Deferred Shading in Callisto

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 su componente Z, dependiendo del modo de representación utilizado (OpenGL  positivo, Direct3D  negativo).

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.

20

21

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:

A8R8G8B8

Hardware Support Deferring Cost

A2R10G10B10

A16R16G16B16F

R16FG16F Geforce FX or better

Geforce FX or better

Radeon 9500 or better

Radeon 9500 or better

None

None

All

Radeon 9500 or better

One mad

One mad

One mad

One mad

One dp2a

One nrm

One nrm

One rsq

One mov

Decoding Cost

One rcp

Sampling and Storage

32 bits

32 bits

32 bits

64 bits

Quality

Poor

Good for rough surfaces

Excellent

Excellent

1

1 (2 bits, very low precision)

0

Free Components

1

I LUSTRACIÓN 12 - L INE AR E YE -S P ACE D EPTH

Position from Depth

Para obtener las componentes x e y a partir del LESD se procederá como se comenta a continuación.

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).

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:

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. 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).

FAR FRUSTUM PLANE

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: RECTÁNGULO ÁNGULO ALINEADO EN PANTALLA



La RT resultante tendrá el siguiente aspecto:

Deferred Shading in Callisto

float fDepth = IN.ViewPos.z / frustumCoord.z; Out.Pos = float4(fDepth, 0, 0, 1);

Deferred Shading in Callisto



22

23

<=;:

:;:

Al propagar el vector 6789 desde el vertex shader hacia el pixel shader, se verá interpolado de tal forma que se corresponderá con el pixel a calcular calcular:

La estructura resultante te es similar a la siguiente:

FAR FRUSTUM PLANE

RECTÁNGULO ALINEADO EN PANTALLA

=:

o o o

=<=

RT0  Depth : R32F (32 bits, nada libre) RT1  Normals (x,y) + sign bit : G16R16F (32 bits, nada libre) 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 Buffer de lo más optimizado posible. 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: 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 ía el atributo resultante, Specular Power, de 8 bits. Se obtendría pues un diseño muy completo de G G-Buffer 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 sis sistema.

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:

Volúmenes de luz

=  =)*  =#+*%>

Se ha visto como optimizando el diseño del G G-Buffer Buffer se consigue reducir el ancho de banda total en cada fotograma.

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):

Una vez generado el G-Buffer, Buffer, éste es utilizado en cada pasada correspondiente al cálculo de iluminación para compu 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.

//VERTEX SHADER

… //FrustumFar  esquina correspondiente al plano más alejado del view frustum

OUT.EyeScreenRay = IN.FrustumFar;

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.

… //PIXEL SHADER



Existen varios tipos de fuente de luz, cada uno de ellos se puede asociar con una primitiva que representa su volumen final:

//Se obtiene la profundidad correspondiente al pixel

(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.

Deferred Shading in Callisto

//Vp = Pfp x Pdepth

pos =

Deferred Shading in Callisto

float pDepth = tex2D(GBufferTexture1, IN.TC0).r;

24

25

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.

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.

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: 

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.

Deferred Shading in Callisto



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.

26

27

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

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.

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

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.

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

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.

} //compute 2D unsigned int unsigned int unsigned int unsigned int

Existen diferentes métodos para determinar los pixeles que deberán ser procesados: Scissor Test, Stencil Cull, ZCull.

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;

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.

return bbox2D; }

position, const float range) in world space

En Direct3D se activará el scissor test de la siguiente manera:

bbox3D[0].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

bbox3D[1].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;

bounding box of projected coordinates minX = 0xFFFFFFFF; maxX = 0x00000000; minY = 0xFFFFFFFF; maxY = 0x00000000;

28

29

... pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); ... RECT rect = DetermineClipRect(light.Position, light.Range); pd3dDevice->SetScissorRect(&rect);

El algoritmo es el siguiente:

La siguiente imagen ilustra el rectángulo resultante proyectado en pantalla pantalla:

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.

GREATER VIEW FRUSTUM

En el caso de spot lights habrá que proceder de forma distinta tinta 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

El algoritmo para calcular el rectángulo final será similar al siguiente:

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 den dentro del volumen, por lo que la luz se aplicará a toda la pantalla pantalla.

o

Utilizar las esquinas para formar el rectángulo y posteriormente pasárselo a Direct3D como en el caso anterior.

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.

LESS

Deferred Shading in Callisto

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 dee iluminación iluminación.

Deferred Shading in Callisto

Z - Cull

VIEW FRUSTUM

30

31

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.

GREATER

VIEW FRUSTUM

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. Pero el Z-Cull 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.

Deferred Shading in Callisto

Cuando se activa el Z-Culling, 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 eles dentro de la proyecci proyección ón del volumen de luz que se ha excluido del cálculo de iluminación.

Deferred Shading in Callisto

En la imagen anterior coincide que no se encuentra ningún elemento delante de la esfera por lo que el Z 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.

32

33

I LUSTRACIÓN 13 - I NCONVE NIE NTE Z-C UL L

Stencil Cull

 Stencil Ref = X  Todoss los demás operandos ((Stencil Fail, Stencil Pass) a KEEP  CullMode = (Back Faces)

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.

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.

Se trata de un algoritmo que utiliza dos pasadas por cada fuente de luz:

La siguiente imagen muestra, una vez pa pasado sado el segundo depth test y filtrar por el stencil buffer, la región final en la que se realizará el cálculo de iluminaci iluminación (marcada por líneas verdes y rojas):

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 Stencil Func = ALWAYS Stencil Pass = REPLACE (con el valor X) Todos los demás operandos (Stencil Fail, Stencil Pass) a KEEP CullMode = (Front Faces)

P

En la imagen se pueden ver las caras (back-faces en esta pasada) del volumen de luz que pasan el depth test.

VIEW FRUSTUM

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. 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.

P

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

Deferred Shading in Callisto

o

Deferred Shading in Callisto

VIEW FRUSTUM

34

35

Como inconvenientes, se pueden destacar dos de baja importancia: 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.

36

Related Documents