X

Shaders réactifs audio avec Three.js et Shader Park



De notre parrain :

Accédez aux boîtes de réception quand cela compte le plus avec les automatisations de messagerie basées sur les données de Mailchimp. Inscrivez-vous à Mailchimp maintenant.

Dans ce didacticiel, nous expliquerons comment créer un shader réactif audio à l’aide de Three.js et de Shader Park. Les prérequis incluent une compréhension de base de Three.js, mais aucune connaissance préalable de Shader Park ou des shaders n’est requise.

Regardez le didacticiel vidéo et suivez-le !

Vous pouvez voir la démo ici.

Shader Park est une bibliothèque JavaScript pour créer des shaders 2D et 3D procéduraux interactifs qui vous permet d’explorer la programmation des shaders via une interface JavaScript sans la complexité de GLSL. Le langage est inspiré de P5.js et prend actuellement en charge les plugins pour Three.js, Hydra et TouchDesigner. La documentation sur la bibliothèque Shader Park peut être trouvée ici. Il existe un certain nombre de vidéos de didacticiel de démarrage que vous pouvez trouver ici et qui sont très utiles à explorer avant de plonger dans ce didacticiel.

Commencer

Vous pouvez suivre le didacticiel YouTube et ce didacticiel en utilisant le modèle de démarrage. Il y a aussi un projet terminé sur lequel nous allons travailler et que vous pouvez également utiliser pour rendre n’importe quel shader de Shader Park audio réactif. Si vous avez des questions, n’hésitez pas à contacter Shader Park Discord. La documentation de Shader Park est disponible ici.

Commencez par créer un compte sur Glitch.com, ouvrez le modèle de démarrage et appuyez sur le bouton Remix en haut à droite de la page pour pouvoir commencer à suivre.

Vous pouvez également télécharger le projet et le modifier dans l’éditeur de codage de votre choix en allant dans Outils / Importer Exporter et Télécharger le projet.

Si la fenêtre d’aperçu n’est pas déjà ouverte sur Glitch, vous pouvez cliquer sur le bouton Aperçu et ouvrir le volet d’aperçu

Aperçu du projet

Commençons par le fichier script.js.

Remarquez que dans le modèle de démarrage, nous avons déjà une scène de base dans Three.js configurée pour vous. Cela inclut une scène, une caméra, un rendu et des commandes d’orbite, ainsi qu’une géométrie de sphère avec un matériau qui lui est appliqué.

Pour ce tutoriel, nous allons créer notre propre matériel personnalisé avec Shader Park, configurer notre réactivité audio et les combiner ensemble.

Nous allons d’abord échanger le matériau et le maillage de la géométrie de la sphère avec celui généré par Shader Park. Remarquez qu’en haut du modèle de démarrage, nous importons déjà la fonction createSculptureWithGeometry de shader-park-core.

import { createSculptureWithGeometry } from 'https://unpkg.com/shader-park-core/dist/shader-park-core.esm.js';

Supprimons le matériau et le maillage existants. Commentez les lignes 35 et 36 et décommentez 38-45 pour créer notre shader (illustré ci-dessous).

createSculptureWithGeometry prend les paramètres suivants :

  1. une géométrie de three.js (cela peut être n’importe quelle géométrie
  2. votre code de parc de shader sous forme de chaîne (c’est du JavaScript, mais il est converti en GLSL, un langage de programmation qui fonctionnera sur la carte graphique de votre ordinateur)
  3. une fonction de rappel qui renvoie un dictionnaire d’uniformes (nous les couvrirons sous peu, mais pour l’instant, considérez-les comme des entrées de votre shader que nous pouvons mettre à jour en externe à l’aide de JavaScript)
// let material = new MeshBasicMaterial( { color: 0x33aaee} );
// let mesh = new Mesh(geometry, material);

let mesh = createSculptureWithGeometry(geometry, spCode(), () => {
  return {
    time: state.time,
    // pointerDown: state.pointerDown,
    mouse: state.mouse,
    // audio: state.audio,
  }
})

Remarquez que nous obtenons maintenant un cube 3D, mais où est passée notre géométrie de sphère ?

Accédez à sp-code.js (c’est là que nous mettons notre code Shader Park) et vous verrez la définition de notre boîte. Essayez d’agrandir la boîte à environ 1,5.

export function spCode() {
  return `
      box(vec3(1.5));
  `;
}

D’accord, donc la sphère est de retour et si vous cliquez dans le volet de prévisualisation et faites glisser la sphère autour, vous pouvez commencer à voir les bords de notre boîte. Alors que se passe-t-il ici ?

Nous utilisons toujours notre géométrie de sphère comme boîte englobante, mais Shader Park a maintenant généré un matériau pour nous dans les coulisses et l’a appliqué à la géométrie. Ce qui est vraiment excitant, c’est que nous sommes capables de simuler un shader 3D à l’intérieur du matériau et cela nous donne des techniques vraiment intéressantes pour jouer avec lesquelles nous aborderons bientôt.

Ajouter des entrées/uniformes à notre shader

Par défaut, la position et l’heure de la souris ne sont pas accessibles dans le shader. Pour les rendre disponibles, nous devons les transmettre. Lorsque vous travaillez avec l’éditeur Web intégré de Shader Park, la configuration est effectuée automatiquement, mais dans le code Three.js, nous devons ajouter manuellement les entrées (normalement appelées uniformes dans les shaders) dans le rappel createSculptureWithGeometry. Nous utiliserons un uniforme appelé “audio” plus tard pour passer l’analyse audio de three.js dans notre shader.

Testons-le en créant une entrée/uniforme en ajoutant une entrée dans le fichier sp-code.js appelée “taille” et utilisons-la pour modifier la taille de la boîte :

export function spCode() {
  return `
      let size = input();
      box(vec3(size));
  `;
}

Ensuite, dans notre fichier script.js, nous devons ajouter un uniforme appelé “taille” et transmettre une valeur pour la taille. Il est important de noter que le nom peut être tout ce que nous choisissons, mais il doit correspondre à la fois au code Shader Park et à la fonction de rappel createSculptureWithGeometry.

let mesh = createSculptureWithGeometry(geometry, spCode(), () => {
  return {
    time: state.time,
    size: .5,
    // pointerDown: state.pointerDown,
    mouse: state.mouse,
    // audio: state.audio,
  }
})

Notez qu’il existe également un objet d’état que vous pouvez utiliser pour stocker vos variables de la même manière que nous utilisons le temps. Remarquez state.time.

Allez-y et supprimez l’uniforme de taille de notre code Shader Park et créezSculptureWithGeometry. Ajoutons des événements de clic en utilisant l’événement pointerDown. Allez-y et décommentez le pointeur vers le bas de createSculptureWithGeometry à la ligne 41 et également dans l’objet d’état à la ligne 26 ainsi que currPointerdown à la ligne 27.

Nous devrons définir cela dans notre sp-code.js. Essayez de l’utiliser comme taille de la boîte.

export function spCode() {
  return `
      let pointerDown = input();
      box(vec3(pointerDown));
  `;
}

Maintenant, lorsque vous cliquez sur le volet de prévisualisation, vous verrez la taille de la boîte changer. Notez que le code pour gérer l’événement du pointeur vers le bas est défini sur les lignes 54, 55, et qu’une interpolation d’accélération/linéaire est également appliquée dans la boucle de rendu sur la ligne 77. Vous pouvez également faire quelque chose de similaire si vous vouliez créer un shader qui répondre à l’événement de défilement de page. Ensuite, ajoutons notre réactivité audio.

Ajouter de la réactivité audio

Consultez la documentation sur le site Web Three.js pour l’analyseur audio. Nous allons l’ajouter à notre projet, mais au lieu d’utiliser un THREE défini globalement, nous importerons simplement tous les composants individuels de Three.js afin que notre site Web soit plus léger. Assurez-vous de jeter un œil au getFrequencyData fonction que nous utiliserons pour extraire les notes graves de notre audio.

Nous allons également configurer un bouton de chargement audio sur lequel vous pouvez appuyer pour lire l’audio, car vous avez besoin d’une action physique pour démarrer l’audio sur mobile.

// create an Audio source
const sound = new Audio( listener );

let button = document.querySelector('.button');
button.innerHTML = "Loading Audio..."
// Notice that we removed display none

// load a sound and set it as the Audio object's buffer
const audioLoader = new AudioLoader();
audioLoader.load( 'https://cdn.glitch.global/59b80ec2-4e5b-4b54-b910-f3441cac0fd6/OP1Beat.wav?v=1667175863547', function( buffer ) {
  sound.setBuffer( buffer );
  sound.setLoop(true);
  sound.setVolume(0.5);
  button.innerHTML = "Play Audio"
  button.addEventListener('pointerdown', () => {
    sound.play();
    button.style.display = 'none';
  }, false);
});

// create an AudioAnalyser, passing in the sound and desired fftSize
// get the average frequency of the sound
const analyser = new AudioAnalyser( sound, 32 );

Pour ajouter un fichier audio à Glitch, accédez aux actifs, puis faites glisser et déposez une chanson. Une fois téléchargé, cliquez sur l’actif et vous obtiendrez une URL vers le média. Vous pouvez remplacer l’URL existante qui est le premier paramètre de audioLoader.load().

Allez-y et décommentez le l’audio et currAudio de l’état sur les lignes 28 et 29 et de createSculptureWithGeometry sur les lignes 41 et 43. nous les utiliserons pour garder une trace de… vous l’avez deviné… notre audio :). Nous utiliserons l’audio de l’image précédente pour ajouter une interpolation d’accélération / linéaire afin que nos visuels soient lisses et fluides.

Ensuite, nous devons obtenir l’analyse audio, afin que nous puissions ajouter ce qui suit à notre boucle de rendu à la ligne 78 :

if(analyser) {
  state.currAudio += Math.pow((analyser.getFrequencyData()[2] / 255) * .81, 8) + clock.getDelta() * .5;
  state.audio = .2 * state.currAudio + .8 * state.audio;
}

Le getFrequencyData()[2] va nous restituer les données du 2ème bin de l’analyse audio qui représente les notes basses. Les valeurs sont comprises entre 0 et 255, nous divisons donc par 255 pour les normaliser de 0 à 1. Ensuite, nous réduisons un peu les valeurs et les augmentons à une puissance de 8. Augmenter l’analyse audio à une puissance rend nécessaire un changement plus radical de l’audio pour que la valeur de sortie atteigne près de 1, par exemple lorsqu’une grosse caisse frappe. Cela avait l’air bien pour ma chanson, mais pourrait nécessiter des ajustements pour la vôtre, alors assurez-vous de jouer avec ces valeurs. Ensuite, nous ajoutons clock.getDelta().*5 afin que nous ayons toujours un petit mouvement même si l’audio ne joue pas.

Sur la 2ème ligne de code, nous définissons la variable audio réelle que nous transmettrons au shader en utilisant une interpolation linéaire, nous obtenons donc 20% de l’audio actuel et 80% de l’audio de l’image précédente. Cela ajoute un joli lissage. Notez également que nous sommes += le state.currAudio donc il comptera en continu. Cette valeur ne fonctionnera bien que dans une animation où nous utiliserions habituellement le temps dans notre code Shader Park.

Création de notre Shader dans Shader Park

Rendez-vous sur l’éditeur Web intégré de Shader Park. Si vous n’avez pas encore créé de compte, vous pouvez rapidement créer un compte afin de pouvoir enregistrer votre projet.

Encore une fois, je vous recommande fortement de consulter les didacticiels de démarrage sur Shader Park afin de vous familiariser avec la langue. Vous pouvez trouver la série de didacticiels vidéo ici.

Commençons par ajouter un boxFrame et une sphere et mélangeons la géométrie avec une entrée. Par défaut, nous sommes dans un mode union avec géométrie où vous pouvez ajouter n’importe quoi à la scène, mais ici nous pouvons utiliser mixGeo pour fusionner facilement entre deux géométries. Nous utilisons une entrée / uniforme appelée pointerDown donc plus tard, lorsque nous l’introduisons sur le site Web, nous pouvons utiliser nos événements de clic.

Ensuite, nous pouvons ajouter de la couleur. Les valeurs de couleur vont de 0 à 1 dans un format RVB. Il y a aussi un assistant HSV si vous le souhaitez également. Ici, nous utilisons une couleur de base de bleu et ajoutons une petite quantité de la normale de l’objet 3D pour avoir une certaine variété dans la couleur.

Pour créer l’effet d’entraînement, nous devons utiliser du bruit, qui peut générer pour nous des valeurs aléatoires continues et régulières. Cela peut créer des choses comme l’apparence des montagnes ou la surface de l’eau.

Le bruit a besoin d’une position dans l’espace pour être échantillonné. Nous allons donc utiliser getSpace(). Si vous venez d’un milieu de programmation de shaders, c’est notre fragCoord avec une valeur az qui lui est attachée, donc vous récupérez une position xyz.

Nous pouvons utiliser notre fonction de bruit pour colorer notre forme ainsi que pour déplacer notre forme.

Notez que nous ajoutons un vec3 à s. Cela nous permet de traduire où nous échantillonnons la fonction de bruit. Dans ce cas, nous traduisons dans la direction z avec un peu de temps pour que le mouvement semble aléatoire. Nous ajoutons aussi notre bruit n à la taille de notre sphère et en la réduisant. Notez que vous pouvez utiliser la fonction de bruit dans pour colorer et déplacer votre objet.

Nous pouvons également échantillonner notre bruit avec la direction du rayon, qui nous donne l’angle que la caméra pointe vers l’objet.

Créons une autre variable de bruit appelée n1 et échantillonnons le bruit en utilisant getRayDirection(). Modifiez également la taille de votre sphère pour utiliser n1 au lieu de n.

Une technique vraiment cool consiste à prendre la coordonnée d’un bruit et à la passer dans la position d’un autre.

Prenons n1 et passez-le en position pour n. Changeons la taille de la sphère en utilisant n au lieu de n1.

Ensuite, nous allons augmenter la taille de notre boxFrame, déplacer notre espace de coordonnées avec la souris pour qu’il réponde au mouvement de la souris et ajouter un bel éclairage avec du métal et de la brillance.

Ensuite, nous pouvons réduire le nombre d’itérations utilisées par le moteur de marche de rayons sous-jacent. La technique du ray marching est ce qui nous permet de créer un shader 3D, en abaissant les itérations jusqu’à 5 on obtient quelque chose de plus proche d’un shader 2D. Cela a également une amélioration des performances.

Enfin, nous allons créer une entrée pour l’audio et remplacer la variable temps avec l’audio tout au long de notre shader.

La dernière étape consiste à réintégrer ce code dans notre projet. Vous pouvez copier le code Shader Park et le mettre dans sp-code.js :

export function spCode() {
  return `
      let pointerDown = input();
      let audio = input()

      setMaxIterations(5)

      let s = getSpace();
      let r = getRayDirection();

      let n1 = noise(r * 4 + vec3(0, 0, audio*.1));
      let n = noise(s + vec3(0, 0, audio*.1) + n1);

      metal(n*.5+.5);
      shine(n*.5+.5);

      displace(mouse.x*2, mouse.y*2, 0)
      color(normal * .1 + vec3(0, 0, 1));
      boxFrame(vec3(2), abs(n) * .1 + .04 );
      mixGeo(pointerDown);
      sphere(n * .5 + .8);
  `;
}

Et c’est tout!

J’espère que vous avez apprécié ce tutoriel. Je suis vraiment excité de voir ce que vous créez en utilisant ça!

Donner vie aux lettres : coder une animation de typographie SVG cinétique

Animation de typographie aléatoire