要完成這個效果有三個問題要解決
- 自然的粒子分布
- 粒子的拋物線移動
- 粒子的擴散效果
我採用簡單的方式達成
- 用亂數產生分段的粒子團
- 用二次貝茲曲線建立拋物線
- 用粒子團的放大產生擴散效果
因為我用簡單的亂數分布粒子,粒子團會有立方體現象,球體分布會更好,但多個區段連起來就不明顯了。
二次貝茲曲線最大的困難在計算出結尾座標的位置,透過轉換到世界空間去定位出平面高度的座標,再轉換回物體空間來計算出結尾座標。
動畫用 AnimationMixer 去控制,只要定出每段粒子團的開始播放時間,粒子團就會接續的移動。
程式:
/* 初始化渲染器 */ const renderer = new THREE.WebGLRenderer({ antialias: true }); document.getElementById('Container').appendChild(renderer.domElement); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); /* 初始化場景 */ const scene = new THREE.Scene(); scene.background = new THREE.Color('#111'); /* 背景顏色 */ scene.add(new THREE.AmbientLight('#FFF', 0.5)); /* 加入環境光 */ /* 地面 */ const floorMesh = new THREE.Mesh( new THREE.PlaneGeometry(200, 200), new THREE.MeshBasicMaterial({ color: '#DDD', depthWrite: false }) ); floorMesh.rotation.x = THREE.MathUtils.degToRad(-90); scene.add(floorMesh); /* 初始化鏡頭 */ const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 300); camera.position.set(0, 10, 22); /* 初始化軌道控制,鏡頭的移動 */ const orbitControls = new THREE.OrbitControls(camera, renderer.domElement); orbitControls.update(); /* 動畫刷新器 */ const mixers = []; function newMixer(target) { const mixer = new THREE.AnimationMixer(target); mixers.push(mixer); return mixer; } /* 動畫計時器 */ const clock = new THREE.Clock(); /* 渲染週期 */ function renderCycle() { const delta = clock.getDelta(); mixers.forEach(x => x.update(delta)); renderer.render(scene, camera); requestAnimationFrame(renderCycle); } renderCycle(); /*---------------------------------------------------------------*/ /* 水管物件,水滴粒子群組會附加在這裡 */ const pipeMesh = new THREE.Mesh( new THREE.CylinderGeometry(0.5, 0.5, 9, 16), new THREE.MeshBasicMaterial( {color: '#eea236'} ) ); pipeMesh.rotation.x = THREE.MathUtils.degToRad(0); pipeMesh.position.set(0, 4, 0); scene.add(pipeMesh); const dripGroup = new THREE.Group(); dripGroup.position.set(0, 4, 0); pipeMesh.add(dripGroup); /* 水滴材質 */ const dripMaterial = new THREE.PointsMaterial({ map: new THREE.TextureLoader().load('circle.png'), color: '#0aa', size: 1, opacity: 0.7, depthWrite: false, transparent: true, }); /* 建立水滴,用亂數建立粒子點 */ for (let i = 0; i < 60; i++) { const vertices = []; for (let j = 0; j < 40; j++) { const x = Math.random() - 0.5; const y = Math.random() - 0.5; const z = Math.random() - 0.5; vertices.push(x, y, z); } const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); const particles = new THREE.Points(geometry, dripMaterial); dripGroup.add(particles); newMixer(particles); /* 水滴動畫 */ } /*---------------------------------------------------------------*/ const cycle = 2; /* 循環週期,會影響水流速度 */ const scale = 8; /* 擴散大小 */ const length = 14; /* 水流長度 */ /* 二次貝茲曲線,用來取得拋物線的座標點 */ const curve = new THREE.QuadraticBezierCurve3(); curve.v1.set(0, length, 0); /* 曲線控制點座標 */ /* 計算結尾座標 */ const rootPos = dripGroup.getWorldPosition(new THREE.Vector3()); const quaternion = dripGroup.getWorldQuaternion(new THREE.Quaternion()); const toPos = curve.v1.clone(); toPos.applyQuaternion(quaternion); /* 轉換到世界空間 */ /* 當水流是向上時,增加平面位置 */ if (toPos.y > (length / 3)) { toPos.x *= 1.8; toPos.z *= 1.8; } toPos.y = Math.min(-rootPos.y, toPos.y * 1.5); /* 將結尾拉回平面高度 */ toPos.applyQuaternion(quaternion.conjugate()); /* 轉換回物體空間 */ curve.v2.copy(toPos); /* 曲線結尾點座標 */ /* 建立拋物線及擴散動畫 */ const points = curve.getPoints(10); /* 曲线分段 */ const curveTime = []; const curvePos = []; points.forEach((v, i) => { curveTime.push(cycle * i / points.length); curvePos.push(v.x, v.y, v.z); }); const posTrack = new THREE.VectorKeyframeTrack('.position', curveTime, curvePos); const scaleTrack = new THREE.VectorKeyframeTrack('.scale', [0, cycle], [0, 0, 0, scale, scale, scale]); const clip = new THREE.AnimationClip('scale', cycle, [posTrack, scaleTrack]); mixers.forEach((mixer, i) => { const action = mixer.clipAction(clip); action.time = cycle * i / mixers.length; action.play(); });
1 回應:
Here all content so useful and helpful for beginner and experience both.This site is so amazing,This sites gives good knowledge of Threejs.This is very helpful for me.
張貼留言