Skip to content
Learni
View all tutorials
Graphisme 3D

Comment maîtriser HLSL pour shaders Unity en 2026

18 minINTERMEDIATE

Introduction

HLSL est le langage de shaders de Microsoft utilisé dans Unity via le pipeline Built-in ou HDRP. À un niveau intermédiaire, il permet de créer des effets visuels personnalisés comme du lighting avancé ou des distorsions. Comprendre la séparation vertex/fragment et l'utilisation des sémantiques est essentiel pour des performances optimales sur GPU. Ce tutoriel vous guide pas à pas avec des exemples concrets et compilables directement dans Unity 2023+.

Prérequis

  • Unity 2023.3 ou supérieur
  • Connaissances de base en C# et shaders
  • Visual Studio ou Rider pour l'édition
  • Un projet 3D avec un modèle simple

Structure de base du shader

CustomLighting.shader
Shader "Custom/IntermediateLighting" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1,1,1,1)
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        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; };
            sampler2D _MainTex; float4 _Color;
            v2f vert (appdata v) {
                v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o;
            }
            fixed4 frag (v2f i) : SV_Target { return tex2D(_MainTex, i.uv) * _Color; }
            ENDCG
        }
    }
}

Ce shader de base définit les propriétés, le vertex shader et le fragment shader. Les sémantiques POSITION et SV_POSITION sont obligatoires pour le pipeline graphique.

Ajout de normales et éclairage simple

CustomLighting.shader
struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; };
struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 normal : TEXCOORD1; };
v2f vert (appdata v) {
    v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv;
    o.normal = UnityObjectToWorldNormal(v.normal); return o;
}
fixed4 frag (v2f i) : SV_Target {
    float3 lightDir = _WorldSpaceLightPos0.xyz;
    float diff = max(0, dot(i.normal, lightDir));
    fixed4 col = tex2D(_MainTex, i.uv) * _Color;
    return col * diff;
}

On ajoute les normales pour calculer un éclairage diffus basique. Cela illustre la transformation des normales de l'espace objet vers l'espace monde.

Shader avec texture et attenuation

CustomLighting.shader
fixed4 frag (v2f i) : SV_Target {
    float3 lightDir = _WorldSpaceLightPos0.xyz;
    float diff = max(0, dot(normalize(i.normal), normalize(lightDir)));
    fixed4 tex = tex2D(_MainTex, i.uv);
    float atten = 1.0 - length(_WorldSpaceLightPos0.xyz - i.vertex.xyz) * 0.1;
    return tex * _Color * diff * atten;
}

Ajout d'une attenuation linéaire basée sur la distance à la lumière. Évitez les calculs coûteux dans le fragment shader quand c'est possible.

Pass multiple pour outline

CustomLighting.shader
Pass { /* Premier pass lighting principal */ }
Pass {
    Cull Front
    CGPROGRAM
    #pragma vertex vertOutline
    #pragma fragment fragOutline
    v2f vertOutline(appdata v) {
        v2f o; float3 norm = normalize(v.normal);
        v.vertex.xyz += norm * 0.02; o.vertex = UnityObjectToClipPos(v.vertex); return o;
    }
    fixed4 fragOutline(v2f i) : SV_Target { return fixed4(0,0,0,1); }
    ENDCG
}

Un second pass avec Cull Front permet de créer un outline simple en décalant les vertices le long des normales.

Inclusion d'un fichier .cginc custom

LightingUtils.cginc
#ifndef LIGHTING_UTILS_INCLUDED
#define LIGHTING_UTILS_INCLUDED
float3 CalculateDiffuse(float3 normal, float3 lightDir) {
    return max(0, dot(normalize(normal), normalize(lightDir)));
}
#endif

Créer un fichier .cginc réutilisable permet de partager du code entre plusieurs shaders et d'améliorer la maintenabilité.

Bonnes pratiques

  • Préférez les calculs dans le vertex shader quand possible pour économiser les ressources GPU
  • Utilisez des sémantiques correctes (SV_Target, TEXCOORDn)
  • Testez toujours sur plusieurs plateformes (DirectX, Vulkan)
  • Évitez les branches dynamiques dans le fragment shader
  • Profilez avec le Frame Debugger de Unity

Erreurs courantes

  • Oublier de normaliser les vecteurs avant le dot product
  • Mauvaise transformation des normales (utiliser UnityObjectToWorldNormal)
  • Dépasser la limite de 8 interpolateurs TEXCOORD sans optimisation
  • Ne pas gérer les variantes de shader pour les différentes qualités graphiques

Pour aller plus loin

Approfondissez vos compétences avec nos formations avancées sur les shaders.