Ajouter un shader

Ce tutoriel est destiné à la version Live.

heavym tutorials

Tutoriel HeavyM

Présenté par l'équipe HeavyM

Pas-à-pas



Ajouter un shader dans HeavyM


Télécharger les shaders


Pour ajouter un shader dans HeavyM, il faut ajouter un player en faisant un drag and drop depuis l’icône correspondant au player dans la barre d’outils. Une forme rectangulaire délimitant votre player apparaît dans votre plan de travail. Et un panneau de paramètres apparaît sur la gauche.





Vous pouvez ouvrir ce panneau à tout moment en sélectionnant votre player et le laisser affiché en cliquant sur l’icône en forme de punaise. Pour en savoir plus sur le fontionnement des players en général, vous pouvez consulter notre tuto sur le sujet.


Par défaut, un player est en mode média pour ajouter une vidéo ou une image. Pour passer en mode shader, vous devez cliquer sur l’icône correspondante dans la partie paramètres de votre player.





Vous pouvez ensuite choisir votre shader en appuyant sur l’icône +. Cela vous permet d’accéder au fichiers disponibles sur votre ordinateur et chercher votre fragment shader. Il doit s’agir d’un fichier avec l’extension . frag.



Comprendre le fonctionnement des shaders


Qu’est-ce qu’un shader ?


Un shader est un programme informatique qui communique directement avec la carte graphique pour afficher une image. Si vous voulez comprendre les shaders dans le détails, voici un tutoriel complet et bien fait : https://openclassrooms.com/courses/les-shaders-en-glsl.


Pour les utiliser dans HeavyM, il n’y a pas besoin de tout comprendre mais il est bien d’avoir quelques bases. Dans HeavyM, on utilise uniquement des fragment shaders. Pour faire simple il s’agit du programme qui détermine la couleur de chaque pixel dans l’image que vous voulez afficher. Pour faire fonctionner ces programmes, il faut au minimum une carte graphique supportant une version OpenGL 2.0.


Couleur en sortie


void main( void )
{
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}


Il s’agit ici d’un très court code qui assigne à chaque pixel la couleur rouge. Vous pouvez déjà tester ce petit bout de code dans HeavyM. Copiez ce texte dans un éditeur de texte et sauvez-le avec l’extension . frag afin de pouvoir l’importer dans HeavyM.


En appuyant sur Play et en mode “active on top”, votre player se comportera comme une forme rouge.





Pour comprendre : chaque pixel passe dans ce petit bout de code. gl_fragColor est le nom que l’on donne à la couleur qu’on obtient. On lui assigne un vecteur correspondant à une couleur en format RGBA avec des valeurs entre 0 et 1 (ici R = 1, G = 0, B = 0, A = 1).


Vous pouvez maintenant essayer de modifier ces paramètres pour obtenir différentes couleurs avec différentes opacités en faisant varier les paramètres entre 0 et 1.


Position en entrée


Mais le but n’est pas de donner la même couleur à chaque pixel. On peut donc modifier cette couleur en fonction de la position du pixel. Cette position, on la récupère sous forme de vecteur 2D qu’on appelle gl_FragCoord, si l’on ne veut récupérer que le x (abscisse) ou le y (ordonnée) : gl_FragCoord.x et gl_FragCoord.y.


Un effet simple est de faire varier les composantes de la couleur de sortie en fonction de la position pour faire un dégradé. Par exemple :


void main( void )
{
      float red = gl_FragCoord.y / 1000.0;
      float blue = gl_FragCoord.x / 1000.0;
      gl_FragColor = vec4(red, 0.0, blue, 1.0);
}


Dans ce code, on déclare un nombre red égal à la coordonnée y du pixel divisé par 1000. De même pour le nombre blue égale à la coordonnée x du pixel divisé par 1000. On a ainsi, pour le pixel situé en (0,0), c’est à dire en haut à gauche, des valeurs de rouge et de bleu égales à 0, il sera donc noir. Alors que le pixel situé en (1280, 720) (pour une résolution de 720p), c’est à dire en bas à droite, a des valeurs de rouge et de bleu égales à 1 ou plus., il sera donc rose (rouge + bleu).


Ici, on divise par 1000 pour avoir une valeur contenue environ entre 0 et 1, mais dans la réalité, cela dépend de la résolution de votre shader.





Vous pouvez ensuite appliquer toutes sortes de transformations mathématiques sur vos couleurs pour obtenir les effets voulus. Pour cela, nous vous conseillons de consulter des tutoriels sur les shaders qui vous expliqueront comment écrire votre code.


Uniform Values


Mais tout cela ne permet que d’avoir une image fixe. Dans un mapping, c’est quand même plus sympa d’avoir une image animée, il faut alors prendre en compte un facteur temps. Pour cela, on peut communiquer à un shader des valeurs qui lui seront communiquées à chaque frame : ces valeurs sont appelées Uniform values.

Dans HeavyM, nous avons décidé d’envoyer 6 valeurs :


  • HM_x, HM_y, HM_z et HM_speed : des nombres entiers entre 0 et 100 que vous pouvez modifier depuis l’interface du logiciel.
  • HM_resolution: un vecteur (x,y) correspondant à la résolution de votre shader.
  • HM_time: un nombre float correspondant au temps écoulé depuis l’ouverture de votre projection en seconde.

uniform float HM_x;
uniform float HM_y;
uniform float HM_z;
uniform float HM_speed;
uniform float HM_time;
uniform vec2 HM_resolution;

void main( void )
{
      float red = (gl_FragCoord.y / HM_resolution.y);
      float blue = (gl_FragCoord.x / HM_resolution.x);
      float green = (sin(HM_time) + 1.0) * 0.5;
      gl_FragColor = vec4(red, green, blue, 1.0);
}


En premier lieu, on déclare ces uniform values, pour dire au shader que l’on va les utiliser, comment elles s’appellent et ce qu’elles contiennent. On peut ensuite les utiliser dans le shader lui même.
Dans l’exemple au-dessus, la composante verte de la couleur est égale au sinus du temps (on a ainsi une valeur entre -1 et 1) auquel on ajoute 1 et qu’on multiplie par 0.5 pour obtenir une valeur entre 0 et 1 qui varie en fonction du temps.





En autre exemple, voici un code qui dessine un cercle qui change de couleur en fonction du temps.
Le rayon du cercle est dépendant de la variable HM_x que l’on peut modifier dans le logiciel.


uniform float HM_x;
uniform float HM_y;
uniform float HM_z;
uniform float HM_speed;
uniform float HM_time;
uniform vec2 HM_resolution;

void main(void)
{
         float x = (gl_FragCoord.x / HM_resolution.x) - 0.5;
         float y = (gl_FragCoord.y / HM_resolution.y) - 0.5;

         float rayon = HM_x / 100.0;

      float d = sqrt(x*x + y*y);
      float red;
      float mov = 0.5 * sin(HM_time);

      if( d < rayon)
      {
         red = 0.7 - 2.0 * d + mov;
      }
      else
      {
         red = 0.6 * d - mov;
      }
      gl_FragColor = vec4(red, 0.0, 0.4, 1.0);
}





Modifier un shader déjà existant


Si vous voulez pouvoir utiliser des shaders trouvés sur internet, sur les sites tels que https://www.interactiveshaderformat.com/ ou http://glslsandbox.com/, il faut pouvoir comprendre un minimum le code de ces shaders.


Mais la principale chose à modifier dans ces shaders sont les Uniforms values. Celles-ci sont choisies par celui qui exécute le shader. Dans HeavyM, nous avons choisi d’en mettre 6 et de les appeler d’une certaine façon (voir plus haut). Vous devez donc adapter le shader pour prendre en compte ces uniform values et les appeler de la même façon.


Prenons un exemple : http://glslsandbox.com/e#40449.0.


#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float time;
uniform vec2 resolution;

void main( void ) {

      vec2 position = ( gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
      vec3 destColor = vec3(1.0, 0.0, 0.8 );
      float f = 0.0;

      for(float i = 0.0; i < 50.0; i++){

         float s = sin(time + i ) ;
         float c = cos(time + i );
         f += 0.003 / abs(length(8.0* position *f - vec2(c, s)) -0.4);
      }

      gl_FragColor = vec4(vec3(destColor * f), 1.0);
}


Dans ce code, glslsandbox appelle les uniform values time et resolution. Vous devez donc remplacer toutes les occurrences de time par HM_time et toutes les occurrences de resolution par HM_resolution.


#ifdef GL_ES
precision mediump float;
#endif

#extension GL_OES_standard_derivatives : enable

uniform float HM_time;
uniform vec2 HM_resolution;

void main( void ) {

      vec2 position = ( gl_FragCoord.xy * 2.0 - HM_resolution) / min(HM_resolution.x, HM_resolution.y);
      vec3 destColor = vec3(1.0, 0.0, 0.8 );
      float f = 0.0;

      for(float i = 0.0; i < 50.0; i++){

         float s = sin(HM_time + i ) ;
         float c = cos(HM_time + i );
         f += 0.003 / abs(length(8.0* position *f - vec2(c, s)) -0.4);
      }

      gl_FragColor = vec4(vec3(destColor * f), 1.0);
}





Cette aide était-elle claire ?

Donnez-nous votre avis et participez à l’amélioration des tutos HeavyM !