Partikel

Starter pack

Partikel adalah cara yang bagus untuk menambahkan kehidupan ke dalam adegan Anda. Mereka dapat digunakan dalam berbagai cara, seperti salju, hujan, api, asap, atau efek sihir. Mereka sering digunakan untuk menciptakan efek atmosfer, seperti kabut, debu, atau percikan api.

Dalam pelajaran ini, kita akan mengeksplorasi berbagai cara untuk membuat partikel menggunakan Threejs dan React Three Fiber untuk membuat adegan salju malam dengan langit berbintang dan efek salju turun:

Lihat bagaimana kepingan salju jatuh dan bintang-bintang berkelip di langit. ❄️✨

Bintang

Kode awal kita berisi "Pemandangan Musim Dingin Low Poly" oleh EdwiixGG di atas sebuah kubus dan sumber cahaya yang dianimasikan.

Tampak bagus tetapi kita dapat membuatnya lebih menarik dengan menambahkan bintang ke langit.

Mari kita mulai dengan menambahkan bintang ke langit. Cara termudah dengan React Three Fiber adalah menggunakan komponen Stars dari pustaka drei.

Dalam components/Experience.jsx:

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

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

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

Langit berbintang

Dan voilà, langit kita sekarang dipenuhi dengan bintang gemerlap yang indah!

Kita dapat bermain dengan parameternya seperti factor untuk menyesuaikan ukuran berdasarkan jarak atau speed untuk menyesuaikan waktu efek fade.

Lihat dokumentasi untuk semua parameter yang tersedia.

Mari kita periksa bagaimana cara kerjanya dengan melihat kode sumber dari komponen Stars.

Kita dapat melihat bahwa untuk merender bintang mereka menggunakan points yang diisi dengan tiga atribut pada geometri:

  • position: untuk menentukan posisi masing-masing bintang
  • colors: untuk menentukan warna masing-masing bintang
  • size: untuk menentukan ukuran masing-masing bintang

Kemudian sebuah ShaderMaterial kustom bernama StarfieldMaterial bertanggung jawab menampilkan titik dengan benar berdasarkan nilai atribut dan uniform waktu untuk efek memudar.

Pertama-tama, pendekatan ini sangat bagus, ringan, dan sepenuhnya diproses pada GPU yang berarti Anda dapat memasang jumlah bintang yang sangat besar.

Tetapi secara visual saya melihat dua hal yang dapat ditingkatkan:

  • Bintang direpresentasikan sebagai persegi.
  • Efek fade disinkronkan di antara semua bintang yang menghasilkan efek berkedip.

Karena kita tidak memiliki kontrol atas aspek-aspek tersebut dengan komponen Stars, mari buat sistem bintang kita sendiri!

Bintang Kustom

Untuk memiliki kontrol yang lebih mudah atas bintang-bintang, kita akan mengendalikan logika mereka di sisi CPU menggunakan instancing.

Jangan khawatir jika ini bukan cara yang paling dioptimalkan, untuk jumlah bintang yang masuk akal, ini akan baik-baik saja dan jauh lebih fleksibel. Kita akan belajar bagaimana menangani partikel kita di sisi GPU saat kita membangun VFX engine sederhana kita dan saat mempelajari TSL nanti di bab ini.

PS: Instancing masih merupakan cara yang efisien untuk merender sejumlah besar objek dengan geometri yang sama seperti yang terlihat dalam pelajaran optimisasi.

Instances

Mari mulai dengan membuat komponen StarrySky kita sendiri di file baru 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} />;
};

Kita membuat InstancedMesh menggunakan plane geometry yang dikombinasikan dengan mesh basic material.

Berkat komponen <Instance /> dari Drei, kita dapat membuat instance dari mesh ini dan mengendalikan setiap partikel (instance) secara individu.

Sekarang mari kita hilangkan komponen Stars dengan yang kustom kita di components/Experience.jsx:

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

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

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

Sekarang kita memiliki langit yang kacau ini:

Custom starry sky filled with planes

Ini adalah titik awal yang baik!

Ayo sesuaikan ukuran bintang-bintang. Dalam useMemo yang bertanggung jawab untuk mengatur posisi partikel, kita bisa menambahkan atribut 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),
    })),
  []
);

Dan dalam komponen Particle, kita bisa meneruskan atribut size ini ke komponen Instance:

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

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

Sekarang lebih baik, kita memiliki bintang-bintang dengan ukuran yang berbeda-beda:

Custom starry sky with different sizes

Namun kita memiliki masalah, bintang-bintang diposisikan antara -20 hingga 20 menggunakan randFloatSpread(20) tetapi kita ingin bintang-bintang diposisikan jauh di langit.

Untuk melakukannya, mari kita tetapkan z selalu pada 0 dan sesuaikan posisi x agar berada antara 5 dan 15.

Graph explaining the x axis repartition

Bintang-bintang kita akan diposisikan secara acak antara 5 dan 15 pada sumbu x.

Dan agar semua berada di sekitar tengah, kita rotasi posisi y antara 0 dan 2 * Math.PI.

Graph explaining the y axis repartition

Tengahnya tidak akan mengandung bintang, dan bintang-bintang akan tersebar ke segala arah.

Dalam useMemo yang bertanggung jawab untuk mengatur posisi partikel, kita bisa menyesuaikan atribut position dan menambahkan atribut 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.