feat: VRM 3D Avatar System
Summary
为 Study with Miku 添加 VRM 3D 虚拟角色系统。用户可导入自己的 VRM 模型(VRoid Studio / VRoid Hub / Booth.pm),角色作为全屏背景渲染,替代当前的视频背景模式。角色根据番茄钟状态变化表情和姿态。
Motivation
利用 VRM 生态让用户自带模型,零美术成本。3D 角色响应专注状态(工作时专注、休息时微笑、空闲时打瞌睡),比静态视频循环更有陪伴感。
核心设计
背景模式切换
视频和 Avatar 是两种互斥的全屏背景,用户在设置中切换:

canvas 与当前 <video> 共用相同的 CSS 定位和 z-index,overlay 和所有 UI 照常叠加在上方。切换视频按钮在 Avatar 模式下隐藏。
3D 场景构成
角色身处一个可自定义的 3D 场景中,由低面数 GLB 模型搭建:

- 场景预设:内置 3 套场景——书房(桌椅、书架、台灯、窗户)、咖啡厅(圆桌、咖啡杯、植物、暖光)、卧室(床、月光窗、小夜灯)
- 自定义场景:用户可导入自己的
.glb 场景文件
- 相机预设:半身特写 / 全身 / 桌面视角(3 种,设置中选择)
- 氛围灯光:白天(暖白光)/ 黄昏(橙色调)/ 夜晚(冷紫调),影响整体光照色温
- 场景物件:均为低面数模型,保证性能;通过 OPFS 存储自定义场景文件
角色放置与防穿模
场景通过锚点系统定义角色的位置、朝向和姿态。GLB 场景文件中内嵌一个名为 CharacterAnchor 的空物体(Blender Empty),携带以下元数据:
| 锚点属性 |
说明 |
示例 |
position |
角色脚底/臀部世界坐标 |
(0, 0.45, -0.5) |
rotation |
角色朝向 |
(0, 180°, 0) 面向相机 |
pose |
姿态预设名 |
sitting-desk / sitting-casual / standing |
seatHeight |
座面高度(用于臀部 Y 对齐) |
0.45 |
deskHeight |
桌面高度(用于手臂放置) |
0.75 |
scale |
角色缩放建议值 |
1.0 |
姿态预设定义各关节旋转(VRM 骨骼标准化,所有模型通用):
| 预设 |
关键骨骼 |
适用场景 |
sitting-desk |
髋/膝弯曲 90°,手臂前伸搁桌面,上身微前倾 |
书房 |
sitting-casual |
髋/膝弯曲,手放腿上,身体靠后 |
咖啡厅、卧室 |
standing |
自然站姿,手臂下垂 |
通用 |
防穿模策略:
- 参数化对齐——根据 VRM 模型实际骨骼长度 + 锚点的
seatHeight / deskHeight 计算偏移,让臀部落在座面、手臂搁在桌面
- 容差建模——场景物件建模时留缝隙(桌椅稍薄),比精确贴合更不易穿模
- 用户微调——设置中提供 Y 轴偏移滑块,应对个别模型比例特殊的情况
- 自定义场景 fallback——无
CharacterAnchor 的 GLB 文件默认居中站姿,用户可手动调整
角色状态响应
角色根据番茄钟状态自动切换表情,姿态基底由场景锚点决定(坐姿/站姿不变),在此基础上叠加上半身动作变化,过渡动画 ~500ms ease-out:
| 番茄钟状态 |
角色表情 |
叠加动作(上半身) |
常驻动画 |
| 空闲 |
放松、微笑 |
轻微歪头,手臂自然 |
呼吸 + 眨眼 |
| 专注中 |
专注、严肃 |
头微前倾,姿态端正 |
呼吸 + 眨眼(频率降低) |
| 休息中 |
开心、放松 |
歪头,伸懒腰 |
呼吸 + 眨眼 |
| 暂停 |
困倦 |
头微低,趴桌(坐姿时) |
呼吸 + 慢速眨眼 |
常驻动画(呼吸 + 随机眨眼)在所有状态下持续运行,SpringBone 提供头发/衣服物理摆动。
设置界面设计
新增 Avatar 设置 Tab
在 SettingsSidebar 中 "媒体" 和 "账号" 之间新增 "Avatar" tab,图标 lucide:person-standing。
Tab 内容布局

模型导入流程
点击"导入 VRM"卡片 → 文件选择器(accept=.vrm)→ 校验扩展名 + 大小上限 100MB → 进度条 → 存入 OPFS → 列表新增卡片 → 自动设为活跃并渲染。
模型卡片交互
每个模型卡片显示模型名称(VRM 元数据或文件名)、添加日期、活跃标记。点击切换活跃,长按/右键删除确认。
技术架构
新增文件
src/
avatar/
constants.js ← 枚举 (AvatarState) 和配置常量
VRMRenderer.js ← Three.js + VRM 渲染引擎
SceneManager.js ← 场景预设加载 + 自定义场景管理
animations.js ← 状态表情/姿态预设 + 过渡插值
performanceMonitor.js ← FPS 监控,自动降级画质
services/
vrmStorage.js ← OPFS 存储 VRM + GLB 文件
composables/
useAvatar.js ← 单例 Composable (参照 usePlayer.js)
components/
AvatarCanvas.vue ← 全屏 canvas 背景组件
settings/tabs/
TabAvatar.vue ← Avatar 设置页
需修改的文件
src/App.vue — 条件渲染 <video> 或 <AvatarCanvas>(懒加载)
src/config/constants.js — 新增 STORAGE_KEYS 和 DATA_TYPES
src/components/SettingsModal.vue — 注册 TabAvatar
src/components/settings/SettingsSidebar.vue — 添加导航项
src/composables/useDataSync.js — 同步 avatar 偏好
package.json — 添加 three + @pixiv/three-vrm
vite.config.js — manualChunks 分包 + PWA 缓存规则
新增依赖
bun add three @pixiv/three-vrm
VRM 文件存储
OPFS 存储(参照现有音频文件存储模式),元数据存 localStorage:
OPFS: /vrm-models/1707123456_abc123.vrm ← 角色模型
OPFS: /scene-models/1707123456_abc123.glb ← 自定义场景(可选)
localStorage: swm_avatar_models ← [{ id, name, fileName, addedAt }]
localStorage: swm_avatar_settings ← { scenePreset, cameraPreset, ambiance }
性能策略
| 设备能力 |
画质 |
策略 |
| 高端 |
HIGH |
全分辨率 + SpringBone + 阴影 |
| 中端 |
MEDIUM |
pixelRatio=1 + SpringBone |
| 低端 |
LOW |
pixelRatio=1 + 跳过 SpringBone + 简化灯光 |
启动后 5 秒自动检测 FPS,低于 30 自动降级。
云同步
仅同步偏好设置(不含 VRM/GLB 文件):
{ enabled, activeModelName, scenePreset, cameraPreset, ambiance, qualityLevel }
风险
| 风险 |
应对 |
| Three.js 包体积 ~600KB gzipped |
defineAsyncComponent 懒加载 + 分包 |
| VRM/GLB 文件大(10-100MB) |
OPFS 存储 + 文件大小校验 |
| 内置场景模型的托管 |
预设场景 GLB 打包到 public/ 或按需从 CDN 加载 |
| 移动端性能 |
自动降级画质 |
| 内存泄漏 |
显式 dispose 所有 Three.js 资源 |
| 角色穿模 |
锚点参数化对齐 + 容差建模 + 用户 Y 轴微调 |
| 自定义场景无锚点 |
fallback 居中站姿,用户手动调整 |
| OPFS 不支持 |
检测后隐藏 Avatar 功能 |
feat: VRM 3D Avatar System
Summary
为 Study with Miku 添加 VRM 3D 虚拟角色系统。用户可导入自己的 VRM 模型(VRoid Studio / VRoid Hub / Booth.pm),角色作为全屏背景渲染,替代当前的视频背景模式。角色根据番茄钟状态变化表情和姿态。
Motivation
利用 VRM 生态让用户自带模型,零美术成本。3D 角色响应专注状态(工作时专注、休息时微笑、空闲时打瞌睡),比静态视频循环更有陪伴感。
核心设计
背景模式切换
视频和 Avatar 是两种互斥的全屏背景,用户在设置中切换:
canvas 与当前
<video>共用相同的 CSS 定位和 z-index,overlay 和所有 UI 照常叠加在上方。切换视频按钮在 Avatar 模式下隐藏。3D 场景构成
角色身处一个可自定义的 3D 场景中,由低面数 GLB 模型搭建:
.glb场景文件角色放置与防穿模
场景通过锚点系统定义角色的位置、朝向和姿态。GLB 场景文件中内嵌一个名为
CharacterAnchor的空物体(Blender Empty),携带以下元数据:position(0, 0.45, -0.5)rotation(0, 180°, 0)面向相机posesitting-desk/sitting-casual/standingseatHeight0.45deskHeight0.75scale1.0姿态预设定义各关节旋转(VRM 骨骼标准化,所有模型通用):
sitting-desksitting-casualstanding防穿模策略:
seatHeight/deskHeight计算偏移,让臀部落在座面、手臂搁在桌面CharacterAnchor的 GLB 文件默认居中站姿,用户可手动调整角色状态响应
角色根据番茄钟状态自动切换表情,姿态基底由场景锚点决定(坐姿/站姿不变),在此基础上叠加上半身动作变化,过渡动画 ~500ms ease-out:
常驻动画(呼吸 + 随机眨眼)在所有状态下持续运行,SpringBone 提供头发/衣服物理摆动。
设置界面设计
新增 Avatar 设置 Tab
在 SettingsSidebar 中 "媒体" 和 "账号" 之间新增 "Avatar" tab,图标
lucide:person-standing。Tab 内容布局
模型导入流程
点击"导入 VRM"卡片 → 文件选择器(accept=
.vrm)→ 校验扩展名 + 大小上限 100MB → 进度条 → 存入 OPFS → 列表新增卡片 → 自动设为活跃并渲染。模型卡片交互
每个模型卡片显示模型名称(VRM 元数据或文件名)、添加日期、活跃标记。点击切换活跃,长按/右键删除确认。
技术架构
新增文件
需修改的文件
src/App.vue— 条件渲染<video>或<AvatarCanvas>(懒加载)src/config/constants.js— 新增 STORAGE_KEYS 和 DATA_TYPESsrc/components/SettingsModal.vue— 注册 TabAvatarsrc/components/settings/SettingsSidebar.vue— 添加导航项src/composables/useDataSync.js— 同步 avatar 偏好package.json— 添加three+@pixiv/three-vrmvite.config.js— manualChunks 分包 + PWA 缓存规则新增依赖
VRM 文件存储
OPFS 存储(参照现有音频文件存储模式),元数据存 localStorage:
性能策略
启动后 5 秒自动检测 FPS,低于 30 自动降级。
云同步
仅同步偏好设置(不含 VRM/GLB 文件):
风险