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