前面我们渲染粒子都是默认的 Sprite,而 Sprite 有个特点是永远正对摄像头,所以只能看到一个面。
但有时候我们需要让粒子翻转到背面。
比如漫天飞舞的树叶,它会做 360 度的旋转。
这种情况用 Sprite 渲染粒子就不行了。
能不能用网格模型 Mesh 来作为粒子呢?
可以的,three.quarks 支持 mesh 模式。
而且漫天飞舞的树叶除了翻转外,位置也会做随机的变化,这需要噪声,three.quarks 也做了内置的支持。
我们来试一下:
npx create-vite three-quarks-mesh-noise

进入项目,安装依赖:
npm install
npm install --save three
npm install --save-dev @types/three
改下 src/main.js
import './style.css';
import * as THREE from 'three';
import {
OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';
const scene = new THREE.Scene();
scene.add(mesh);
const helper = new THREE.AxesHelper(1000);
// scene.add(helper);
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 10000);
camera.position.set(0, 500, 400);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)
const clock = new THREE.Clock();
function render() {
const delta = clock.getDelta();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
document.body.append(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
创建 Scene、Camera、Renderer。
相机在 0,0,20 的位置,正对 XY 平面。
改下 style.css
body {
margin: 0;
}
安装下粒子效果库:
npm install --save three.quarks
在 mesh.js 初始化下 three.quarks
import * as THREE from "three";
import { ApplySequences, BatchedParticleRenderer,ConeEmitter, ConstantValue, DonutEmitter, GridEmitter, IntervalValue, ParticleSystem, RandomColor, RectangleEmitter, TextureSequencer, Vector3, Vector4 } from "three.quarks";
const group = new THREE.Group();
const batchRenderer = new BatchedParticleRenderer();
group.add(batchRenderer);
const loader = new THREE.TextureLoader();
const texture = loader.load('./point.png');
const particles = new ParticleSystem({
duration: 20,
looping: true,
startLife: new ConstantValue(20),
startSpeed: new IntervalValue(100, 200),
startSize: new IntervalValue(10, 20),
startColor: new RandomColor(
new Vector4(1, 1, 1, 1),
new Vector4(1, 0.7, 0, 1)
),
emissionOverTime: new IntervalValue(1000, 2000),
shape: new GridEmitter({
width: 500,
height: 500,
column: 10,
row: 10
}),
material: new THREE.MeshBasicMaterial({
map: texture,
transparent: true
})
});
group.add(particles.emitter);
batchRenderer.addSystem(particles);
export {
batchRenderer
}
export default group;
用网格粒子发射器 GridEmitter。
10 * 10 的发射粒子。
point.png 从这里下载:


在 main.js 引入:
import { batchRenderer } from './mesh.js';
const clock = new THREE.Clock();
function render() {
const delta = clock.getDelta();
renderer.render(scene, camera);
requestAnimationFrame(render);
if(batchRenderer) {
batchRenderer.update(delta);
}
}
render();
跑一下,看下效果:
npm run dev


换张图片:


size 改大,发射的数量改小:

看下效果:

因为是 Sprite,所以无论怎么旋转,树叶都是正对屏幕的。
我们换成 mesh 试一下:


指定 renderMode 是渲染网格模型,用的 geometry 是 PlaneGeometry,开启双面渲染
const geometry = new THREE.PlaneGeometry(10, 10);
instancingGeometry: geometry,
startSize: new IntervalValue(5, 10),
renderMode: RenderMode.Mesh,
material: new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
side: THREE.DoubleSide
})

现在就不再是永远正对摄像头了,因为现在渲染的是网格模型。
如何让粒子在生命周期内旋转呢?
显然是要做行为控制:


最开始指定一个随机旋转角度。
然后在生命周期中,绕某个轴来做随机角度的旋转。
startRotation: new IntervalValue(Math.PI / 6, Math.PI / 3),
particles.addBehavior(
new Rotation3DOverLife(
new AxisAngleGenerator(
new THREE.Vector3(0, 1, 1).normalize(),
new IntervalValue(Math.PI / 10, Math.PI)
)
)
)
看下效果:

现在就有随风飞舞的树叶的感觉了。
现在运动位置太规律了,加点噪声:

particles.addBehavior(
new Noise(
new ConstantValue(0.5),
new IntervalValue(50, 100)
)
)
第一个参数是改变频率,第二个参数是改变力度。

加大一些,效果更明显:


然后调解下参数:
网格宽高调大:

width: 1000,
height: 1000,
让 emitter 旋转一下,从上往下落:

particles.emitter.rotateX(Math.PI / 2);
然后调下相机角度:

camera.position.set(0, -500, 200);
camera.lookAt(0, -500, 0);
去掉 OrbitControls,不然 lookAt 会被重置:

这里我还顺便改了下颜色值是红和黄之间随机:

startColor: new RandomColor(
new Vector4(1, 0, 0, 1),
new Vector4(1, 1, 0, 1)
),
看下效果:

现在就有那种落叶纷飞的感觉了。
不过噪声移动频率还是有点大,改小一点:

频率改小,力度改大。

现在效果更好一点。
案例代码上传了小册仓库
总结
这节我们学了如何渲染网格模型粒子,以及噪声效果。
默认粒子是 Sprite 渲染的,永远正对屏幕,不能看到背面。
有时候需要切换成 Mesh 渲染,只要指定 geometry 然后修改 renderMode 就可以了。
首先,我们用 GridEmitter 来发射树叶粒子。
用 startRotation 修改了树叶开始旋转角度,Rotation3DOverLife 修改了树叶运动过程中的旋转角度。
然后用 Noise 给运动过程加了一些连续随机的位置移动。
这样就有树叶上下纷飞的效果了。