JSAR 粒子系统实战:打造炫酷 3D 烟花秀
前言
当我第一次在Rokid AR 眼镜上看到 3D 烟花在真实空间中绽放时,那种震撼是难以言表的。作为一名前端开发者,我一直在寻找一种简单的方式来开发 AR 应用,而 JSAR(JavaScript Spatial Application Runtime) 正是我梦寐以求的工具。
本文将带你从零开始,用 JSAR 创建一个令人惊艳的 3D 烟花粒子系统。不需要复杂的 3D 建模,不需要学习 Unity 或 Unreal Engine,只需要你熟悉的 JavaScript/TypeScript 和一点点创意。

什么是 JSAR?为什么选择它?
JSAR 简介
JSAR 是 Rokid 开源的空间应用运行时,它让 Web 开发者能够使用熟悉的技术栈(JavaScript/TypeScript + Babylon.js)来开发 3D 空间应用。
核心优势:
- 🚀 低门槛:Web 开发者无需学习复杂的 3D 引擎
- 🎨 强大的 3D 能力:基于 Babylon.js,功能完善
- 🔧 标准化:使用 XSML(类似 HTML)定义 3D 空间
- 📱 跨平台潜力:一次编写,多端运行
为什么做烟花项目?
粒子系统是 3D 图形学中的经典主题,烟花效果则是粒子系统最直观、最炫酷的应用之一。通过这个项目,你将学会:
- Babylon.js 粒子系统的完整使用
- JSAR 空间应用的开发流程
- 3D 动画和交互设计
- 性能优化技巧
开发环境准备
所需工具
- Node.js:v18.12.1 或更高版本
- VSCode:推荐安装 JSAR DevTools 扩展
- 基础知识:JavaScript/TypeScript、基本的 3D 概念
创建项目
bash
# 使用官方脚手架创建项目
npm init @yodaos-jsar/widget my-fireworks
# 进入项目目录
cd my-fireworks
# 安装依赖
npm install

执行完成后,你会看到以下项目结构:
perl
my-fireworks/
├── main.xsml # XSML主文件
├── package.json # 项目配置
├── lib/
│ └── main.ts # TypeScript代码
├── model/ # 3D模型文件夹
└── icon.png # 应用图标

核心概念:Babylon.js 粒子系统
在开始编码之前,我们需要理解粒子系统的基本原理。
什么是粒子系统?
粒子系统通过创建大量微小的粒子来模拟复杂的自然现象,如:
- 🎆 烟花、爆炸
- 🔥 火焰、烟雾
- 💧 水流、喷泉
- ❄️ 雪花、雨滴
Babylon.js ParticleSystem 关键参数
typescript
const ps = new BABYLON.ParticleSystem('particles', 2000, scene);
// 1. 粒子外观
ps.particleTexture = texture; // 粒子贴图
ps.color1 = new BABYLON.Color4(...); // 起始颜色
ps.color2 = new BABYLON.Color4(...); // 中间颜色
ps.colorDead = new BABYLON.Color4(...); // 消亡颜色
// 2. 粒子行为
ps.minSize = 0.1; // 最小尺寸
ps.maxSize = 0.5; // 最大尺寸
ps.minLifeTime = 0.5; // 最短生命周期
ps.maxLifeTime = 2.0; // 最长生命周期
// 3. 发射器
ps.emitter = position; // 发射位置
ps.createSphereEmitter(1); // 球形发射器(全方向)
ps.emitRate = 1000; // 每秒发射粒子数
// 4. 物理效果
ps.gravity = new BABYLON.Vector3(0, -9.8, 0); // 重力
ps.minEmitPower = 5; // 最小发射速度
ps.maxEmitPower = 10; // 最大发射速度
// 5. 渲染模式
ps.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD; // 叠加混合(发光效果)
循序渐进:4 步打造烟花系统
步骤 1:基础粒子系统
目标:创建一个简单的粒子喷泉,理解粒子系统的基本使用。
创建文件 step1-basic-particle.xsml
:
xml
<xsml version="1.0">
<head>
<title>步骤1:基础粒子系统</title>
<script>
const scene = spatialDocument.scene;
// 设置相机位置
if (scene.activeCamera) {
scene.activeCamera.position = new BABYLON.Vector3(0, 3, -10);
scene.activeCamera.setTarget(BABYLON.Vector3.Zero());
}
// 添加光源
const light = new BABYLON.HemisphericLight('light',
new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.7;
// 创建粒子系统
const particleSystem = new BABYLON.ParticleSystem('particles', 2000, scene);
// 粒子纹理(使用在线资源)
particleSystem.particleTexture = new BABYLON.Texture(
'https://playground.babylonjs.com/textures/flare.png',
scene
);
// 发射器位置(原点)
particleSystem.emitter = new BABYLON.Vector3(0, 0, 0);
// 粒子颜色(红色到橙色渐变)
particleSystem.color1 = new BABYLON.Color4(1, 0, 0, 1);
particleSystem.color2 = new BABYLON.Color4(1, 0.5, 0, 1);
particleSystem.colorDead = new BABYLON.Color4(0.5, 0, 0, 0);
// 粒子大小
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.3;
// 粒子生命周期(秒)
particleSystem.minLifeTime = 1;
particleSystem.maxLifeTime = 2;
// 发射速率(每秒粒子数)
particleSystem.emitRate = 500;
// 发射方向(向上的锥形)
particleSystem.createConeEmitter(1, Math.PI / 4);
// 发射速度
particleSystem.minEmitPower = 2;
particleSystem.maxEmitPower = 4;
// 重力(向下)
particleSystem.gravity = new BABYLON.Vector3(0, -5, 0);
// 混合模式(叠加,产生发光效果)
particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD;
// 启动粒子系统
particleSystem.start();
console.log('✅ 基础粒子系统创建完成');
</script>
</head>
<space></space>
</xsml>
运行效果:粒子从中心点向上喷射,受重力影响逐渐下落,形成喷泉效果。

关键知识点:
createConeEmitter()
创建锥形发射器,粒子向上喷射- 重力让粒子向下加速,模拟真实物理
BLENDMODE_ADD
让粒子叠加后产生更亮的发光效果
步骤 2:球形爆炸效果
目标:将粒子发射方向改为 360 度全方向,模拟烟花爆炸。
创建文件 step2-sphere-explosion.xsml
:
xml
<xsml version="1.0">
<head>
<title>步骤2:球形爆炸效果</title>
<script>
const scene = spatialDocument.scene;
// 设置相机
if (scene.activeCamera) {
scene.activeCamera.position = new BABYLON.Vector3(0, 3, -10);
scene.activeCamera.setTarget(new BABYLON.Vector3(0, 3, 0));
}
// 光源
const light = new BABYLON.HemisphericLight('light',
new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.6;
// 创建粒子系统
const ps = new BABYLON.ParticleSystem('explosion', 2000, scene);
ps.particleTexture = new BABYLON.Texture(
'https://playground.babylonjs.com/textures/flare.png',
scene
);
// 发射器位置(空中)
ps.emitter = new BABYLON.Vector3(0, 5, 0);
// 蓝色烟花
ps.color1 = new BABYLON.Color4(0, 0.5, 1, 1);
ps.color2 = new BABYLON.Color4(0, 1, 1, 0.8);
ps.colorDead = new BABYLON.Color4(0, 0.3, 0.5, 0);
ps.minSize = 0.1;
ps.maxSize = 0.4;
ps.minLifeTime = 0.5;
ps.maxLifeTime = 1.5;
ps.emitRate = 1000;
// 关键:球形发射器,粒子向所有方向发射
ps.createSphereEmitter(1);
// 更快的发射速度,形成爆炸效果
ps.minEmitPower = 5;
ps.maxEmitPower = 8;
ps.gravity = new BABYLON.Vector3(0, -9.8, 0);
ps.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD;
ps.updateSpeed = 0.01;
ps.start();
// 创建标记球体,显示爆炸中心
const marker = BABYLON.MeshBuilder.CreateSphere('marker',
{ diameter: 0.2 }, scene);
marker.position = new BABYLON.Vector3(0, 5, 0);
const markerMat = new BABYLON.StandardMaterial('markerMat', scene);
markerMat.emissiveColor = new BABYLON.Color3(1, 1, 0);
marker.material = markerMat;
console.log('✅ 球形爆炸效果创建完成');
</script>
</head>
<space></space>
</xsml>
运行效果:粒子从中心点向四面八方爆炸,形成球形烟花效果。
关键改进:
createSphereEmitter(1)
替代锥形发射器,实现全方向爆炸- 提高发射速度(5-8),让粒子快速扩散
- 降低生命周期(0.5-1.5 秒),烟花快速消散
步骤 3:多彩烟花
目标:创建多种颜色的烟花,依次绽放。
创建文件 step3-multi-colors.xsml
:
javascript
// 核心代码片段
const colors = [
{ name: '红色', color1: new BABYLON.Color4(1, 0, 0, 1),
color2: new BABYLON.Color4(1, 0.5, 0, 0.8) },
{ name: '蓝色', color1: new BABYLON.Color4(0, 0.5, 1, 1),
color2: new BABYLON.Color4(0, 1, 1, 0.8) },
{ name: '绿色', color1: new BABYLON.Color4(0, 1, 0, 1),
color2: new BABYLON.Color4(0.5, 1, 0, 0.8) },
{ name: '紫色', color1: new BABYLON.Color4(1, 0, 1, 1),
color2: new BABYLON.Color4(0.8, 0, 1, 0.8) },
{ name: '黄色', color1: new BABYLON.Color4(1, 1, 0, 1),
color2: new BABYLON.Color4(1, 0.8, 0, 0.8) }
];
function createFirework(position, colorConfig) {
const ps = new BABYLON.ParticleSystem('firework', 1500, scene);
// ... 配置粒子系统
ps.color1 = colorConfig.color1;
ps.color2 = colorConfig.color2;
ps.start();
// 短暂发射后停止
setTimeout(() => ps.stop(), 100);
setTimeout(() => ps.dispose(), 2000);
}
// 依次发射不同颜色的烟花
colors.forEach((colorConfig, index) => {
setTimeout(() => {
const x = (index - 2) * 3;
createFirework(new BABYLON.Vector3(x, 5, 0), colorConfig);
}, index * 800);
});
运行效果:5 种颜色的烟花从左到右依次绽放。
技术要点:
- 使用
setTimeout
控制烟花发射时间 - 调用
ps.stop()
停止发射新粒子(已发射的粒子继续运动) - 及时
dispose()
销毁粒子系统,释放内存
步骤 4:随机发射
目标:在随机位置、随机时间发射随机颜色的烟花。
创建文件 step4-random-launch.xsml
:
javascript
// 随机颜色池(16种颜色)
const colorPalette = [
'#FF1744', '#F50057', '#D500F9', '#651FFF',
'#3D5AFE', '#2979FF', '#00B0FF', '#00E5FF',
'#1DE9B6', '#00E676', '#76FF03', '#FFEA00',
'#FFC400', '#FF9100', '#FF3D00'
];
function launchRandomFirework() {
// 随机位置
const x = (Math.random() - 0.5) * 15;
const y = Math.random() * 5 + 4;
const z = (Math.random() - 0.5) * 15;
const position = new BABYLON.Vector3(x, y, z);
// 随机颜色
const color = colorPalette[Math.floor(Math.random() * colorPalette.length)];
createFirework(position, color);
console.log(`🎆 发射烟花 位置:(${x.toFixed(1)}, ${y.toFixed(1)}, ${z.toFixed(1)})`);
}
// 定时自动发射
setInterval(() => {
launchRandomFirework();
}, 1200);
运行效果:烟花在 3D 空间中随机位置不断绽放。
编程技巧:
- 使用
Math.random()
生成随机坐标 Color3.FromHexString()
方便地使用十六进制颜色setInterval()
实现定时发射
完整版:最终烟花系统
整合所有功能,创建最终版 fireworks.xsml
。
完整代码已包含:
- 烟花类封装
- 自动内存管理
- 丰富的交互控制
- 性能优化
运行项目:
javascript
<xsml version="1.0">
<head>
<title>3D烟花粒子系统</title>
<script>
console.log('🎆 烟花粒子系统启动中...');
// 烟花配置
const FIREWORK_COLORS = [
'#FF1744', '#F50057', '#D500F9', '#651FFF',
'#3D5AFE', '#2979FF', '#00B0FF', '#00E5FF',
'#1DE9B6', '#00E676', '#76FF03', '#FFEA00',
'#FFC400', '#FF9100', '#FF3D00'
];
class Firework {
constructor(scene, position) {
this.scene = scene;
this.position = position;
this.color = FIREWORK_COLORS[Math.floor(Math.random() * FIREWORK_COLORS.length)];
this.particleSystem = null;
this.isAlive = true;
this.createParticleSystem();
}
createParticleSystem() {
// 创建粒子系统
const particleSystem = new BABYLON.ParticleSystem(
'firework',
2000,
this.scene
);
// 粒子纹理(使用内置的flare纹理)
particleSystem.particleTexture = new BABYLON.Texture(
'https://playground.babylonjs.com/textures/flare.png',
this.scene
);
// 发射器位置
particleSystem.emitter = this.position;
// 颜色渐变
const color = BABYLON.Color3.FromHexString(this.color);
particleSystem.color1 = new BABYLON.Color4(color.r, color.g, color.b, 1);
particleSystem.color2 = new BABYLON.Color4(color.r * 0.8, color.g * 0.8, color.b * 0.8, 0.8);
particleSystem.colorDead = new BABYLON.Color4(color.r * 0.5, color.g * 0.5, color.b * 0.5, 0);
// 粒子大小
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.5;
// 粒子生命周期
particleSystem.minLifeTime = 0.5;
particleSystem.maxLifeTime = 2.0;
// 发射速率
particleSystem.emitRate = 1000;
// 混合模式(发光效果)
particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD;
// 重力
particleSystem.gravity = new BABYLON.Vector3(0, -9.8, 0);
// 发射方向(球形爆炸)
particleSystem.createSphereEmitter(1);
// 发射速度
particleSystem.minEmitPower = 5;
particleSystem.maxEmitPower = 10;
particleSystem.updateSpeed = 0.01;
// 启动粒子系统
particleSystem.start();
this.particleSystem = particleSystem;
// 0.5秒后停止发射,粒子会继续运动直到生命周期结束
setTimeout(() => {
this.particleSystem.stop();
}, 100);
// 3秒后销毁
setTimeout(() => {
this.dispose();
}, 3000);
}
dispose() {
if (this.particleSystem) {
this.particleSystem.dispose();
this.particleSystem = null;
}
this.isAlive = false;
}
}
try {
const scene = spatialDocument.scene;
console.log('✅ Scene获取成功');
// 设置相机
if (scene.activeCamera) {
scene.activeCamera.position = new BABYLON.Vector3(0, 5, -20);
scene.activeCamera.setTarget(new BABYLON.Vector3(0, 5, 0));
console.log('📷 相机设置完成');
}
// 添加环境光
scene.lights.forEach(light => light.dispose());
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.5;
// 创建地面参考
const ground = BABYLON.MeshBuilder.CreateGround('ground', {
width: 50,
height: 50
}, scene);
const groundMaterial = new BABYLON.StandardMaterial('groundMat', scene);
groundMaterial.diffuseColor = new BABYLON.Color3(0.1, 0.1, 0.15);
groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
ground.material = groundMaterial;
// 烟花管理
const fireworks = [];
// 自动发射烟花
let autoLaunch = true;
setInterval(() => {
if (autoLaunch) {
launchFirework();
}
}, 1500);
// 发射烟花函数
function launchFirework() {
const x = (Math.random() - 0.5) * 20;
const y = Math.random() * 5 + 5;
const z = (Math.random() - 0.5) * 20;
const position = new BABYLON.Vector3(x, y, z);
const firework = new Firework(scene, position);
fireworks.push(firework);
// 清理已销毁的烟花
for (let i = fireworks.length - 1; i >= 0; i--) {
if (!fireworks[i].isAlive) {
fireworks.splice(i, 1);
}
}
console.log(`🎆 发射烟花!位置: (${x.toFixed(1)}, ${y.toFixed(1)}, ${z.toFixed(1)})`);
}
// 全局控制
window.launchFirework = launchFirework;
window.toggleAutoLaunch = () => {
autoLaunch = !autoLaunch;
console.log(autoLaunch ? '🔄 自动发射已开启' : '⏸️ 自动发射已暂停');
};
window.launchMultiple = (count = 5) => {
for (let i = 0; i < count; i++) {
setTimeout(() => {
launchFirework();
}, i * 200);
}
console.log(`🎇 发射 ${count} 个烟花!`);
};
// 初始发射
setTimeout(() => {
launchMultiple(3);
}, 500);
console.log('✨ 烟花系统初始化完成!');
console.log('🎮 控制提示:');
console.log(' - 在控制台输入 launchFirework() 发射单个烟花');
console.log(' - 在控制台输入 launchMultiple(10) 发射多个烟花');
console.log(' - 在控制台输入 toggleAutoLaunch() 切换自动发射');
} catch (error) {
console.error('💥 烟花系统创建失败:', error);
}
</script>
</head>
<space>
<!-- 烟花在夜空中绽放 -->
</space>
</xsml>

Rokid AR 眼镜上的表现
这个烟花系统在 Rokid AR 眼镜上运行效果令人惊艳。当你戴上眼镜,烟花仿佛在你眼前的真实空间中绽放,你可以:
- 🚶 走近观察:靠近查看每个粒子的运动轨迹
- 👀 360 度观看:转动头部从不同角度欣赏烟花
- 🎯 空间感知:烟花距离你的远近非常真实
- 🎨 沉浸体验:光影效果在真实环境中叠加
Rokid 的 JSAR 运行时在 AR 眼镜上提供了流畅的 60fps 体验,粒子系统的性能表现优异。这得益于 Babylon.js 的高效渲染引擎和 Rokid 硬件的强大性能。
开发提示:在 Rokid 设备上调试时,建议使用 JSAR DevTools 的远程调试功能,实时查看日志和性能指标。
性能优化建议
1. 粒子数量控制
javascript
// 不好的做法:粒子过多
const ps = new BABYLON.ParticleSystem('particles', 10000, scene);
// 推荐做法:适中的粒子数
const ps = new BABYLON.ParticleSystem('particles', 1500, scene);
经验值:单个烟花 1000-2000 粒子,同时存在不超过 10 个烟花。
2. 及时销毁
javascript
function createFirework(position, color) {
const ps = new BABYLON.ParticleSystem('firework', 1500, scene);
// ... 配置
ps.start();
// 0.1秒后停止发射
setTimeout(() => ps.stop(), 100);
// 3秒后完全销毁(确保所有粒子消亡)
setTimeout(() => ps.dispose(), 3000);
}
3. 纹理复用
javascript
// 全局纹理,避免重复加载
let particleTexture = null;
function getParticleTexture(scene) {
if (!particleTexture) {
particleTexture = new BABYLON.Texture(
'https://playground.babylonjs.com/textures/flare.png',
scene
);
}
return particleTexture;
}
4. 使用对象池
javascript
class FireworkPool {
constructor(scene, poolSize = 20) {
this.scene = scene;
this.pool = [];
this.active = [];
for (let i = 0; i < poolSize; i++) {
const ps = this.createParticleSystem();
this.pool.push(ps);
}
}
get() {
let ps = this.pool.pop();
if (!ps) {
ps = this.createParticleSystem();
}
this.active.push(ps);
return ps;
}
release(ps) {
ps.stop();
ps.reset();
const index = this.active.indexOf(ps);
if (index > -1) {
this.active.splice(index, 1);
this.pool.push(ps);
}
}
}
Rokid 与空间计算的未来
Rokid 通过开源 JSAR,为 Web 开发者打开了空间计算的大门。我们不再需要学习复杂的 AR SDK 或 3D 引擎,只需要用熟悉的 JavaScript 就能创建令人惊艳的 AR 应用。
这个烟花项目只是一个开始。你可以基于此创建:
- 教育应用:化学分子结构、天文星系可视化
- 数据可视化:3D 图表、实时数据展示
- 互动游戏:空间射击、解谜游戏
- 艺术创作:生成艺术、交互装置
空间计算的时代已经到来,而 Rokid 正在用 JSAR 让这个未来变得触手可及。
参考资料
- JSAR 官方文档 :[https://jsar.netlify.app/](jsar.netlify.app/)
- Babylon.js 粒子系统文档 :[https://doc.babylonjs.com/features/featuresDeepDive/particles](doc.babylonjs.com/features/fe...)
- Rokid 开发者平台 :[https://developer.rokid.com/](developer.rokid.com/)
- Babylon.js Playground :[https://playground.babylonjs.com/](playground.babylonjs.com/)