在三维场景中加载模型是最常见的需求之一。虽然可以直接使用 Three.js 的 GLTFLoader,但在不同投影方式下需要手动处理坐标转换,比较麻烦。今天就来学习 mapvthree 提供的 SimpleModel 类,看看它是如何简化这个过程的。
了解 SimpleModel
SimpleModel 是 mapvthree 对 Three.js 模型加载的封装,主要解决了以下问题:
原生 Three.js 加载方式的问题
如果直接使用 Three.js 的 GLTFLoader 加载模型:- import * as THREE from 'three';
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
- const loader = new GLTFLoader();
- loader.load('assets/models/tree/tree18.glb', gltf => {
- const model = gltf.scene;
-
- model.position.set(x, y, z);
- model.rotateX(Math.PI / 2);
- model.scale.setScalar(10);
-
- engine.add(model);
- });
复制代码 问题:这种方式只适用于平面投影(EPSG:3857),在 ECEF 等其他投影上,需要额外进行投影旋转,非常麻烦。
SimpleModel 的优势
SimpleModel 是对 Three.js 的封装,具有以下优势:
- 自动投影转换:自动根据当前投影进行坐标旋转,无需手动处理
- 简化接口:统一的配置参数,更易使用
- 多种加载方式:支持从 URL 加载,也支持传入已有的 Object3D 实例
- 格式兼容:支持所有 Three.js 支持的模型格式(glb、gltf 等)
- 坐标系转换:自动处理 Y-Up 到 Z-Up 的坐标系转换
- 事件监听:提供加载完成事件,方便后续处理
我的理解:SimpleModel 本质上是对 Three.js 的 GLTFLoader 和 Object3D 的封装,让我们不需要关心底层的投影转换细节,专注于业务逻辑。
第一步:基本使用 - 从 URL 加载模型
最简单的方式是从 URL 加载模型文件。
基本示例
- import * as mapvthree from '@baidumap/mapv-three';
- const container = document.getElementById('container');
- const engine = new mapvthree.Engine(container, {
- map: {
- center: [120.628, 27.786, 0],
- range: 1000,
- pitch: 80,
- projection: 'EPSG:3857',
- provider: null,
- },
- });
- // 加载单体模型
- const model = engine.add(new mapvthree.SimpleModel({
- name: '树木模型',
- object: 'assets/models/tree/tree18.glb',
- point: [120.628, 27.786, 0],
- scale: [10, 10, 10],
- rotation: [Math.PI / 2, 0, 0],
- }));
复制代码 我的发现:只需要提供模型路径和位置,引擎会自动处理加载和投影转换。
我的理解:
- object 参数:模型文件的 URL 路径,支持 glb/gltf 格式(以及所有 Three.js 支持的格式)
- point 参数:模型在地图上的位置,格式为 [经度, 纬度, 高度]
- scale 参数:模型缩放比例,格式为 [x缩放, y缩放, z缩放]
- rotation 参数:模型旋转角度,格式为 [roll, pitch, heading],单位为弧度
第二步:设置模型位置
模型位置使用地理坐标 [经度, 纬度, 高度] 来表示。
构造时设置位置
- const model = engine.add(new mapvthree.SimpleModel({
- object: 'assets/models/tree/tree18.glb',
- point: [120.628, 27.786, 0], // 经度、纬度、高度
- }));
复制代码 动态修改位置
- // 通过 point 属性修改
- model.point = [120.629, 27.787, 50];
- // 或通过 setTransform 方法
- model.setTransform({
- point: [120.629, 27.787, 50],
- });
复制代码 我的发现:可以随时动态修改模型位置,引擎会自动更新模型的实际坐标。
我的理解:
- 高度(z 值)是相对于地面的高度
- 引擎会自动将地理坐标转换为场景坐标
- 不同投影方式下的转换逻辑由引擎自动处理
第三步:设置模型旋转和缩放
模型的旋转和缩放也非常简单。
设置旋转
- const model = engine.add(new mapvthree.SimpleModel({
- object: 'assets/models/tree/tree18.glb',
- point: [120.628, 27.786, 0],
- rotation: [Math.PI / 2, 0, 0], // [roll, pitch, heading]
- }));
- // 动态修改旋转
- model.setTransform({
- rotation: [0, Math.PI / 4, Math.PI / 2],
- });
复制代码 我的理解:
- rotation[0](roll):绕 x 轴旋转
- rotation[1](pitch):绕 y 轴旋转
- rotation[2](heading):绕 z 轴旋转
- 单位是弧度,不是角度(角度需要转换:角度 * Math.PI / 180)
设置缩放
- const model = engine.add(new mapvthree.SimpleModel({
- object: 'assets/models/tree/tree18.glb',
- point: [120.628, 27.786, 0],
- scale: [10, 10, 10], // [x缩放, y缩放, z缩放]
- }));
- // 动态修改缩放
- model.setTransform({
- scale: [20, 20, 20],
- });
复制代码 我的发现:可以对 x、y、z 三个方向分别设置缩放比例,实现非均匀缩放。
使用 Three.js 的 Vector3
- import * as THREE from 'three';
- // 也可以使用 Three.js 的 Vector3
- model.setTransform({
- point: new THREE.Vector3(120.628, 27.786, 50),
- scale: new THREE.Vector3(15, 15, 15),
- rotation: new THREE.Vector3(Math.PI / 2, 0, 0),
- });
复制代码 我的理解:SimpleModel 兼容数组和 Vector3 两种格式,可以根据习惯选择。
第四步:监听加载完成事件
模型加载是异步的,可以通过事件监听器获取加载完成的通知。
监听 loaded 事件
- const model = engine.add(new mapvthree.SimpleModel({
- object: 'assets/models/tree/tree18.glb',
- point: [120.628, 27.786, 0],
- }));
- // 监听模型加载完成事件
- model.addEventListener('loaded', e => {
- console.log('模型加载完成:', e.value);
-
- // 在加载完成后可以进行后续操作
- // 例如:添加动画、修改材质等
- });
复制代码 我的发现:在 loaded 事件中,可以通过 e.value 获取加载的模型对象,进行进一步的自定义操作。
我的理解:
- 如果需要在加载完成后进行操作(如修改材质、添加动画),一定要使用 loaded 事件
- 不要在构造函数返回后立即操作模型,因为此时模型可能还没加载完成
第五步:动态更新模型变换
使用 setTransform 方法可以灵活地更新模型的位置、旋转和缩放。
只更新部分参数
- // 只更新位置
- model.setTransform({
- point: [120.629, 27.787, 50],
- });
- // 只更新旋转
- model.setTransform({
- rotation: [0, Math.PI / 4, 0],
- });
- // 只更新缩放
- model.setTransform({
- scale: [15, 15, 15],
- });
复制代码 我的发现:setTransform 方法的参数都是可选的,可以只更新需要修改的参数。
同时更新多个参数
- // 同时更新位置、旋转和缩放
- model.setTransform({
- point: [120.629, 27.787, 50],
- rotation: [Math.PI / 2, Math.PI / 4, 0],
- scale: [20, 20, 20],
- });
复制代码 我的理解:setTransform 是更新模型变换的推荐方式,比直接修改 position、rotation、scale 属性更安全。
第六步:理解 autoYUpToZUp 参数
很多三维模型(如从建模软件导出的模型)使用 Y 轴向上的坐标系,而地理场景通常使用 Z 轴向上。
autoYUpToZUp 的作用
- const model = engine.add(new mapvthree.SimpleModel({
- object: 'assets/models/tree/tree18.glb',
- point: [120.628, 27.786, 0],
- autoYUpToZUp: true, // 默认为 true
- }));
复制代码 我的理解:
- autoYUpToZUp = true(默认):自动将 Y-Up 坐标系转换为 Z-Up,模型会自动旋转 90 度
- autoYUpToZUp = false:不进行坐标系转换,保持模型原始方向
- 这个参数仅对通过 URL 加载的模型有效,对直接传入的 Object3D 实例无效
什么时候需要关闭
- // 如果模型本身就是 Z-Up,或者已经做了正确的旋转
- const model = engine.add(new mapvthree.SimpleModel({
- object: 'assets/models/building.glb',
- point: [120.628, 27.786, 0],
- autoYUpToZUp: false, // 不需要自动转换
- }));
复制代码 我的发现:如果发现加载的模型方向不对(如倒着的、躺着的),可能是 autoYUpToZUp 的设置问题。
第七步:直接传入 Object3D 实例
除了从 URL 加载,还可以直接传入已经加载好的 Three.js Object3D 实例。
传入已有的 Object3D
- import * as THREE from 'three';
- import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
- // 先用 Three.js 原生方式加载
- const loader = new GLTFLoader();
- loader.load('assets/models/tree/tree18.glb', gltf => {
- const mesh = gltf.scene;
-
- // 将加载好的模型传给 SimpleModel
- const model = engine.add(new mapvthree.SimpleModel({
- name: '树木模型',
- object: mesh, // 传入 Object3D 实例
- point: [120.628, 27.786, 0],
- scale: [10, 10, 10],
- }));
- });
复制代码 我的理解:
- 这种方式适合需要对模型进行预处理的场景
- 例如:修改材质、合并多个模型、自定义加载逻辑等
- 传入 Object3D 时,autoYUpToZUp 参数不生效
传入自定义创建的 Mesh
- import * as THREE from 'three';
- // 创建一个自定义的立方体
- const geometry = new THREE.BoxGeometry(10, 10, 10);
- const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
- const cube = new THREE.Mesh(geometry, material);
- // 将自定义 Mesh 传给 SimpleModel
- const model = engine.add(new mapvthree.SimpleModel({
- name: '立方体',
- object: cube,
- point: [120.628, 27.786, 0],
- }));
复制代码 我的发现:可以传入任何 Three.js 的 Object3D 对象,不仅限于从文件加载的模型。
我的理解:这展示了 SimpleModel 的灵活性,它本质上是一个位置和变换管理器,可以管理任何 Three.js 对象。
第八步:完整示例
我想写一个完整的示例,把学到的都用上:- import * as mapvthree from '@baidumap/mapv-three';
- import * as THREE from 'three';
- const container = document.getElementById('container');
- const engine = new mapvthree.Engine(container, {
- map: {
- center: [120.628, 27.786, 0],
- range: 1000,
- pitch: 80,
- projection: 'EPSG:3857',
- provider: null,
- },
- rendering: {
- enableAnimationLoop: true,
- },
- });
- // 加载单体模型
- const model = engine.add(new mapvthree.SimpleModel({
- name: '树木模型',
- object: 'assets/models/tree/tree18.glb',
- point: [120.628, 27.786, 0],
- scale: [10, 10, 10],
- rotation: [Math.PI / 2, 0, 0],
- autoYUpToZUp: true, // 自动坐标系转换
- }));
- // 监听加载完成事件
- model.addEventListener('loaded', e => {
- console.log('模型加载完成:', e.value);
-
- // 可以在这里进行后续操作
- // 例如:修改材质、添加动画等
- });
- // 动态更新模型位置(例如:5秒后移动模型)
- setTimeout(() => {
- model.setTransform({
- point: [120.629, 27.787, 50],
- rotation: [Math.PI / 2, Math.PI / 4, 0],
- scale: [15, 15, 15],
- });
- }, 5000);
复制代码 我的感受:掌握了 SimpleModel,加载单体模型变得非常简单,不需要关心投影转换的细节!
第九步:踩过的坑
作为一个初学者,我踩了不少坑,记录下来避免再犯:
坑 1:模型不显示
原因:模型路径错误,或者模型文件格式不支持。
解决:
- 检查模型文件路径是否正确
- 确认模型文件格式是否为 glb/gltf(或其他 Three.js 支持的格式)
- 打开浏览器控制台查看是否有加载错误
- 检查模型位置是否在视野范围内
坑 2:模型方向不对
原因:坐标系转换问题,或者旋转设置不正确。
解决:
- 尝试修改 autoYUpToZUp 参数(true/false)
- 调整 rotation 参数,尝试不同的旋转角度
- 在建模软件中检查模型的坐标系和方向
坑 3:模型太大或太小
原因:模型原始尺寸与场景比例不匹配。
解决:
- 调整 scale 参数,尝试不同的缩放比例
- 如果模型太小看不见,尝试放大 10 倍、100 倍
- 如果模型太大,尝试缩小到 0.1、0.01
坑 4:动态修改不生效
原因:在模型加载完成前就进行了修改操作。
解决:
- 使用 loaded 事件,确保在模型加载完成后再操作
- 使用 setTransform 方法而不是直接修改属性
- 检查是否启用了 enableAnimationLoop(某些操作需要渲染循环)
坑 5:不同投影下模型位置不对
原因:没有正确理解 SimpleModel 的自动投影转换。
解决:
- 确认传入的是地理坐标 [经度, 纬度, 高度],而不是场景坐标
- 不要手动计算投影转换,SimpleModel 会自动处理
- 如果需要场景坐标,使用 engine.map.projectArrayCoordinate() 转换
坑 6:传入 Object3D 时 autoYUpToZUp 不生效
原因:autoYUpToZUp 只对通过 URL 加载的模型有效。
解决:
- 如果传入 Object3D 实例,需要手动处理坐标系转换
- 或者使用 rotation 参数手动旋转
- 或者在加载时就处理好坐标系
我的学习总结
经过这一天的学习,我掌握了:
- SimpleModel 的本质:对 Three.js 加载方式的封装,自动处理投影转换
- 支持的格式:所有 Three.js 支持的模型格式(glb、gltf 等)
- 两种加载方式:从 URL 加载,或传入 Object3D 实例
- 位置设置:使用地理坐标 [经度, 纬度, 高度]
- 旋转和缩放:使用数组或 Vector3 格式
- 动态更新:使用 setTransform 方法
- 坐标系转换:理解 autoYUpToZUp 的作用
- 事件监听:使用 loaded 事件处理加载完成后的逻辑
我的感受:SimpleModel 让加载单体模型变得非常简单,不需要关心底层的投影转换细节。它本质上是对 Three.js 的封装,所以如果熟悉 Three.js,上手会非常快!
下一步计划:
- 学习如何加载和管理多个模型
- 学习 LODModel 实现性能优化
- 学习如何给模型添加动画
学习笔记就到这里啦!作为一个初学者,我觉得 SimpleModel 是一个非常实用的工具类,它简化了三维模型的加载和管理。关键是要理解它是对 Three.js 的封装,支持所有 Three.js 支持的模型格式,并能自动处理不同投影方式下的坐标转换。希望我的笔记能帮到其他初学者!大家一起加油!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |