深入理解MapvTHREE Rendering渲染管理器的核心API
一句话总结
engine.rendering 是mapvthree的渲染管理中枢,掌握它的核心API,你就能精确控制3D场景的每一帧渲染!
🤔 为什么要聊这个?
不知道大家有没有遇到过这样的场景:
- 想在每一帧渲染前做一些计算,比如根据相机位置更新某些数据?
- 想根据地图缩放层级动态显示或隐藏某些图层?
- 想手动控制渲染频率来优化性能?
- 想在特定时机暂停渲染来进行截图?
如果你有这些需求,那今天这篇文章就是为你准备的!
Rendering(渲染管理器)是mapvthree引擎的核心组件之一,它随着Engine初始化自动创建,通过engine.rendering访问。
📖 渲染生命周期:两个关键的钩子函数
在介绍具体API之前,我们先来理解一下渲染的生命周期。每一帧的渲染大致分为这几个阶段:
准备阶段(Prepare) → 更新阶段(Update) → 渲染阶段(Render)
mapvthree为我们提供了两个关键的钩子函数,让我们能在不同阶段插入自己的逻辑:
1. PrepareRenderListener - 渲染准备阶段
// 添加监听器
engine.addPrepareRenderListener((engine, renderState) => {
// 在这里执行准备工作
// 比如根据相机状态更新数据、计算可见性等
console.log('准备渲染,当前帧数:', renderState.frameCount);
});
// 移除监听器
engine.removePrepareRenderListener(myCallback);
什么时候用?
- 需要根据相机位置/缩放等信息做决策时
- 需要在场景物体更新之前准备数据时
- 实现LOD(细节层次)逻辑时
2. BeforeRenderListener - 渲染前阶段
// 添加监听器
engine.addBeforeRenderListener((engine, renderState) => {
// 在这里执行渲染前的最后准备
// 此时场景物体已经完成了位置更新
console.log('即将渲染...');
});
// 移除监听器
engine.removeBeforeRenderListener(myCallback);
什么时候用?
- 需要在所有物体更新完成后做最终调整时
- 需要获取最终的场景状态进行计算时
两者的区别
| 特性 | PrepareRenderListener | BeforeRenderListener |
|---|---|---|
| 执行时机 | 场景物体更新之前 | 场景物体更新之后 |
| 适用场景 | 数据准备、可见性判断 | 最终调整、状态获取 |
| 回调参数 | engine, renderState | engine, renderState |
🎬 渲染控制:手动触发渲染
requestRender() - 请求渲染
当你修改了场景中的某些数据,需要立即看到效果时,可以调用这个方法:
// 修改了一些数据后...
myPoint.color = '#ff0000';
// 请求渲染更新
engine.requestRender();
💡:如果你已经开启了enableAnimationLoop,通常不需要手动调用requestRender(),因为引擎会自动在每帧进行渲染。
⚡ 动画循环:控制渲染频率
enableAnimationLoop - 开启/关闭循环渲染
// 开启循环渲染(适合动态场景)
engine.rendering.enableAnimationLoop = true;
// 关闭循环渲染(适合静态场景,节省性能)
engine.rendering.enableAnimationLoop = false;
animationLoopFrameTime - 设置渲染间隔
// 设置渲染间隔为40ms(约25fps)
engine.rendering.animationLoopFrameTime = 40;
// 设置为16ms(约60fps)
engine.rendering.animationLoopFrameTime = 16;
使用场景:
- 有动画效果时:开启
enableAnimationLoop - 静态展示时:关闭循环渲染,通过
requestRender()按需渲染 - 移动端性能优化:适当增大
animationLoopFrameTime降低帧率
⏸️ 暂停与恢复:freezeUpdate
有时候我们需要暂停渲染,比如进行截图、调试或者节省资源:
// 暂停渲染
engine.rendering.freezeUpdate = true;
// 恢复渲染
engine.rendering.freezeUpdate = false;
典型应用:截图前暂停动画,保证画面稳定。
📐 分辨率相关设置
pixelRatio - 设备像素比
// 获取当前像素比
console.log(engine.rendering.pixelRatio);
// 设置像素比(影响清晰度和性能)
engine.rendering.pixelRatio = 2; // 高清
engine.rendering.pixelRatio = 1; // 标准(更好的性能)
resolution - 渲染分辨率
// 获取当前分辨率
const res = engine.rendering.resolution;
console.log(`宽度: ${res.x}, 高度: ${res.y}`);
🌤️ 天空与天气
Rendering还提供了对天空和天气实例的访问:
// 获取天空实例
const sky = engine.rendering.sky;
if (sky) {
sky.time = 3600 * 15; // 设置为下午3点
}
// 获取天气实例
const weather = engine.rendering.weather;
if (weather) {
weather.weather = 'rainy'; // 设置为下雨
}
🔧 其他有用的属性
// 获取Three.js渲染器
const renderer = engine.rendering.renderer;
// 获取相机
const camera = engine.rendering.camera;
// 获取场景
const scene = engine.rendering.scene;
// 获取渲染状态(包含视图矩阵、帧数等信息)
const renderState = engine.rendering.renderState;
🎯 实战案例:根据地图层级动态控制图层显隐
现在来看一个实际的应用场景:当地图缩放到特定层级时,动态加载或卸载矢量图层。
这在地球级别的应用中非常常见——远距离看地球时不需要显示详细的矢量数据,只有放大到一定程度才需要加载。
需求分析
- 当 zoom > 15 时,显示矢量图层
- 当 zoom <= 15 时,隐藏矢量图层
- 状态切换时避免重复操作
代码实现
import * as mapvthree from '@baidu/mapv-three';
// 创建引擎(使用地球投影)
const engine = new mapvthree.Engine(document.getElementById('map_container'), {
map: {
projection: 'ecef',
},
});
// 添加天空
const sky = engine.add(new mapvthree.DynamicSky());
// 创建地图视图(初始不带矢量图层)
const mapView = window.mapview = engine.add(new mapvthree.MapView({
terrainProvider: null,
vectorSurfaceOptions: {
restrictViewportLevel: true,
},
}));
// 用于跟踪当前 vectorProvider 的状态
let currentVectorProvider = null;
let lastZoomState = null; // 记录上一次的状态,避免重复操作
// 使用 PrepareRenderListener 实现层级控制
engine.addPrepareRenderListener(() => {
const currentZoom = engine.map.getZoom();
const shouldShowVector = currentZoom > 15;
// 只在状态变化时才操作,避免每帧都执行
if (lastZoomState !== shouldShowVector) {
if (shouldShowVector && !currentVectorProvider) {
// zoom > 15 且当前没有 vectorProvider,添加它
console.log('添加 BaiduVectorTileProvider,zoom:', currentZoom);
currentVectorProvider = new mapvthree.BaiduVectorTileProvider({
// 配置选项
});
mapView.setVectorProvider(currentVectorProvider);
}
else if (!shouldShowVector && currentVectorProvider) {
// zoom <= 15 且当前有 vectorProvider,删除它
console.log('删除 BaiduVectorTileProvider,zoom:', currentZoom);
mapView.setVectorProvider(null);
currentVectorProvider = null;
}
lastZoomState = shouldShowVector;
}
});
关键点解析
-
使用
addPrepareRenderListener:在每帧渲染前检查地图缩放层级 -
状态缓存
lastZoomState:避免每帧都执行添加/移除操作,只在状态真正变化时才操作
两种方式的对比
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
setVectorProvider | 动态控制矢量图层显隐 | 轻量、快速 | 只能控制vectorProvider |
| 删除重建MapView | 切换整个地图风格 | 彻底、干净 | 开销较大,会重新加载所有数据 |
为什么用 PrepareRenderListener 而不是 BeforeRenderListener?
因为我们需要在场景物体更新之前就做出决策——是否需要加载新的图层。如果用 BeforeRenderListener,可能会导致一帧的延迟。
核心要点回顾
让我们用一张表来总结今天学到的内容:
| API | 作用 | 常用场景 |
|---|---|---|
addPrepareRenderListener | 在渲染准备阶段执行回调 | LOD、动态加载、可见性判断 |
addBeforeRenderListener | 在渲染前执行回调 | 最终状态调整、数据同步 |
requestRender | 手动请求渲染 | 静态场景数据更新后 |
enableAnimationLoop | 开启循环渲染 | 动态场景、动画效果 |
animationLoopFrameTime | 设置渲染间隔 | 性能优化、帧率控制 |
freezeUpdate | 暂停/恢复渲染 | 截图、调试 |
pixelRatio | 设备像素比 | 清晰度与性能平衡 |
你可以思考一下:
- 如果要实现一个"性能模式",在用户不操作时降低帧率,应该怎么做?
- 如何利用这些API实现一个简单的性能监控面板?
如果这篇文章对你有帮助,别忘了点个赞支持一下!👍