Consigue un 20% de descuento usando el código RELEASE en la compra de tu libro. Por tiempo limitado.

Introducción al lenguaje de programación de shaders

3.2.4. ShaderLab Stencil

Esta publicación está disponible en...

Según la documentación oficial en Unity:

“El Stencil Buffer almacena un valor entero de ocho bits (0 a 255) para cada píxel en el Frame Buffer. Antes de ejecutar el fragment shader stage para un píxel determinado, la GPU puede comparar el valor actual en el Stencil Buffer con un valor de referencia determinado. Este proceso se denomina Stencil Test. Si el Stencil Test pasa, la GPU realiza la prueba de profundidad (depth test). Si el Stencil Test falla, la GPU omite el resto del procesamiento de ese píxel. Esto quiere decir que puede usar el Stencil Buffer como una máscara para indicar a la GPU qué píxeles dibujar y qué píxeles descartar”.

¿qué quiere decir esto? Para entenderlo debemos tomar en consideración que el Stencil Buffer en sí, es una “textura” que debe ser creada y para ello se almacena un valor entero de 0 a 255 para cada píxel en el Frame Buffer.

(Fig. 034)

Como ya sabemos, cuando posicionamos objetos en nuestra escena, la información de estos es enviada al vertex shader stage (e.g. posición de los vértices). Dentro de esta etapa, los atributos de nuestro objeto son transformados desde object-space a world-space, luego a view-space y finalmente a clip-space. Este proceso ocurre sólo para aquellos objetos que se encuentran dentro del frustum de la cámara.

Cuando esta información ha sido procesada correctamente, es enviada al rasterizador el cual permite proyectar en píxeles las coordenadas de nuestros objetos en la escena, sin embargo, antes de llegar a este punto pasa por una etapa de procesamiento anterior llamada Culling and Depth testing. En esta etapa ocurren varios procesos que podemos manipular dentro de nuestro shader, entre ellos podemos mencionar: Cull, ZWrite, ZTest y Stencil.

Básicamente lo que hace el Stencil Buffer es activar el Stencil Test, el cual nos permite descartar “fragmentos” (píxeles) para que no sean procesados en el fragment shader stage, generando así un efecto de máscara en nuestro shader. La función que realiza el Stencil Test posee la siguiente sintaxis:

if ( StencilRef & StencilReadMask [Comp] StencilBufferValue & StencilReadMask)
{
    Accept Pixel.
}
else
{
    Discard Pixel.
}

StencilRef” es el valor que vamos a pasar al Stencil Buffer a modo de referencia, ¿qué quiere decir esto? Recordemos que el Stencil Buffer es una “textura” que cubre el área en píxeles del objeto que la posee. El StencilRef funciona como un id que se asigna a todos los píxeles que se encuentran en el Stencil Buffer.

Para ejemplificar, vamos a hacer el valor del StencilRef igual a 2.

(Fig. 035)

En el ejemplo anterior, todos los píxeles que cubren el área de la cápsula han sido marcados con el valor del StencilRef, por lo tanto, ahora el Stencil Buffer es igual a dos.

Posteriormente, para todos aquellos píxeles que poseen un valor de referencia, se crea una máscara (StencilReadMask) que por defecto posee el valor 255.

(Fig. 036)

De este modo, la operación anterior queda de la siguiente manera.

if ( 2 & 255 [Comp] 2 & 255)
{
    Accept Pixel.
}
else
{
    Discard Pixel.
}

Comp” se refiere a una función de comparación la cual va a permitir el retorno de un valor verdadero o falso. Si el valor es verdadero el programa escribe el píxel en el Frame Buffer, de otra manera, el píxel es descartado.

Dentro de las funciones de comparación, podemos encontrar los siguientes operadores predefinidos:

OperatorFunction
Comp NeverLa función siempre va a retornar falso.
Comp Less<.
Comp Equal==.
Comp LEqual≤.
Comp Greater>.
Comp NotEqual!=.
Comp GEqual≥.
Comp AlwaysLa operación siempre va a retornar verdadero.

Para utilizar el Stencil Buffer vamos a necesitar al menos dos shaders: Uno para la máscara y otro para el objeto enmascarado.

Vamos a suponer que tenemos tres objetos en nuestra escena: Un cubo, una esfera y un quad. La esfera se encuentra dentro del cubo, y el quad queremos utilizarlo como “máscara” para ocultar elcubo y así, la esfera en su interior pueda verse. Para ejemplificar la máscara, crearemos un shader llamado USB_stencil_ref y su sintaxis sería la siguiente: Para ejemplificar la máscara, crearemos un shader llamado USB_stencil_ref y su sintaxis sería la siguiente:

SubShader     
{
    Tags { “Queue” = “Geometry -1” }
    ZWrite Off
    ColorMask 0

    Stencil
    {
        Ref 1
        Comp Always
        Pass Replace
    }
}
(Fig. 037)

Analicemos lo anteriormente señalado. Lo primero que hicimos fue configurar el “Queue” igual a “Geometry menos uno”. Geometry por defecto es igual a 2000, por lo tanto, Geometry menos 1 es igual a 1999, esto va a permitir que nuestro quad (máscara) al cual le aplicaremos este shader sea procesado primero en el Z-Buffer.

Sin embargo como sabemos, Unity por defecto procesa los objetos según su posición en la escena con respecto a la cámara, por lo tanto si deseamos desactivar esta función debemos configurar la propiedad “ZWrite en Off”.

Luego configuramos “ColorMask en cero” para que los píxeles de la máscara sean descartados en el Frame Buffer y así luzca transparente.

Hasta este punto nuestro quad aún no funciona como máscara, por lo tanto, lo que debemos hacer a continuación es agregar el comando “Stencil” para que así funcione como tal.

“Ref 2” (StencilRef), nuestro valor de referencia, es comparado en la GPU con el contenido actual del Stencil Buffer utilizando la operación definida en la “operación de comparación”.

“Comp Always” se asegura de establecer un “2” en el Stencil Buffer, tomando en consideración el área que abarca el quad en pantalla. Finalmente “Pass Replace” especifica que los valores actuales del Stencil Buffer sean “reemplazados” por los valores del StencilRef.

Ahora vamos a analizar el objeto que deseamos enmascarar. Para ejemplificar, nuevamente crearemos un shader, pero a este le llamaremos USB_stencil_value. Su sintaxis es la siguiente:

SubShader 
{
    Tags { “Queue” = “Geometry” }
    Stencil
    {
        Ref 1
        Comp NotEqual
        Pass Keep
    }
}
(Fig. 038)

A diferencia de nuestro shader anterior, mantendremos el Z-Buffer activo para que sea renderizado en la GPU según su posición respecto a la cámara. Luego agregamos el comando Stencil para que nuestro objeto pueda ser enmascarado. Agregamos “Ref 2” nuevamente para vincular este shader con USB_stencil_ref para la máscara.

Luego, en la operación de comparación, asignamos “Comp NotEqual”. Esto quiere decir que el área del cubo que será expuesta alrededor del quad será renderizada debido a que el Stencil Test pasa (no hay comparación o es verdadero).

En cambio, para el área que cubre el quad, al ser igual (Equal) entonces el Stencil Test no pasará y los píxeles serán descartados. “Pass Keep” quiere decir que el Cubo mantiene el contenido actual del Stencil Buffer.

En el caso que deseemos trabajar con más de una máscara, podemos pasar un número distinto de “2” a la propiedad Ref.

Siguenos para mantenerte informado sobre todas las novedades, actualizaciones y más.

Únete al grupo para compartir tus experiencias con otros desarrolladores.

¡Suscríbete a nuestro canal y sigue aprendiendo sobre desarrollo de juegos!

jettelly-logo

Jettelly Team

Somos un equipo de desarrolladores independientes con más de 9 años de experiencia en videojuegos. Como estudio independiente, hemos desarrollado Nom Noms el que publicamos con Hyperbeard en 2019. Actualmente estamos desarrollando La Biblia de Shader en Unity.

Síguenos en nuestras redes sociales.