如何使用JavaScript结合MediaPipe实现眼镜虚拟试戴?
基于MediaPipe JavaScript的3D眼镜虚拟试戴实现方案
核心实现逻辑不复杂,全程不需要依赖重型AR引擎,跑通基础版只需要4步,最终效果和参考动效完全一致:
- 核心原理:调用MediaPipe Face Mesh实时输出人脸468个高精度关键点,定位眼镜佩戴的锚点位置,计算头部的旋转、缩放、位移参数,把眼镜素材(2D贴图/3D模型)做透视变换后叠加到摄像头画面上。
1. 基础页面与依赖准备
不需要本地安装包,直接通过CDN引入MediaPipe相关依赖即可,页面只需要一个隐藏的video标签承接摄像头流,一个canvas做最终画面渲染:
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh/face_mesh.js" crossorigin="anonymous"></script> <video id="inputVideo" playsinline style="display: none;"></video> <canvas id="outputCanvas" width="1280" height="720"></canvas>
2. 固定锚点选取
MediaPipe Face Mesh输出的关键点索引是固定的,直接取这5个点就能完成眼镜定位,不需要额外做图像识别:
- 左太阳穴锚点:索引234
- 右太阳穴锚点:索引454
- 鼻梁支撑锚点:索引6
- 左眼眶下沿锚点:索引253
- 右眼眶下沿锚点:索引33
这几个点不会受表情、眨眼动作影响,定位稳定性足够。
3. 核心检测逻辑初始化
初始化Face Mesh检测器和摄像头流,配置参数只需要开单人检测、中等置信度阈值即可,在普通手机上也能跑到30帧以上:
const video = document.getElementById('inputVideo'); const canvas = document.getElementById('outputCanvas'); const ctx = canvas.getContext('2d'); // 提前加载好你的眼镜素材,2D版用透明PNG即可,3D版可以替换为Three.js模型加载 const glassImg = new Image(); glassImg.src = './your-glass-asset.png'; // 初始化人脸网格检测器 const faceMesh = new FaceMesh({ locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh/${file}` }); faceMesh.setOptions({ maxNumFaces: 1, refineLandmarks: true, minDetectionConfidence: 0.5, minTrackingConfidence: 0.5 }); faceMesh.onResults(renderFrame); // 启动摄像头 const camera = new Camera(video, { onFrame: async () => await faceMesh.send({image: video}), width: 1280, height: 720 }); camera.start();
4. 逐帧渲染逻辑
每一帧检测完成后,先把摄像头原始画面画到canvas,再根据关键点计算眼镜的位置、大小、旋转角度,叠加绘制即可:
function renderFrame(results) { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(results.image, 0, 0, canvas.width, canvas.height); if (!results.multiFaceLandmarks?.length) return; const points = results.multiFaceLandmarks[0]; // 把归一化坐标转成canvas实际像素坐标 const getCoord = (index) => ({ x: points[index].x * canvas.width, y: points[index].y * canvas.height }); const leftTemple = getCoord(234); const rightTemple = getCoord(454); const noseBridge = getCoord(6); // 计算眼镜参数 const glassWidth = Math.hypot(rightTemple.x - leftTemple.x, rightTemple.y - leftTemple.y) * 1.1; const rotate = Math.atan2(rightTemple.y - leftTemple.y, rightTemple.x - leftTemple.x); const posX = noseBridge.x; const posY = noseBridge.y - glassWidth * 0.1; // 鼻梁位置往上微调,避免眼镜位置靠下 // 绘制眼镜 ctx.save(); ctx.translate(posX, posY); ctx.rotate(rotate); ctx.drawImage(glassImg, -glassWidth/2, -glassWidth*0.3, glassWidth, glassWidth*0.6); ctx.restore(); }
效果优化方案
- 要实现参考动效里的真实3D质感,不要用单张PNG贴图,引入轻量Three.js加载GLB格式的3D眼镜模型,把通过锚点计算出的头部俯仰、偏航、侧滚角直接同步给Three.js相机,模型会自动跟随头部做三维旋转,不会有2D贴图的扁平感
- 增加平滑滤波:对每帧计算出的位置、角度、缩放值做0.15-0.2系数的线性插值,避免画面抖动
- 增加遮挡判断:利用人脸轮廓关键点判断眼镜腿、镜框边缘是否被侧脸遮挡,对遮挡区域做裁剪或半透明处理,真实感会明显提升
- iOS端一定要给video标签加
playsinline属性,避免摄像头强制全屏导致渲染失效
内容的提问来源于stack exchange,提问作者Azamat Vahabov
相关产品推荐
相关产品推荐

