Unity Come creare uno shader

Shader è un piccolo script che contiene calcoli matematici e algoritmi per calcolare il colore di ciascun pixel renderizzato, in base all'input di illuminazione e alla configurazione del materiale.

Unity utilizza Shader scritti nelle seguenti lingue:

  • Un linguaggio di programmazione chiamato HLSL viene utilizzato per scrivere i programmi shader stessi.
  • Un linguaggio specifico per Unity chiamato ShaderLab viene utilizzato per definire un oggetto Shader, che funge da contenitore per i programmi shader.

Per creare uno shader in Unity procedi nel seguente modo:

Crea uno Shader

  • Fai clic con il pulsante destro del mouse sulla vista Progetto -> 'Create' -> 'Shader'

A seconda della versione Unity che stai utilizzando, le opzioni dello Shader potrebbero differire, ma ecco cosa significa ciascuna opzione:

  1. 'Standard Surface Shader': Questo shader è progettato per funzionare con il sistema Unity's Physically Based Rendering (PBR). Consente agli sviluppatori di creare materiali che rispondono alle condizioni di illuminazione in modo realistico. Supporta varie funzionalità di rendering come mappatura normale, evidenziazioni speculari e riflessioni. È uno shader versatile che offre un buon equilibrio tra realismo e prestazioni.
  2. 'Unlit Shader': Come suggerisce il nome, uno shader non illuminato non considera le condizioni di illuminazione. Viene spesso utilizzato per il rendering di effetti che non richiedono un'illuminazione realistica, come elementi dell'interfaccia utente, sistemi di particelle o effetti speciali. Gli shader non illuminati sono in genere più efficienti e possono essere utili in situazioni in cui è necessario il pieno controllo dell'aspetto di un oggetto senza alcun calcolo dell'illuminazione.
  3. 'Image Effect Shader': Gli effetti immagine sono utilizzati per applicare gli effetti di post-elaborazione al intero schermo o target di rendering specifici. Consentono agli sviluppatori di modificare l'immagine renderizzata finale dopo che il rendering principale è stato completato. Esempi di effetti immagine includono sfocatura, gradazione del colore, distorsione o filtri stilizzati. Possono essere utilizzati per migliorare la qualità visiva o creare effetti artistici specifici.
  4. 'Compute Shader': Un compute shader è un tipo di shader che gira sulla GPU ma non opera direttamente sui pixel. Viene utilizzato per calcoli generici su dati paralleli, consentendo agli sviluppatori di eseguire calcoli complessi o simulazioni in modo efficiente. Gli shader di calcolo sono comunemente usati per attività come simulazioni fisiche, generazione procedurale o elaborazione dati.
  5. 'Ray Tracing Shader': Gli shader di ray tracing utilizzano la tecnologia di ray tracing, che simula il comportamento della luce in modo più accurato rispetto alle tradizionali tecniche di rasterizzazione. Gli shader di ray tracing vengono in genere utilizzati per ottenere luci, riflessi e ombre altamente realistici nelle applicazioni in tempo reale. Richiedono hardware potente e sono spesso utilizzati in campi ad alta intensità grafica come i giochi o la visualizzazione architettonica.
  • Dopo aver selezionato lo shader, digitare qualsiasi nome e premere Invio

Il nuovo Shader viene creato e può essere aperto in qualsiasi editor di script e modificato in base alle proprie esigenze.

Predefinito 'Standard Surface Shader':

Shader "Custom/NewSurfaceShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Predefinito 'Unlit Shader':

Shader "Unlit/NewUnlitShader"
{
    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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            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;
            }
            ENDCG
        }
    }
}

Predefinito 'Image Effect Shader':

Shader "Hidden/NewImageEffectShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

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

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

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                // just invert the colors
                col.rgb = 1 - col.rgb;
                return col;
            }
            ENDCG
        }
    }
}

Predefinito 'Compute Shader':

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain

// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> Result;

[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // TODO: insert actual code here!

    Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0.0);
}

Predefinito 'Ray Tracing Shader':

RWTexture2D<float4> RenderTarget;

#pragma max_recursion_depth 1

[shader("raygeneration")]
void MyRaygenShader()
{
    uint2 dispatchIdx = DispatchRaysIndex().xy;
   
    RenderTarget[dispatchIdx] = float4(dispatchIdx.x & dispatchIdx.y, (dispatchIdx.x & 15)/15.0, (dispatchIdx.y & 15)/15.0, 0.0);
}

Conclusione

Ogni tipo di shader ha i suoi punti di forza e usi. È importante scegliere lo shader appropriato in base alle tue esigenze specifiche e agli effetti visivi che intendi ottenere nel tuo progetto.