SlideShare una empresa de Scribd logo
1 de 40
Deferred Shading in Callisto     Callisto ENGINE Deferred Shading in Callisto       Tabla de contenido TOC  
1-3
    Introducción PAGEREF _Toc210150043  3Single Pass – Multiple Lights PAGEREF _Toc210150044  3Multiple Passes – Multiple Lights PAGEREF _Toc210150045  4Deferred Shading PAGEREF _Toc210150046  4Historia PAGEREF _Toc210150047  5G-Buffer PAGEREF _Toc210150048  7Multiple Render Targets PAGEREF _Toc210150049  7Position PAGEREF _Toc210150050  10Normal PAGEREF _Toc210150051  10Albedo PAGEREF _Toc210150052  12GBuffer Pass PAGEREF _Toc210150053  13Vertex Shader PAGEREF _Toc210150054  13Pixel Shader PAGEREF _Toc210150055  13Shading Pass PAGEREF _Toc210150056  15Vertex Shader PAGEREF _Toc210150057  16Pixel Shader PAGEREF _Toc210150058  17Composición final PAGEREF _Toc210150059  18Optimizaciones PAGEREF _Toc210150060  19Diseño del G-Buffer PAGEREF _Toc210150061  19Empaquetado de atributos PAGEREF _Toc210150062  20Eye-Space Normals PAGEREF _Toc210150063  20Position from Depth PAGEREF _Toc210150064  22Volúmenes de luz PAGEREF _Toc210150065  25Fase de Actualización PAGEREF _Toc210150066  26Fase de Renderizado PAGEREF _Toc210150067  28Shader LOD PAGEREF _Toc210150068  36Otras optimizaciones PAGEREF _Toc210150069  37Inconvenientes PAGEREF _Toc210150070  38Transparencia PAGEREF _Toc210150071  38Anti-aliasing PAGEREF _Toc210150072  38Múltiples Materiales PAGEREF _Toc210150073  38DS en DirectX10 PAGEREF _Toc210150074  39Integración PAGEREF _Toc210150075  40Sombras PAGEREF _Toc210150076  40Ambient Occlusion (SSAO) PAGEREF _Toc210150077  40Efectos de Post-Procesado PAGEREF _Toc210150078  40Glow PAGEREF _Toc210150079  40HDR PAGEREF _Toc210150080  40Motion Blur PAGEREF _Toc210150081  40Depth of Field PAGEREF _Toc210150082  40Relief / Parallax Mapping PAGEREF _Toc210150083  40Skinned Meshes/Skin shader PAGEREF _Toc210150084  40Blend Shapes PAGEREF _Toc210150085  40Conclusión PAGEREF _Toc210150086  41Bibliografía PAGEREF _Toc210150087  42 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. ( REF _Ref205225064  Ilustración 1 - Neverwinter Nights (2002)   VS   Mass Effect (2008) ) Ilustración  SEQ Ilustración  ARABIC 1 - Neverwinter Nights (2002)   VS   Mass Effect (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. 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.  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: 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. 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. 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. Como en el ejemplo anterior se proporciona el siguiente seudocódigo: for each light dofor each object affected by light doframebuffer += brdf(object,light); Asimismo se detectan algunos problemas o limitaciones: Como en el caso anterior, se realizan cálculos innecesarios por redibujado de pixeles. 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. 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.  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-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 object dorender lighting properties of object to G-Buffer;for each light doframebuffer += brdf (G-buffer,light); 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.  21145-1889Posteriormente 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.     Ilustración  SEQ Ilustración  ARABIC 2 – Rendering of 3d-shapes (Saito and Takahashi) 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. 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).  22225635Recientemente, 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.         Ilustración  SEQ Ilustración  ARABIC 3 – S.T.A.L.K.E.R. Shadow of Chernobyl 215392068580 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.  Ilustración 4 - Tabula Rasa 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 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.  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).   RT   A  B  G  R 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. En general los MRTs poseen las siguientes limitaciones o características: 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. 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 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. 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: NORMAL.XLIBRECOLOR.BCOLOR.GCOLOR.RPOSITION.ZPOSITION.XNORMAL.ZPOSITION.YLIBRENORMAL.YG16R16FRT3RT2RT1RT0  A2RGB10 RGBA8G16R16F LIBRE 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.  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. 32 bpp64 bpp128 bppnMRTs = 214 GB/seg18 GB/seg26 GB/segnMRTs = 316 GB/seg22 GB/seg34 GB/segnMRTs = 418 GB/seg26 GB/seg42 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. 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. 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: Ilustración  SEQ Ilustración  ARABIC 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. Normal Se trata del vector normal correspondiente a cada pixel almacenado en view space.  -4381574930Como 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. Ilustración 6 - Mapa de normales en Tangent SpaceEn 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. 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. Para ello habrá que formar la matriz tangencial utilizando los vectores tangent, binormal y normal  que a su vez estarán en view space.  MVS= TxTyTzBxByBzNxNyNz  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:  NVS=NTS ×  MVS Posteriormente, el vector resultante de la ecuación anterior será normalizado para no perder precisión en los cálculos posteriores.  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. 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: Ilustración 7 - RT2 (view space normals) 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. 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: Ilustración 8 - RT3 (albedo 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). 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. 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: Se transforma el vector posición en view space que corresponderá al valor almacenado en la RT0/RT1. 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 MVS utilizada en la creación del vector normal (en view space) almacenado en RT2. 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;Out.Pos = mul(IN.pos, g_mWorldViewProjection);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 MVS (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= float4(IN.interPos.x, IN.interPos.y, 0, 1);Out.PosZ= 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;} 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: Ilustración 9 - Pixel Coordinate System Mientras que el segundo considera como origen de coordenadas la esquina superior izquierda del pixel: Ilustración 10 - Texel Coordinate System 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 PANTALLAWH∆U =  12 × W∆V =  12 × Ht=(∆U ,∆V)t=(1+ ∆U ,∆V)t=(∆U ,1+ ∆V)t=(1+ ∆U ,1+ ∆V) 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. 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.  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 {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;//ambienthalf3 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.  La textura de atenuación es similar a la siguiente: En resumen, en el pixel shader se realizan las siguientes acciones: Se recuperan los atributos almacenados anteriormente en el G-Buffer. Se calcula la atenuación de la luz basada en la posición actual y en el rango máximo de iluminación. Se realiza el cálculo de iluminación correspondiente al pixel que, a grandes rasgos, se puede definir como: Iads= att(Idiff+ Ispec) Itotal = Iambient+ Iads En el pixel shader solamente se realiza el cálculo correspondiente a  Iads. La componente Iambient se deberá calcular en una pasada diferente y añadir posteriormente al resultado final.  Composición final 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: Ilustración 11 - Resultado 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. Supongamos que se desean almacenar los siguientes atributos en el G-Buffer: 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.  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.  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. 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);} Eye-Space Normals 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: z= ±1- x2- y2 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). 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: EYE+ -NV 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í:  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 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:   A8R8G8B8A2R10G10B10R16FG16FA16R16G16B16FHardware SupportAllRadeon 9500 or betterGeforce FX or betterRadeon 9500 or betterGeforce FX or betterRadeon 9500 or betterDeferring CostOne madOne madNoneNoneDecoding CostOne madOne nrmOne madOne nrmOne movOne dp2aOne rsqOne rcpSampling and Storage32 bits32 bits32 bits64 bitsQualityPoorGood for rough surfacesExcellentExcellentFree Components11  (2 bits, very low precision)01 Position from Depth 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.  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: Ilustración 12 - Linear Eye-Space Depth 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: RECTÁNGULO ALINEADO EN PANTALLAFAR FRUSTUM PLANEVRTR VFPTR  Al propagar el vector VFPTR  desde el vertex shader hacia el pixel shader, se verá interpolado de tal forma que se corresponderá con el pixel a calcular: RECTÁNGULO ALINEADO EN PANTALLAFAR FRUSTUM PLANEPR PFP  Por último para recuperar el vector position original, bastará con multiplicar el vector PFP  por la profundidad obtenida del G-Buffer como se muestra a continuación: VP=Pfp  × Pdepth 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 frustumOUT.EyeScreenRay = IN.FrustumFar;…//PIXEL SHADER…//Se obtiene la profundidad correspondiente al pixelfloatpDepth = 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: 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 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: 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 G-Buffer se consigue reducir el ancho de banda total en cada fotograma. 9588529210Una 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.  Existen varios tipos de fuente de luz, cada uno de ellos se puede asociar con una primitiva que representa su volumen final: Point Lights, que transmiten luz con la misma intensidad en todas direcciones. Su volumen de luz corresponde a una esfera. Spot Lights, que transmiten luz en una dirección fija determinada mediante un cono, que a su vez representa su volumen. 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: 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. 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. 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. 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. 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. RECT DetermineClipRect(const D3DXVECTOR3& position, const float range){//compute 3D bounding box of light in world spaceD3DXVECTOR3 bbox3D[8];bbox3D[0].x = position.x - range;  bbox3D[0].y = position.y + range;  bbox3D[0].z = position.z - range;bbox3D[1].x = position.x + range;  bbox3D[1].y = position.y + range;  bbox3D[1].z = position.z - range;bbox3D[2].x = position.x - range;  bbox3D[2].y = position.y - range;  bbox3D[2].z = position.z - range;bbox3D[3].x = position.x + range;  bbox3D[3].y = position.y - range;  bbox3D[3].z = position.z - range;bbox3D[4].x = position.x - range;  bbox3D[4].y = position.y + range;  bbox3D[4].z = position.z + range;bbox3D[5].x = position.x + range;  bbox3D[5].y = position.y + range;  bbox3D[5].z = position.z + range;bbox3D[6].x = position.x - range;  bbox3D[6].y = position.y - range;  bbox3D[6].z = position.z + range;bbox3D[7].x = position.x + range;  bbox3D[7].y = position.y - range;  bbox3D[7].z = position.z + range; //project coordinatesD3DXMATRIX 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 extentsif (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 coordinatesprojBox[i].x = ((projBox[i].x + 1.0f) / 2.0f) * iScreenWidth;projBox[i].y = ((-projBox[i].y + 1.0f) / 2.0f) * iScreenHeight;}//compute 2D bounding box of projected coordinatesunsigned int minX = 0xFFFFFFFF;unsigned int maxX = 0x00000000;unsigned int minY = 0xFFFFFFFF;unsigned int maxY = 0x00000000;for (int i = 0; i < 8; ++i){unsigned int x = static_cast,[object Object]
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto
Deferred Shading In Callisto

Más contenido relacionado

Similar a Deferred Shading In Callisto

Dark Basic Pro
Dark Basic ProDark Basic Pro
Dark Basic ProMentoring
 
Practica2[Pds][Reporte]
Practica2[Pds][Reporte]Practica2[Pds][Reporte]
Practica2[Pds][Reporte]Edgar Martinez
 
Manual de sap 2000 en español 3
Manual de sap 2000 en español 3Manual de sap 2000 en español 3
Manual de sap 2000 en español 3Julio Terrones
 
Manual sap2000-exelente-38pag
Manual sap2000-exelente-38pagManual sap2000-exelente-38pag
Manual sap2000-exelente-38pagEmerson Ojeda
 
Redes neuronales artificiales_aplicadas
Redes neuronales artificiales_aplicadasRedes neuronales artificiales_aplicadas
Redes neuronales artificiales_aplicadasIvan Gooseman
 
Informe de proyecto final de control discreto
Informe de proyecto final de control discretoInforme de proyecto final de control discreto
Informe de proyecto final de control discretoSamuel Guiza Jerez
 
Presentación videojuegos
Presentación videojuegosPresentación videojuegos
Presentación videojuegosdiegoargumosa
 
Presentación videojuegos
Presentación videojuegosPresentación videojuegos
Presentación videojuegosdiegoargumosa
 
Jp2k15(b)
Jp2k15(b)Jp2k15(b)
Jp2k15(b)Lo Lei
 
Plantilla Implementación
Plantilla ImplementaciónPlantilla Implementación
Plantilla Implementaciónenyarl
 
Cesnavarra 2009-boletín 2
Cesnavarra 2009-boletín 2Cesnavarra 2009-boletín 2
Cesnavarra 2009-boletín 2Cein
 
Tesis Javier Luiso - Sensor de Flujo Óptico
Tesis Javier Luiso - Sensor de Flujo ÓpticoTesis Javier Luiso - Sensor de Flujo Óptico
Tesis Javier Luiso - Sensor de Flujo ÓpticoJavier Luiso
 
DarkBasic Pro
DarkBasic ProDarkBasic Pro
DarkBasic ProMentoring
 
Imii guia lab_15_2
Imii guia lab_15_2Imii guia lab_15_2
Imii guia lab_15_2tonhonw
 
Reparacion de monitores
Reparacion de monitoresReparacion de monitores
Reparacion de monitoresjosesuruy
 

Similar a Deferred Shading In Callisto (20)

Dark Basic Pro
Dark Basic ProDark Basic Pro
Dark Basic Pro
 
Motores de graficos
Motores de graficosMotores de graficos
Motores de graficos
 
Inf164
Inf164Inf164
Inf164
 
Practica2[Pds][Reporte]
Practica2[Pds][Reporte]Practica2[Pds][Reporte]
Practica2[Pds][Reporte]
 
Manual de sap 2000 en español 3
Manual de sap 2000 en español 3Manual de sap 2000 en español 3
Manual de sap 2000 en español 3
 
Manual sap2000-exelente-38pag
Manual sap2000-exelente-38pagManual sap2000-exelente-38pag
Manual sap2000-exelente-38pag
 
Redes neuronales artificiales_aplicadas
Redes neuronales artificiales_aplicadasRedes neuronales artificiales_aplicadas
Redes neuronales artificiales_aplicadas
 
53816 9 georas-arcview
53816 9 georas-arcview53816 9 georas-arcview
53816 9 georas-arcview
 
Tarjetas
TarjetasTarjetas
Tarjetas
 
Informe de proyecto final de control discreto
Informe de proyecto final de control discretoInforme de proyecto final de control discreto
Informe de proyecto final de control discreto
 
Presentación videojuegos
Presentación videojuegosPresentación videojuegos
Presentación videojuegos
 
Presentación videojuegos
Presentación videojuegosPresentación videojuegos
Presentación videojuegos
 
Jp2k15(b)
Jp2k15(b)Jp2k15(b)
Jp2k15(b)
 
Plantilla Implementación
Plantilla ImplementaciónPlantilla Implementación
Plantilla Implementación
 
Cesnavarra 2009-boletín 2
Cesnavarra 2009-boletín 2Cesnavarra 2009-boletín 2
Cesnavarra 2009-boletín 2
 
G P Un Vidia
G P Un VidiaG P Un Vidia
G P Un Vidia
 
Tesis Javier Luiso - Sensor de Flujo Óptico
Tesis Javier Luiso - Sensor de Flujo ÓpticoTesis Javier Luiso - Sensor de Flujo Óptico
Tesis Javier Luiso - Sensor de Flujo Óptico
 
DarkBasic Pro
DarkBasic ProDarkBasic Pro
DarkBasic Pro
 
Imii guia lab_15_2
Imii guia lab_15_2Imii guia lab_15_2
Imii guia lab_15_2
 
Reparacion de monitores
Reparacion de monitoresReparacion de monitores
Reparacion de monitores
 

Deferred Shading In Callisto

  • 1.