Particelle

Starter pack

Le particelle sono un ottimo modo per aggiungere vita alla tua scena. Possono essere utilizzate in vari modi, come neve, pioggia, fuoco, fumo o effetti magici. Spesso vengono utilizzate per creare effetti atmosferici, come nebbia, polvere o scintille.

In questa lezione, esploreremo diversi modi per creare particelle usando Threejs e React Three Fiber per creare questa scena notturna con un cielo stellato ed effetto nevicata:

Osserva come i fiocchi di neve cadono e le stelle brillano nel cielo. ❄️✨

Stelle

Il nostro codice iniziale contiene questa "Scena Invernale Low Poly" di EdwiixGG sopra un cubo e una sorgente luminosa animata.

Sembra bello ma possiamo renderlo più interessante aggiungendo stelle al cielo.

Iniziamo aggiungendo stelle al cielo. Il modo più semplice con React Three Fiber è utilizzare il componente Stars dalla libreria drei.

In components/Experience.jsx:

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

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

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

Cielo stellato

E voilà, il nostro cielo è ora pieno di belle stelle splendenti!

Possiamo giocare con i suoi parametri come factor per regolare la dimensione in base alla distanza o speed per regolare il timing dell'effetto fade.

Consulta la documentazione per tutti i parametri disponibili.

Vediamo come funziona dietro le quinte esplorando il codice sorgente del componente Stars.

Possiamo vedere che per renderizzare le stelle vengono utilizzati points riempiti con tre attributi sulla geometria:

  • position: per determinare la posizione di ciascuna stella
  • colors: per determinare il colore di ciascuna stella
  • size: per determinare la dimensione di ciascuna stella

Poi un ShaderMaterial personalizzato chiamato StarfieldMaterial è responsabile di visualizzare i punti correttamente basati su quei valori degli attributi e l'uniform time per l'effetto di fading.

In primo luogo, questo approccio è ottimo, è leggero e completamente elaborato sulla GPU, il che significa che potresti potenzialmente inserire un numero molto elevato di stelle.

Ma visivamente vedo due cose che potrebbero essere migliorate:

  • Le stelle sono rappresentate come quadrati.
  • L'effetto di fading è sincronizzato tra tutte le stelle risultando in un effetto di lampeggiamento.

Poiché non abbiamo controllo su questi aspetti con il componente Stars, creiamo il nostro sistema di stelle!

Stelle personalizzate

Per avere un controllo più facile sulle stelle, gestiremo la loro logica sul lato CPU utilizzando l'istanziazione.

Non preoccuparti se non è il modo più ottimizzato, per un numero ragionevole di stelle andrà bene ed è molto più flessibile. Impareremo come gestire le nostre particelle sul lato GPU quando costruiremo il nostro semplice motore VFX e quando impareremo TSL più avanti in questo capitolo.

PS: L'istanziazione è comunque un modo efficiente per rendere un grande numero di oggetti con la stessa geometria, come visto nella lezione sull'ottimizzazione.

Istanze

Cominciamo creando il nostro componente StarrySky in un nuovo file 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} />;
};

Stiamo creando un InstancedMesh utilizzando una plane geometry combinata con un mesh basic material.

Grazie al componente <Instance /> di Drei, siamo in grado di creare istanze di questo mesh e di controllare ciascuna particella (istanza) individualmente.

Ora sostituiamo il componente Stars con il nostro personalizzato in components/Experience.jsx:

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

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

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

Ora abbiamo questo cielo caotico:

Custom starry sky filled with planes

È un buon punto di partenza!

Regoliamo le dimensioni delle stelle. Nel useMemo responsabile dell'impostazione delle posizioni delle particelle, possiamo aggiungere un attributo 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),
    })),
  []
);

E nel componente Particle, possiamo passare questo attributo size al componente Instance:

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

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

Ora è meglio, abbiamo stelle di diverse dimensioni:

Custom starry sky with different sizes

Ma abbiamo un problema, le stelle sono posizionate tra -20 e 20 usando randFloatSpread(20), ma vogliamo che le stelle siano posizionate lontano nel cielo.

Per fare ciò, manteniamo la z sempre a 0 e regoliamo la posizione x tra 5 e 15.

Graph explaining the x axis repartition

Le nostre stelle saranno posizionate casualmente tra 5 e 15 sull'asse x.

E per essere tutto intorno al centro, ruotiamo la posizione y tra 0 e 2 * Math.PI.

Graph explaining the y axis repartition

Il centro non conterrà mai stelle e le stelle saranno sparse in tutte le direzioni.

Nel useMemo responsabile dell'impostazione delle posizioni delle particelle, possiamo regolare l'attributo position e aggiungere un attributo 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.