Cesium如何高性能的实现上万条道路的流光穿梭效果

大家好,我是日拱一卒的攻城师不浪,专注可视化、数字孪生、前端、nodejs、AI学习、GIS等学习沉淀,这是2024年输出的第20/100篇文章;

前言

在智慧城市的项目中,经常会碰到这样一个需求:领导要求将全市的道路都能够用一个光流效果展示,能够一眼了解整个城市的路网概况。

Cesium高性能实现道路流光,并附修改底图颜色

今天,我们将在Cesium中从零到一实现这样的效果,一共2种方案可供选择:

  • entity实现:性能一般;
  • primitives实现:性能刚刚滴;

entity实现

首先我们需要拿到渲染道路所需要的数据,可以选择geojson数据格式,直接用Cesium.GeoJsonDataSource加载数据。

// 使用entity渲染
let _dataSource = null;
const material = new RoadThroughLine(1000, "/images/spriteline.png");
const onStartEntity = () => {
  // 道路闪烁线
  _dataSource = new Cesium.GeoJsonDataSource();
  _dataSource.load("/json/qdRoad_less.geojson").then(function (dataSource) {
    // 获取每条道路的实体
    const entities = dataSource.entities.values;
    // 设置每条道路的属性,宽度和材质等
    for (let i = 0; i < entities.length; i++) {
      let entity = entities[i];
      entity.polyline.width = 1.7;
      entity.polyline.material = material;
    }
  });
  viewer.dataSources.add(_dataSource);
};

啊?这就完了?细心的小伙伴可能发现了RoadThroughLine这个类,对,这不是Cesium提供的,而是需要我们自己封装,主要用来给道路赋予材质以及动画渲染的。

entity有一个属性是material,这个属性接收用户自定义材质,这个material需要的值是一个类的实例化,所以需要先去定义好这个类。

新开一个文件定义:

import * as Cesium from "cesium";
// 构造函数
function Spriteline1MaterialProperty(duration, image) {
  this._definitionChanged = new Cesium.Event(); // Cesium的事件订阅
  this.duration = duration; // 参数:光流的持续时间
  this.image = image; // 参数:光流的材质贴图
  this._time = performance.now(); // 记录时间线
}
Object.defineProperties(Spriteline1MaterialProperty.prototype, {
  isConstant: {
    get: function () {
      return false;
    },
  },
  definitionChanged: {
    get: function () {
      return this._definitionChanged;
    },
  },
  color: Cesium.createPropertyDescriptor("color"), // createPropertyDescriptor为color属性创建'setter'和'getter'的函数”
  duration: Cesium.createPropertyDescriptor("duration"),
});
// 设置材质类型名称
Spriteline1MaterialProperty.prototype.getType = function (time) {
  return "Spriteline1";
};
// 设置材质的值
Spriteline1MaterialProperty.prototype.getValue = function (time, result) {
  if (!Cesium.defined(result)) {
    result = {};
  }
  result.image = this.image;
  result.time =
    ((performance.now() - this._time) % this.duration) / this.duration;
  return result;
};

Cesium.Material.Spriteline1Type = "Spriteline1";
// 着色器代码
Cesium.Material.Spriteline1Source = `
// 定义一个名为czm_getMaterial的函数,它接受一个czm_materialInput类型的参数materialInput
czm_material czm_getMaterial(czm_materialInput materialInput)
{
// 使用Cesium提供的函数来获取默认材质。
czm_material material = czm_getDefaultMaterial(materialInput);
 // 从传入的materialInput中获取二维纹理坐标。
vec2 st = materialInput.st; 
// 使用texture函数对指定的纹理图像进行采样,并使用fract函数来实现纹理的流动效果。
// 这里的speed变量控制流动速度,用于实现动态效果。
vec4 colorImage = texture(image, vec2(fract(st.s - time), st.t));
// 将采样到的透明度附着给材质的透明度alpha属性
material.alpha = colorImage.a;
// 将采样得到的纹理的rgb值乘以1.5,设置为材质的diffuse颜色。
// 这里乘以1.5是为了增强颜色的亮度。
material.diffuse = colorImage.rgb * 1.5 ;
return material;
}
`;
// _materialCache是Cesium.Material的私有属性,用来缓存自定义材质
Cesium.Material._materialCache.addMaterial(Cesium.Material.Spriteline1Type, {
  fabric: {
    type: Cesium.Material.Spriteline1Type,
    // uniforms的属性都是传给着色器代码的Spriteline1Source
    uniforms: {
      color: new Cesium.Color(1, 0, 0, 0.5),
      image: "",
      transparent: true,
      time: 20,
    },
    source: Cesium.Material.Spriteline1Source,
  },
  translucent: function (material) {
    return true;
  },
});

export default Spriteline1MaterialProperty;

流光材质图片

OK,代码注释已经都标明了,我们只需要实例化这个类,就可以渲染成功了。

primitive

如果是数据量比较小的情况下,entity渲染性能还能接受,但如果是一个市甚至是一个省的街道,那直接就把甲方爸爸卡到医院ICU了~

例如我这里有个数据,json数据达到了将近7万行。

其实这个数据量还不算大,比这大的还有很多,如果用entity的方案的话,再加上向服务器请求数据的时间,大概要等个几秒钟。

所以我们做项目,要尽可能的将性能调优,不放过每一个影响性能的蛀虫代码,因为千里之堤,溃于蚁穴

Primitive是Cesium提供的性能更优的几何体实例,只不过是Entity封装了了更多的常用方法,所以导致其比较重。

Primitive相对轻量化,Cesium在底层对其进行了一些性能调优,并且开发者可以更自由的使用。

let primitives = null;
const onStartPimitive = async () => {
  // 先拿到道路的json数据
  const { res } = await getGeojson(jsonUrl);
  const { features } = res;
  // primitive的实例集合
  const instance = [];
  if (features?.length) {
    features.forEach((item) => {
      const arr = item.geometry.coordinates;
      arr.forEach((el) => {
        let arr1 = [];
        el.forEach((_el) => {
          arr1 = arr1.concat(_el);
        });
        // 多线段几何体创建
        const polyline = new Cesium.PolylineGeometry({
          positions: Cesium.Cartesian3.fromDegreesArray(arr1),
          width: 1.7,
          vertexFormat: Cesium.PolylineMaterialAppearance.VERTEX_FORMAT, // 顶点属性,默认即可
        });
        const geometry = Cesium.PolylineGeometry.createGeometry(polyline);
        instance.push(
          new Cesium.GeometryInstance({
            geometry,
          })
        );
      });
    });
    // 着色器编写,跟上方entity的基本一致
    let source = `czm_material czm_getMaterial(czm_materialInput materialInput)
      {
         czm_material material = czm_getDefaultMaterial(materialInput);
         vec2 st = materialInput.st;
         vec4 colorImage = texture(image, vec2(fract((st.s - speed * czm_frameNumber * 0.001)), st.t));
         material.alpha = colorImage.a * color.a;
         material.diffuse = colorImage.rgb * 1.5 ;
         return material;
      }`;

    const material = new Cesium.Material({
      fabric: {
        uniforms: {
          color: Cesium.Color.fromCssColorString("#7ffeff"),
          image: "/images/spriteline.png",
          speed: 10,
        },
        source,
      },
      translucent: function () {
        return true;
      },
    });
    // 材质着色的外观
    const appearance = new Cesium.PolylineMaterialAppearance();
    appearance.material = material;
    const primitive = new Cesium.Primitive({
      geometryInstances: instance,
      appearance,
      asynchronous: false,
    });

    primitives = viewer.scene.primitives.add(primitive);
  }
};

最后

作为cesium的开发者,日常开发过程中,遇到大量几何体的绘制和渲染的需求,建议还是直接无脑上Primitive,性能要比Entity好太多。

做程序,不要以能实现就好为目的,要尽可能追求产品的完美体验,也能不断提高自己开发能力的上限。

【开源地址】:https://github.com/tingyuxuan2302/cesium-vue3-vite/blob/github/src/views/material/throughRoad.vue

有需要进技术产品开发交流群(可视化&GIS)可以加我:brown_7778,也欢迎数字孪生可视化领域的交流合作。

最后,如果觉得文章对你有帮助,也希望可以一键三连👏👏👏,支持我持续开源和分享~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/736760.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PADS学习笔记

1.PADS设计PCB流程 封装库&#xff08;layout&#xff09;&#xff0c;原理图库&#xff08;logic&#xff09;的准备原件封装的匹配&#xff08;logic&#xff09;原理图的绘制&#xff08;logic&#xff09;导网表操作&#xff08;logic&#xff09;导入结构&#xff08;lay…

SpringBoot + 虚拟线程,鸟枪换大炮!

“虚拟”线程&#xff0c;望文生义&#xff0c;它是“假”的&#xff0c;它不直接调度操作系统的线程&#xff0c;而是由JVM再提供一层线程的接口抽象&#xff0c;由普通线程调度&#xff0c;即一个普通的操作系统线程可以调度成千上万个虚拟线程。 虚拟线程比普通线程的消耗要…

Centos7安装自动化运维Ansible

自动化运维Devops-Ansible Ansible是新出现的自动化运维工具&#xff0c;基于Python 开发&#xff0c;集合了众多运维工具&#xff08;puppet 、cfengine、chef、func、fabric&#xff09;的优点&#xff0c;实现了批量系统配置 、批量程序部署、批量运行命令 等功能。Ansible…

Java8 --- Gradle7.4整合IDEA

目录 一、Gradle整合IDEA 1.1、Groovy安装 1.1.1、配置环境变量 ​编辑 1.2、创建项目 ​编辑 1.3、Groovy基本语法 1.3.1、基本语法 1.3.2、引号 1.3.3、语句结构 1.3.4、数据类型 1.3.5、集合操作 1.4、使用Gradle创建普通Java工程 1.5、使用Gradle创建Java ss…

JavaScript之类(1)

class基础语法结构&#xff1a; 代码&#xff1a; class MyClass {constructor() { ... }method1() { ... }method2() { ... }method3() { ... }... } 解释&#xff1a; 属性解释class是我们定义的类型(类)MyClass是我们定义的类的名称 constructor()我们可以在其中初始化对象m…

基于YOLOv5的PCB板缺陷检测系统的设计与实现(PyQT页面+YOLOv5模型+数据集)

简介 随着电子设备的广泛应用,PCB(印刷电路板)作为其核心部件,其质量和可靠性至关重要。然而,PCB生产过程中常常会出现各种缺陷,如鼠咬伤、开路、短路、杂散、伪铜等。这些缺陷可能导致设备故障,甚至引发严重的安全问题。为了提高PCB检测的效率和准确性,我们基于YOLOv…

2-14 基于matlab的GA优化算法优化车间调度问题

基于matlab的GA优化算法优化车间调度问题。n个工作在m个台机器上加工。已知每个工作中工序加工顺序、各工序的加工时间以及每个工件所包含的工序&#xff0c;在满足约束条件的前提下&#xff0c;目的是确定机器上各工件顺序&#xff0c;以保证某项性能指标最优。程序功能说明&a…

华为数通——单臂路由

单臂路由&#xff1a;指在三层设备路由器的一个接口上通过配置子接口&#xff08;或“逻辑接口”&#xff0c;并不存在真正物理接口&#xff09;的方式&#xff0c;实现原来相互隔离的不同VLAN&#xff08;虚拟局域网&#xff09;之间的互联互通。但是仅仅允许单播通信。 单臂路…

如何在Qt Designer中管理QSplitter

问题描述 当按下按钮时&#xff0c;我希望弹出一个对话框&#xff0c;用户可以在其中选择内容并最终按下 ‘Ok’ 按钮。我想在这个对话框中放置一个 QSplitter&#xff0c;左侧面板将显示树状结构&#xff0c;右侧将显示其他内容。如何正确实现这一点&#xff1f; 从 Qt 的示…

6.S081的Lab学习——Lab8: locks

文章目录 前言一、Memory allocator(moderate)提示&#xff1a;解析 二、Buffer cache(hard)解析&#xff1a; 三、Barrier (moderate)解析&#xff1a; 总结 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招。打算尝试6.S081&#xff0c;将它的Lab逐一实现&#xff0c;并…

番外篇 | 基于YOLOv5-RCS的明火烟雾检测 | 源于RCS-YOLO

前言:Hello大家好,我是小哥谈。RCS-YOLO是一种目标检测算法,它是基于YOLOv3算法的改进版本。通过查看RCS-YOLO的整体架构可知,其中包括RCS-OSA模块。RCS-OSA模块在模型中用于堆叠RCS模块,以确保特征的复用并加强不同层之间的信息流动。本文就给大家详细介绍如何将RCS-YOLO…

【原创】springboot+mysql海鲜商城设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

yii2 ActiveForm使用技巧

持续更新&#xff1a; 1、搜索输入框&#xff1a;form-inline <?php $form ActiveForm::begin([action > [index],method > get,options > [class > form-inline] &#xff08;增加此行代码&#xff09; ]); ?>

大型企业网络DHCP服务器配置安装实践@FreeBSD

企业需求 需要为企业里的机器配置一台DHCP服务器。因为光猫提供DHCP服务的能力很差&#xff0c;多机器dhcp多机器NAT拓扑方式机器一多就卡顿。使用一台路由器来进行子网络的dhcp和NAT服务&#xff0c;分担光猫负载&#xff0c;但是还有一部分机器需要放到光猫网络&#xff0c;…

本地离线模型搭建指南-中文大语言模型底座选择依据

搭建一个本地中文大语言模型&#xff08;LLM&#xff09;涉及多个关键步骤&#xff0c;从选择模型底座&#xff0c;到运行机器和框架&#xff0c;再到具体的架构实现和训练方式。以下是一个详细的指南&#xff0c;帮助你从零开始构建和运行一个中文大语言模型。 本地离线模型搭…

【网络安全的神秘世界】已解决burpsuite报错Failed to start proxy service on 127.0.0.1:8080

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 解决burpsuite无法在 127.0.0.1&#xff1a;8080 上启动代理服务端口被占用以及抓不到本地包的问题 Burpsuite无法启动proxy…

RabbitMQ(六)仲裁队列、流式队列、异地容灾(联邦队列Federation Queue)

文章目录 仲裁队列1、创建交换机2、创建仲裁队列3、验证主节点宕机不影响消息发送和接收 流式队列&#xff08;不推荐&#xff0c;Kafka主场&#xff09;概念 异地容灾一、Federation插件概述 二、Federation交换机1、总体说明2、准备工作3、启用联邦插件4、添加上游连接端点5、…

【Flink metric】Flink指标系统的系统性知识:以便我们实现特性化数据的指标监控与分析

文章目录 一. Registering metrics&#xff1a;向flink注册新自己的metrics1. 注册metrics2. Metric types:指标类型2.1. Counter2.2. Gauge2.3. Histogram(ing)4. Meter 二. Scope:指标作用域1. User Scope2. System Scope ing3. User Variables 三. Reporter ing四. System m…

XSS跨站攻击漏洞

XSS跨站攻击漏洞 一 概述 1 XSS概述 xss全称为&#xff1a;Cross Site Scripting&#xff0c;指跨站攻击脚本&#xff0c;XSS漏洞发生在前端&#xff0c;攻击的是浏览器的解析引擎&#xff0c;XSS就是让攻击者的JavaScript代码在受害者的浏览器上执行。 XSS攻击者的目的就是…