普通视图

发现新文章,点击刷新页面。
昨天以前首页

数字孪生大屏必看:Cesium 3D 模型选中交互,3 种高亮效果拿来就用!

作者 李剑一
2026年3月15日 16:23

接前文,3D模型加载到页面以后肯定要执行各种各样的操作,模型在大屏上的主要作用是执行相应的建筑物交互。

问题

这里需要注意3D模型的加载仍然存在一些问题。

首先是如果你使用的GLTF文件,在某些特殊情况下可能导致模型的网格加载异常,会出现无法选中的情况。

这一点我目前没发现有什么太好的解决方案,所以这里采用GLB文件规避掉了这个问题。

image.png

如果你手里只有GLTF文件,可以考虑使用 Blender 进行一下转换。

另外模型本身离地高度都是 0 的话可能存在无法选中的问题,所以这里建议背景设置离地高度为 -0.5,普通模型正常为 0

还有就是模型选中以后的显示问题。

解决方案

模型无法选中和选择错误的问题通过两个方案进行规避,一个是height离地高度,一个是pickable拾取

首先设置背景的离地高度为 -0.5,普通可以选中的模型离地高度为 0

image.png

另外设置一下 pickPriority拾取优先级pickable是否可拾取

最后就是设置模型的选中效果,这里我简单写了三种效果给大家选择,可以自行决定。

实际代码

模型添加的时候增加拾取优先级参数:

const buildingEntity = viewer.entities.add({
    id: options.id, // 唯一ID,点击交互时识别核心
    name: options.properties.name || options.id, // 建筑名称(可选)
    position: position,
    orientation: orientation, // 控制模型朝向
    pickPriority: options.pickPriority, // (核心)添加拾取优先级
    pickable: options.pickable,  // (核心)允许拾取
    model: {
        uri: options.modelUrl, // glTF/glb 模型路径
        scale: options.scale || 1.0, // 保证模型真实比例(建模时单位为米)
        minimumPixelSize: 0, // 取消最小像素限制,模型随地图缩放正常变化
        maximumScale: 20000, // 最大缩放限制
        runAnimations: false, // 静态建筑关闭动画(节省性能)
        clampToGround: true, // 贴地(自动适配地形高度,可选)
    },
    properties: options.properties || {}, // 绑定自定义属性(如状态接口)
});

这里的参数其实在模型选择那里可以再增加一层判断。

模型选中方法主要有三种:

// 1. 轮廓线方案
/**
 * 选中指定模型
 * @param {Cesium.Entity} entity 要选中的模型实体
 * @param {Function} onSelect 选中回调(如展示状态弹窗)
 * @param {Function} onUnselect 取消选中回调(用于先取消之前的选中)
 */
const selectModel = (entity, onSelect, onUnselect) => {
    // 取消之前的选中状态(包括回调执行)
    if (selectedEntity) {
        unselectModel(onUnselect);
    }

    // 校验是否为模型实体
    if (!entity || !entity.model) {
        console.warn('❌ 选中的不是模型实体');
        return;
    }

    // 标记为当前选中实体
    selectedEntity = entity;

    // 轮廓线高亮(更醒目,性能略高,需 Cesium 1.90+)
    entity.model.outlineColor = Cesium.Color.RED;
    entity.model.outlineWidth = 2;
    entity.model.outline = true;

    // 执行选中回调(绑定业务逻辑)
    if (typeof onSelect === 'function') {
        onSelect(entity);
    }
};

如果没有特殊要求,轮廓线方案其实非常简单实用。

// 2. 颜色高亮,修改模型材质
originalModelMaterial = entity.model.color || Cesium.Color.WHITE.clone();
entity.model.color = Cesium.Color.fromCssColorString('#fb0528').withAlpha(0.8);
// 强制刷新
viewer.scene.requestRender();

这里需要注意,修改模型材质一定要执行强制刷新

// 3. 模型遮罩效果
viewer.entities.add({
    position: entity.position,
    orientation: entity.orientation,
    model: {
        uri: entity.model.uri, // 复用同一个模型文件
        scale: 0.35, // 稍微大一点
        color: Cesium.Color.fromCssColorString('#409EFF').withAlpha(0.5), // 半透明蓝
        silhouetteColor: Cesium.Color.BLUE, // 可选:配合轮廓
        silhouetteSize: 2.0
    }
});

这种效果也非常不错,复用模型文件稍大一号,让他完美的遮住原始模型,给出一个透明色作为材质,显得很有科技感。

总结

模型设计的时候推荐大家优先使用 GLB 格式替代 GLTF 规避网格加载异常问题,

另外通过pickPriority(拾取优先级)和pickable(是否可拾取)参数,从逻辑层面控制模型的交互规则,彻底解决 点错模型、点不到模型 的问题。

后续增加相关图标的单击和操作,实现小型设备的交互。

解决 Cesium 网络卡顿!5 分钟加载天地图,内网也能流畅用,附完整代码

作者 李剑一
2026年3月13日 09:30

接上文,之前使用 Cesium.Ion 已经成功将地球效果展示出来了,飞入效果也非常不错。详细可以参考这篇文章:# 拿来就用!Vue3+Cesium 飞入效果封装,3D大屏多场景直接复用

但是仍然存在一个问题没解决, Cesium.Ion 的服务部署在外面,但是我们这边因为众所周知的原因网络受到一些限制。

image.png

虽然Cesium的服务是不被禁止访问的,但是访问速度和丢包率也是异常"喜人",所以之前还是打算在这个地方做一下优化。

解决思路

其实想要解决这个问题也非常的简单,将卫星地图(瓦片地图)换成我们自己的服务即可,访问咱们这边的服务是没啥问题的。

目前基本上是两个思路

  • 使用在线服务,主要是天地图、腾讯、高德等等几家
  • 使用离线服务,自己下载瓦片地图,自己搭建服务

这两种路线我都用了,可以说如果你有资源的话,那么我强烈建议你自己搭建离线地图服务,效果非常好。

最关键的是这套系统就能够实现离线部署了,在某些私有化场景下非常契合。

image.png

但是两个问题需要解决,资源存储空间

目前资源问题勉强能凑合解决一下,但是存储空间确实没有。毕竟地图下载下来也是真不小,另外目前没有离线部署的需求,所以考虑使用在线服务。

在线服务不推荐腾讯、高德几家,一来配置起来并不好整,我之前尝试腾讯的,鼓捣了半天仅仅弄好了个矢量图,卫星图花了一下午时间也没弄好。

切换到天地图,只用了5分钟就齐活了。

解决方案

使用天地图服务首先去天地图官网注册个账号,地址给大家放一下:www.tianditu.gov.cn/

首先进入控制台,选择开发管理下的开发者认证,认证一下个人开发者

只有这样才能够创建应用,生成tk

image.png

然后进入开发管理 > 应用管理 > 我的应用 > 创建新应用,简单填写一下必要信息,就能够创建一个新应用了。

复制一下应用密钥(tk)

实际代码

初始化加载 Cesium图层 的地方设置为 false

// 初始化 Cesium 地球
const initCesium = async () => {
    // 创建 Cesium 视图实例
    viewer.value = new Cesium.Viewer('cesiumContainer', {
        // 隐藏默认控件,简化界面
        timeline: false,
        animation: false,
        baseLayerPicker: false,
        geocoder: false,
        homeButton: false,
        infoBox: false,
        sceneModePicker: false,
        navigationHelpButton: false,
        // 开启深度检测,避免地形闪烁
        scene3DOnly: true,
        requestRenderMode: true,
        // 不加载默认的 Cesium Ion 影像图层
        baseLayer: false
    });

    // 隐藏 Cesium 版权信息(可选)
    viewer.value._cesiumWidget._creditContainer.style.display = 'none';

    // 等待 Cesium 完全加载完成
    await waitForCesiumFullyLoaded();
    
    // 添加天地图地图影像图层
    addTianDituImageryLayer();

    // 触发 cesiumReady 事件
    emit('cesiumReady', viewer.value);
}

天地图图层主要有两部分,一个是卫星影像底图,另一个是注记图层,当然如果不考虑名称,注记图层可以不添加。

/**
 * 添加天地图地图影像图层(卫星图 + 注记)
 */
const addTianDituImageryLayer = () => {
    if (!viewer.value) return;

    // 使用天地图卫星影像,tk (密钥)
    const webKey = '你的tk';

    // 天地图卫星影像底图
    const imgProvider = new Cesium.WebMapTileServiceImageryProvider({
        url: 'https://t{s}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&FORMAT=tiles&tk=' + webKey,
        layer: 'tdtImgBasicLayer',
        style: 'default',
        format: 'image/jpeg',
        tileMatrixSetID: 'GoogleMapsCompatible',
        maximumLevel: 18,
        minimumLevel: 1,
        subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
        credit: new Cesium.Credit('天地图'),
        // 启用 CORS
        enablePickFeatures: false
    });

    // 添加卫星影像图层
    viewer.value.imageryLayers.addImageryProvider(imgProvider);

    // 天地图注记图层(地名标注)
    const ciaProvider = new Cesium.WebMapTileServiceImageryProvider({
        url: 'https://t{s}.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&TILEMATRIX={TileMatrix}&TILEROW={TileRow}&TILECOL={TileCol}&FORMAT=tiles&tk=' + webKey,
        layer: 'tdtAnnoLayer',
        style: 'default',
        format: 'image/jpeg',
        tileMatrixSetID: 'GoogleMapsCompatible',
        maximumLevel: 18,
        minimumLevel: 1,
        subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
        credit: new Cesium.Credit('天地图注记'),
        // 启用 CORS
        enablePickFeatures: false
    });

    // 添加注记图层(叠加在影像之上)
    viewer.value.imageryLayers.addImageryProvider(ciaProvider);

    console.log('卫星影像加载完成!');
};

这里需要注意:记得将 enablePickFeatures 设为false,避免出现跨域问题。

总结

后续看是否有合适的项目,我会将离线地图的资源和创建方式分享给大家。

如果你的资源足够强,甚至能看到非常精细的卫星图像。

离线地图的玩法也远比在线地图要多得多,你甚至可以DIY某个地方的卫星图像,做出现实版的我的世界

另外需要注意,天地图的API调用是有限制的,详情可以参考下图。

20260309-限额.png

❌
❌