3D 室内地图
github 地址:three-indoor-map
技术栈:Vue.js | Three.js | Typescript 等
商场室内地图,商场后台管理系统内的核心模块,为商场运营人员提供客行分析服务,主要功能包括以下内容:
-
地图加载(多层 3D 地图 / 单层 2D 地图)
-
图片文字精灵
-
行为轨迹动画
-
热力图
-
闪点动画
-
地图标注 / 编辑
技术细节
场景树优化
空间结构分割
使用包围盒层次,来组织商场的店铺和楼层;把每层楼划分为包围盒,构建出一个层次结构,这样可以快速剔除不可见的楼层和店铺,提高渲染和交互的性能。
步骤:
1、构建视锥体:根据相机的位置、视野角度、近裁剪面和远裁剪面等参数,构建相机的视锥体。视锥体通常通过六个面来定义,包括近裁剪面、远裁剪面和四个侧面。
2、对象包围盒检测:对场景中的每个物体,使用其包围盒(边界框)来检测是否与视锥体相交。包围盒是一个简单的几何形状,用于粗略表示物体的边界范围。
3、视锥体剔除:根据包围盒和视锥体的相交关系,判断物体是否位于视锥体内部。如果一个物体的包围盒与视锥体不相交,那么可以确定该物体完全位于视野外,可以将其剔除。
4、可见物体渲染:只有通过视锥体剔除的物体才会被认为是可见的,它们将进入渲染管线进行后续的光栅化、着色和投影等操作。
层级加载
将不同楼层分批下载,根据视野范围动态加载和卸载楼层数据,只加载当前楼层及附近楼层的店铺信息,减少内存占用和加载时间,提高场景响应速度;
数据压缩和优化
商场地图的数据进行压缩和优化,以减少存储空间和加载时间。可以使用合适的压缩算法对地图数据进行压缩,并优化数据结构,以提高数据读取和处理的效率。
异步加载和级别细节
结合异步加载和级别细节的技术,根据观察者的位置和需要,动态加载不同细节级别的模型和纹理。可以根据观察者的距离和视野范围,选择加载低细节的模型和纹理,以减少渲染开销。
轨迹动画
核心思路
1、创建几何体:表示运动的物体
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
2、定义数组存储坐标点(这些坐标点将用于定义动画的路径)
const points = [
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(2, 0, 2),
new THREE.Vector3(0, 2, 4),
new THREE.Vector3(-2, 0, 2),
new THREE.Vector3(0, -2, 0),
];
3、创建 Tween.js 的动画对象
const tween = new TWEEN.Tween(sphere.position)
.to(points[points.length - 1], 5000) // 结束值和持续时间
.onUpdate(() => {
// 更新物体的位置
sphere.position.copy(sphere.position);
})
.start();
4、创建一个更新函数,在每一帧中更新动画对象
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
renderer.render(scene, camera);
}
animate();
优化
WebWorker:数据分块处理、数据压缩算法(减小传输数据量)
数据分块
将大的数据集分割成较小的块,每次将一个块发送给 Web Worker 进行处理。这样可以避免一次性发送大量数据,减少数据传输的延迟和开销;
在 Web Worker 内部,对每个块进行处理,并返回处理后的结果。主线程接收到结果后,可以根据需要将多个块的结果合并在一起;
数据压缩
Gzip 算法;在主线程里压缩数据,发送压缩后的数据给 WebWorker;在 WebWorker 内部进行解压缩处理;
三维坐标转换二维坐标
通过 Projector 对象的 projectVector 方法(3 → 2)
// 创建一个 Projector 对象
const projector = new THREE.Projector();
// 定义一个三维坐标
const position = new THREE.Vector(x, y, z);
// 使用 projectVector 方法将三维坐标转换为二维坐标
const screenPosition = projector.projectVector(position, camera);
通过 Vector3 对象的 project 方法(3 → 2)
const position = new THREE.Vector3(x, y, z);
position.project(camera);
使用 Raycaster 对象,根据屏幕上的二维坐标发射一条射线,并检测射线与与场景中的物体是否相交,从而确定三维坐标(2 → 3)
碰撞检测
给每个店铺模型定义一个碰撞体积,AABB 盒子,较大的碰撞体积表示这家店铺更大,更重要,需要在更远的距离显示详细信息;
碰撞检测:在每个帧中,使用碰撞检测算法(包围盒/Ammo.js),检测观察者和店铺模型的碰撞关系;
根据检测结果,决定是否显示店铺的详细信息;