很多智慧医院、智慧楼宇项目,招投标阶段并不需要「全系统上线」,而是要一份看得懂、点得动、加载不崩的 3D 演示。
纯长页面、平面拓扑图,很难讲清空间关系和动线。Three.js 很适合做这一层:浏览器里打开就能漫游,客户不用装客户端。
本文不讲大而全平台,只做一个最小可演示骨架:
- 空场景 + 灯光 + 相机控制
- 加载一层建筑/科室楼层模型(或用简易几何体占位)
- 实现轨道式漫游,保证演示不晕、不卡
场景可以是病房楼层,也可以是门诊、后勤、机房------方法通用。适合:前端想切入医疗/楼宇可视化、需要投标 Demo、想把能力做成模板的同学。
一、先定演示目标,而不是先堆特效
投标 / 方案演示里,客户真正关心的通常是三件事:
- 空间能不能看懂?→ 楼层轮廓、走廊、功能分区清晰
- 能不能自己转一转?→ 旋转、缩放、漫游流畅
- 以后能不能接数据?→ 预留点位 / 拾取扩展位(下期写)
所以第 1 版刻意做减法:
- 不上全院多楼栋
- 不上复杂着色器、后处理堆满
- 一栋/一层可漫游 + 稳定流畅观感
产品化思路:先做成「简易投标 Demo」档,再迭代点位弹窗、后端对接、多楼层切换。
二、技术选型(够用就好)
- Three.js:场景、相机、渲染
- GLTFLoader:加载 glb/gltf(工业交付最常见)
- OrbitControls:快速可交互
- 可选:DRACO / Meshopt 解码(模型一大就建议上,下期展开)
浏览器端交付的好处:客户当场用手机/笔记本打开链接就能看,减少「装环境」摩擦。
三、最小项目结构
text
ward-demo/
index.html
main.js
styles.css
models/
ward-floor.glb
index.html:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>医疗数字孪生 · 楼层漫游 Demo</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div id="app">
<canvas id="c"></canvas>
<div class="hud">医疗数字孪生 · 楼层漫游 Demo(拖拽旋转 / 滚轮缩放)</div>
</div>
<script type="module" src="./main.js"></script>
</body>
</html>
styles.css:
css
html, body, #app { margin: 0; height: 100%; overflow: hidden; background: #0b1220; }
#c { display: block; width: 100%; height: 100%; }
.hud {
position: fixed; left: 16px; bottom: 16px;
padding: 8px 12px; border-radius: 8px;
background: rgba(0,0,0,.55); color: #e8eefc; font: 13px/1.4 system-ui;
}
四、场景骨架:渲染循环 + 控制器
main.js(Three.js r160+,按你的打包方式改 import):
js
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputColorSpace = THREE.SRGBColorSpace;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0b1220);
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 2000);
camera.position.set(8, 6, 10);
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
controls.target.set(0, 1.2, 0);
controls.maxPolarAngle = Math.PI * 0.49;
controls.minDistance = 2;
controls.maxDistance = 40;
scene.add(new THREE.HemisphereLight(0xb1e1ff, 0x444466, 1.0));
const dir = new THREE.DirectionalLight(0xffffff, 1.2);
dir.position.set(5, 10, 7);
scene.add(dir);
// 占位:示意「一层功能区 + 走廊」,可替换为病房/门诊/机房等真实 GLB
function addPlaceholderFloor() {
const floor = new THREE.Mesh(
new THREE.BoxGeometry(20, 0.2, 12),
new THREE.MeshStandardMaterial({ color: 0x2a3b55 })
);
floor.position.y = -0.1;
scene.add(floor);
const room = new THREE.Mesh(
new THREE.BoxGeometry(3, 2.4, 4),
new THREE.MeshStandardMaterial({ color: 0x6ea8fe })
);
room.position.set(-4, 1.2, 0);
scene.add(room);
const corridor = new THREE.Mesh(
new THREE.BoxGeometry(10, 0.05, 2.2),
new THREE.MeshStandardMaterial({ color: 0x9aa7bd })
);
corridor.position.set(2, 0.02, 0);
scene.add(corridor);
}
addPlaceholderFloor();
async function loadFloorModel(url) {
const loader = new GLTFLoader();
const gltf = await loader.loadAsync(url);
const root = gltf.scene;
root.traverse((obj) => {
if (obj.isMesh) {
obj.castShadow = true;
obj.receiveShadow = true;
}
});
root.scale.setScalar(1);
scene.add(root);
}
// 有模型时取消注释:
// loadFloorModel('./models/floor.glb');
function onResize() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
}
addEventListener('resize', onResize);
function tick() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(tick);
}
tick();
五、从「能转」到「像投标 Demo」的 4 个细节
- 限制极角:避免相机钻到地板下
- 限制距离:防止滚轮飞到外太空
- 像素比封顶:setPixelRatio(Math.min(devicePixelRatio, 2)),笔记本更稳
- HUD 一句话:告诉客户怎么操作,减少演示冷场
如果客户要「走进去」的沉浸感,再上 PointerLock / 简易碰撞;投标第一版用 Orbit 往往更稳、更不容易晕。
六、模型从哪来?(务实路径)
- Day 0:盒子占位,先跑通交互与录屏
- Day 1--2:Blender 粗模 / 现成低模,走廊 + 功能区块面即可
- 交付前:客户 CAD/设计图重拓扑,控面数,避免直接丢超模
经验原则:投标 Demo 宁可「面数少但帧率稳」,也不要「很精美但手机打开白屏」。轻量化下期专门写。
七、你可以把这个骨架产品化成什么
- 简易投标演示 Demo:单层漫游 + 基础交互 → 适合方案汇报
- 可对接完整 3D 页:点位弹窗、设备状态、接口联调 → 下一阶段
- 源码模板:把灯光、控制器、加载器、HUD 收成可复用工程 → 适合重复售卖
同一套技术能力,场景可复用到病房、门诊、楼宇、园区;用标准化边界拆成不同产品,比每次从零谈需求更轻松。
八、小结与下期预告
今天完成的是数字孪生演示的「地基」:
- 场景 / 光 / 相机 / 控制器闭环
- 占位几何体或 GLB 可替换
- 演示友好的限制与 HUD