要完成這個效果有三個問題要解決
- 自然的粒子分布
- 粒子的拋物線移動
- 粒子的擴散效果
我採用簡單的方式達成
- 用亂數產生分段的粒子團
- 用二次貝茲曲線建立拋物線
- 用粒子團的放大產生擴散效果
因為我用簡單的亂數分布粒子,粒子團會有立方體現象,球體分布會更好,但多個區段連起來就不明顯了。
二次貝茲曲線最大的困難在計算出結尾座標的位置,透過轉換到世界空間去定位出平面高度的座標,再轉換回物體空間來計算出結尾座標。
動畫用 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.
張貼留言