前言
圣诞节来临之际,用 Three.js 结合 MediaPipe 手势识别,打造一款支持无接触交互的 3D 圣诞树应用。效果包含粒子化圣诞树、多形态切换、照片上传嵌入、手势控制,视觉与交互双在线!
手势控制 3D 奢华圣诞树 ------ 从粒子系统到交互实现
一、核心技术栈
| 技术 / 库 | 作用 |
|---|---|
| Three.js r160 | 3D 场景渲染、粒子系统、后期辉光处理 |
| MediaPipe Tasks Vision | 手部关键点识别、手势解析 |
| Canvas API | 摄像头画面绘制、程序化纹理生成 |
二、核心功能实现
1. Three.js 基础环境搭建
快速初始化场景、相机、渲染器
function initThree() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 2000);
camera.position.z = 55;
// 高性能渲染器配置
renderer = new THREE.WebGLRenderer({ antialias: false, powerPreference: "high-performance" });
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('canvas-container').appendChild(renderer.domElement);
// 分组管理:背景/主体/照片
bgGroup = new THREE.Group();
mainGroup = new THREE.Group();
scene.add(bgGroup, mainGroup);
}
2. 粒子系统构建圣诞树核心视觉
自定义Particle类,封装粒子多形态位置计算与动画逻辑,支持树形态 / 散开形态 / 聚焦形态切换。
class Particle {
constructor(mesh, type) {
this.mesh = mesh;
this.posTree = new THREE.Vector3(); // 树形态位置
this.posScatter = new THREE.Vector3(); // 散开形态位置
this.calculatePositions();
}
// 计算螺旋锥形(树)和球形云(散开)位置
calculatePositions() {
// 树形态:螺旋坐标计算
const h = 28, t = Math.random();
const y = t*h - h/2;
const rBase = 9*(1-t), angle = t*Math.PI*12;
this.posTree.set(Math.cos(angle)*rBase, y, Math.sin(angle)*rBase);
// 散开形态:球形随机坐标
const rScatter = 10 + Math.random()*15;
const theta = Math.random()*Math.PI*2, phi = Math.acos(2*Math.random()-1);
this.posScatter.set(rScatter*Math.sin(phi)*Math.cos(theta), rScatter*Math.sin(phi)*Math.sin(theta), rScatter*Math.cos(phi));
}
// 每帧更新粒子位置与动画
update(dt, mode, focusTarget) {
let target = this.posTree;
if(mode === 'SCATTER') target = this.posScatter;
else if(mode === 'FOCUS' && this.mesh === focusTarget) {
target = new THREE.Vector3(0,0,40); // 聚焦到镜头前
this.mesh.scale.set(5,5,5);
}
this.mesh.position.lerp(target, 3*dt); // 平滑移动
}
}
生成粒子时,搭配金属材质与发光效果,打造奢华质感:
function createParticles() {
const goldMat = new THREE.MeshStandardMaterial({ color: 0xffd700, metalness: 1, emissive: 0x664400 });
// 生成金色球体/方块粒子
for(let i=0; i<1800; i++) {
const mesh = new THREE.Mesh(new THREE.SphereGeometry(0.5), goldMat);
mainGroup.add(mesh);
particleSystem.push(new Particle(mesh, 'GOLD'));
}
// 树顶星星
const star = new THREE.Mesh(new THREE.OctahedronGeometry(1.5), new THREE.MeshStandardMaterial({ color: 0xffdd88, emissive: 0xffaa00, emissiveIntensity: 2 }));
star.position.y = 15;
mainGroup.add(star);
}
3. MediaPipe 手势交互核心
通过识别手部关键点,解析指点 / 手掌张开 / 手掌放松三种手势,实现对应交互逻辑:
async function initMediaPipe() {
const vision = await FilesetResolver.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.3/wasm");
handLandmarker = await HandLandmarker.createFromOptions(vision, {
modelAssetPath: "https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task",
runningMode: "VIDEO"
});
// 启动摄像头与手势检测
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "user" } });
document.getElementById('webcam').srcObject = stream;
predictWebcam();
}
// 手势解析逻辑
function processGestures(lm) {
const indexTip = lm[8], middleTip = lm[12], wrist = lm[0];
const dIndex = Math.hypot(indexTip.x - wrist.x, indexTip.y - wrist.y);
const dMiddle = Math.hypot(middleTip.x - wrist.x, middleTip.y - wrist.y);
const isPointing = dIndex>0.1 && dMiddle < dIndex*0.7; // 指点手势
const isPalmOpen = (dIndex + Math.hypot(lm[16].x-wrist.x, lm[16].y-wrist.y))/2 > 0.35; // 手掌张开
if(isPointing) {
STATE.mode = 'FOCUS'; // 聚焦照片/星星
STATE.focusTarget = particleSystem.filter(p=>p.type==='PHOTO')[STATE.currentPhotoIndex]?.mesh || starMesh;
} else if(isPalmOpen) {
STATE.mode = 'SCATTER'; // 散开+手势旋转
STATE.spinVel.y = (lm[9].x - STATE.palmCenter.x)*6;
} else {
STATE.mode = 'TREE'; // 恢复树形态
}
}
4. 扩展功能:照片上传与后期辉光
-
照片上传:将用户照片生成 3D 相框,融入粒子系统,支持手势聚焦查看
function addPhotoToScene(texture) {
const group = new THREE.Group();
// 金色相框
group.add(new THREE.Mesh(new THREE.BoxGeometry(1.35, 1.35, 0.1), new THREE.MeshStandardMaterial({ color: 0xffd700 })));
// 照片平面
const photo = new THREE.Mesh(new THREE.PlaneGeometry(1.2,1.2), new THREE.MeshBasicMaterial({ map: texture }));
photo.position.z = 0.06;
group.add(photo);
mainGroup.add(group);
particleSystem.push(new Particle(group, 'PHOTO'));
} -
后期辉光:添加 UnrealBloomPass,提升画面奢华感
function setupPostProcessing() {
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(bloomPass);
}
本项目不仅是一个节日趣味应用,更是 Three.js+MediaPipe 手势交互的实战案例,涵盖了粒子系统、3D 几何体、后期处理、手势识别等核心知识点,希望能为前端开发者提供有价值的参考。祝大家圣诞快乐,编码愉快!