Version: 2019.2
Shaders: ShaderLab y shaders con función fija
Scriptable Render Pipeline

Shaders: programas vertex y fragment

Este tutorial le enseñará los conceptos básicos de cómo escribir programas vertex y fragment en los shaders de Unity. Para una introducción básica a ShaderLab, mire el tutorial Introducción. Si desea escribir shaders que interactúan con la iluminación, lea sobre Surface Shaders en su lugar.

Comencemos con una pequeña recapitulación de la estructura general de un shader:

Shader "MyShaderName"
{
    Properties
    {
        // material properties here
    }
    SubShader // subshader for graphics hardware A
    {
        Pass
        {
            // pass commands ...
        }
        // more passes if needed
    }
    // more subshaders if needed
    FallBack "VertexLit" // optional fallback
}

Here at the end we introduce a new command: FallBack “VertexLit”. The Fallback command can be used at the end of the shader; it tells which shader should be used if no SubShaders from the current shader can run on user’s graphics hardware. The effect is the same as including all SubShaders from the fallback shader at the end. For example, if you were to write a fancy normal-mapped shader, then instead of writing a very basic non-normal-mapped subshader for old graphics cards you can just fallback to built-in VertexLit shader.

The basic building blocks of the shader are introduced in the first shader tutorial while the full documentation of Properties, SubShaders and Passes are also available.

A quick way of building SubShaders is to use passes defined in other shaders. The command UsePass does just that, so you can reuse shader code in a neat fashion. As an example the following command uses the pass with the name “FORWARD” from the built-in Specular shader: UsePass “Specular/FORWARD”.

In order for UsePass to work, a name must be given to the pass one wishes to use. The Name command inside the pass gives it a name: Name “MyPassName”.

Programas Vertex y fragment

Describimos un pass que usaba sólo una instrucción de combinación de textura única en el first tutorial. Ahora es el momento de demostrar cómo podemos usar vertex y fragment programs en nuestro pass.

Cuando se utilizan programas vertex y fragment (la denominada “pipeline programable”), la mayoría de la funcionalidad programada (“pipeline de función fija”) en el hardware gráfico está desactivada. Por ejemplo, el uso de un fragment program apaga las transformaciones 3D estándar, la iluminación y la generación de coordenadas de textura por completo. Del mismo modo, el uso de un fragment program reemplaza cualquier modo de combinación de texturas que se definiría en los comandos de SetTexture; Por lo que los comandos SetTexture no son necesarios.

Escribir programas vertex/fragment requiere un conocimiento profundo de las transformaciones en 3D, iluminación y espacios de coordenadas, porque hay que reescribir la funcionalidad fija que se construye en APIs como OpenGL usted mismo. Por otro lado, usted puede hacer mucho más de lo que está construido!

Utilizando Cg/HLSL en ShaderLab

Shaders en ShaderLab por lo general se escriben en el lenguaje de programación Cg/HLSL. Cg y HLSL del estilo DX9 son para todos los usos prácticos un mismo idioma, por lo que usaremos Cg y HLSL intercambiablemente (mire esta página para más detalles).

El código Shader se escribe incrustando “fragmentos Cg/HLSL” en el texto del shader. Los fragmentos se compilan en el ensamblador shader de bajo nivel por el editor de Unity y el shader final que se incluye en los archivos de datos de su juego sólo contiene este assembly de bajo nivel o bytecode, que es específico de la plataforma. Cuando selecciona un shader en el Proyecto View, el inspector tiene un botón para mostrar el código del shader compilado, que podría ayudar como una ayuda de depuración. Unity compila automáticamente fragmentos Cg para todas las plataformas relevantes (Direct3D 9, OpenGL, Direct3D 11, OpenGL ES, etc.). Tenga en cuenta que, debido a que el código Cg/HLSL es compilado por el editor, no puede crear shaders desde scripting en tiempo de ejecución.

En general, los fragmentos se colocan dentro de los bloques de Pass. Ellos se parecen a esto:

Pass {
    // ... the usual pass state setup ...

    CGPROGRAM
    // compilation directives for this snippet, e.g.:
    #pragma vertex vert
    #pragma fragment frag

    // the Cg/HLSL code itself

    ENDCG
    // ... the rest of pass setup ...
}

El ejemplo siguiente demuestra un shader completo que procesa las normales de objeto como colores:

Shader "Tutorial/Display Normals" {
    SubShader {
        Pass {

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                float4 pos : SV_POSITION;
                fixed3 color : COLOR0;
            };

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.color = v.normal * 0.5 + 0.5;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4 (i.color, 1);
            }
            ENDCG

        }
    }
}

Cuando se aplica a un objeto, se obtendrá una imagen como esta:

Nuestro shader “Display Normals” no tiene propiedades, contiene un solo SubShader con un solo pass que está vacío excepto el código Cg/HLSL. Vamos a diseccionar el código parte por parte:

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// ...
ENDCG

El fragmento completo se escribe entre las palabras clave CGPROGRAM y ENDCG. Al principio, las directivas de compilación se dan como declaraciones #pragma:

  • #pragma vertex name le dice al código que contiene un programa vertex en la función dada (vert aquí).
  • #pragma fragment name dice que el código contiene un programa fragment en la función dada (frag aquí).

Seguir las directivas de compilación es simplemente código Cg/HLSL plano. Nosotros comenzamos incluyendo un archivo incorporado include:

#include "UnityCG.cginc"

El archivo UnityCG.cginc contiene declaraciones y funciones de uso común para que los shaders puedan mantenerse más pequeños (mire la página archivos include shader para más detalles). Aquí usaremos la estructura appdata_base de ese archivo. Podríamos simplemente definirlos directamente en el shader y no incluir el archivo por supuesto.

A continuación definimos una estructura de “vertex a fragment” (aquí llamada v2f) - qué información se pasa desde el vertex program al fragment program. Pasamos los parámetros de posición y color. El color se calculará en el vertex program y sólo tendrá output en el fragment program.

Nosotros procedemos al definir el vertex program - función vert. Aquí calculamos la posición y la normal output input como un color: o.color = v.normal * 0.5 + 0.5;

Los componentes normales están en el rango –1..1, mientras que los colores están en el rango 0..1, por lo que escalamos y sesgamos la normal en el código anterior. A continuación se define un fragment programa - función frag que sólo tiene como output el color calculado y 1 como el componente alfa:

fixed4 frag (v2f i) : SV_Target
{
    return fixed4 (i.color, 1);
}

Esto es todo, nuestro shader ha terminado! Incluso este simple shader es muy útil para visualizar las normales del mesh.

Por supuesto, este shader no responde a las luces en absoluto, y ahí es donde las cosas se ponen un poco más interesantes; lea acerca de Surface Shaders para más detalles.

Utilizando propiedades shader en código Cg/HLSL

When you define properties in the shader, you give them a name like _Color or _MainTex. To use them in Cg/HLSL you just have to define a variable of a matching name and type. See properties in shader programs page for details.

Aquí hay un shader completo que muestra una textura modulada por un color. Por supuesto, usted podría hacer fácilmente lo mismo en una llamada de combinador de textura, pero el punto aquí es sólo para mostrar cómo usar las propiedades en Cg:

Shader "Tutorial/Textured Colored" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0.5)
        _MainTex ("Texture", 2D) = "white" { }
    }
    SubShader {
        Pass {

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"

        fixed4 _Color;
        sampler2D _MainTex;

        struct v2f {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
        };

        float4 _MainTex_ST;

        v2f vert (appdata_base v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 texcol = tex2D (_MainTex, i.uv);
            return texcol * _Color;
        }
        ENDCG

        }
    }
}

The structure of this shader is the same as in the previous example. Here we define two properties, namely _Color and _MainTex. Inside Cg/HLSL code we define corresponding variables:

fixed4 _Color;
sampler2D _MainTex;

Mire Accediendo Propiedades Shader en Cg/HLSL para más información.

Los programas vertex y fragment aquí no hacen nada extravagante; El vertex program utiliza la macro TRANSFORM_TEX de UnityCG.cginc para asegurarse de que la escala y el offset de la textura se apliquen correctamente, y el fragment program solamente muestrea la textura y se multiplica por la propiedad del color.

Resumen

Hemos demostrado cómo se escriben programas shader personalizado en unos pocos pasos fáciles. Si bien los ejemplos que se muestran aquí son muy simples, no hay nada que lo impida de escribir programas shader arbitrariamente complejos! Esto puede ayudarle a aprovechar al máximo Unity y lograr resultados de renderización óptimos.

El manual de referencia completo de ShaderLab está aquí, y más ejemplos en la página de ejemplos de vertex y fragment shader. También tenemos un foro para shaders en forum.unity3d.com así que puede ir ahí para obtener ayuda con sus shaders! Que tenga una programación feliz, y disfrute el poder de Unity y ShaderLab.

Shaders: ShaderLab y shaders con función fija
Scriptable Render Pipeline