Cesium 曲线管道教程

Cesium 曲线管道教程

曲线管道 ·Curve Pipe· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • Cesium Entity 高层实体 API

效果说明

本案例演示曲线管道效果:基于 WebGL 实现「曲线管道」可视化效果,附完整可运行源码;核心用到 Cesium。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

  • Viewer聚合 Scene、Camera、Clock 与渲染循环,是 Cesium 应用入口。
  • Entity面向点线面/模型/标签的高层 API;与 Primitive 相比更适合交互与属性驱动。
  • 阅读下方完整源码时,建议从init/load/animate三条主线入手,再深入 shader 与工具函数。

实现步骤

  • 创建 Viewer,配置地形/影像(若案例需要)并设置初始相机
  • requestAnimationFrame循环中更新状态并 render(Cesium 为viewer.render或自动渲染)
  • 代码要点

    import * as Cesium from 'cesium'

    import { Color, defined, Event, Material, Property } from 'cesium'

    const box = document.getElementById('box')

    const viewer = new Cesium.Viewer(box, {

    animation: false,//是否创建动画小器件,左下角仪表

    baseLayerPicker: false,//是否显示图层选择器,右上角图层选择按钮

    baseLayer: Cesium.ImageryLayer.fromProviderAsync(Cesium.ArcGisMapServerImageryProvider.fromUrl(GLOBAL_CONFIG.getLayerUrl())),

    fullscreenButton: false,//是否显示全屏按钮,右下角全屏选择按钮

    timeline: false,//是否显示时间轴

    infoBox: false,//是否显示信息框

    })

    viewer._cesiumWidget._creditContainer.style.display = "none"

    viewer.clock.shouldAnimate = true

    //定位北京 viewer.camera.flyTo({

    destination: Cesium.Cartesian3.fromDegrees(116.41, 36.91, 1000000),

    orientation: {

    heading: Cesium.Math.toRadians(0),

    pitch: Cesium.Math.toRadians(-90),

    roll: 0

    }

    })

    /飞线材质类/ class PolylineTrailLinkMaterialProperty {

    constructor(image, color = Color.WHITE, duration = 1000, repeat = 60) {

    this._definitionChanged = new Event()

    this._color = undefined

    this.color = color

    this.duration = duration

    this._time = new Date().getTime()

    this.image = image

    Material._materialCache.addMaterial('PolylineTrailLink', {

    fabric: {

    type: 'PolylineTrailLink',

    uniforms: {

    color: color.withAlpha(1.0),

    image: image,

    time: 0,

    repeat

    },

    source:czm_material czm_getMaterial(czm_materialInput materialInput) { czm_material material = czm_getDefaultMaterial(materialInput); vec2 st = materialInput.st; vec4 sampledColor = texture(image, vec2(fract(repeat*st.s - time), st.t)); material.alpha = sampledColor.a * color.a; material.diffuse = (sampledColor.rgb + color.rgb) / 2.0; return material; }

    },

    translucent: () => true

    })

    }

    get isConstant() { return false }

    get definitionChanged() { return this._definitionChanged }

    getType(_) { return 'PolylineTrailLink' }

    getValue(time, result) {

    if (!defined(result)) result = {}

    result.color = Property.getValueOrClonedDefault(this._color, time, Color.WHITE, result.color)

    result.image = this.image

    result.time = (new Date().getTime() - this._time) % this.duration / this.duration

    return result

    }

    equals(other) { return this === other || Property.equals(this._color, other._color) }

    }

    // 生成一组飞线动画 [ [[116.41, 36.91], [130.40, 45.39]], [[116.41, 36.91], [114.11, 39.44]], [[116.41, 36.91], [109.62, 25.72]], [[116.41, 36.91], [121.48, 31.22]], [[116.41, 36.91], [13.78, 12.31]], [[116.41, 36.91], [74.12, 33.50]], ].forEach(([p1, p2]) => createPlaneCurve(p1, p2))

    // 组合 function createPlaneCurve(p1, p2) {

    const { curvePoints } = getGenerateCurve(p1, p2, { maxHeight: 100000 })

    viewer.entities.add({ polylineVolume: createCurvePipe(curvePoints)

    })

    }

    /生成曲线/ function getGenerateCurve(start, end, params = {}) {

    const [startLongitude, startLatitude] = start

    const [endLongitude, endLatitude] = end

    const startCartographic = Cesium.Cartographic.fromDegrees(startLongitude, startLatitude)

    const endCartographic = Cesium.Cartographic.fromDegrees(endLongitude, endLatitude)

    const geodesic = new Cesium.EllipsoidGeodesic(startCartographic, endCartographic)

    const curvePoints = []

    for (let t = 0; t <= 1; t += (params.step || 0.01)) {

    const pointCartographic = geodesic.interpolateUsingFraction(t)

    pointCartographic.height = (params.maxHeight || 400000)Math.sin(Math.PIt)

    const pointCartesian = Cesium.Cartographic.toCartesian(pointCartographic)

    curvePoints.push(pointCartesian)

    }

    endCartographic.height = 0

    const endPointCartesian = Cesium.Cartographic.toCartesian(endCartographic)

    curvePoints.push(endPointCartesian)

    function getCurvePointAtTime(t) {

    const pointCartographic = geodesic.interpolateUsingFraction(t)

    pointCartographic.height = (params.maxHeight || 400000)Math.sin(Math.PIt)

    return Cesium.Cartographic.toCartesian(pointCartographic)

    }

    return { curvePoints, getCurvePointAtTime }

    }

    function createCurvePipe(curvePoints, params = {}) {

    const getShape = (radius) => {

    const positions = [];

    for (let i = 0; i < 360; i++) {

    if (i % 1 === 0) {

    const radians = Cesium.Math.toRadians(i);

    positions.push(

    new Cesium.Cartesian2(

    (radius / 2) * Math.cos(radians),

    (radius / 2) * Math.sin(radians)

    )

    );

    }

    }

    return positions;

    }

    const radius = 16000.0

    const _ps = curvePoints.map(i => {

    const [longitude, latitude, height] = cartesian3ToDegrees(i)

    return Cesium.Cartesian3.fromDegrees(longitude, latitude, height - radius)

    })

    return {

    positions: _ps,

    shape: getShape(2 * radius),

    cornerType: Cesium.CornerType.ROUNDED,

    material: new PolylineTrailLinkMaterialProperty(FILE_HOST + 'images/channels/lmap.png', Cesium.Color.RED, 2000)

    }

    }

    function cartesian3ToDegrees(cartesian3, type = 'Array') {

    const cartographic = Cesium.Cartographic.fromCartesian(cartesian3) // 笛卡尔坐标转经纬度

    const longitude = Cesium.Math.toDegrees(cartographic.longitude) // 弧度转度

    const latitude = Cesium.Math.toDegrees(cartographic.latitude) // 弧度转度

    const height = cartographic.height // 高度

    return type === 'Array' ? [longitude, latitude, height] : { longitude, latitude, height }

    }

    完整源码:GitHub

    小结

    • 本文提供曲线管道完整 Cesium.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Cesium.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库