outline_feature_4

en Tutoriales, UE4

Efecto de contorno (Parte 4)

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

ViewFrustum


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.

depth_frustum

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.

depth_limit_panel

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.

preview_plane_limit

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

material_depth_limit_alt

O utilizando una manera un poco más elegante pero con el mismo resultado:

material_depth_limit

Como se puede ver ahora el cofre más alejado no tiene ningún efecto de contorno visible

depth_limit_preview

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.

hlsl_depth_detail

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

Poniéndolo todo junto con el material de anteriores tutoriales:

outline_occlusion_limit_material_

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 😛

depth_limit_youtube_img

Tutorial files

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!

Escribe un comentario

Comentario