Hemos recibido varias peticiones sobre el tutorial para aplicar efecto de contorno y hemos decidido ampliarlo un poco para hablar sobre dos de estas peticiones: zonas opacas y el uso de la distancia en escena.
Parte 1: Efecto de contorno
Parte 2: Oclusión, colores y resplandor
Parte 3: Zonas opacas
Parte 4: Límite de profundidad
Límite de profundidad
Lo primero que tenemos que hacer es comprender como se mide la profundidad. El valor del buffer de profundidad personalizada (Custom depth) es relativo a la posición de la cámara activa, para medirlo podemos dibujar (usando la imaginación) un frustum desde la posición de la cámara. La altura del fustrum se corresponderá con la profundidad máxima a la que el efecto de contorno será aplicado.
Una opción para ver el límite en tiempo real es añadir un plano a la cámara del personaje con un material traslúcido. Para nuestro ejemplo la coordenada X del plano determinará la frontera del efecto.
Ahora en la escena podemos ver el panel rojo traslúcido para indicarnos el límite del efecto, si el personaje se mueve por el mapa el panel va con el.
El material
Vamos a empezar creando un material que nos pintara el area del efecto. Con este simple material podremos ver facilmente la zona en la que el efecto será aplicado, elegimos un valor de profundidad máxima de 500 para este ejemplo
O utilizando una manera un poco más elegante pero con el mismo resultado:
Como se puede ver ahora el cofre más alejado no tiene ningún efecto de contorno visible
Para aplicar este límite al material final tenemos que reemplazar el nodo SceneTexture:CustomDepth del material anterior con un nuevo nodo HLSL, esto es necesario porque no tenemos ninguna información de profundidad en la linea del contorno, esta es añadida en tiempo real al objeto pero no le pertenece, no tiene ningún valor de Custom Stencil o Custom Depth . Por lo tanto necesitamos una variación de nuestro anterior nodo para el cálculo del borde (Neighboring pixel calculation), tenemos que utilizar los valores de la superficie del objeto para calcular la «profundidad» para cada pixel del del contorno.
Por lo tanto en vez de usar solo el valor del Custom Stencil…
float TL = GetScreenSpaceData(ScreenPosition + float2(-offset_h, -offset_v), false).GBuffer.CustomStencil.r;
…tenemos que añadir una comprobación sobre el valor del Custom Depth
float TL = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(-offset_h, -offset_v)), false).GBuffer.CustomStencil.r > 0 ? GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(-offset_h, -offset_v)), false).GBuffer.CustomDepth.r : 0;
Finalmente el código HLSL de este nuevo nodo (Neighboring-depth-pixel-calculation) quedará así:
#if SCENE_TEXTURES_DISABLED return 0; #endif float offset_h = SceneTexelSize.r * Thickness; float offset_v = SceneTexelSize.g * Thickness; float TL = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(-offset_h, -offset_v)), false).GBuffer.CustomStencil.r > 0 ? GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(-offset_h, -offset_v)), false).GBuffer.CustomDepth.r : 0; float TM = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(0, -offset_v)), false).GBuffer.CustomStencil.r > 0 ?GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(0, -offset_v)), false).GBuffer.CustomDepth.r: 0; float TR = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(offset_h, -offset_v)), false).GBuffer.CustomStencil.r > 0 ? GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(offset_h, -offset_v)), false).GBuffer.CustomDepth.r: 0; float ML = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(-offset_h, 0)), false).GBuffer.CustomStencil.r > 0 ? GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(-offset_h, 0)), false).GBuffer.CustomDepth.r: 0; float MR = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(offset_h, 0)), false).GBuffer.CustomStencil.r > 0 ? GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(offset_h, 0)), false).GBuffer.CustomDepth.r: 0; float BL = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(-offset_h, offset_v)), false).GBuffer.CustomStencil.r > 0 ? GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(-offset_h, offset_v)), false).GBuffer.CustomDepth.r: 0; float BM = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(0, offset_v)), false).GBuffer.CustomStencil.r > 0 ? GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(0, offset_v)), false).GBuffer.CustomDepth.r: 0; float BR = GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(offset_h, offset_v)), false).GBuffer.CustomStencil.r > 0 ? GetScreenSpaceData(ViewportUVToBufferUV(ScreenPosition + float2(offset_h, offset_v)), false).GBuffer.CustomDepth.r: 0; return max(TL, max(TM, max(TR, max(ML, max(MR, max(BL, max(BM, BR ) ) ) ) ) ) );
Poniéndolo todo junto con el material de anteriores tutoriales:
Esta modificación se puede utilizar para simular una burbuja «opaca» alrededor del personaje que limita el efecto de contorno. Es una manera de limitar la super-visión del jugador para no revelar todos los secretos del mapa 😛
Tutorial files
Te puede interesar:
Ayudanos con este blog!
El último año he estado dedicando cada vez más tiempo a la creación de tutoriales, en su mayoria sobre desarrollo de videojuegos. Si crees que estos posts te han ayudado de alguna manera o incluso inspirado, por favor considera ayudarnos a mantener este blog con alguna de estas opciones. Gracias por hacerlo posible!