Particules

Starter pack

Les particules sont un excellent moyen d'ajouter de la vie à votre scène. Elles peuvent être utilisées de différentes manières, telles que la neige, la pluie, le feu, la fumée ou des effets magiques. Elles sont souvent utilisées pour créer des effets atmosphériques, tels que du brouillard, de la poussière ou des étincelles.

Dans cette leçon, nous allons explorer différentes manières de créer des particules en utilisant Threejs et React Three Fiber pour créer cette scène nocturne enneigée avec un ciel étoilé et un effet de chute de neige :

Voyez comment les flocons de neige tombent et les étoiles scintillent dans le ciel. ❄️✨

Étoiles

Notre code de départ contient cette "Low Poly Winter Scene" par EdwiixGG sur un cube et une source de lumière animée.

C'est joli, mais nous pouvons le rendre plus intéressant en ajoutant des étoiles dans le ciel.

Commençons par ajouter des étoiles dans le ciel. Le moyen le plus simple avec React Three Fiber est d'utiliser le composant Stars de la bibliothèque drei.

Dans components/Experience.jsx :

// ...
import { Stars } from "@react-three/drei";

export const Experience = () => {
  // ...

  return (
    <>
      <Stars />
      {/* ... */}
    </>
  );
};

Ciel étoilé

Et voilà, notre ciel est maintenant rempli de belles étoiles scintillantes !

Nous pouvons jouer avec ses paramètres tels que factor pour ajuster la taille en fonction de la distance ou speed pour ajuster le timing de l'effet de fade.

Reportez-vous à la documentation pour tous les paramètres disponibles.

Voyons comment il fonctionne sous le capot en parcourant le code source du composant Stars.

Nous pouvons voir que pour rendre les étoiles, ils utilisent des points remplis de trois attributs sur la géométrie :

  • position: pour déterminer la position de chaque étoile
  • colors: pour déterminer la couleur de chaque étoile
  • size: pour déterminer la taille de chaque étoile

Ensuite, un ShaderMaterial personnalisé nommé StarfieldMaterial est responsable de l'affichage correct des points en fonction de ces valeurs d'attributs et de l'uniforme de temps pour l'effet de fading.

Tout d'abord, cette approche est excellente, elle est légère et complètement traitée sur le GPU, ce qui signifie que vous pourriez potentiellement afficher un très grand nombre d'étoiles.

Mais visuellement, je vois deux choses qui pourraient être améliorées :

  • Les étoiles sont représentées sous forme de carrés.
  • L'effet de fading est synchronisé entre toutes les étoiles, entraînant un effet de clignotement.

Comme nous n'avons pas de contrôle sur ces aspects avec le composant Stars, créons notre propre système d'étoiles !

Étoiles Personnalisées

Pour avoir un contrôle plus facile sur les étoiles, nous gérerons leur logique du côté CPU en utilisant l'instanciation.

Ne vous inquiétez pas si ce n'est pas la façon la plus optimisée, pour un nombre raisonnable d'étoiles, ce sera correct et beaucoup plus flexible. Nous apprendrons comment gérer nos particules du côté GPU lorsque nous construirons notre simple moteur VFX et en apprenant TSL plus tard dans ce chapitre.

PS : L'instanciation reste un moyen efficace de rendre un grand nombre d'objets avec la même géométrie, comme vu dans la leçon d'optimisation.

Instances

Commençons par créer notre propre composant StarrySky dans un nouveau fichier components/StarrySky.jsx :

import { Instance, Instances } from "@react-three/drei";
import { useMemo, useRef } from "react";
import { randFloatSpread } from "three/src/math/MathUtils.js";

export const StarrySky = ({ nbParticles = 1000 }) => {
  const particles = useMemo(
    () =>
      Array.from({ length: nbParticles }, (_, idx) => ({
        position: [
          randFloatSpread(20),
          randFloatSpread(20),
          randFloatSpread(20),
        ],
      })),
    []
  );

  return (
    <Instances range={nbParticles} limit={nbParticles} frustumCulled={false}>
      <planeGeometry args={[1, 1]} />
      <meshBasicMaterial />
      {particles.map((props, i) => (
        <Particle key={i} {...props} />
      ))}
    </Instances>
  );
};

const Particle = ({ position }) => {
  const ref = useRef();

  return <Instance ref={ref} position={position} />;
};

Nous créons un InstancedMesh utilisant une plane geometry combinée à un mesh basic material.

Grâce au composant <Instance /> de Drei, nous sommes capables de créer des instances de ce mesh et de contrôler chaque particule (instance) individuellement.

Maintenant, remplaçons le composant Stars par notre composant personnalisé dans components/Experience.jsx :

// ...
import { StarrySky } from "./StarrySky";

export const Experience = () => {
  // ...

  return (
    <>
      <StarrySky />
      {/* ... */}
    </>
  );
};

Nous avons maintenant ce ciel chaotique :

Ciel étoilé personnalisé rempli de plans

C'est un bon point de départ !

Ajustons la taille des étoiles. Dans le useMemo responsable de la définition des positions des particules, nous pouvons ajouter un attribut size :

import { randFloat, randFloatSpread } from "three/src/math/MathUtils.js";

// ...

const particles = useMemo(
  () =>
    Array.from({ length: nbParticles }, (_, idx) => ({
      position: [randFloatSpread(20), randFloatSpread(20), randFloatSpread(20)],
      size: randFloat(0.1, 0.25),
    })),
  []
);

Et dans le composant Particle, nous pouvons passer cet attribut size au composant Instance :

const Particle = ({ position, size }) => {
  const ref = useRef();

  return <Instance ref={ref} position={position} scale={size} />;
};

Maintenant c'est mieux, nous avons des étoiles de tailles différentes :

Ciel étoilé personnalisé avec différentes tailles

Mais nous avons un problème, les étoiles sont positionnées entre -20 et 20 en utilisant randFloatSpread(20) mais nous voulons que les étoiles soient positionnées loin dans le ciel.

Pour ce faire, gardons le z toujours à 0 et ajustons la position x pour être entre 5 et 15.

Graphique expliquant la répartition de l'axe x

Nos étoiles seront positionnées aléatoirement entre 5 et 15 sur l'axe x.

Et pour être tout autour du centre, nous faisons pivoter la position y entre 0 et 2 * Math.PI.

Graphique expliquant la répartition de l'axe y

Le centre ne contiendra jamais d'étoiles et les étoiles seront dispersées dans toutes les directions.

Dans le useMemo responsable de la définition des positions des particules, nous pouvons ajuster l'attribut position et ajouter un attribut rotation :

const particles = useMemo(
  () =>
    Array.from({ length: nbParticles }, (_, idx) => ({
      position: [randFloat(5, 15), randFloatSpread(20), 0],
      rotation: [0, randFloat(0, Math.PI * 2), 0],
      size: randFloat(0.1, 0.25),
    })),
  []
);
Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.