Three.js做一个粒子发射器
2022-11-07 本文已影响0人
思我恋
import * as THREE from "three";
import TWEEN from "@tweenjs/tween.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import gltfUrl from "@/assets/models/11-5-1-processed.glb?url";
import pngUrl from "@/assets/textures/raindrop.png?url";
window.THREE = THREE;
export default function () {
let camera: THREE.PerspectiveCamera,
scene: THREE.Scene,
renderer: THREE.WebGLRenderer,
controls: OrbitControls;
const textureLoader = new THREE.TextureLoader();
const container = document.createElement("div");
let clock = new THREE.Clock();
let group: THREE.Group;
let itemHeight: number;
init();
return container;
function init() {
createScene();
createObj();
createRenderer();
createCamera();
createControls();
animate();
}
function createObj() {
// 创建的单个粒子的尺寸为(radius * 2, height + radius * 2, radius * 2)
const radius = 4;
const height = 192;
itemHeight = radius * 2 + height;
// 存放样条曲线的点集
const points = [];
//上半部分四分之一圆弧
for (let i = Math.PI / 2; i > 0; i -= 0.3) {
points.push(
new THREE.Vector2(
Math.cos(i) * radius,
Math.sin(i) * radius + height / 2
)
);
}
//中间直线
for (let i = height / 2; i > -height / 2; i -= height) {
points.push(new THREE.Vector2(radius, i));
}
//下半部分四分之一圆弧
for (let i = 0; i <= Math.PI / 2; i += 0.3) {
points.push(
new THREE.Vector2(
Math.cos(i) * radius,
-Math.sin(i) * radius - height / 2
)
);
}
// 补充一个点,去掉底部的小洞
points.push(new THREE.Vector2(0, -radius - height / 2));
console.log(points);
const geometry = new THREE.LatheGeometry(points);
const pngec = textureLoader.load(pngUrl);
// 围绕中心点旋转180度
// pngec.center = new THREE.Vector2(0.5, 0.5);
// pngec.rotation = Math.PI;
// pngec.mapping = THREE.UVMapping;
const material = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0.6,
vertexColors: false,
map: pngec,
blending: THREE.AdditiveBlending,
color: new THREE.Color(0xffffff),
});
const mesh = new THREE.Mesh(geometry, material);
group = new THREE.Group();
group.name = "粒子发射器";
for (let i = 0; i < 10; i++) {
const copy_mesh = mesh.clone();
const position = new THREE.Vector3(
THREE.MathUtils.randFloat(0, 1000),
0,
THREE.MathUtils.randFloat(0, 1000)
);
copy_mesh.position.set(position.x, position.y, position.z);
copy_mesh.userData = {
startTime: THREE.MathUtils.randFloat(0, 6),
speed: THREE.MathUtils.randFloat(1, 10),
// speed: 1,
};
copy_mesh.scale.set(1, 0, 1);
group.add(copy_mesh);
}
scene.add(group);
}
function createCamera() {
camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
15000
);
camera.position.set(200, 500, 200);
}
function createScene() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x333333);
scene.add(new THREE.AxesHelper(1000));
}
function createControls() {
controls = new OrbitControls(camera, renderer.domElement);
}
function createRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
}
//
function animate() {
requestAnimationFrame(animate);
// 获取动画已经开始的时间
const elapsedTime = clock.getElapsedTime();
const topHeight = 1000;
group.traverse((obj) => {
// 粒子在y轴,由0到1000之间移动
if (obj.type === "Mesh") {
const startTime = obj.userData.startTime;
const speed = obj.userData.speed;
if (elapsedTime >= startTime || true) {
const position_y = obj.position.y;
// 用于获取物体的物理尺寸
const sizeVect = new THREE.Vector3();
const box = new THREE.Box3().setFromObject(obj);
box.getSize(sizeVect);
// 以下用于处理缩放
let scaleY = obj.scale.y;
if (position_y <= itemHeight / 2) {
// 处于增长阶段
scaleY = scaleY + speed / itemHeight;
} else if (position_y >= topHeight - itemHeight / 2) {
// 处于缩小阶段
scaleY = scaleY - speed / itemHeight;
}
// 处理缩放的极限值
if (scaleY >= 1) scaleY = 1;
if (scaleY <= 0) scaleY = 0;
obj.scale.setY(scaleY);
// position记录的是中心点
// 以下用于处理位移
let add_posi_y = position_y + (scaleY === 1 ? speed : speed / 2);
if (add_posi_y >= topHeight) add_posi_y = 0;
obj.position.setY(add_posi_y);
// console.log(box.max);
}
}
});
controls.update();
renderer.render(scene, camera);
}
}