粒子是为场景增添生命力的绝佳方式。它们可以用于多种场合,例如雪、雨、火、烟雾或魔法效果。它们通常用于创造大气效果,如雾气、灰尘或火花。
在本课程中,我们将研究使用 Threejs 和 React Three Fiber 创建粒子的不同方法,以创建这个带有星空和降雪效果的夜景:
看雪花如何飘落,星星如何在天空闪烁。❄️✨
星星
我们的起始代码包含了这段由 EdwiixGG 制作的 "Low Poly Winter Scene",位于一个立方体和一个动画光源之上。
看起来不错,但如果在天空中添加星星,会更有趣。
让我们从在天空中添加星星开始。使用 React Three Fiber 最简单的方法是使用 drei 库中的 Stars 组件。
在 components/Experience.jsx
中:
// ... import { Stars } from "@react-three/drei"; export const Experience = () => { // ... return ( <> <Stars /> {/* ... */} </> ); };
瞧,我们的天空现在充满了闪烁的可爱星星!
我们可以调整其参数,例如 factor
以根据距离调整大小或 speed
以调整 fade 效果的时间。
有关所有可用参数,请参阅文档。
让我们通过浏览 Stars 组件的源代码来了解其底层工作原理。
我们可以看到,为了渲染星星,他们使用了 points,并在几何上填充了三个属性:
position
:用于确定每个星星的位置colors
:用于确定每个星星的颜色size
:用于确定每个星星的大小
然后,一个名为 StarfieldMaterial
的自定义 ShaderMaterial 负责根据这些属性值和时间统一变量正确显示点效果。
首先,这种方法是非常出色的,轻量化,完全在 GPU 上处理,这意味着您可以放置大量星星。
但在视觉上,我看到有两个方面可以提高:
- 星星表示为方块。
- 所有星星的渐变效果同步,导致 闪烁 效果。
由于我们无法通过 Stars
组件控制这些方面,因此让我们来创建我们自己的星星系统!
自定义星空
为了更轻松地控制星星,我们将在CPU端处理它们的逻辑,使用实例化技术。
不用担心这不是最优化的方法,对于合理数量的星星来说,这样做是足够的并且更加灵活。我们将在本章稍后的部分构建我们的简单VFX 引擎和学习TSL时,了解如何在GPU端处理我们的粒子。
PS: 即使对于几何形状相同的大量物体,实例化仍然是一种有效的渲染方式,正如在**优化课程**中提到的。
实例
让我们从一个新的文件 components/StarrySky.jsx
中创建我们自己的 StarrySky
组件开始:
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} />; };
我们使用平面几何体结合mesh basic material创建了一个InstancedMesh。
感谢来自Drei的 <Instance />
组件,我们能够创建这个mesh的实例并且单独控制每个粒子(实例)。
现在让我们在 components/Experience.jsx
中用我们自定义的组件替换掉 Stars
组件:
// ... import { StarrySky } from "./StarrySky"; export const Experience = () => { // ... return ( <> <StarrySky /> {/* ... */} </> ); };
我们现在有一个混乱的天空:
这是一个好的起点!
让我们调整星星的大小。在负责设置粒子位置的 useMemo
中,我们可以添加一个 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), })), [] );
在 Particle
组件中,我们可以将这个 size
属性传递给 Instance
组件:
const Particle = ({ position, size }) => { const ref = useRef(); return <Instance ref={ref} position={position} scale={size} />; };
现在效果更好了,星星有不同的尺寸:
但我们有个问题,星星的位置在 -20
到 20
之间,使用的是 randFloatSpread(20)
,但我们希望星星远在天空中。
为此,我们让 z
始终为 0
并将 x
位置调整到 5
和 15
之间。
我们的星星将随机地位于 x
轴上 5
和 15
之间。
为了围绕中心,我们将 y
位置旋转在 0
和 2 * Math.PI
之间。
中心将永远没有星星,并且星星将散布在所有方向。
在负责设置粒子位置的 useMemo
中,我们可以调整 position
属性并添加一个 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), })), [] );
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
One-time payment. Lifetime updates included.