首页 > 其他分享 >Cesium 实现可视域分析

Cesium 实现可视域分析

时间:2024-07-03 09:53:11浏览次数:16  
标签:分析 shadowMap viewer 视域 Cesium new lightCamera options

	*前言:尝试了网上好多个版本的可视域分析,感觉都有一些问题,我这个也可能不是最完美的,但是我觉得对我来说够用了,实现效果如下*

image

此示例基于vue3上实现,cesium版本1.101.0vite-plugin-cesium版本1.2.22

新建一个名为ViewshedAnalysis.js的JS文件

import glsl from './glsl2'

/**
 * @param {Cesium.Viewer} viewer Cesium三维视窗。
 * @param {Object} options 选项。
 * @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
 * @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
 * @param {Number} options.viewDistance 观测距离(单位`米`,默认值100)。
 * @param {Number} options.viewHeading 航向角(单位`度`,默认值0)。
 * @param {Number} options.viewPitch 俯仰角(单位`度`,默认值0)。
 * @param {Number} options.horizontalViewAngle 可视域水平夹角(单位`度`,默认值90)。
 * @param {Number} options.verticalViewAngle 可视域垂直夹角(单位`度`,默认值60)。
 * @param {Cesium.Color} options.visibleAreaColor 可视区域颜色(默认值`绿色`)。
 * @param {Cesium.Color} options.invisibleAreaColor 不可视区域颜色(默认值`红色`)。
 * @param {Boolean} options.enabled 阴影贴图是否可用。
 * @param {Boolean} options.softShadows 是否启用柔和阴影。
 * @param {Boolean} options.size 每个阴影贴图的大小。
 */
class ViewshedAnalysis {
    constructor(viewer, options) {
        this.viewer = viewer
        this.viewPosition = options.viewPosition //开始坐标
        this.viewPositionEnd = options.viewPositionEnd //结束坐标
        this.viewDistance = this.viewPositionEnd
            ? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd)
            : options.viewDistance || 100.0 //观测距离
        this.viewHeading = this.viewPositionEnd
            ? this.getHeading(this.viewPosition, this.viewPositionEnd)
            : options.viewHeading || 0.0
        this.viewPitch = this.viewPositionEnd
            ? this.getPitch(this.viewPosition, this.viewPositionEnd)
            : options.viewPitch || 0.0
        this.horizontalViewAngle = options.horizontalViewAngle || 90.0 //可视域的水平夹角
        this.verticalViewAngle = options.verticalViewAngle || 60.0 //可视域的垂直夹角
        this.visibleAreaColor = options.visibleAreaColor || Cesium.Color.GREEN
        this.invisibleAreaColor = options.invisibleAreaColor || Cesium.Color.RED
        this.enabled = typeof options.enabled === 'boolean' ? options.enabled : true
        this.softShadows = typeof options.softShadows === 'boolean' ? options.softShadows : true
        this.size = options.size || 2048
    }

    add() {
        this.createLightCamera()
        this.createShadowMap()
        this.drawFrustumOutline(); //视锥线
        this.drawSketch()
        this.createPostStage()
    }

    update() {
        this.clear()
        this.add()
    }

    /**
     * @method 更新终点坐标,从而实时更新绘制的实体的方向和半径
     *
     */
    updatePosition(viewPositionEnd) {
        this.viewPositionEnd = viewPositionEnd
        this.viewDistance = Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) //观测距离
        this.viewHeading = this.getHeading(this.viewPosition, this.viewPositionEnd)
        this.viewPitch = this.getPitch(this.viewPosition, this.viewPositionEnd)
    }

    clear() {
        if (this.sketch) {
            this.viewer.entities.remove(this.sketch)
            this.sketch = null
        }
        /*  if (this.frustumOutline) {
          this.viewer.scene.primitives.destroy();
          this.frustumOutline = null;
      } */
        if (this.postStage) {
            this.viewer.scene.postProcessStages.remove(this.postStage)
            this.postStage = null
        }
    }

    /**
     * @method 创建相机
     */
    createLightCamera() {
        this.lightCamera = new Cesium.Camera(this.viewer.scene)
        this.lightCamera.position = this.viewPosition
        this.lightCamera.frustum.near = this.viewDistance * 0.001
        this.lightCamera.frustum.far = this.viewDistance
        const hr = Cesium.Math.toRadians(this.horizontalViewAngle)
        const vr = Cesium.Math.toRadians(this.verticalViewAngle)
        const aspectRatio =
            (this.viewDistance * Math.tan(hr / 2) * 2) / (this.viewDistance * Math.tan(vr / 2) * 2)
        this.lightCamera.frustum.aspectRatio = aspectRatio
        if (hr > vr) {
            this.lightCamera.frustum.fov = hr
        } else {
            this.lightCamera.frustum.fov = vr
        }
        this.lightCamera.setView({
            destination: this.viewPosition,
            orientation: {
                heading: Cesium.Math.toRadians(this.viewHeading || 0),
                pitch: Cesium.Math.toRadians(this.viewPitch || 0),
                roll: 0
            }
        })
    }

    /**
     * @method 创建阴影贴图
     */
    createShadowMap() {
        this.shadowMap = new Cesium.ShadowMap({
            context: this.viewer.scene.context,
            lightCamera: this.lightCamera,
            enabled: this.enabled,
            isPointLight: true,
            pointLightRadius: this.viewDistance,
            cascadesEnabled: false,
            size: this.size,
            softShadows: this.softShadows,
            normalOffset: false,
            fromLightSource: false
        })
        this.viewer.scene.shadowMap = this.shadowMap
    }

    /**
     * @method 创建PostStage
     * 导入的glsl是做片元着色的
     */
    createPostStage() {
        const fs = glsl
        const postStage = new Cesium.PostProcessStage({
            fragmentShader: fs,
            uniforms: {
                shadowMap_textureCube: () => {
                    this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
                    return Reflect.get(this.shadowMap, '_shadowMapTexture')
                },
                shadowMap_matrix: () => {
                    this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
                    return Reflect.get(this.shadowMap, '_shadowMapMatrix')
                },
                shadowMap_lightPositionEC: () => {
                    this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
                    return Reflect.get(this.shadowMap, '_lightPositionEC')
                },
                shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: () => {
                    this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
                    const bias = this.shadowMap._pointBias
                    return Cesium.Cartesian4.fromElements(
                        bias.normalOffsetScale,
                        this.shadowMap._distance,
                        this.shadowMap.maximumDistance,
                        0.0,
                        new Cesium.Cartesian4()
                    )
                },
                shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: () => {
                    this.shadowMap.update(Reflect.get(this.viewer.scene, '_frameState'))
                    const bias = this.shadowMap._pointBias
                    const scratchTexelStepSize = new Cesium.Cartesian2()
                    const texelStepSize = scratchTexelStepSize
                    texelStepSize.x = 1.0 / this.shadowMap._textureSize.x
                    texelStepSize.y = 1.0 / this.shadowMap._textureSize.y

                    return Cesium.Cartesian4.fromElements(
                        texelStepSize.x,
                        texelStepSize.y,
                        bias.depthBias,
                        bias.normalShadingSmooth,
                        new Cesium.Cartesian4()
                    )
                },
                camera_projection_matrix: this.lightCamera.frustum.projectionMatrix,
                camera_view_matrix: this.lightCamera.viewMatrix,
                helsing_viewDistance: () => {
                    return this.viewDistance
                },
                helsing_visibleAreaColor: this.visibleAreaColor,
                helsing_invisibleAreaColor: this.invisibleAreaColor
            }
        })
        this.postStage = this.viewer.scene.postProcessStages.add(postStage)
    }

    /**
     * @method 创建视锥线
     */
    drawFrustumOutline() {
        const scratchRight = new Cesium.Cartesian3()
        const scratchRotation = new Cesium.Matrix3()
        const scratchOrientation = new Cesium.Quaternion()
        const position = this.lightCamera.positionWC
        const direction = this.lightCamera.directionWC
        const up = this.lightCamera.upWC
        let right = this.lightCamera.rightWC
        right = Cesium.Cartesian3.negate(right, scratchRight)
        let rotation = scratchRotation
        Cesium.Matrix3.setColumn(rotation, 0, right, rotation)
        Cesium.Matrix3.setColumn(rotation, 1, up, rotation)
        Cesium.Matrix3.setColumn(rotation, 2, direction, rotation)
        let orientation = Cesium.Quaternion.fromRotationMatrix(rotation, scratchOrientation)

        let instance = new Cesium.GeometryInstance({
            geometry: new Cesium.FrustumOutlineGeometry({
                frustum: this.lightCamera.frustum,
                origin: this.viewPosition,
                orientation: orientation
            }),
            id: Math.random().toString(36).substr(2),
            attributes: {
                color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.YELLOWGREEN),
                show: new Cesium.ShowGeometryInstanceAttribute(true)
            }
        })

        this.frustumOutline = this.viewer.scene.primitives.add(
            new Cesium.Primitive({
                geometryInstances: [instance],
                appearance: new Cesium.PerInstanceColorAppearance({
                    flat: true,
                    translucent: false
                })
            })
        )
    }
    /**
     * @method 创建视网
     * 在实时绘制椭球实体时,其实不是一直创建entity,而是改变实体的方向(orientation)和改变椭球的半径(radii)
     */
    drawSketch() {
        this.sketch = this.viewer.entities.add({
            name: 'sketch',
            position: this.viewPosition,
            orientation: new Cesium.CallbackProperty(() => {
                return Cesium.Transforms.headingPitchRollQuaternion(
                    this.viewPosition,
                    Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - this.horizontalViewAngle, this.viewPitch, 0.5)
                )
            }, false),
            ellipsoid: {
                //椭球的半径
                radii: new Cesium.CallbackProperty(() => {
                    return new Cesium.Cartesian3(this.viewDistance,
                             this.viewDistance,
                            this.viewDistance)
                }, false),
                innerRadii: new Cesium.Cartesian3(2.0, 2.0, 2.0), //椭球内部的半径
                minimumClock: Cesium.Math.toRadians(-this.horizontalViewAngle / 2), //椭圆形的最小时钟角度
                maximumClock: Cesium.Math.toRadians(this.horizontalViewAngle / 2), //椭球的最大时钟角度
                minimumCone: Cesium.Math.toRadians(this.verticalViewAngle + 7.75), //椭圆形的最小圆锥角
                maximumCone: Cesium.Math.toRadians(180 - this.verticalViewAngle - 7.75), //椭球的最大圆锥角
                fill: false, //椭圆是否填充所提供的的材料
                outline: true, //是否勾勒出椭圆形
                subdivisions: 256, //每个轮廓环的样本数,确定曲率的粒度
                stackPartitions: 64, //堆栈数的属性
                slicePartitions: 64, //径向切片数量的属性
                outlineColor: Cesium.Color.YELLOWGREEN //轮廓的颜色
            }
        })
    }

    /**
     * @method 获取偏航角
     */
    getHeading(fromPosition, toPosition) {
        let finalPosition = new Cesium.Cartesian3()
        let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition)
        Cesium.Matrix4.inverse(matrix4, matrix4)
        Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition)
        Cesium.Cartesian3.normalize(finalPosition, finalPosition)
        return Cesium.Math.toDegrees(Math.atan2(finalPosition.x, finalPosition.y))
    }

    /**
     * @method 获取俯仰角
     */
    getPitch(fromPosition, toPosition) {
        let finalPosition = new Cesium.Cartesian3()
        let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition)
        Cesium.Matrix4.inverse(matrix4, matrix4)
        Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition)
        Cesium.Cartesian3.normalize(finalPosition, finalPosition)
        return Cesium.Math.toDegrees(Math.asin(finalPosition.z))
    }
}

export default ViewshedAnalysis

引入js:

import ViewShed from '@/utils/analysis/visibility/ViewshedAnalysis.js'

调用方法:

const shootAreaAnalysis = (type) => {
    store.setSelected('shootArea')
    let i = 0
    var horizontalViewAngle = 90  //视角水平张角
    var verticalViewAngle = 60    //视角垂直张角
    var endPosition = null
    var viewShed = null
    var handler = new Cesium.ScreenSpaceEventHandler(window.Viewer.scene.canvas)
    handler.setInputAction(movement => {
        i++
        if (i === 1) {
            var startPosition = window.Viewer.scene.pickPosition(movement.position) //鼠标点击一次获取开始坐标
            if (!startPosition) return
            viewShed = new ViewShed(window.Viewer, {
                viewPosition: startPosition,
                viewPositionEnd: startPosition,
                horizontalViewAngle: horizontalViewAngle,
                verticalViewAngle: verticalViewAngle
            })
            // 鼠标移动的事件
            handler.setInputAction(movement => {
                endPosition = window.Viewer.scene.pickPosition(movement.endPosition)
                if (!endPosition) return
                viewShed.updatePosition(endPosition)
                if (!viewShed.sketch) {
                    viewShed.drawSketch()
                }
            }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
        }
        // 鼠标点击两次获取结束坐标
        if (i === 2) {
            i = 0
            endPosition = window.Viewer.scene.pickPosition(movement.position)
            viewShed.updatePosition(endPosition)
            viewShed.update()
            handler && handler.destroy()  //销毁鼠标事件
            store.setSelected(null)
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
}

glsl2.js 文件我找了两个, 我测试都是可以实现的,只是效果问题,可以切换了尝试一下
下载地址

标签:分析,shadowMap,viewer,视域,Cesium,new,lightCamera,options
From: https://www.cnblogs.com/tanxj/p/18281034

相关文章

  • Pandas数据清洗处理分析
    Pandas是一个开源的Python数据分析库,它提供了快速、灵活且表达力强的数据结构,旨在使数据清洗和分析工作变得更加简单易行。以下是Pandas在数据清洗和分析中常用的一些功能:1.**数据加载**:Pandas可以读取多种格式的数据,包括CSV、Excel、SQL数据库、JSON等。2.**数据框(DataF......
  • 基于VLC可见光通信的室内光通信信道信噪比分析matlab仿真
    1.算法运行效果图预览     2.算法运行软件版本matlab2022a 3.部分核心程序 Pr=POW_all.*H;%接收功率(毫瓦)POW_r=Pr./1000;%接收功率(瓦)Pr_dbm=10.*log10(POW_r);%接收功率(dBm)%信噪比(SNRIb=202e-6;%背景光子通......
  • Linux源码阅读笔记08-进程调度API系统调用案例分析
    kthread_create_on_nodekthread_create_on_node函数功能:指定存储节点创建新内核线程。源码如下:操作实战#include<linux/module.h>#include<linux/pid.h>#include<linux/sched.h>#include<linux/kthread.h>#include<linux/wait.h>intMyThreadFunc(void*......
  • MIMO系统基于STBC编码在瑞利信道下的误比特率分析
    目录目录............................................................1摘要...........................................................1第一章绪论.....................................................21.1  对课题的认识与理解...........................
  • C语言小项目-词法分析器
    1.什么是词法分析器?        词法分析器是编译器中的第一个阶段,其主要任务是扫描输入的源代码字符流,并将字符组成的序列转换为有意义的标记(Token)。每个Token包含一个词法单元的信息,如关键字、标识符、运算符、常量等。例如,对于表达式inta=10;,词法分析器会生成诸......
  • CB链分析与利用超详细
    环境配置commons-beanutils1.8.3commons-logging:commons-logging:1.2jdk8u71pom.xml添加<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.8.3</vers......
  • CC7分析与利用超详细
    CC7分析与利用cc7分析cc7也是接着LazyMap.get()方法向上找。这里先给出LazyMap.get()执行命令的构造:packageorg.example;importorg.apache.commons.collections.Transformer;importorg.apache.commons.collections.functors.ChainedTransformer;importorg.apache......
  • 慢sql问题分析与总结
    慢SQL问题是指在数据库查询中执行时间过长(通常超过预期阈值,比如100毫秒)的SQL语句。这些长时间运行的查询可能会严重影响系统的响应速度和用户体验。对慢SQL问题的分析和总结主要包括以下几个步骤:识别问题:首先,使用数据库性能监控工具(如MySQL的EXPLAIN、SQLServer的Profiler......
  • 车站选票代码分析与展示(C++版)
    目录程序的主要功能1.主窗口:2.管理员窗口:3.普通顾客窗口:主要数据结构函数间调用关系算法流程图1.查询算法流程图​编辑2.乘客买票算法流程图程序运行结果1.主窗口->管理员窗口a.管理员窗口->验证窗口b.管理员增加车次信息c.浏览全部车辆信息d.注销车次信息e.车......
  • cesium 添加 Echarts图层(航线图)
    cesium添加Echarts航线图(下面附有源码)1、实现思路1、在scene上面新增一个canvas画布2、通坐标转换,将经纬度坐标转为屏幕坐标来实现3、将ecarts中每个series数组中元素都加coordinateSystem:‘cesiumEcharts’2、示例代码<!DOCTYPEhtml><htmllang=......