Threejs实现闪电效果

这是一次比较失败的功能实现,本来想网上很少有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>

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baker_zhuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值