百度地图MapvTHREE Rendering渲染管理器的核心API

4 阅读6分钟

深入理解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);

什么时候用?

  • 需要在所有物体更新完成后做最终调整时
  • 需要获取最终的场景状态进行计算时

两者的区别

特性PrepareRenderListenerBeforeRenderListener
执行时机场景物体更新之前场景物体更新之后
适用场景数据准备、可见性判断最终调整、状态获取
回调参数engine, renderStateengine, 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;
    }
});

关键点解析

  1. 使用 addPrepareRenderListener:在每帧渲染前检查地图缩放层级

  2. 状态缓存 lastZoomState:避免每帧都执行添加/移除操作,只在状态真正变化时才操作

两种方式的对比

方式适用场景优点缺点
setVectorProvider动态控制矢量图层显隐轻量、快速只能控制vectorProvider
删除重建MapView切换整个地图风格彻底、干净开销较大,会重新加载所有数据

为什么用 PrepareRenderListener 而不是 BeforeRenderListener?

因为我们需要在场景物体更新之前就做出决策——是否需要加载新的图层。如果用 BeforeRenderListener,可能会导致一帧的延迟。

核心要点回顾

让我们用一张表来总结今天学到的内容:

API作用常用场景
addPrepareRenderListener在渲染准备阶段执行回调LOD、动态加载、可见性判断
addBeforeRenderListener在渲染前执行回调最终状态调整、数据同步
requestRender手动请求渲染静态场景数据更新后
enableAnimationLoop开启循环渲染动态场景、动画效果
animationLoopFrameTime设置渲染间隔性能优化、帧率控制
freezeUpdate暂停/恢复渲染截图、调试
pixelRatio设备像素比清晰度与性能平衡

你可以思考一下:

  1. 如果要实现一个"性能模式",在用户不操作时降低帧率,应该怎么做?
  2. 如何利用这些API实现一个简单的性能监控面板?

如果这篇文章对你有帮助,别忘了点个赞支持一下!👍