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.0.1. Estructura de un shader vertex/fragment

Esta publicación está disponible en...

Para analizar su estructura crearemos un shader tipo Unlit Shader y lo llamaremos “USB_simple_color”. Como ya sabemos, este tipo de shader es un modelo básico de color y no posee gran optimización en su código, esto nos va permitir analizar en profundidad las distintas propiedades y funciones.

Cuando creamos un shader por primera vez, Unity agrega código por defecto para facilitar el proceso de compilación del mismo. Dentro del programa podemos encontrar bloques de código estructurados de tal manera que la GPU pueda interpretarlos. Si abrimos nuestro shader USB_simple_color, su estructura debería lucir de la siguiente manera:

Shader “Unlit / USB_simple_color”
{
    Properties
    {
        _MainTex (“Texture”, 2D) = “white” {}
    }
    SubShader
    {
        Tags {“RenderType” = “Opaque”}
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include “UnityCG.cginc”

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler 2D _MainTex;
            float4 _MainTex;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o, o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
        }
    }
}

Es muy probable que no entendamos del todo lo que está ocurriendo en los distintos bloques de código del shader que acabamos de crear. No obstante, para comenzar nuestro estudio, pondremos atención a su estructura general.

Shader “InspectorPath / shaderName”
{
    Properties
    {
        // block properties
    }
    SubShader
    {
        // SubShader configuration in this block
        Pass
        {
            CGPROGRAM          
            // Cg - HLSL program in this block
            ENDCG              
        }
    }
    Fallback “ExampleOtherShader”
}

(Tanto en Cg como en HLSL la estructura del shader es la misma, lo único que cambia son los bloques de programa en Cg y HLSL. Ambos compilan en versiones actuales de Unity por compatibilidad)

La estructura principal de un shader luce como el ejemplo anterior. El shader inicia con una ruta en el inspector (InspectorPath) y un nombre (shaderName), luego siguen las propiedades (e.g. texturas, vectores, colores, etc), luego el SubShader y al final de todo está el Fallback que es opcional.

El “inspectorPath” se refiere al lugar donde seleccionaremos nuestro shader para aplicarlo a un material. Esta selección se realiza mediante el Inspector de Unity.

Debemos recordar que no podemos aplicar un shader directamente a un objeto poligonal, en cambio habrá que hacerlo a través de un material previamente creado. Nuestro shader USB_simple_color posee la ruta “Unlit” por defecto, esto quiere decir que: desde Unity debemos seleccionar nuestro material, ir al inspector, buscar la ruta “Unlit” y aplicar el material llamado USB_simple_color.

Un factor estructural que debemos tomar en consideración es que la GPU va a leer el programa desde arriba hacia abajo de manera lineal, por lo tanto, si en un futuro cercano creamos una función en nuestro programa, y luego, la posicionamos por debajo del bloque de código en donde será utilizada, entonces la GPU no podrá leerla, generando un error en el procesamiento del shader, por ende, el Fallback asignará un shader distinto para que el hardware gráfico pueda seguir su proceso.

Haremos el siguiente ejercicio para entender este concepto.

// 1 . declare our function
float4 ourFunction()            
{
    // your operation here ...
} 

// 2. use our function
fixed4 frag (v2f i) : SV_Target
{
    // function being used here
    float4 f = ourFunction();         
    return f;
}

Es posible que la sintaxis de las funciones anteriores no se entienda del todo. Estas han sido creadas sólo para conceptualizar la posición de una función respecto a otra.

En la sección 2.4.3 hablaremos en detalle sobre la estructura de una función. Por ahora, lo único importante es que en el ejemplo anterior su estructura está correcta, porque la función llamada “ourFunction” ha sido escrita por sobre el bloque de código en donde es utilizada. La GPU primero leerá la función “ourFunction” y luego continuará con la etapa de fragmento llamada “frag”. Veamos un caso distinto del anterior.

Veamos un caso distinto del anterior.

// 2. use our function
fixed4 frag (v2f i) : SV_Target
{
    // function being used here
    float4 f = ourFunction();        
    return f;
}

// 1 . declare our function
float4 ourFunction()            
{
    // your operation here ...
}

Por el contrario, esta estructura va a generar un “error”, porque la función llamada “ourFunction” ha sido escrita por debajo del bloque de código en donde se está utilizando.

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.