使用 Three.js 创建烟花粒子特效教程

使用 Three.js 创建烟花粒子特效教程

今天,我们将使用 Three.js 来实现一个简单而美观的烟花粒子效果。烟花会在屏幕随机位置生成,粒子在爆炸后呈现出散射、下降、逐渐消散的动态效果。先来看一下效果。

请添加图片描述

第一步:搭建基础场景

在正式实现烟花效果前,我们需要一个可以显示内容的 Three.js 场景。以下代码实现了最基本的场景、相机和渲染器设置。

import * as THREE from "three";// 场景设置
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000
);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);// 相机位置
camera.position.z = 50;
代码逻辑说明
  1. scene:创建一个场景对象,是 Three.js 中所有 3D 元素的容器。
  2. camera:创建透视相机,用于观察场景,参数:
    • 75:视角(FOV)。
    • window.innerWidth / window.innerHeight:宽高比。
    • 0.1 和 1000:相机的近、远平面。
  3. renderer:渲染器将 3D 场景绘制到浏览器中。
  4. 设置相机位置:通过 camera.position.z 拉远视角,确保看到整个场景。

第二步:定义烟花粒子类

在这一步,我们将设计一个 Firework 类,用于模拟每个烟花的粒子效果。
一个烟花包含以下逻辑:

  1. 初始属性
    • 粒子的位置(positions)、速度(velocities)以及颜色(colors)。
    • 设置粒子生命周期(life),用于控制粒子的消失。
  2. 初始化粒子:在烟花爆炸时,粒子会沿着随机方向散开。
  3. 更新粒子状态:粒子随着时间更新位置、缩小大小,并模拟重力作用。
  4. 销毁粒子:当粒子生命周期耗尽时,清除其资源。
1. 定义类和基础属性
class Firework {constructor(x, y, z) {this.geometry = new THREE.BufferGeometry(); // 几何对象存储粒子数据this.count = 10000; // 粒子数量this.positions = new Float32Array(this.count * 3); // 粒子位置this.velocities = []; // 粒子速度this.colors = new Float32Array(this.count * 3); // 粒子颜色this.sizes = new Float32Array(this.count); // 粒子大小this.life = new Float32Array(this.count); // 粒子生命周期}
}

代码逻辑说明

  1. positions:粒子的 3D 空间坐标(x, y, z)。
  2. velocities:粒子的速度向量,用于控制运动方向和速度。
  3. colors:粒子的颜色,以 RGB 格式表示,每个粒子独立设置。
  4. sizes:粒子的大小,用于动态变化。
  5. life:生命周期,用于控制粒子消散。
2. 初始化粒子数据

我们为每个粒子分配一个初始位置和随机运动方向。

for (let i = 0; i < this.count; i++) {const phi = Math.random() * Math.PI * 2; // 水平方向角度const theta = Math.random() * Math.PI;  // 垂直方向角度const velocity = 2 + Math.random() * 2; // 随机速度// 计算速度向量this.velocities.push(velocity * Math.sin(theta) * Math.cos(phi),velocity * Math.sin(theta) * Math.sin(phi),velocity * Math.cos(theta));// 设置初始位置this.positions[i * 3] = x;this.positions[i * 3 + 1] = y;this.positions[i * 3 + 2] = z;// 设置颜色为红色调this.colors[i * 3] = 1.0; // 红色this.colors[i * 3 + 1] = Math.random() * 0.2; // 随机绿色偏移this.colors[i * 3 + 2] = Math.random() * 0.2; // 随机蓝色偏移// 初始大小和生命周期this.sizes[i] = 0.3;this.life[i] = 1.0;
}

代码逻辑说明

  1. 随机方向:通过球面坐标计算粒子散射方向。
  2. 颜色随机性:让每个粒子的颜色略有不同,使整体效果更自然。
  3. 生命周期与大小:初始化每个粒子的生命周期和大小,稍后会动态更新。
3. 创建材质与几何

将粒子属性绑定到 Three.js 的 BufferGeometry 对象上,并为其定义材质。

this.geometry.setAttribute("position",new THREE.BufferAttribute(this.positions, 3)
);
this.geometry.setAttribute("color",new THREE.BufferAttribute(this.colors, 3)
);
this.geometry.setAttribute("size",new THREE.BufferAttribute(this.sizes, 1)
);const material = new THREE.PointsMaterial({size: 0.3,vertexColors: true,blending: THREE.AdditiveBlending,transparent: true,opacity: 0.8,
});this.points = new THREE.Points(this.geometry, material);
scene.add(this.points);

代码逻辑说明

  1. 几何属性:通过 setAttribute 绑定粒子的位置、颜色和大小。
  2. 粒子材质
    • vertexColors: true:允许粒子使用自定义颜色。
    • blending: THREE.AdditiveBlending:粒子叠加效果。
    • transparent: true:支持透明度设置。
  3. 添加到场景:通过 scene.add 将粒子效果添加到 Three.js 场景中。

第三步:更新粒子状态

在这一步,我们将为粒子添加运动、重力效果,并实现逐渐消失的逻辑。粒子在其生命周期内会不断更新位置、大小和透明度,直至完全消散。

1. 更新粒子的逻辑

我们在 Firework 类中定义一个 update 方法,用于逐帧更新粒子状态。粒子的行为包括:

  • 位置更新:根据速度调整粒子位置。
  • 重力效果:粒子会受到向下的重力作用。
  • 生命周期减少:粒子逐渐消散。
  • 尺寸变化:粒子大小随着生命周期减小。
update() {let alive = false; // 标记烟花是否仍然活跃for (let i = 0; i < this.count; i++) {if (this.life[i] > 0) {alive = true;// 更新位置this.positions[i * 3] += this.velocities[i * 3] * 0.1;this.positions[i * 3 + 1] += this.velocities[i * 3 + 1] * 0.1;this.positions[i * 3 + 2] += this.velocities[i * 3 + 2] * 0.1;// 添加重力效果this.velocities[i * 3 + 1] -= 0.05;// 减少生命周期this.life[i] -= 0.015;// 缩小粒子尺寸this.sizes[i] = this.life[i] * 0.3;}}// 更新几何数据this.geometry.attributes.position.needsUpdate = true;this.geometry.attributes.size.needsUpdate = true;return alive;
}

代码逻辑说明

  1. 位置更新:粒子会以其速度向指定方向移动,使用 positions[i * 3 + j] 更新每个轴的坐标。
  2. 重力效果:通过 velocities[i * 3 + 1] 对 y 轴速度施加一个固定的负值,模拟重力。
  3. 生命周期和大小
    • 每帧减少 life[i],模拟粒子的逐渐消失。
    • 粒子尺寸 sizes[i] 由生命周期决定,越接近结束越小。
  4. 几何更新:通过设置 needsUpdatetrue 通知 Three.js 更新粒子属性。

2. 清除已消散的粒子

当粒子完全消失后,我们需要将它从场景中移除,并释放相关的资源。
Firework 类中,定义一个 dispose 方法:

dispose() {scene.remove(this.points); // 从场景移除this.geometry.dispose();   // 释放几何资源this.points.material.dispose(); // 释放材质资源
}

第四步:管理烟花的生成与销毁

现在,我们有了基础场景和粒子类,接下来需要一个管理系统来:

  1. 随机生成烟花:在随机位置创建新的 Firework 实例。
  2. 逐帧更新所有烟花:调用 update 方法并移除已消散的烟花。
  3. 监听窗口变化:确保画布始终适配窗口大小。
1. 存储活跃烟花

创建一个数组 fireworks,用于存储当前所有的活跃烟花:

const fireworks = [];// 随机生成烟花
function createRandomFirework() {const x = (Math.random() * 2 - 1) * 30; // 随机 x 坐标const y = (Math.random() * 2 - 1) * 25; // 随机 y 坐标fireworks.push(new Firework(x, y, 0));  // 将新烟花添加到数组
}

2. 动画循环

animate 函数中,每帧执行以下操作:

  1. 随机生成新的烟花。
  2. 更新现有烟花的状态。
  3. 移除消散的烟花。
  4. 渲染场景。
function animate() {requestAnimationFrame(animate);// 随机生成烟花if (Math.random() < 0.05) {createRandomFirework();}// 更新所有烟花for (let i = fireworks.length - 1; i >= 0; i--) {const alive = fireworks[i].update();if (!alive) {fireworks[i].dispose();fireworks.splice(i, 1); // 从数组移除已消散的烟花}}// 渲染场景renderer.render(scene, camera);
}

3. 窗口大小调整

为了适配不同设备,我们需要监听窗口大小变化并调整相机和渲染器:

function onWindowResize() {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);
}window.addEventListener("resize", onWindowResize, false);

第五步:启动动画

最后,启动动画循环:

animate();

此时,项目已经完成!运行代码后,你将看到屏幕上随机生成烟花,每个烟花都会散射出无数的粒子,粒子在运动过程中逐渐消失。

到此,我们就完成了烟花

代码

github

https://github.com/calmound/threejs-demo/tree/main/yanhua

gitee

https://gitee.com/calmound/threejs-demo/tree/main/yanhua

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/65249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神经网络-VggNet

2014年VggNet被推出&#xff0c;获取了ILSVRC2014比赛分类项目的第二名&#xff0c;第一名是GoogleNet&#xff0c;该网络在下节介绍&#xff0c;本节主要介绍VggNet。 VggNet可以称为是一个家族&#xff0c;根据层数的不同包括了A、A-LRN、B、C、D等网络结构&#xff0c;其中…

docker-compose搭建sfpt服务器

1. 搭建 创建sftp目录&#xff0c;进入该目录创建docker-compose.yml文件内容如下&#xff1a; version: 3.7services:sftp:image: atmoz/sftpcontainer_name: sftpports:- "122:22"volumes:- ./sftp-data:/homeenvironment:SFTP_USERS: "liubei:liubei161:10…

计算机视觉目标检测-1

文章目录 摘要Abstract1.目标检测任务描述1.1 目标检测分类算法1.2 目标定位的简单实现思路1.2.1 回归位置 2.R-CNN2.1 目标检测-Overfeat模型2.1.1 滑动窗口 2.2 目标检测-RCNN模型2.2.1 非极大抑制&#xff08;NMS&#xff09; 2.3 目标检测评价指标 3.SPPNet3.1 spatial pyr…

减速机润滑油的选用原则

减速机在投入运行前必须加入适当粘度的润滑油&#xff0c;须使齿轮间摩擦减小&#xff0c;遇高负荷及冲击负荷时&#xff0c;减速机才能充分发挥其机能。那么&#xff0c;应该如何选择减速机的润滑油呢&#xff1f; 1、粘度选择&#xff1a;粘度是齿轮油的一个重要理化指标&…

解线性方程组

直接三角分解&#xff08;LU分解&#xff0c;Doolittle分解&#xff09; ATM分解&#xff08;追赶法&#xff0c;Crout分解&#xff0c;克劳特分解&#xff09; 平方根法&#xff08;Cholesky分解&#xff0c;乔列斯基分解&#xff09; 矩阵的范数

使用 OpenCV 在图像中添加文字

在图像处理任务中&#xff0c;我们经常需要将文本添加到图像中。OpenCV 提供了 cv2.putText() 函数&#xff0c;可以很方便地在图像上绘制文本&#xff0c;支持多种字体、颜色、大小和位置等参数。 本文将详细介绍如何使用 OpenCV 在图像中添加文字&#xff0c;介绍 cv2.putTe…

HAL库STM32硬件IIC驱动数字电位器MCP4017

目录 一、芯片特性 二、硬件电路 三、工程搭建 四、IIC硬件地址 五、驱动程序 项目需要&#xff0c;最近用到了一个IIC接口的数字电位器&#xff0c;型号&#xff1a;MCP4017T-502E。对应阻值5K&#xff0c;使用STM32G030F6的硬件IIC驱动&#xff0c;发现简单的不得了&…

uni-app 中使用微信小程序第三方 SDK 及资源汇总

&#x1f380;&#x1f380;&#x1f380;uni-app 跨端开发系列 &#x1f380;&#x1f380;&#x1f380; 一、uni-app 组成和跨端原理 二、uni-app 各端差异注意事项 三、uni-app 离线本地存储方案 四、uni-app UI库、框架、组件选型指南 五、uni-app 蓝牙开发 六、uni-app …

17.2、应急事件场景与处理流程

目录 常见网络安全应急事件场景网络安全应急处理流程应急演练类型 常见网络安全应急事件场景 应急事件的处理场景&#xff0c;分成四类场景&#xff0c;恶意程序事件&#xff0c;网络攻击事件&#xff0c;还有网站相关的一些安全事件&#xff0c;最后是拒绝服务事件 恶意程序…

19_HTML5 Web Workers --[HTML5 API 学习之旅]

HTML5 Web Workers 是一种允许 JavaScript 在后台线程中运行的技术&#xff0c;从而不会阻塞用户界面或其他脚本的执行。通过使用 Web Workers&#xff0c;你可以执行复杂的计算任务而不影响页面的响应速度&#xff0c;提升用户体验。 Web Workers 的特点 Web Workers 是 HTM…

《解锁 Python 数据挖掘的奥秘》

《解锁 Python 数据挖掘的奥秘》 一、Python 数据挖掘基础&#xff08;一&#xff09;Python 基础与数据挖掘环境搭建&#xff08;二&#xff09;数据挖掘基本流程概述 二、Python 数据挖掘核心技术&#xff08;一&#xff09;数据收集与预处理技术&#xff08;二&#xff09;常…

爆改RagFlow

Rag理论概述 由近期 RAGFlow 的火爆看 RAG 的现状与未来 Ragflow解析参数说明 ♥ RagFlow源码解析 实际的文件解析通过接口 /v1/document/run 进行触发的&#xff0c;实际的处理是在 api/db/services/task_service.py 中的 queue_tasks() 中完成的&#xff0c;此方法会根据文件…

【GeekBand】C++设计模式笔记15_Proxy_代理模式

1. “接口隔离” 模式 在组件构建过程中&#xff0c;某些接口之间直接的依赖常常会带来很多问题&#xff0c;甚至根本无法实现。采用添加一层间接&#xff08;稳定&#xff09;接口&#xff0c;来隔离本来互相紧密关联的接口是一种常见的解决方案。典型模式 FacadeProxyAdapte…

springboot测试类里注入不成功且运行报错

目录 出错信息 原因 出错信息 写测试类的时候&#xff0c;一直说我注入不成功 而且我运行的时候报错了 java.lang.IllegalStateException: Unable to find a SpringBootConfiguration, you need to use ContextConfiguration or SpringBootTest(classes...) with your te…

Docker下TestHubo安装配置指南

TestHubo是一款开源免费的测试管理工具&#xff0c; 下面介绍Docker 私有部署的安装与配置。TestHubo 私有部署版本更适合有严格数据安全要求的企业&#xff0c;支持在本地或专属服务器上运行&#xff0c;以实现对数据和系统的完全控制。 1、Docker 服务端安装 Docker安装包下…

Redis实战篇(四、高级数据结构的使用)

目录 五、达人探店 1.发布探店笔记 2.查看探店笔记 3.点赞功能 4.点赞排行榜 六、好友关注 1.关注和取消关注 2.共同关注 3.关注推送 &#xff08;1&#xff09;Feed流实现方案分析 &#xff08;2&#xff09;推送到粉丝收件箱 &#xff08;3&#xff09;实现分页查询…

基本操作:iframe、alert

背景 如果你的目标元素出现在一个iframe标签下&#xff0c;则不能直接定位&#xff0c;必须先完成切换才能进行定位操作&#xff0c;如下图 整个理解为一个大的房间&#xff0c;里面是客厅&#xff0c;driver进到客厅后&#xff0c;如果想操作iframe A里的数据&#xff0c;需…

【C++11】类型分类、引用折叠、完美转发

目录 一、类型分类 二、引用折叠 三、完美转发 一、类型分类 C11以后&#xff0c;进一步对类型进行了划分&#xff0c;右值被划分纯右值(pure value&#xff0c;简称prvalue)和将亡值 (expiring value&#xff0c;简称xvalue)。 纯右值是指那些字面值常量或求值结果相当于…

IntelliJ Idea常用快捷键详解

文章目录 IntelliJ Idea常用快捷键详解一、引言二、文本编辑与导航1、文本编辑2、代码折叠与展开 三、运行和调试四、代码编辑1、代码补全 五、重构与优化1、重构 六、使用示例代码注释示例代码补全示例 七、总结 IntelliJ Idea常用快捷键详解 一、引言 在Java开发中&#xff…

kafka的备份策略:从备份到恢复

文章目录 一、全量备份二、增量备份三、全量恢复四、增量恢复 前言&#xff1a;Kafka的备份的单元是partition&#xff0c;也就是每个partition都都会有leader partiton和follow partiton。其中leader partition是用来进行和producer进行写交互&#xff0c;follow从leader副本进…