通过cesium实现空间点对地面圆周的发散效果

图片效果

需求:

1.实现空间某一点到地面圆的发散效果

2.地面圆有一定高度。

3.线条要有动画,圆要旋转

思路:

1.圆使用cesium中的wall实现,将圆拆分成多份,将多份wall拼接成圆。

2.拆分点的需要通过终止点进行计算,需要将经纬度点转换成笛卡尔坐标系的点,然后求出当前点的对应法向量(Cesium.eastNorthUpToFixedFrame)。

3.线条间隔通过material中的fbric中的glsl修改纹理的局部展示不展示。

4.墙体旋转和线条的运动通过scene中的preRender函数进行监听,然后实时修改

代码实现:

 

//cesiumContainer.js
import * as Cesium from 'cesium'
import { shaderLines } from './shader.js'

class CesiumContainer {
  constructor(params) {
    this.viewer = null
    this.scene = null
    this.camera = null
    this.host = params.host
    this.wallData = null
    this.startLongitude = null
    this.startLatitude = null
    this.height = null
    this.radius = null
    this.step = null
    this.wallHeight = null
    this.distance = null
    this.primitiveLine = null
    this.pointNormal = null
    this.primitiveWall = null
    this.material = null
    this.sourceFbric = null
  }
}

Object.assign(CesiumContainer.prototype, {
  init() {
    this.setToken()
    const params = {}
    this.viewer = new Cesium.Viewer('viewer-container', params)
    this.scene = this.viewer.scene
    this.camera = this.viewer.scene.camera

     this.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(120, 42, 1100),
      orientation: {
        heading:  Cesium.Math.toRadians(-112),
        pitch:  Cesium.Math.toRadians(-12),
        rool: 0.0
      },
      duration: 0
    })
    this.createDTWallLine(120, 42, 10000, 1000, 10000, 32) // 墙线中心点位置,高度,半径长度,墙步长
  },
  
  setToken() {
    Cesium.Ion.defaultAccessToken = 'xxx'
  },

  createDTWallLine(startLongitude, startLatitude, height, wallHeight, radius, step = 8) {
    this.startLongitude = startLongitude;
    this.startLatitude = startLatitude;
    this.height = height;
    this.radius = radius;
    this.step = step;
    this.wallHeight = wallHeight
    this.distance = radius
    this.pointNormal = Cesium.Ellipsoid.WGS84.geocentricSurfaceNormal(Cesium.Cartesian3.fromDegrees(this.startLongitude, this.startLatitude, 0), new Cesium.Cartesian3())

    // 获取拆分点
    this.getAllPoints()

    this.createWall();

    // 绘制线条
    this.createDtLine();

    this.ainimaton();
  },

  createWall() {
    const wallGeometries = []
    this.wallData.forEach((line, index) => {
        let wallGeometry
        if(index !== this.wallData.length - 1) {
            wallGeometry = new Cesium.WallGeometry({
                positions: Cesium.Cartesian3.fromDegreesArrayHeights([
                    line[0], line[1], 0,
                    line[0], line[1], line[2],
                    this.wallData[index + 1][0], this.wallData[index + 1][1], 0,
                    this.wallData[index + 1][0], this.wallData[index + 1][1], this.wallData[index + 1][2],
                ]),
            })
        } else {
            wallGeometry = new Cesium.WallGeometry({
                positions: Cesium.Cartesian3.fromDegreesArrayHeights([
                    line[0], line[1], 0,
                    line[0], line[1], line[2],
                    this.wallData[0][0], this.wallData[0][1], 0,
                    this.wallData[0][0], this.wallData[0][1], this.wallData[0][2],
                ]),
            })
        }
        wallGeometries.push(wallGeometry)
    })

    const wallInstanceFir = [];
    const wallInstanceSec = []
    wallGeometries.forEach((elt, index) => {
        if(index % 2 ===1) {
            wallInstanceFir.push(
                new Cesium.GeometryInstance({
                    geometry: elt,
                    attributes : {
                        color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE)
                    }
                }) 
            )
        } else {
            wallInstanceSec.push(
            new Cesium.GeometryInstance({
                geometry: elt,
                attributes : {
                    color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE)
                }
            })  
            )
        }
    })

    
    this.primitiveWall = this.viewer.scene.primitives.add(new Cesium.Primitive({
        geometryInstances: [...wallInstanceSec, ...wallInstanceFir],
        appearance: new Cesium.PerInstanceColorAppearance()
    }))
   
  },

  createDtLine() {
    this.sourceFbric = shaderLines()
    let polylineCollection = new Cesium.PolylineCollection();
    this.material = new Cesium.Material({
        fabric: {
            source: this.sourceFbric,
            uniforms: {
              color:  Cesium.Color.fromCssColorString('rgb(255, 0, 0)'),
              other: 0,
            }
        },
        // translucent: true
    })
    this.wallData.forEach(line => {
        polylineCollection.add({
            positions: [
                Cesium.Cartesian3.fromDegrees(this.startLongitude, this.startLatitude, this.height),
                Cesium.Cartesian3.fromDegrees(line[0], line[1], line[2])
            ],
            width: 5,
            material: this.material
        });
    })
    this.primitiveLine = this.viewer.scene.primitives.add(polylineCollection)
  },

  getAllPoints() {
    const startCartographic = new Cesium.Cartographic(Cesium.Math.toRadians(this.startLongitude), Cesium.Math.toRadians(this.startLatitude), 0.0);
    const startCartesian = Cesium.Ellipsoid.WGS84.cartographicToCartesian(startCartographic);
    const transform = Cesium.Transforms.eastNorthUpToFixedFrame(startCartesian);

    //根据步长计算x,y偏移量
    this.wallData = [];
    for (let i= 0; i < this.step; i++) {
        const distanceX = this.distance * Math.cos(2 * Math.PI * i / this.step);
        const distanceY = this.distance * Math.sin(2 * Math.PI * i / this.step);

        const offsetInENU = new Cesium.Cartesian3(distanceX, distanceY, 0.0);
        const newCartesian = Cesium.Matrix4.multiplyByPoint(transform, offsetInENU, new Cesium.Cartesian3());
        const newCartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic(newCartesian);
        const newLongitude = Cesium.Math.toDegrees(newCartographic.longitude);
        const newLatitude = Cesium.Math.toDegrees(newCartographic.latitude);
        this.wallData.push(
            [
                newLongitude, newLatitude, this.wallHeight
            ]
        )
    }
  },

  ainimaton() {
    let angle = 1.0;
    let other = 0.125;
    this.viewer.scene.preRender.addEventListener((scene, time) => {
        if(this.primitiveLine && this.pointNormal) {
            angle += 0.002;
            const quaternionRot = Cesium.Quaternion.fromAxisAngle(this.pointNormal, angle, new Cesium.Quaternion()) ;
            const finalMatrix = Cesium.Matrix4.fromTranslationQuaternionRotationScale(new Cesium.Cartesian3(0.0, 0.0, 0.0), quaternionRot, new Cesium.Cartesian3(1.0, 1.0, 1.0));//Cesium.Matrix4.multiply(Cesium.Matrix4.fromTranslation(Cesium.Cartesian4.fromCartesian3(this.pointNormal)), rotatedMatrix, new Cesium.Matrix4());

            this.primitiveLine.modelMatrix = finalMatrix;
            this.primitiveWall.modelMatrix = finalMatrix;
            other -= 0.001;
            if(other <= -0.25) other = 0;
            this.material.uniforms.other = other;
        }
    });
  }
})

export default CesiumContainer

//shader.js
export const shaderLines = () => {
    const fabricData = `
        uniform vec4 color;
        uniform vec4 color1;
        uniform vec2 center;
        uniform float other;

        float getColorAlpha(vec2 st) {
            float alphaTemp;
            if((st.x + other) < 0.) {
              alphaTemp = 0.;
            } else if((st.x + other) < .125) {
              alphaTemp = 1.;
            } else if ( (st.x + other) < .25) {
              alphaTemp = 0.;
            } else if ( (st.x + other) < .375) {
              alphaTemp = 1.;
            } else if ( (st.x + other) < .5) {
              alphaTemp = 0.;
            } else if ( (st.x + other) < .625) {
              alphaTemp = 1.;
            } else if ( (st.x + other) < .75) {
              alphaTemp = 0.;
            } else if ( (st.x + other) < .875) {
              alphaTemp = 1.;
            } else {
              alphaTemp = 0.;
            }
            return alphaTemp;
        }

        czm_material czm_getMaterial(czm_materialInput materialInput) {
            czm_material material = czm_getDefaultMaterial(materialInput);
            vec2 st = materialInput.st;
            // float alpha = distance(st.x, center.x);
            material.diffuse = color.rgb;
            material.alpha = getColorAlpha(st);
            return material;
        }
     `
     return fabricData
}
posted on 2024-12-17 15:30  张小饭啊  阅读(48)  评论(0)    收藏  举报