【Three.js 实战】手势控制 3D 奢华圣诞树 —— 从粒子系统到交互实现

前言

圣诞节来临之际,用 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 几何体、后期处理、手势识别等核心知识点,希望能为前端开发者提供有价值的参考。祝大家圣诞快乐,编码愉快!

相关推荐
二狗哈3 小时前
Cesium快速入门30:CMZL动画
javascript·3d·webgl·cesium·地图可视化
巴拉巴拉~~4 小时前
Flutter高级动画艺术:掌握交错动画,打造丝滑精致的UI体验
javascript·flutter·ui
咸鱼加辣4 小时前
【前端框架】路由配置
javascript·vue.js·前端框架
咸鱼加辣4 小时前
【前端框架】一段普通的 JavaScript 程序
开发语言·javascript·前端框架
雪域迷影4 小时前
怎么将.ts文件转换成.js文件?
javascript·typescript·npm·tsc
narukeu4 小时前
聊下 rewriteRelativeImportExtensions 这个 TypeScript 配置项
前端·javascript·typescript
小北方城市网4 小时前
第2课:零基础前端框架实操入门——从核心语法到第一个完整项目
javascript·ai·正则表达式·json·html5
二狗哈4 小时前
Cesium快速入门29:CMZL数据格式加载
3d·状态模式·webgl·cesium·着色器·地图可视化
枫子有风4 小时前
Day6 前端开发(from teacher;HTML,CSS,JavaScript,Web APIs,Node.js,Vue)
javascript·css