WebGL 部件拾取:从点击网格到教材知识点面板
Raycaster 只是第一步,元数据与高亮反馈才构成完整交互。
三维发动机点选部件后,要高亮网格并打开对应知识点。这条链路要稳、要快、还要对触摸友好。我们踩过的坑:Blender 合并网格后 userData 丢失、射线打到透明整流罩、高亮 emissive 过曝看不清结构、目录点击与 3D 选中态不同步。下面是从点击到理解的完整实现要点。
拾取
用 Three.js Raycaster 从 normalized 设备坐标发射射线,只检测 interactiveParts 数组,避免每帧遍历全场景。交点映射到部件 ID:加载 glTF 后按节点名批量写入 userData.partId,与 CMS 知识点主键一致。触摸端用 pointerdown 统一鼠标与手指,防抖 150ms 避免误触;pointermove 不做拾取,省 GPU。
若用 InstancedMesh,需在 instance 层维护 ID 映射表。透明件单独层,可配置「是否参与拾取」,默认只拾取外壳实体。双击同一部件可取消选中,符合桌面习惯;移动端用再次点击取消。
高亮与联动
选中态用 emissive 提亮或后处理 OutlinePass;未选中部件略降透明度,保留结构上下文。右侧知识点面板滚动到对应章节并展开折叠块,URL hash 同步 #part=fan 便于分享。反向:点目录项 → 相机 flyTo 部件包围盒中心并高亮。全局单一 selectedPartId 驱动 3D 与 UI,避免多处 setState 打架。
性能
拾取只在 click/pointer 事件触发,不做每帧 raycast。装饰网格设 raycast = () => null。移动端关闭 outline,改用 emissive;帧率低于 24 提示简化显示。教学交互闭环:看见 → 点中 → 理解;若点中无反馈或反馈延迟超过 200ms,学生会以为「坏了」而流失。
无障碍与测试
键盘 Tab 可聚焦部件列表,Enter 触发与点击等价的选中与高亮。aria-live 区域播报「已选中压气机第一级」,读屏用户可参与。单元测试 mock Raycaster 交点;E2E 用 Playwright 点击 canvas 坐标验证面板联动。回归清单包含:合并网格后 userData 是否还在、旋转模型后拾取是否仍准、iOS Safari 双指缩放后射线是否偏移。
与动画系统协作
爆炸动画播放中暂停拾取,或拾取时打断爆炸回到装配位,避免射线打到已飞远的网格。转子转动时用包围盒中心做逻辑锚点,而非每帧 mesh 顶点,减少 jitter。选中部件可触发微动效(轻微 pulse),强化「点中了」的确认感,但时长不超过 300ms,免干扰阅读。
工程化建议
将 partId 与 CMS、动画配置抽成单一 JSON schema,Blender 导出脚本校验命名,缺字段 build 失败。Canvas 容器监听 resize,更新 camera aspect 与 Raycaster 坐标映射,避免全屏切换后点偏。日志采样记录 miss 点击(无交点),分析是否 UI 挡层抢了事件,这类问题在平板横屏特别常见。
拾取命中后可缓存最近交点与法线,供后续标注工具复用。若未来支持 AR 叠加,同一套 partId 映射可延伸到 WebXR hit test,避免重做内容关联。教学 3D 的长期资产是 ID 体系,不是某一版 shader。
大班级同时在线时,避免每人默认加载 4K 环境贴图;按设备 tier 下发配置。拾取失败率超过百分之五应告警,往往是模型更新后命名 drift。与教材编审约定:改知识点文案不必动模型,改 part 结构必须走联合验收,防止 ID 断裂。
演示前清空浏览器缓存的策略要有文档:教师机与学生机 CDN 缓存不一致,会导致「我这边能点你那边不能点」。建议在开学第一周集中推送静态资源版本号,之后仅 patch 文案。拾取链路稳定了,再把精力放在习题联动与测验统计上,ROI 更高。
小结
部件拾取要同时服务鼠标与触摸,并把元数据、高亮、知识点面板做成双向联动。性能上优先裁剪可交互集合,避免每帧全场景扫描。教学场景的成功标准很明确:学生点得中、看得清、能对应到教材章节。
实践中把上述清单变成可勾选的发布门禁,比事后补救更省时间。