前端实现交互式3D人体肌肉解剖图:基于 Three.js + React Three Fiber 的完整方案


本文将详细介绍如何在前端实现一个交互式的3D人体肌肉解剖展示工具,用户可以旋转、缩放模型,点击任意肌肉查看中英文名称。

为什么要做这个?

传统的肌肉解剖学习通常依赖静态图片或昂贵的3D软件。作为健身爱好者,我希望能有一个免费、易用的在线工具来学习肌肉解剖知识。于是我决定自己动手,基于开源的 Z-Anatomy 项目,在浏览器中实现一个交互式的3D肌肉解剖图。

如果你想先体验效果,可以试试这个在线的3D肌肉功能解剖工具。

技术架构概览

┌─────────────────────────────────────────────────────────────┐ │ 用户浏览器 │ ├─────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │ │ GLB 模型 │ -> │ Three.js │ -> │ React Three │ │ │ │ (Draco) │ │ 场景渲染 │ │ Fiber 组件 │ │ │ └─────────────┘ └─────────────┘ └─────────────────┘ │ │ │ │ │ ┌──────────────────┼──────────────────┐ │ │ v v v │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 射线检测 │ │ 肌肉高亮 │ │ i18n 翻译 │ │ │ │ Raycaster │ │ 材质切换 │ │ 中英文名称 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────────┘ 

模型来源与处理

Z-Anatomy 开源项目

Z-Anatomy 是一个开源的人体解剖学 Blender 项目,包含完整的人体肌肉、骨骼、器官等结构。原始文件约 300MB,包含 1000+ 个独立的 mesh 对象。

模型导出与压缩

为了在 Web 端使用,我们需要将 Blender 文件导出为 GLB 格式,并使用 Draco 压缩:

# export_cli.py - Blender 命令行导出脚本import bpy # 选择所有肌肉相关的对象 muscle_keywords =['muscle','deltoid','bicep','tricep','pectoralis',...]for obj in bpy.data.objects:if obj.type=='MESH': name_lower = obj.name.lower()ifany(keyword in name_lower for keyword in muscle_keywords): obj.select_set(True)# 导出为 GLB,启用 Draco 压缩 bpy.ops.export_scene.gltf( filepath='muscle-anatomy.glb', export_format='GLB', use_selection=True, export_draco_mesh_compression_enable=True, export_draco_mesh_compression_level=6,)

导出后的模型从 300MB 压缩到约 6.8MB,非常适合 Web 加载。

核心实现

1. 场景搭建 (React Three Fiber)

// muscle-scene.tsx import { Canvas } from '@react-three/fiber'; import { OrbitControls, Environment } from '@react-three/drei'; export function MuscleScene({ onMuscleClick, onMuscleHover, hoveredMuscle, selectedMuscle }) { return ( <Canvas camera={{ position: [1.5, 0.3, 2], fov: 50 }}> {/* 环境光照 */} <ambientLight intensity={0.6} /> <directionalLight position={[5, 5, 5]} intensity={0.8} /> {/* 3D 模型 */} <Suspense fallback={null}> <MuscleModel onMuscleClick={onMuscleClick} onMuscleHover={onMuscleHover} hoveredMuscle={hoveredMuscle} selectedMuscle={selectedMuscle} /> </Suspense> {/* 轨道控制器 */} <OrbitControls enablePan={false} minDistance={1} maxDistance={5} target={[0, 0.5, 0]} /> {/* 环境贴图 */} <Environment preset="studio" /> </Canvas> ); } 

2. 模型加载与肌肉识别

// muscle-model.tsx import { useGLTF } from '@react-three/drei'; import * as THREE from 'three'; // 肌肉识别关键词 const muscleKeywords = [ 'muscle', 'deltoid', 'bicep', 'tricep', 'pectoralis', 'latissimus', 'trapezius', 'rectus', 'oblique', 'gluteus', 'quadricep', 'vastus', 'hamstring', 'gastrocnemius', 'soleus', // ... 更多关键词 ]; // 需要隐藏的非肌肉对象 const HIDDEN_KEYWORDS = [ 'region', 'fascia', 'bursa', 'ligament', 'membrane', '.j', '.t', // Z-Anatomy 的标签后缀 ]; function isMuscle(name: string): boolean { const lowerName = name.toLowerCase(); return muscleKeywords.some(keyword => lowerName.includes(keyword)) && !HIDDEN_KEYWORDS.some(keyword => lowerName.includes(keyword)); } export function MuscleModel({ onMuscleClick, onMuscleHover, hoveredMuscle, selectedMuscle }) { const { scene } = useGLTF('/models/muscle-anatomy.glb'); // 克隆场景并处理材质 const clonedScene = useMemo(() => { const clone = scene.clone(true); clone.traverse((child) => { if (child instanceof THREE.Mesh) { if (shouldHide(child.name)) { child.parent?.remove(child); return; } if (isMuscle(child.name)) { // 为肌肉设置红色系材质 child.material = child.material.clone(); child.material.color = new THREE.Color(0xc44d4d); child.material.roughness = 0.7; child.material.metalness = 0.1; } } }); return clone; }, [scene]); return ( <primitive object={clonedScene} position={[0, -0.84, 0]} onPointerOver={handlePointerOver} onPointerOut={handlePointerOut} onClick={handleClick} /> ); } 

3. 交互高亮效果

// 高亮颜色配置 const HIGHLIGHT_COLOR = new THREE.Color(0x5ac57a); // 薄荷绿 - 悬停 const SELECTED_COLOR = new THREE.Color(0x4caf50); // 深绿 - 选中 // 更新高亮效果 useEffect(() => { clonedScene.traverse((child) => { if (child instanceof THREE.Mesh && isMuscle(child.name)) { const material = child.material as THREE.MeshStandardMaterial; const muscleId = getMuscleIdFromModelName(child.name); const isHovered = muscleId === hoveredMuscle; const isSelected = muscleId === selectedMuscle; if (isSelected) { material.emissive = SELECTED_COLOR; material.emissiveIntensity = 0.5; } else if (isHovered) { material.emissive = HIGHLIGHT_COLOR; material.emissiveIntensity = 0.3; } else { material.emissive = new THREE.Color(0x000000); material.emissiveIntensity = 0; } } }); }, [hoveredMuscle, selectedMuscle, clonedScene]); 

4. 肌肉名称映射与多语言

Z-Anatomy 的命名格式是 Muscle name.l(左侧)/ Muscle name.r(右侧),我们需要将其映射到标准的肌肉 ID:

// muscles.tsconst muscleAliases: Record<string,string[]>={'pectoralis_major':['pectoralis major','sternocostal head of pectoralis major','clavicular head of pectoralis major',],'biceps_brachii':['biceps brachii','long head of biceps brachii','short head of biceps brachii',],// ... 更多映射};exportfunctiongetMuscleIdFromModelName(modelName:string):string|undefined{const cleanName = modelName .replace(/\.(l|r|el|er)$/i,'')// 移除左右后缀.toLowerCase().trim();for(const[muscleId, aliases]of Object.entries(muscleAliases)){if(aliases.some(alias => cleanName.includes(alias))){return muscleId;}}returnundefined;}

多语言翻译存储在 i18n 模块中:

// locales/zh/muscle-anatomy.tsexportconst muscleAnatomy ={ muscles:{ pectoralis_major:'胸大肌', biceps_brachii:'肱二头肌', triceps_brachii:'肱三头肌', latissimus_dorsi:'背阔肌', rectus_abdominis:'腹直肌', gluteus_maximus:'臀大肌', quadriceps_femoris:'股四头肌', gastrocnemius:'腓肠肌',// ... 150+ 肌肉翻译},};

性能优化

1. 模型压缩

  • 使用 Draco 压缩,模型从 300MB 压缩到 6.8MB
  • 在 Blender 中使用 Decimate Modifier 降低面数

2. 动态加载

// 使用 Next.js 动态导入,禁用 SSR const MuscleScene = dynamic( () => import('./muscle-scene').then((mod) => mod.MuscleScene), { ssr: false } ); 

3. 材质优化

  • 克隆材质避免全局修改
  • 使用 emissive 属性实现高亮,避免重新创建材质

应用场景

这套技术方案可以应用于多种场景:

  1. 健身教育:帮助健身爱好者了解肌肉位置和名称
  2. 医学教学:辅助医学生学习人体解剖
  3. 康复指导:展示特定肌肉群的位置
  4. 游戏开发:角色肌肉系统参考

如果你对健身相关的工具感兴趣,可以看看这些在线计算器:

总结

本文介绍了如何在前端使用 Three.js + React Three Fiber 实现交互式3D人体肌肉解剖展示。核心技术点包括:

  1. 模型处理:从 Z-Anatomy 导出并压缩 GLB 模型
  2. 场景搭建:使用 React Three Fiber 构建 3D 场景
  3. 交互实现:射线检测 + 材质高亮
  4. 多语言支持:肌肉名称中英文翻译

这套方案完全在浏览器端运行,无需后端服务,非常适合构建教育类的3D可视化应用。


相关链接:

Read more

Web-Check+cpolar:全方位检查网站还能随时随地访问,太方便了!

Web-Check+cpolar:全方位检查网站还能随时随地访问,太方便了!

文章目录 * 前言 * 1.关于Web-Check * 2.功能特点 * 3.安装Docker * 4.创建并启动Web-Check容器 * 5.本地访问测试 * 6.公网远程访问本地Web-Check * 7.内网穿透工具安装 * 8.创建远程连接公网地址 * 9.使用固定公网地址远程访问 前言 Web-Check 能分析网站的 IP 信息、SSL 证书、DNS 记录、性能和安全配置等,适合网站开发者、运维和安全人员使用,优点是信息全面,能一键获取网站多维度数据。 使用时发现它对新手很友好,操作简单,不过检测结果需要一定专业知识解读,建议结合实际需求重点关注关键指标,如开放端口和 SSL 配置。 但它默认只能在局域网内使用,要是想和异地团队共享检测结果,或者在外网随时查看网站状态,就很不方便,得依赖复杂的网络配置。 而搭配 cpolar 后,能生成公网访问地址,

Qwen3-VL-WEBUI部署优化:4090D显卡配置参数详解

Qwen3-VL-WEBUI部署优化:4090D显卡配置参数详解 1. 背景与应用场景 随着多模态大模型在视觉理解、图文生成和智能代理等领域的广泛应用,阿里推出的 Qwen3-VL 系列模型凭借其强大的跨模态能力迅速成为行业焦点。其中,Qwen3-VL-4B-Instruct 作为该系列中性能与效率兼顾的代表型号,特别适合在消费级显卡上进行本地化部署与交互式应用。 本文聚焦于 Qwen3-VL-WEBUI 的实际部署过程,重点针对单张 NVIDIA RTX 4090D 显卡环境下的系统配置、推理优化与资源调度策略,提供一套完整可落地的技术方案。通过合理设置 WebUI 启动参数、显存管理机制和后端加速选项,实现低延迟、高响应的多模态交互体验。 2. Qwen3-VL-WEBUI 核心特性解析 2.1 模型能力全景 Qwen3-VL 是目前 Qwen 系列中最先进的视觉语言模型(VLM),具备以下核心增强功能: * 视觉代理能力:可识别 PC 或移动设备 GUI 元素,理解功能逻辑,并调用工具完成自动化任务。 * 视觉编码增强:

【征文计划】玩转 Rokid JSAR:基于 Web 技术栈的 AR 开发环境搭建、核心 API 应用与 3D 时钟等创意项目全流程解析

【征文计划】玩转 Rokid JSAR:基于 Web 技术栈的 AR 开发环境搭建、核心 API 应用与 3D 时钟等创意项目全流程解析

【征文计划】玩转 Rokid JSAR:基于 Web 技术栈的 AR 开发环境搭建、核心 API 应用与 3D 时钟等创意项目全流程解析 前言 随着 AR 技术在消费级场景的普及,开发者对 “低门槛、高兼容” AR 开发工具需求愈发迫切,传统 AR 开发往往依赖专属引擎或复杂语法,导致 Web 开发者难以快速切入,而 Rokid 推出的 JSAR 技术,恰好打破了这一壁垒:以 “可嵌入空间的 Web 运行时” 为核心,让开发者无需学习新的开发范式,仅用 JavaScript/TypeScript 等熟悉的 Web 技术栈,就能快速开发出支持 3D 物体、