Threejs实现闪电效果

news2025/2/24 14:42:16

这是一次比较失败的功能实现,本来想网上很少有threejs实现闪电效果的,但是我觉得好像可以做出来,就尝试着做了,结果做出来的太丑了,但是不能时间白费,所以记录下,总得有个交代。

        首先还是搭建出基础的场景

initScene(){
      this.scene = new THREE.Scene();
      const axesHelper = new THREE.AxesHelper( 10);
      axesHelper.position.set(0,0,0)
      this.scene.add( axesHelper );
    },
    initCamera(){
      this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
      this.camera.position.set(1000,1000,1000);
      this.camera.lookAt(500,100,500)
    },
initRenderer(){
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.container = document.getElementById("container")
      this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
      this.renderer.setClearColor('#000000', 1.0);
      this.container.appendChild(this.renderer.domElement);
    },
    initControl(){
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.maxPolarAngle = Math.PI / 2.2;      // // 最大角度
      this.camera.position.set(1000,1000,1000);
      this.camera.lookAt(500,100,500)
    },
    initAnimate() {
    requestAnimationFrame(this.initAnimate);
      this.renderer.render(this.scene, this.camera);
    },

有了基础的场景之后,开始绘制闪电,首先闪电是歪歪扭扭的不是笔直的,所以想着要用到随机数,使用粒子效果做出一个个点,并且是歪歪扭扭往一个方向延伸的,

initLightNing() {
      // Geometry
      const particlesGeometry = new THREE.BufferGeometry()
      const count = 5000
      const positions = new Float32Array(count * 3)
      let beginX = 0;let beginY = 0;let beginZ = 0;
      for(let i = 0; i < count * 3; i = i+3 ){
        positions[i] = beginX + (Math.random())
        positions[i+1] = beginY + (Math.random()*0.8)
        positions[i+2] = beginZ + (Math.random())
        beginX = positions[i];
        beginY = positions[i+1];
        beginZ = positions[i+2];
      }
      particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
      // Material
      const particlesMaterial = new THREE.PointsMaterial({
        size: 4,
        sizeAttenuation: true
      })
      particlesMaterial.color = new THREE.Color(0xffffff);
      // Points
      const particles = new THREE.Points(particlesGeometry, particlesMaterial)
      particles.name = 'lightning'
      this.scene.add(particles)
    },

然后就有了这样的效果,一切往我想的方向进行,得到了一条歪歪扭扭的曲线

这样的曲线当做闪电的主分支,然后加点叉就好了,叉肯定也是需要歪歪扭扭的,且要和主线链接,所以应该是随机找主线的某个点进行开叉,并往下走一点歪歪扭扭的曲线,且曲线的长度不一致,然后我就做了一个可以生成小分叉的曲线,小分叉的开始点由主分叉传过来,因为要连接,且个数不一致也由主分支决定长度。为了效果更好点,我还贴心的做了三个这样的小分叉,往x,y和z三个轴方向偏移一点

addForkX(beginX,beginY,beginZ,count){
      const positions = new Float32Array(count * 3)
      let startX = beginX;let startY = beginY;let startZ = beginZ;
      for(let i = 0; i < count * 3; i = i+3 ){
        positions[i] = startX + (Math.random())
        positions[i+1] = startY + (Math.random()*0.4)
        positions[i+2] = startZ + (Math.random()*0.5)
        startX = positions[i];
        startY = positions[i+1];
        startZ = positions[i+2];
      }
      return positions;
    },
    addForkY(beginX,beginY,beginZ,count){
      const positions = new Float32Array(count * 3)
      let startX = beginX;let startY = beginY;let startZ = beginZ;
      for(let i = 0; i < count * 3; i = i+3 ){
        positions[i] = startX + (Math.random()*0.5)
        positions[i+1] = startY + (Math.random()*0.4)
        positions[i+2] = startZ + (Math.random())
        startX = positions[i];
        startY = positions[i+1];
        startZ = positions[i+2];
      }
      return positions;
    },
    addForkZ(beginX,beginY,beginZ,count){
      const positions = new Float32Array(count * 3)
      let startX = beginX;let startY = beginY;let startZ = beginZ;
      for(let i = 0; i < count * 3; i = i+3 ){
        positions[i] = startX + (Math.random()*0.4)
        positions[i+1] = startY + (Math.random()*0.5)
        positions[i+2] = startZ + (Math.random())
        startX = positions[i];
        startY = positions[i+1];
        startZ = positions[i+2];
      }
      return positions;
    },

此时主分支代码修改如下:

initLightNing() {
      const particlesGeometry = new THREE.BufferGeometry()
      const count = 5000
      const positions = new Float32Array(count * 3)
      let beginX = 0;let beginY = 0;let beginZ = 0;
      for(let i = 0; i < count * 3; i = i+3 ){
        let count = 0;
        if(Math.random() > 0.993){
          count = Math.floor(Math.random()*400)
          //将分叉的数据填充到主体的数据集合中
          let forkPosition = []
          if(Math.random()<0.35){
            forkPosition = this.addForkX(beginX,beginY,beginZ,count)
          }else if(Math.random()<0.7){
            forkPosition = this.addForkY(beginX,beginY,beginZ,count)
          }else{
            forkPosition = this.addForkZ(beginX,beginY,beginZ,count)
          }
          for (let j = 0; j < forkPosition.length; j++) {
            positions[i+j] = forkPosition[j]
          }
          i = i+count*3
          continue;
        }
        positions[i] = beginX + (Math.random())
        positions[i+1] = beginY + (Math.random()*0.8)
        positions[i+2] = beginZ + (Math.random())
        beginX = positions[i];
        beginY = positions[i+1];
        beginZ = positions[i+2];
      }
      particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
      // Material
      const particlesMaterial = new THREE.PointsMaterial({
        size: 4,
        sizeAttenuation: true
      })
      particlesMaterial.color = new THREE.Color(0xffffff);
      // Points
      const particles = new THREE.Points(particlesGeometry, particlesMaterial)
      particles.name = 'lightning'
      this.scene.add(particles)
    },

然后就得到这样的画面,一个主分叉和多个小分叉,但是明显和闪电差距有点远,后来想着加上动画会不会好点,

设置出现闪电,2/3秒的时候闪电消失,然后下一秒的时候又出现闪电,

initAnimate() {
      this.count++;
      if(this.count === 40){
        this.scene.children.forEach(item=>{
          if(item.name === 'lightning'){
            this.scene.remove(item);
          }
        })
      }else if(this.count === 60){
        this.initLightNing();
        this.count = 0;
      }
      requestAnimationFrame(this.initAnimate);
      this.renderer.render(this.scene, this.camera);
    },

然后就有了这样的动画,不过怎么看都不像闪电,倒过来放上树叶倒像小树苗。最终原因应该是闪电的分叉,应该是需要前期往两侧,尖部往下,而且偏紫色,有高亮效果,我这不过是歪歪扭扭的主线加歪歪扭扭的支线。

闪电

完整源码如下:


<template>
  <div>
    <div id="container"></div>
  </div>
</template>

<script>
import * as THREE from 'three'
import {OrbitControls} from "three/addons/controls/OrbitControls";

export default {
  name: "lightning-example",
  data() {
    return{
      scene:null,
      camera:null,
      cameraCurve:null,
      renderer:null,
      container:null,
      controls:null,
      fireParticles:null,
      count:0,
    }
  },
  methods:{
    initScene(){
      this.scene = new THREE.Scene();
      const axesHelper = new THREE.AxesHelper( 10);
      axesHelper.position.set(0,0,0)
      this.scene.add( axesHelper );
    },
    initCamera(){
      this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
      this.camera.position.set(1000,1000,1000);
      this.camera.lookAt(500,100,500)
    },
    initLight(){
      //添加两个平行光
      const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);
      directionalLight1.position.set(300,-300,300)
      this.scene.add(directionalLight1);
      const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.5);
      directionalLight2.position.set(300,300,300)
      this.scene.add(directionalLight2);
    },
    addForkX(beginX,beginY,beginZ,count){
      const positions = new Float32Array(count * 3)
      let startX = beginX;let startY = beginY;let startZ = beginZ;
      for(let i = 0; i < count * 3; i = i+3 ){
        positions[i] = startX + (Math.random())
        positions[i+1] = startY + (Math.random()*0.4)
        positions[i+2] = startZ + (Math.random()*0.5)
        startX = positions[i];
        startY = positions[i+1];
        startZ = positions[i+2];
      }
      return positions;
    },
    addForkY(beginX,beginY,beginZ,count){
      const positions = new Float32Array(count * 3)
      let startX = beginX;let startY = beginY;let startZ = beginZ;
      for(let i = 0; i < count * 3; i = i+3 ){
        positions[i] = startX + (Math.random()*0.5)
        positions[i+1] = startY + (Math.random()*0.4)
        positions[i+2] = startZ + (Math.random())
        startX = positions[i];
        startY = positions[i+1];
        startZ = positions[i+2];
      }
      return positions;
    },
    addForkZ(beginX,beginY,beginZ,count){
      const positions = new Float32Array(count * 3)
      let startX = beginX;let startY = beginY;let startZ = beginZ;
      for(let i = 0; i < count * 3; i = i+3 ){
        positions[i] = startX + (Math.random()*0.4)
        positions[i+1] = startY + (Math.random()*0.5)
        positions[i+2] = startZ + (Math.random())
        startX = positions[i];
        startY = positions[i+1];
        startZ = positions[i+2];
      }
      return positions;
    },
    //闪电主分支
    initLightNing() {
      const particlesGeometry = new THREE.BufferGeometry()
      const count = 5000
      const positions = new Float32Array(count * 3)
      let beginX = 0;let beginY = 0;let beginZ = 0;
      for(let i = 0; i < count * 3; i = i+3 ){
        let count = 0;
        if(Math.random() > 0.993){
          count = Math.floor(Math.random()*400)
          //将分叉的数据填充到主体的数据集合中
          let forkPosition = []
          if(Math.random()<0.35){
            forkPosition = this.addForkX(beginX,beginY,beginZ,count)
          }else if(Math.random()<0.7){
            forkPosition = this.addForkY(beginX,beginY,beginZ,count)
          }else{
            forkPosition = this.addForkZ(beginX,beginY,beginZ,count)
          }
          for (let j = 0; j < forkPosition.length; j++) {
            positions[i+j] = forkPosition[j]
          }
          i = i+count*3
          continue;
        }
        positions[i] = beginX + (Math.random())
        positions[i+1] = beginY + (Math.random()*0.8)
        positions[i+2] = beginZ + (Math.random())
        beginX = positions[i];
        beginY = positions[i+1];
        beginZ = positions[i+2];
      }
      particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
      // Material
      const particlesMaterial = new THREE.PointsMaterial({
        size: 4,
        sizeAttenuation: true
      })
      particlesMaterial.color = new THREE.Color(0xffffff);
      // Points
      const particles = new THREE.Points(particlesGeometry, particlesMaterial)
      particles.name = 'lightning'
      this.scene.add(particles)
    },
    initRenderer(){
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.container = document.getElementById("container")
      this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
      this.renderer.setClearColor('#000000', 1.0);
      this.container.appendChild(this.renderer.domElement);
    },
    initControl(){
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.maxPolarAngle = Math.PI / 2.2;      // // 最大角度
      this.camera.position.set(1000,1000,1000);
      this.camera.lookAt(500,100,500)
    },
    initAnimate() {
      this.count++;
      if(this.count === 40){
        this.scene.children.forEach(item=>{
          if(item.name === 'lightning'){
            this.scene.remove(item);
          }
        })
      }else if(this.count === 60){
        this.initLightNing();
        this.count = 0;
      }
      requestAnimationFrame(this.initAnimate);
      this.renderer.render(this.scene, this.camera);
    },
    initPage(){
      this.initScene();
      this.initCamera();
      this.initLight();
      this.initRenderer();
      this.initControl();
      this.initAnimate();
      this.initLightNing();
    }
  },
  mounted() {
    this.initPage()
  }
}
</script>

<style scoped>
#container{
  position: absolute;
  width:100%;
  height:100%;
  overflow: hidden;
}

</style>

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

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

相关文章

14-pyspark的DataFrame使用总结

目录 前言DataFrame使用总结 DataFrame的构建方法1&#xff1a;通过列表构建方法2&#xff1a;通过Row对象构建方法3&#xff1a;通过表Schema构建 方法4&#xff1a;rdd结合字符串构建 DataFrame的方法 PySpark实战笔记系列第五篇 10-用PySpark建立第一个Spark RDD(PySpark实战…

DSP笔记-7时钟和系统控制

外部时钟源-晶振 无源晶振&#xff0c;有源晶振&#xff0c; 频率&#xff1a;20MHz&#xff0c;12.5MHz&#xff0c;30MHz&#xff0c;50MHz&#xff0c;32.768MHz SPI&#xff0c;SCI&#xff0c; I2C串行通信 eCAP捕获功能&#xff0c;eQEP解码&#xff0c;ePWM脉冲宽度…

SAP SD学习笔记03 - SD模块中的主数据

上一章讲了SD中的组织单位和SD的简单流程。 SAP SD学习笔记02 - 销售流程中的组织单位-CSDN博客 SAP SD学习笔记01 - 简单走一遍SD的流程&#xff1a;受注&#xff0c;出荷&#xff0c;请求-CSDN博客 这一章讲SD中的主数据&#xff1a; - 得意先Master&#xff08;客户&…

Java设计模式之创建型模式(二)原型模式

原型模式 1、原型模式1-1、应用场景1-2、举个 软栗子1-3、举个 硬栗子1-4、举个实务栗子1-5、代码重构 学习原型模式的目的&#xff1a;原型模式的目的在于通过复制现有的实例来创建新的对象&#xff0c;以避免通过构造函数创建对象时可能带来的性能开销&#xff0c;同时可以控…

实验9 内置对象application

一、实验目的 掌握怎样在JSP中使用内置对象application 二、实验项目内容&#xff08;实验题目&#xff09; 编写代码&#xff0c;掌握application的用法。【参考课本例题4-16 留言板 】 三、源代码以及执行结果截图&#xff1a; example4_16.jsp <% page language"…

python——条件语句

概念 条件语句&#xff0c;简单的理解就是 满足条件执行这些代码&#xff0c;不满足则执行另一些代码 语法 #mermaid-svg-ITs2kv8f87vZuQhT {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ITs2kv8f87vZuQhT .erro…

【网站项目】鲜花销售微信小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

excle如何设置自动显示星期几

目录 一.目的 二.范例结果 三.处理方式 一.目的 excle依据日期&#xff0c;设置后自动显示星期几。 二.范例结果 三.处理方式 公式1TEXT(XXX,DDDD) 公式2TEXT(XXX,DDD)

YoloV8改进策略:Block改进|轻量级的Mamba打造优秀的YoloV8|即插即用,简单易懂|附Block结构图(独家原创)更新中。。。。。

摘要 无Mamba不狂欢&#xff0c;今天给大家带来一个基于轻量级Mamba的改进。模块简单易懂&#xff0c;即插即用&#xff01; 带领大家去征服更高的领域。 论文&#xff1a;《LightM-UNet&#xff1a;Mamba 辅助的轻量级 UNet 用于医学图像分割》 https://arxiv.org/pdf/2403…

欢迎加入PenPad Season 2 ,获得勋章以及海量 Scroll 生态权益

PenPad 是 Scroll 生态中的首个 LaunchPad 平台&#xff0c;该平台继承了 Scroll 生态的技术优势&#xff0c;具备包括隐私在内的系列特点&#xff0c;同时且也被认为是 Scroll 生态最重要的价值入口之一。Penpad 与 Scroll 官方始终保持着合作&#xff0c;同时该项目自启动以来…

解决VM报错:不支持虚拟化的 amd-v/rvi

安装了VMware之后&#xff0c;想测试一下虚拟机嵌套。在勾选虚拟机CPU的虚拟化AMD-V/RVI之后&#xff0c;竟然无法启动&#xff0c;提示“此平台不支持虚拟化的 amd-v/rvi”。 上网找了一下资料&#xff0c;发现是因为Hyper-V与VMware冲突以及Windows Defender的内核隔离导致的…

【基础知识】计算机国企爱考的二进制知识、大学生必须掌握

二进制与十进制相互转换的详细过程及例题解析 二进制和十进制是两种常用的数制系统。二进制系统仅使用0和1两个数字&#xff0c;而十进制系统则使用0到9的十个数字。在计算机科学和数字电路设计等领域&#xff0c;经常需要在这两种数制之间进行转换。本文将详细介绍二进制与十…

(二)ffmpeg 下载安装以及拉流推流示例

一、ffmpeg下载安装 官网&#xff1a;https://www.ffmpeg.org/ 源码下载地址&#xff1a;https://www.ffmpeg.org/download.html#releases 下载源码压缩包 下载完成之后解压并在该目录下打开命令窗口 安装依赖环境&#xff1a; sudo apt-get install build-essential nasm …

组合数学<1>——组合数学基础

今天我们聊聊组合数学。(本期是给刚刚学习组合数学的同学看的&#xff0c;dalao们可以自行忽略) 建议:不会求逆元的出门左转数论<2>&#xff0c;不会数论的出门右转数论<1>。 加乘原理 加乘原理小学奥数就有。 总的来说:加法原理:分类;乘法原理:分步 比如说&a…

OJ 【难度1】【Python】完美字符串 扫雷 A-B数对 赛前准备 【C】精密计时

完美字符串 题目描述 你可能见过下面这一句英文&#xff1a; "The quick brown fox jumps over the lazy dog." 短短的一句话就包含了所有 2626 个英文字母&#xff01;因此这句话广泛地用于字体效果的展示。更短的还有&#xff1a; "The five boxing wizards…

【我的小工具】生成React页面类

有了数据表的结构信息&#xff0c;就能生成React 的页面类&#xff0c;快捷方便。 生成界面如下&#xff1a; 生成的React FrmUser.js页面如下&#xff1a; 只需再写里面的操作逻辑代码。

存储革新:下一代低功耗PCM相变存储器

引言 由于Optane&#xff08;实质为PCM相变存储器&#xff09;被intel放弃以后&#xff0c;小编一直在关注业内有关SCM存储级内存&#xff08;PCM、ReRAM等&#xff09;相关的研究进展。比如之前发布的内容&#xff0c;供存储随笔的读者参考&#xff01; 字节跳动入局存储内存…

如何应用电桥电路的原理?

电桥电路是一种常用的测量技术&#xff0c;它利用了四个电阻的网络来检测电路的平衡状态。在平衡状态下&#xff0c;电桥的输出电压为零&#xff0c;这种特性使得电桥电路非常适合于精确测量电阻、电感、电容等电气参数&#xff0c;以及用于传感器和测量设备中。以下是电桥电路…

第十二届蓝桥杯真题做题笔记

2、卡片 笔记&#xff1a; 直接巧用排列组合求解即可&#xff1a; 我们通过对样例说明进行分析可知&#xff1a;想要分给n个小孩&#xff0c;那么我们就需要满足C(K, 2) K > n才能满足。 #include<bits/stdc.h> using namespace std;int com(int up, int down){i…

从文字到思维:呆马GPT在人工智能领域的创新之旅

引言 生成式预训练变换器&#xff08;Generative Pre-trained Transformer&#xff0c;简称GPT&#xff09;领域是人工智能技术中的一大革新。自OpenAI推出第一代GPT以来&#xff0c;该技术经历了多代发展&#xff0c;不断提升模型的规模、复杂度和智能化程度。GPT模型通过在大…