Votre premier shader de CanvasItem

Introduction

Les shaders sont des programmes spéciaux qui s'exécutent sur le GPU et sont utilisés pour le rendu graphique. Tous les graphismes modernes sont réalisés à l'aide de shaders. Pour une description plus détaillée de ce que sont les shaders, référez vous à What are shaders.

Ce tutoriel se concentre sur les aspects pratiques de l'écriture de programmes de shaders en vous guidant à travers le processus d'écriture d'un shader et ses fonctions de vertex et de fragment. Ce tutoriel s'adresse aux débutants absolus en matière de shaders.

Note

Si vous avez de l'expérience dans l'écriture de shaders et que vous cherchez simplement un aperçu du fonctionnement des shaders dans Godot, consultez Shading Reference.

Configuration

Les shaders CanvasItem sont utilisés pour dessiner les objets 2D dans Godot, tandis que les shaders Spatial sont utilisés pour dessiner les objets 3D.

Pour pouvoir utiliser un shader, il doit être rattaché à un Material qui lui doit être associé à un objet. Les matériaux sont de type Resource. Pour dessiner plusieurs objets avec le même matériau, il doit être associé à chaque objet.

Tous les objets dérivés d'un CanvasItem ont une propriété matérielle. Cela inclut tous les éléments GUI, Sprites, TileMaps, MeshInstance2Ds etc. Ils ont également la possibilité d'hériter du matériel de leurs parents. Cela peut être utile si vous avez un grand nombre de nœuds pour lesquels vous souhaitez utiliser le même matériau.

Pour commencer, créez un nœud Sprite. Vous pouvez utiliser n'importe quel CanvasItem, mais pour ce tutoriel, nous utiliserons un Sprite.

Dans l'inspecteur, cliquez à côté de "Texture" où il est indiqué "[empty]" et sélectionnez "Load", puis "Icon.png". Pour les nouveaux projets, c'est l'icône Godot. Vous devriez maintenant voir l'icône dans le viewport.

Ensuite, regardez dans l'inspecteur, sous la section CanvasItem, cliquez à côté de "Material" et sélectionnez "New ShaderMaterial". Cela crée une nouvelle ressource matérielle. Cliquez sur la sphère qui apparaît. Actuellement, Godot ne sait pas si vous écrivez un CanvasItem Shader ou un Spatial Shader et il prédit la sortie de shaders spatiaux. Ce que vous voyez est donc la sortie du Spatial Shader par défaut.

Cliquez à côté de "Shader" et sélectionnez "New Shader". Enfin, cliquez sur la nouvelle ressource de shaders et l'éditeur de shaders s'ouvrira. Vous êtes maintenant prêt à commencer à écrire votre premier shader.

Votre premier shader de CanvasItem

Dans Godot, tous les shaders commencent par une ligne précisant de quel type de shader il s'agit. Il utilise le format suivant :

shader_type canvas_item;

Comme nous écrivons un shader CanvasItem, nous spécifions canvas_item à la première ligne. Tout notre code sera sous cette déclaration.

Cette ligne indique au moteur quelles variables et fonctionnalités intégrées doivent vous être fournies.

Dans Godot, vous pouvez passer outre trois fonctions pour contrôler le fonctionnement du shader : vertex, fragment, et light. Ce tutoriel vous guidera dans l'écriture d'un shader avec les fonctions vertex et fragment. Les fonctions lumières sont beaucoup plus complexes que les fonctions vertex et fragments et ne seront donc pas traitées ici.

Votre première fonction fragment

La fonction fragment s'exécute pour chaque pixel d'un Sprite et détermine la couleur de ce pixel.

Ils sont limités aux pixels couverts par le Sprite, ce qui signifie que vous ne pouvez pas en utiliser un pour, par exemple, créer un contour autour d'un Sprite.

La fonction fragment la plus élémentaire ne fait rien d'autre qu'attribuer une seule couleur à chaque pixel.

Nous le faisons en écrivant un vec4 dans la variable intégrée COLOR. vec4 est l'abréviation de construire un vecteur avec 4 nombres. Pour plus d'informations sur les vecteurs, voir le tutoriel Mathématique vectorielle COLOR est à la fois une variable d'entrée de la fonction fragment et la sortie finale de celle-ci.

void fragment(){
  COLOR = vec4(0.4, 0.6, 0.9, 1.0);
}
../../../_images/blue-box.png

Félicitations ! Vous avez fini. Vous avez réussi à écrire votre premier shader dans Godot.

Maintenant, rendons les choses plus complexes.

Il existe de nombreuses entrées pour la fonction fragment que vous pouvez utiliser pour calculer COLOR. UV est l'un d'entre eux. Les coordonnées UV sont spécifiées dans votre Sprite (sans que vous le sachiez !) et elles indiquent au shader où lire les textures pour chaque partie du maillage.

Dans la fonction de fragment, vous ne pouvez lire qu'à partir de UV, mais vous pouvez l'utiliser dans d'autres fonctions ou pour attribuer des valeurs à COLOR directement.

Le UV varie entre 0-1 de gauche à droite et de haut en bas.

../../../_images/iconuv.png
void fragment() {
  COLOR = vec4(UV, 0.5, 1.0);
}
../../../_images/UV.png

Utilisation de TEXTURE intégrée

Lorsque vous voulez ajuster une couleur dans un Sprite, vous ne pouvez pas simplement ajuster la couleur à partir de la texture manuellement comme dans le code ci-dessous.

void fragment(){
  //this shader will result in an all white rectangle
  COLOR.b = 1.0;
}

La fonction fragment par défaut lit à partir d'une texture et l'affiche. Lorsque vous écrasez la fonction de fragment par défaut, vous perdez cette fonctionnalité, vous devez donc l'implémenter vous-même. Vous lisez à partir de textures en utilisant la fonction texture. Certains nœuds, comme les Sprites, ont une variable de texture dédiée qui peut être accessible dans le shader en utilisant TEXTURE. Utilisez-le avec UV et texture pour dessiner le Sprite.

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = 1.0; //set blue channel to 1.0
}
../../../_images/blue-tex.png

Entrée uniforme

Une entrée uniforme est utilisée pour faire passer les données dans un shader qui sera le même dans tout le shader.

Vous pouvez utiliser des variables uniformes en les définissant en haut de votre shader de cette manière :

uniform float size;

Pour plus d'informations sur l'utilisation, voir le document Shading Language doc.

Ajoutez une variable uniforme pour changer l'intensité du bleu dans notre Sprite.

uniform float blue = 1.0; // you can assign a default value to uniforms

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = blue;
}

Vous pouvez maintenant modifier la quantité de bleu dans le Sprite à partir de l'éditeur. Retournez voir l'inspecteur sous l'endroit où vous avez créé votre shader. Vous devriez voir une section intitulée "Shader Param". Dépliez cette section et vous verrez la variable uniform que vous venez de déclarer. Si vous modifiez la valeur dans l'éditeur, elle écrasera la valeur par défaut que vous avez fournie dans le shader.

Interagir avec les shaders à partir du code

Vous pouvez changer les variables uniformes du code en utilisant la fonction set_shader_param() qui est appelée sur la ressource matérielle du nœud. Avec un nœud Sprite, le code suivant peut être utilisé pour définir la variable uniforme blue.

var blue_value = 1.0
material.set_shader_param("blue", blue_value)

Notez que le nom de la variable uniforme est une chaîne de caractères. La chaîne doit correspondre exactement à la façon dont elle est écrite dans le shader, y compris l'orthographe et la casse.

Votre première fonction vertex

Maintenant que nous avons une fonction fragment, écrivons une fonction vertex.

Utilisez la fonction vertex pour calculer où chaque vertex devrait se trouver à l'écran.

La variable la plus importante dans la fonction de sommet est VERTEX. Au départ, il spécifie les coordonnées des sommets dans votre modèle, mais vous y écrivez également pour déterminer où dessiner réellement ces sommets. VERTEX est un vec2 qui est initialement présenté dans l'espace local (c'est-à-dire non relatif à la caméra, viewport ou aux nœuds parents).

Vous pouvez décaler les sommets en ajoutant directement à VERTEX.

void vertex() {
  VERTEX += vec2(10.0, 0.0);
}

Combiné avec la variable intégrée TIME, cela peut être utilisé pour une animation simple.

void vertex() {
  // Animate Sprite moving in big circle around its location
  VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0);
}

Conclusion

Au fond, les shaders font ce que vous avez vu jusqu'à présent, ils calculent VERTEX et COLOR. C'est à vous d'imaginer des stratégies mathématiques plus complexes pour attribuer des valeurs à ces variables.

Pour vous inspirer, jetez un coup d'œil à certains des tutoriels de shaders les plus avancés, et consultez d'autres sites comme Shadertoy et The Book of Shaders.