赞
踩
实现效果 有两种方式:
实现思路就是通过GeoJsonDataSource、entity绘制线,东躲自定义的shader实现流动效果。其缺点就是,加载大体量的geojson数据,entity绘制元素较多容易卡死
我使用的是VUE3.0写的项目
- /**
- * @description: 加载城市路网OD线
- * @param {*} CesiumViewer
- * @return {*}
- */
-
- import { Spriteline1MaterialProperty } from './Cesium_OD'
- export function urbanRoadNetworkOD() {
- removeRoadOD()
- // 道路穿梭线
- const imageUrl = new URL(
- '../assets/images/specialEffects/lineYellow.png',
- import.meta.url
- ).href
- Cesium.GeoJsonDataSource.load(
- // './geojson/WaterDistributionMainLine5.geojson'
- './geojson/WaterDistributionMainLineLittlel.geojson'
- ).then(function (dataSource) {
- // console.log(dataSource, 'dataSource')
- RoadOdData = dataSource
- CesiumViewer.dataSources.add(RoadOdData)
- const entities = RoadOdData.entities.values
- entities.forEach((el: { polyline: { width: number; material: any } }) => {
- el.polyline.width = 2
- el.polyline.material = new Spriteline1MaterialProperty(1000, imageUrl)
- })
- })
- }
将材质文件保存为单独的.ts文件,在进行entity材质赋值时直接调用即可。主要的逻辑实现代码如下:
- /**
- * 精灵穿梭路光效果,
- * entity的材质使用MaterialProperty,而primitive使用的是material。
- * @Data:2022-01-11
- */
- import * as Cesium from 'cesium'
-
- function Spriteline1MaterialProperty(this: any, duration: any, image: any) {
- this._definitionChanged = new Cesium.Event()
- this.duration = duration
- this.image = image || '/images/ceshi/linBlue.png'
- this._time = performance.now()
- }
- Object.defineProperties(Spriteline1MaterialProperty.prototype, {
- isConstant: {
- get: function () {
- return false
- }
- },
- definitionChanged: {
- get: function () {
- return this._definitionChanged
- }
- },
- color: Cesium.createPropertyDescriptor('color'),
- duration: Cesium.createPropertyDescriptor('duration')
- })
- Spriteline1MaterialProperty.prototype.getType = function (time: any) {
- return 'Spriteline1'
- }
- Spriteline1MaterialProperty.prototype.getValue = function (
- time: any,
- result: { image?: any; time?: any }
- ) {
- if (!Cesium.defined(result)) {
- result = {}
- }
- result.image = this.image
- result.time =
- ((performance.now() - this._time) % this.duration) / this.duration
- return result
- }
- Spriteline1MaterialProperty.prototype.equals = function (e: { duration: any }) {
- return (
- this === e ||
- (e instanceof Spriteline1MaterialProperty && this.duration === e.duration)
- )
- }
- const CesiumExtensions = Object.assign({}, Cesium)
- CesiumExtensions.Spriteline1MaterialProperty = Spriteline1MaterialProperty
- window.Cesium = CesiumExtensions
- Cesium.Material.Spriteline1Type = 'Spriteline1'
- Cesium.Material.Spriteline1Source = `
- czm_material czm_getMaterial(czm_materialInput materialInput)
- {
- czm_material material = czm_getDefaultMaterial(materialInput);
- vec2 st = materialInput.st;
- vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));
- material.alpha = colorImage.a;
- material.diffuse = colorImage.rgb / 2.0;
- return material;
- }
- `
- // st :二维纹理坐标
- // czm_material:保存可用于照明的材质信息
- Cesium.Material._materialCache.addMaterial(Cesium.Material.Spriteline1Type, {
- fabric: {
- type: Cesium.Material.Spriteline1Type,
- uniforms: {
- color: new Cesium.Color(1, 0, 0, 0.5),
- image: '',
- transparent: true,
- time: 20
- },
- source: Cesium.Material.Spriteline1Source
- },
- translucent: function (material: any) {
- return true
- }
- })
- export { Spriteline1MaterialProperty }
此处出现两个坑需要注意:
第一个坑:vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));这里,因为使用的是高版本的cesium1.104,直接写texture2D会报错:[Cesium WebGL] Fragment shader compile log: ERROR: 0:8: ‘texture2D‘,原因是Cesium 新版对 WebGL2
支持有变化!
报错原因:Cesium 自 1.102.0 开始,为了更好支持跨平台,尤其是移动端,Cesium 默认使用 WebGL2 上下文,如果想使用 WebGL1 上下文,需要在地球初始化的时候设置相应的参数。
为了在 WebGL2 上下文中工作,任何自定义材质、自定义图元和自定义着色器都需要升级到使用 GLSL 300。
以下是两种方法解决问题,推荐第二种
。
1. 初始化地球时(不推荐),传入参数,使用 WebGL1 上下文
- const viewer = new Viewer("cesiumContainer", {
- // 指定上下文
- contextOptions: {
- requestWebgl1: true,
- },
- });
2. 修正 GLSL 代码(推荐)
错误代码(texture2D
):
vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t))
修正为:
vec4 colorImage = texture(image, vec2(fract(st.s - time), st.t));
但使用方式2修正后,出现了个问题,uv纹理流动方向顺时针旋转了90度。。。因为对gls语言不太熟,暂时没能力修改shader,代码基本都是参考各大佬的,所以最后还是使用了方式一解决了,也比较省事。。。
第二个坑:如果按右弦大佬的Cesium.Spriteline1MaterialProperty = Spriteline1MaterialProperty定义Cesium新的类,将会报错:TypeError: Cannot add property Spriteline1MaterialProperty, object is not extensible
原因可能是:Cesium 对象已经被其他库或代码修改为不可扩展,导致你无法向它添加新的属性或方法。为了解决这个问题,可以考虑使用另一个对象来替代 Cesium 对象,并向它添加你需要的属性和方法,就可以了
- import Cesium from 'cesium';
-
- // 创建一个新对象,包含所有原始的 Cesium 属性和方法
- const CesiumExtensions = Object.assign({}, Cesium);
-
- // 向该对象添加新的属性和方法
- CesiumExtensions.myCustomMethod = function() {
- // ...
- };
-
- // 将原始的 Cesium 对象替换为新的对象
- window.Cesium = CesiumExtensions;
首先需要使用jQuery异步加载geojson文件,其次依次遍历,使用Cesium.Cartesian3.fromDegreesArray()方法将经纬度坐标转换为笛卡尔坐标,设置geometry的默认颜色、线宽,Cesium.PolylineGeometry创建线段的几何实例,创建一个新的Cesium.Material对象,并设置其fabric属性,用于定义材质的外观和行为。在czm_material czm_getMaterial(czm_materialInput materialInput)函数中,使用texture2D()函数获取当前时间下的纹理颜色,并将其作为材质的漫反射颜色。再将纹理颜色的alpha值设置为材质的透明度。最后,同时将材质返回给Cesium进行渲染。然后将单个geometry合并一次传输,提高渲染效率。最后通过CesiumViewer.clock.onTick事件,根据当前时间更新材质中的time属性。这个属性用于控制纹理的平移,从而实现流动线的效果。
具体代码如下:
.vue组件:
- function getGeojson() {
- // 使用primitive方式加载geojson
- let urlPath = './geojson/WaterDistributionMainLine.geojson'
- // 使用jQuery异步加载json数据
- $.get(urlPath, function (data) {
- let features = reactive([])
- if (data.features && data.features.length > 0) {
- features = reactive(data.features)
- CesiumMap.addDataToGlobe(toRaw(features))
- }
- })
- }
.ts文件:
- export function addDataToGlobe(features) {
- const imageUrl = new URL(
- '../assets/images/specialEffects/lineYellow.png',
- import.meta.url
- ).href
- const instances = []
- for (let i = 0; i < features.length; i++) {
- for (let j = 0; j < features[i].geometry.coordinates.length; j++) {
- const positions = Cesium.Cartesian3.fromDegreesArray(
- features[i].geometry.coordinates[j].flat()
- )
- const polyline = new Cesium.PolylineGeometry({
- positions: positions,
- width: 2,
- vertexFormat: Cesium.PolylineMaterialAppearance.VERTEX_FORMAT
- })
- const polygon = new Cesium.GeometryInstance({
- geometry: polyline,
- id: i
- })
- instances.push(polygon)
- }
- }
-
- const material = new Cesium.Material({
- fabric: {
- type: 'Spriteline1',
- uniforms: {
- color: Cesium.Color.WHITE,
- image: imageUrl,
- transparent: true,
- time: 0
- },
- source: `
- czm_material czm_getMaterial(czm_materialInput materialInput)
- {
- czm_material material = czm_getDefaultMaterial(materialInput);
- vec2 st = materialInput.st;
- vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));
- material.alpha = colorImage.a;
- material.diffuse = colorImage.rgb / 2.0;
- return material;
- }
- `
- }
- })
-
- const polylinePrimitive = CesiumViewer.scene.primitives.add(
- new Cesium.Primitive({
- geometryInstances: instances,
- appearance: new Cesium.PolylineMaterialAppearance({
- flat: true,
- material: material
- })
- })
- )
-
- CesiumViewer.clock.onTick.addEventListener(function (clock) {
- const startTime = Cesium.JulianDate.toDate(
- CesiumViewer.clock.startTime
- ).getTime()
- const currentTime = Cesium.JulianDate.toDate(clock.currentTime).getTime()
- const elapsedTime = (currentTime - startTime) / 1000.0
- material.uniforms.time = elapsedTime
- })
- }
参考文献:
右弦GISer-cesium实现道路穿梭线效果_cesium流动路网_右弦GISer的博客-CSDN博客
非科班Java出身GISer-Cesium 1.02.0 及以上版本下自定义材质报错:[Cesium WebGL] Fragment shader compile log: ERROR: 0:8: ‘texture2D‘_非科班Java出身GISer的博客-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。