Version: 2019.3
Ejemplos del Vertex y Fragment Shader
Accediendo propiedades shader en Cg/HLSL

Semánticas de Shader

When writing HLSL shader programs, input and output variables need to have their “intent” indicated via semantics. This is a standard concept in HLSL shader language; see the Semantics documentation on MSDN for more details.

Usted puede descargar los ejemplos mostrados abajo como un proyecto zipped de Unity, aquí.

Semánticas de entrada Vertex shader

La función principal vertex shader (indicada por el #pragma vertex directiva) necesita tener todas las semánticas en todos los parámetros de input. Estos corresponden a elementos de datos individuales Mesh, como la posición del vértice, el normal mesh y las coordenadas de textura. Mire las entradas del vertex program para más detalles.

Aquí está un ejemplo de un shader de vértices simple que toma la posición de vértice y una coordenada de textura como entrada. El pixel shader visualiza la coordenada de textura como un color.

Shader "Unlit/Show UVs"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                float2 uv : TEXCOORD0 // first texture coordinate input
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.uv = uv;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.uv, 0, 0);
            }
            ENDCG
        }
    }
}

En vez de deletrear todas las entradas individuales una por una, también es posible declarar una estructura de ellas, e indicar la semántica en cada variable miembro individual del struct. Mire ejemplos shader program para aprender a cómo hacer esto.

Semánticas de salida del Fragment Shader

A menudo un fragment (pixel) shader tiene de salida un color, y tiene una semántica SV_Target. El fragment shader en el ejemplo de arriba hace exactamente esto:

fixed4 frag (v2f i) : SV_Target

La función frag tiene un tipo de retorno de fixed4 (baja precisión RGBA color). Como solamente devuelve un solo valor, la semántica es indicada en la función en sí, : SV_Target.

También es posible retornar una estructura con las salidas. El fragment shader de arriba podría haber estado escrito de esta manera también, y haria exactamente lo mismo:

struct fragOutput {
    fixed4 color : SV_Target;
};            
fragOutput frag (v2f i)
{
    fragOutput o;
    o.color = fixed4(i.uv, 0, 0);
    return o;
}

Devolver estructuras del fragment shader es en su mayoría útil para shaders que simplemente no devuelven un solo color. Adicionalmente la semántica soporta por el fragment shader tiene como salida lo siguiente.

SV_TargetN: Varios render targets

SV_Target1,SV_Target2, etc .: Son colores adicionales escritos por el shader. Esto se utiliza cuando se renderiza en más de un destino de render a la vez (conocido como la técnica de renderizado Multiple Render Targets o MRT). SV_Target0 es el mismo que SV_Target.

SV_Depth: Pixel shader output de profundidad

Usualmente el fragment shader no anula el valor del Z buffer, y un valor predeterminado se utiliza de la rasterización triangular regular. Sin embargo, para algunos efectos es útil tener como salida unos valores de profundidad del Z buffer personalizados por pixel.

Tenga en cuenta que en muchas GPUs esto desactiva algunas optimizaciones del búfer de profundidad, por lo que no anula el valor del búfer Z sin una buena razón. El precio de “SV_Depth” varía dependiendo de la arquitectura de la GPU, pero en general es bastante similar al costo de la prueba alfa (usando la función clip() incorporada en HLSL). Renderizar shaders que modifiquen la profundidad después de todos los shaders opacos regulares (por ejemplo, usando AlphaTest rendering queue.

El valor de salida de profundidad debe ser un solo float.

Vertex shader outputs y fragment shader inputs

Un vertex shader necesita output la posición final del espacio de clip de un vértice, de modo que la GPU sepa dónde en la pantalla rasterizarla, y a qué profundidad. Esta salida necesita tener la semántica SV_POSITION y ser de tipo float4.

Cualquier otra salida (“interpoladores” o “variaciones”) producida por el vertex shader son lo que su shader particular necesita. Los valores de salida del vertex shader se interpolarán a través de la cara de los triángulos renderizados y los valores en cada píxel se pasarán como entradas al fragment shader.

Muchas GPUs modernas no se preocupan realmente de la semántica que tienen estas variables; Sin embargo, algunos sistemas antiguos (sobre todo, el shader model 2 GPU en Direct3D 9) tenían reglas especiales sobre la semántica:

  • TEXCOORD0, TEXCOORD1 etc se utilizan para indicar datos arbitrarios de alta precisión, tales como coordenadas y posiciones de textura.
  • La semántica COLOR0 y COLOR1 en las vertex outputs y fragment inputs son para datos de rango de 0–1 de baja precisión (como valores de color simples).

Para un soporte mejor multi-plataforma, etiquete los vertex outputs y fragment inputs con semántica TEXCOORDn.

Mire ejemplos shader program para ejemplos.

Límite de recuento de interpoladores

Hay límites para cuántas variables de interpolador se pueden utilizar en total para pasar la información del vértice al shader de fragmentos. El límite Depende de la plataforma y la GPU, y las directrices generales son:

  • Hasta 8 interpoladores: OpenGL ES 2.0 (iOS/Android), Direct3D 11 9.x level (Windows Phone) y Direct3 9 shader model 2.0 (PCs viejos). Ya que la cuenta de interpoladores es limitada, pero cada interpolador puede ser un vector de 4-componentes, algunos shaders empacan cosas juntas para permanecer dentro de los límites. Por ejemplo, dos coordenadas de textura se pueden pasar en una variable float4 (.xy para una coordenada, .zw para la segunda coordenada).
  • Hasta 10 interpoladores: Direct3D 9 shader model 3.0 (#pragma target 3.0).
  • Hasta 16 interpoladores: OpenGL ES 3.0 (iOS/Android), Metal (iOS).
  • Hasta 32 Interpoladores: Direct3D 10 shader model 4.0 (#pragma target 4.0).

Sin importar de su hardware objetivo en particular, por lo general es una buena idea utilizar tan pocos interpoladores como sea posible por razones de rendimiento.

Otras semánticas especiales

Posición pixel del Espacio de Pantalla: VPOS

Un shader de fragmentos puede recibir la posición del píxel que se representa como una semántica especial VPOS. Esta característica sólo existe a partir del modelo shader 3.0, por lo que el shader necesita tener la directiva de compilación #pragma target 3.0.

En diferentes plataformas el tipo subyacente de la entrada de posición del espacio de la pantalla varía, por lo que para la máxima portabilidad, utilice el tipo UNITY_VPOS_TYPE para este (seráfloat4 en la mayoría de las plataformas y float2 en Direct3D9).

Además, usar la semántica de posición de píxeles hace que sea difícil tener tanto la posición de espacio de clip (SV_POSITION) como VPOS en la misma estructura de vértice a fragmento. Por lo tanto, el shader de vértices debe emitir la posición del espacio de clip como una variable “fuera” separada. Mire el ejemplo siguiente:

Shader "Unlit/Screen Position"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            // note: no SV_POSITION in this struct
            struct v2f {
                float2 uv : TEXCOORD0;
            };

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                float2 uv : TEXCOORD0, // texture coordinate input
                out float4 outpos : SV_POSITION // clip space position output
                )
            {
                v2f o;
                o.uv = uv;
                outpos = UnityObjectToClipPos(vertex);
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
            {
                // screenPos.xy will contain pixel integer coordinates.
                // use them to implement a checkerboard pattern that skips rendering
                // 4x4 blocks of pixels

                // checker value will be negative for 4x4 blocks of pixels
                // in a checkerboard pattern
                screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
                float checker = -frac(screenPos.r + screenPos.g);

                // clip HLSL instruction stops rendering a pixel if value is negative
                clip(checker);

                // for pixels that were kept, read the texture and output it
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

Orientación de la cara: VFACE

Un fragment shader puede recibir una variable que indica si la superficie renderizada está frente a la cámara, o mirando hacia fuera de la cámara. Esto es útil al renderizar la geometría que debe ser visible desde ambos lados, a menudo utilizada en hojas y objetos finos similares. La variable de entrada con semántica VFACE contendrá un valor positivo para los triángulos frontales y un valor negativo para los inversos.

Esta característica sólo existe desde el modelo shader 3.0 en adelante, por lo que el shader necesita tener la directiva de compilación #pragma target 3.0.

Shader "Unlit/Face Orientation"
{
    Properties
    {
        _ColorFront ("Front Color", Color) = (1,0.7,0.7,1)
        _ColorBack ("Back Color", Color) = (0.7,1,0.7,1)
    }
    SubShader
    {
        Pass
        {
            Cull Off // turn off backface culling

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return UnityObjectToClipPos(vertex);
            }

            fixed4 _ColorFront;
            fixed4 _ColorBack;

            fixed4 frag (fixed facing : VFACE) : SV_Target
            {
                // VFACE input positive for frontbaces,
                // negative for backfaces. Output one
                // of the two colors depending on that.
                return facing > 0 ? _ColorFront : _ColorBack;
            }
            ENDCG
        }
    }
}

El shader anterior utiliza el estado Cull para desactivar el desmonte de la cara posterior (por defecto, los triángulos orientados hacia atrás no se renderizan en absoluto). Aquí está el shader aplicado a un montón de Quad meshes, girado en diferentes orientaciones:

Vertex ID: SV_VertexID

A vertex shader can receive a variable that has the “vertex number” as an unsigned integer. This is mostly useful when you want to fetch additional per-vertex data from textures or ComputeBuffers.

Esta característica sólo existe desde DX10 (shader model 4.0) y GLCore/OpenGL ES 3, por lo que el shader necesita tener la directiva de compilación #pragma target 3.5.

Shader "Unlit/VertexID"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.5

            struct v2f {
                fixed4 color : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                uint vid : SV_VertexID // vertex ID, needs to be uint
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // output funky colors based on vertex ID
                float f = (float)vid;
                o.color = half4(sin(f/10),sin(f/100),sin(f/1000),0) * 0.5 + 0.5;
                return o;
            }

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

(Usted puede descargar los ejemplos mostrados arriba como un proyecto Unity zipped, aquí)

Ejemplos del Vertex y Fragment Shader
Accediendo propiedades shader en Cg/HLSL