实现一个烟花效果

news/2024/10/9 18:55:20/文章来源:https://www.cnblogs.com/codeOnMar/p/18406444

 1. 首先创建一个烟花类,有烟花上升的效果,烟花爆炸的两种效果(爆炸型和球型)

 2. 创建线的属性方法,因为上升效果和爆炸效果都会用到

 3. 上升效果为了达到那种螺旋上升的效果可以通过sin函数实现一个点的偏移量

 4. 爆炸效果则是将随机生成多条半径不同的线

 5. 球形效果则是将规则的点和不规则的点结合起来实现的

 6. 每个烟花爆炸消失后重新生成下一个烟花

 7. 为了色彩值更弄,通过 EffectComposer\UnrealBloomPass添加辉光效果

/*** 添加烟花*/
function addFireworks() {// 烟花类
  class Fireworks {constructor(props = {}) {// 烟花高度this.height = props.height || Math.ceil(Math.random() * 8 + 5);// 烟花颜色this.color = props.color || this.getRandomColor();// 烟花的起始位置this.startX = props.position && props.position[0] || Math.random() * 20 - 10;this.startY = -5;this.path = this.createPath();// 烟花绽放结束函数this.fireEnd = null;this.fire();}/*** 生成随机颜色值* @returns THREE.Color*/getRandomColor() {const [minHue, maxHue, minSaturation, maxSaturation, minLightness, maxLightness] = [0, 1, 0, 1, 0, 1];// 生成随机色调const hue = Math.random() * (maxHue - minHue) + minHue;// 生成随机饱和度const saturation = Math.random() * (maxSaturation - minSaturation) + minSaturation;// 生成随机亮度const lightness = Math.random() * (maxLightness - minLightness) + minLightness;// 使用 HSL 颜色空间创建颜色const color = new THREE.Color();color.setRGB(hue, saturation, lightness);return color;}/*** 生成烟花上升的轨迹,为了模拟烟花上升左右摆动效果,增加了sin函数插入部分偏移点*/createPath() {const widthBeta = 0.01; // 烟花轨迹摆动的范围const paths = []; // 轨迹点集const nums = 60; // 轨迹点个数const heigthtBeta = this.height / nums; // 烟花上升的幅度for (let i = 0; i < nums; i++) {paths.push(new THREE.Vector3(this.startX + Math.sin(heigthtBeta * i * Math.PI * 14) * widthBeta, this.startY + heigthtBeta * i, 0));}return paths;}/*** 爆炸效果*/explode() {const random = Math.floor(Math.random() * 2);if (random === 0) {this.radialEffect();} else {this.sphereEffect();}}/*** 球形效果,为了模拟实际的点燃效果,首先生成一些规则的球型点,然后在球面上插入部分随机点*/sphereEffect() {const vertex = `attribute float aScale;attribute vec3 aRandom;uniform float uTime; uniform float uSize;void main() {vec4 modelPosition = modelMatrix * vec4(position, 1.0);modelPosition.xyz += 5.0*uTime;gl_Position = projectionMatrix * (viewMatrix * modelPosition);gl_PointSize = 3.0;}`;const frag = `uniform vec3 uColor;uniform float uOpacity; void main() {float distanceTo = distance(gl_PointCoord , vec2(0.5));// 必须有这段代码,不然辉光效果不会生效if (distanceTo > 0.5) {discard;}float str = distanceTo * 2.0;str = 1.0 - str;str = pow(str,1.5);gl_FragColor = vec4(uColor,str * uOpacity); }`;const positions = [];const [centerX, centerY, centerZ] = [this.startX, this.startY + this.height, 0];const radius = 2;// 生成规则的点const widthSegments = 24;const heightSegments = 24;for (let i = 0; i < widthSegments; i++) {for (let j = 0; j < heightSegments; j++) {const y = radius * Math.sin(j / heightSegments * Math.PI * 2);const innerRadius = radius * Math.cos(j / heightSegments * Math.PI * 2);const x = innerRadius * Math.cos(i / widthSegments * Math.PI * 2);const z = innerRadius * Math.sin(i / widthSegments * Math.PI * 2);positions.push(centerX + x, centerY + y, centerZ + z);}}// 生成随机的点const num = 360;for (let i = 0; i < num; i++) {const randomHAngle = Math.random() * Math.PI * 2 - Math.PI;const randomWAngle = Math.random() * Math.PI * 1;const y = radius * Math.sin(randomHAngle);const innerRadius = radius * Math.cos(randomHAngle);const x = innerRadius * Math.cos(randomWAngle);const z = innerRadius * Math.sin(randomWAngle);positions.push(centerX + x, centerY + y, centerZ + z);}const geometry = new THREE.BufferGeometry();geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));const material = new THREE.ShaderMaterial({vertexShader: vertex,fragmentShader: frag,side: THREE.DoubleSide,uniforms: {uColor: { value: this.color },uTime: { value: 0.01 },uSize: { value: 5.0 },uOpacity: { value: 1.0 }},transparent: true,depthWrite: false // 解决透明的点和点相互压盖时没有透明效果的问题
      });const mesh = new THREE.Points(geometry, material);scene.add(mesh);// 添加定时函数,达到透明度逐渐降低,直至消失的效果let i = 0;const interObj = setInterval(() => {material.uniforms.uOpacity.value -= 0.05;if (material.uniforms.uOpacity.value <= 0) {clearInterval(interObj);scene.remove(mesh);if (this.fireEnd) {this.fireEnd();}}i++;}, 100)}/*** 射线效果,随机生成不同的线长实现*/radialEffect() {function getRandom(src) {const radius = 4;return src + Math.random() * radius - radius / 2;}const nums = 180;const [centerX, centerY, centerZ] = [this.startX, this.startY + this.height, 0];const positions = [];for (let i = 0; i < nums; i++) {positions.push([new THREE.Vector3(centerX, centerY, centerZ),new THREE.Vector3(getRandom(centerX), getRandom(centerY), getRandom(centerZ)),]);}positions.forEach((e, i) => {this.createLine(e, 3, 0.55, 1.5, null, 100, i === 0 ? () => {if (this.fireEnd) {this.fireEnd();}} : null);})}/*** 生成一条线,有箭头效果* @param {*} points 线的关键点,在函数内部会根据这些点通过平滑函数插入多个点* @param {*} lineWidth 线宽* @param {*} lineLength 线长* @param {*} uSpeed 线的移动速度* @param {*} color 线颜色* @param {*} pointNum 插入的点的个数* @param {*} endCallback 线移动结束的回调函数*/createLine(points, lineWidth, lineLength, uSpeed, color, pointNum, endCallback) {const vertex = `attribute float aIndex;uniform float uTime;uniform float uNum; // 线上点个数uniform float uWidth; // 线宽uniform float uLength; // 线宽uniform float uSpeed; // 飞线速度varying float vIndex; // 内部点下标void main() {vec4 viewPosition = viewMatrix * modelMatrix * vec4(position, 1.0);gl_Position = projectionMatrix * viewPosition;vIndex = aIndex;// 通过时间点的增加作为点移动的下标,从末端的第一个点开始,已num点为一个轮回,往复执行运动float num = uNum - mod(floor(uTime * uSpeed), uNum);// 只绘制部分点,多余的不绘制if (aIndex + num >= uNum) {float size = (mod(aIndex + num, uNum) * (1.0 / uLength)) / uNum * uWidth;gl_PointSize = size;}}`;const frag = `varying float vIndex;uniform float uTime;uniform float uLength; // 线宽uniform float uNum;uniform float uSpeed;uniform vec3 uSColor;uniform vec3 uEColor;void main() {// 默认绘制的点是方形的,通过舍弃可以绘制成圆形float distance = length(gl_PointCoord - vec2(0.5, 0.5));if (distance > 0.5) {// discard;float glow = 1.0 - smoothstep(0.2, 0.5, distance);gl_FragColor = vec4(1.0, 1.0, 1.0, glow);} else {float num = uNum - mod(floor(uTime * uSpeed), uNum);// 根据点的下标计算渐变色值vec3 color = mix(uSColor, uEColor, (num + vIndex - uNum) / uNum);// 越靠近末端透明度越大float opacity = ((num + vIndex - uNum)) / uNum;// 根据长度计算显示点的个数,多余的透明显示if (vIndex + num >= uNum && vIndex + num <= uNum * (1.0 + uLength)) {gl_FragColor = vec4(color, opacity);} else {gl_FragColor = vec4(color, 0);}}}`;const nums = pointNum || 500;const curve = new THREE.CatmullRomCurve3(points);const curveArr = curve.getPoints(nums);const flatArr = curveArr.map(e => e.toArray());const lastArr = flatArr.flat();const indexArr = [...Array(nums + 1).keys()];const geometry = new THREE.BufferGeometry();geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(lastArr), 3));geometry.setAttribute('aIndex', new THREE.BufferAttribute(new Float32Array(indexArr), 1));// 创建一根曲线const uniform = {uTime: { value: 0 },uSColor: { value: color || new THREE.Color(this.color) },uEColor: { value: new THREE.Color(0x000000) },uWidth: { value: lineWidth || 6.0 },uNum: { value: nums },uSpeed: { value: uSpeed || 2 },uLength: { value: lineLength || 0.25 }}const material = new THREE.ShaderMaterial({vertexShader: vertex,fragmentShader: frag,side: THREE.DoubleSide,uniforms: uniform,transparent: true,depthWrite: false});const mesh = new THREE.Points(geometry, material);scene.add(mesh);let i = 0;const interObj = setInterval(() => {material.uniforms.uTime.value = i;i++;if (uniform.uSpeed.value * i >= nums) {scene.remove(mesh);clearInterval(interObj);if (endCallback) {endCallback();}}}, 20)}/*** 点燃烟花*/fire() {this.createLine(this.path, 8, 0.45, 2, null, null, () => {this.explode();});}/*** 销毁*/destory() {clearInterval(this.interObj);}}function callback() {const fireObj = new Fireworks();fireObj.fireEnd = callback;}for (let i = 0; i < 5; i++) {setTimeout(() => {const fireObj = new Fireworks();fireObj.fireEnd = callback;}, 500 * i)}
}
烟花类以及添加烟花
/*** 添加相机等基础功能*/
function addEnvir(lightFlag = true, axFlag = true, gridFlag = false) {// 初始化相机camera = new THREE.PerspectiveCamera(100, wWidth / wHeight, 0.01, 3000);camera.position.set(0, 10, 0);camera.lookAt(0, 0, 0);// 创建灯光// 创建环境光const ambientLight = new THREE.AmbientLight(0xf0f0f0, 1.0);ambientLight.position.set(0,0,0);scene.add(ambientLight);if (lightFlag) {// 创建点光源const pointLight = new THREE.PointLight(0xffffff, 1);pointLight.decay = 0.0;pointLight.position.set(200, 200, 50);scene.add(pointLight);}// 添加辅助坐标系if (axFlag) {const axesHelper = new THREE.AxesHelper(150);scene.add(axesHelper);}// 添加网格坐标if (gridFlag) {const gridHelper = new THREE.GridHelper(300, 25, 0x004444, 0x004444);scene.add(gridHelper);}// 创建渲染器renderer = new THREE.WebGLRenderer({ antialias:true, logarithmicDepthBuffer: true });renderer.setClearColor(0x000000, 1);renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(wWidth, wHeight); //设置three.js渲染区域的尺寸(像素px)renderer.outputEncoding = THREE.sRGBEncoding;renderer.render(scene, camera); //执行渲染操作// 创建后处理效果合成器composer = new EffectComposer(renderer);// 添加渲染通道const renderPass = new RenderPass(scene, camera);composer.addPass(renderPass);const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);bloomPass.threshold = 0;bloomPass.strength = 3;bloomPass.radius = 0.5;composer.addPass(bloomPass);controls = new OrbitControls(camera, renderer.domElement);// 设置拖动范围// controls.minPolarAngle = - Math.PI / 2;// controls.maxPolarAngle = Math.PI / 2 - Math.PI / 360;
  controls.addEventListener('change', () => {renderer.render(scene, camera);})gui = new dat.GUI();const clock = new THREE.Clock();function render() {renderer.render(scene, camera);requestAnimationFrame(render);const elapsedTime = clock.getElapsedTime();if (material && material.uniforms && material.uniforms.uTime) {material.uniforms.uTime.value = elapsedTime / 2;// material.uniforms.noiseScale.value = 10.0 + Math.sin(Date.now() * 0.001) * 5.0;
    }if (composer) {composer.render();}}render();document.getElementById('webgl').appendChild(renderer.domElement);
}
添加环境以及辉光效果

 

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

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

相关文章

【Java】反射

Java中的反射机制 动态代理反射 允许对封装类的字段,方法和构造函数的信息进行编程访问 ==》 反射允许对成员变量,成员方法和构造方法的信息进行编程访问 基本操作:获取(获取class对象【字节码对象】) + 解剖 成员变量 Field —— 修饰符、名字、类型、赋值 构造方法 Cons…

DNShell

DNShell 一款基于DNS C2隧道的反弹shell工具。 支持 功能: 支持DNS-recordA-直连型 的C2隧道。 目标: Windows下基于Powershell的反弹。 Linux下基于ShellScript的反弹。 使用方法

【Redis】Redis学习笔记

概况 redis == remote Dictionary Server (远程字典服务) 基于内存的KV键值对内存数据库 作用:分布式缓存,与MySQL共同配合Redis -- 内存 MySQL -- 磁盘Redis -- NoSQL MySQL -- SQL内存存储 和 持久化(RDB+AOF)Redis支持一部将内存中的数据写入硬盘宕机 -- 可自行恢复高…

基于禁忌搜索算法的VRP问题求解matlab仿真,带GUI界面,可设置参数

1.程序功能描述基于禁忌搜索算法的VRP问题求解matlab仿真,带GUI界面,可设置参数。 2.测试软件版本以及运行结果展示MATLAB2022a版本运行 3.核心程序while COUNT<=Itertions ֲ L = zeros(Ant_Num,1); for i=1:Ant_Num Infor_Tabu_tmps = Infor_Tabu(i,:); R = Inf…

CMake 属性之全局属性

CMake 的全局属性是指在 CMake 配置过程中,对整个项目范围生效的设置。这些属性不同于目标 ( Target ) 属性或目录 ( Directory ) 属性,后者仅对特定的目标或目录生效。【写在前面】 CMake 的全局属性是指在 CMake 配置过程中,对整个项目范围生效的设置。 这些属性不同于目标…

自然人信息社工

人,是网络安全全流程中最大的弱点。针对人的攻击往往有出奇不意的效果。而想要利用人的弱点进行攻击,那么对目标的信息收集与了解就是非常重要的了。这篇文章记录了一些常用的用于对人进行身份信息收集的技术。这些技术常被用于溯源取证、社工攻击。 0x00 社工分析中的身份信…

Linux软中断ksoftirqd

前言 在上一篇 LINUX软中断-softirq的描述中,提到过ksoftirqd,这篇文章就介绍ksoftirqd ksoftirqd 是什么? ksoftirqd 是个内核线程,在创建的时候是绑定cpu的,每一个core对应生成一个ksoftirqd 线程 比如当前系统有4个core ~# ps aux | grep ksoftirqd root 3 0.0…

WPF Binding中的RelativeSource属性

一、简介 一个在Binding中比较重要的知识点——RelativeSource. 使用RelativeSource对象指向源对象。用这个可以在当前元素的基础上查找其他对象用于绑定到源对象。在实际使用Binding的过程中大部分时间Binding都放在了数据模板和控件模板中,(数据模板是控件模板用于定义控件…

信息学奥赛复赛复习15-CSP-J2022-01乘方-数据类型、类型转换、数据类型溢出、指数、模拟指数运算

PDF文档公众号回复关键字:202410091 P8813 [CSP-J 2022] 乘方 [题目描述] 小文同学刚刚接触了信息学竞赛,有一天她遇到了这样一个题:给定正整数 a 和 b,求 a^b 的值是多少。 a^b 即 b 个 a 相乘的值,例如 2^3 即为 3 个 2 相乘,结果为 222=8 “简单!”小文心想,同时很快…

Postman 教程:新手必备的操作指南

API 已经成为连接不同系统和服务的重要桥梁,无论你是前端开发者、后端工程师还是测试人员,掌握 API 的开发和测试技能都是非常重要的。Postman 是一个广受欢迎的 API 开发工具,它不仅能够帮助你轻松发送 HTTP 请求,还提供了强大的测试、调试和协作功能。本系列教程旨在帮助…

20222327 2024-2025-1 《网络与系统攻防技术》实验一实验报告

一.实验内容 1.了解Linux系统下的基本操作命令,能够处理一些命令出现的error。 2.掌握了栈与堆的概念以及在进程内存管理中的应用。 3.了解基本的汇编语言指令及其功能。 4.能够深刻理解BoF的原理以及如何运用payload完成BoF的攻击 二.实验过程 任务一 直接修改程序机器指令,…

春秋云镜 Privilege

春秋云镜 Privilege上来先用fscan扫一下.___ _ / _ \ ___ ___ _ __ __ _ ___| | __ / /_\/____/ __|/ __| __/ _` |/ __| |/ / / /_\\_____\__ \ (__| | | (_| | (__| < \____/ |___/\___|_| \__,_|\___|_|\_\ fscan ve…