2025-03-11 08:25:45 +00:00
/ * *
* 可视域分析 。
*
* @ author Helsing
* @ date 2020 / 08 / 28
* @ alias viewshed
* @ class
* @ 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 viewshed extends analyser {
constructor ( viewer , options ) {
super ( viewer ) ;
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 ? getHeading ( this . viewPosition , this . viewPositionEnd ) : ( options . viewHeading || 0.0 ) ;
this . viewPitch = this . viewPositionEnd ? 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 ;
this . options = options ;
this . id = Cesium . createGuid ( ) ;
this . _resultTip = this . viewer . entities . add ( {
id : this . id ,
label : {
fillColor : Cesium . Color . YELLOW ,
showBackground : true ,
font : '14px monospace' ,
horizontalOrigin : Cesium . HorizontalOrigin . LEFT ,
verticalOrigin : Cesium . VerticalOrigin . BOTTOM ,
pixelOffset : new Cesium . Cartesian2 ( 0 , - 10 )
}
} ) ;
this . posArray = [ ] ;
this . _markers = [ ] ;
this . state = this . BEYONANALYSER _STATE . PREPARE ;
this . action ( ) ;
}
action ( ) {
let _self = this ;
var ellipsoid = this . viewer . scene . globe . ellipsoid ;
_self . handler . setInputAction ( function ( movement ) {
var cartesian = latlng . getCurrentMousePosition ( _self . viewer . scene , movement . position ) ;
if ( _self . _markers . length == 0 ) {
var temp1 = Cesium . Cartographic . fromCartesian ( cartesian ) ;
var h1 ;
if ( _self . options . qdOffset ) {
h1 = temp1 . height + _self . options . qdOffset
} else {
h1 = temp1 . height + 1 ;
}
var cartographictemp = Cesium . Cartographic . fromDegrees ( temp1 . longitude / Math . PI * 180 , temp1 . latitude / Math . PI * 180 , h1 ) ;
cartesian = ellipsoid . cartographicToCartesian ( cartographictemp ) ;
} else if ( _self . _markers . length == 1 ) {
var temp1 = Cesium . Cartographic . fromCartesian ( cartesian ) ;
var h1 ;
if ( _self . options . zdOffset ) {
h1 = temp1 . height + _self . options . qdOffset
} else {
h1 = temp1 . height + 1 ;
}
var cartographictemp = Cesium . Cartographic . fromDegrees ( temp1 . longitude / Math . PI * 180 , temp1 . latitude / Math . PI * 180 , h1 ) ;
cartesian = ellipsoid . cartographicToCartesian ( cartographictemp ) ;
}
if ( ! cartesian ) {
return ;
}
_self . posArray . push ( cartesian ) ;
if ( _self . _markers . length == 0 ) {
var startSphere = _self . viewer . entities . add ( {
position : cartesian ,
ellipsoid : {
radii : new Cesium . Cartesian3 ( 1.0 , 1.0 , 1.0 ) ,
material : Cesium . Color . BLUE
} ,
label : {
text : "视线起点" ,
fillColor : Cesium . Color . YELLOW ,
pixelOffset : {
x : 0 ,
y : - 20
} ,
scale : 0.5
}
} ) ;
_self . _markers . push ( startSphere ) ;
_self . state = _self . BEYONANALYSER _STATE . OPERATING ;
} else if ( _self . _markers . length == 1 ) {
var redSphere = _self . viewer . entities . add ( {
position : cartesian ,
ellipsoid : {
radii : new Cesium . Cartesian3 ( 1.0 , 1.0 , 1.0 ) ,
material : Cesium . Color . RED
} ,
label : {
text : "视线终点" ,
fillColor : Cesium . Color . YELLOW ,
pixelOffset : {
x : 0 ,
y : - 20
} ,
scale : 0.5
}
} ) ;
_self . _markers . push ( redSphere ) ;
_self . viewPosition = _self . posArray [ 0 ] ;
_self . viewPositionEnd = cartesian ;
_self . viewDistance = _self . viewPositionEnd ? Cesium . Cartesian3 . distance ( _self . viewPosition , _self . viewPositionEnd ) : ( _self . options . viewDistance || 100.0 ) ;
_self . viewHeading = _self . viewPositionEnd ? getHeading ( _self . viewPosition , _self . viewPositionEnd ) : ( _self . options . viewHeading || 0.0 ) ;
_self . viewPitch = _self . viewPositionEnd ? getPitch ( _self . viewPosition , _self . viewPositionEnd ) : ( _self . options . viewPitch || 0.0 ) ;
_self . state = _self . BEYONANALYSER _STATE . END ;
_self . handler . destroy ( ) ;
_self . handler = null ;
_self . remove ( ) ;
_self . update ( ) ;
}
} , Cesium . ScreenSpaceEventType . LEFT _CLICK ) ;
//移动
var info ;
_self . handler . setInputAction ( function ( movement ) {
var cartesian = _self . viewer . scene . pickPosition ( movement . endPosition ) ;
if ( _self . state === _self . BEYONANALYSER _STATE . PREPARE ) {
info = '点击设定起点' ;
_self . showTip ( _self . _resultTip , true , cartesian , info ) ;
} else if ( _self . state === _self . BEYONANALYSER _STATE . OPERATING ) {
info = '点击设定终点' ;
_self . showTip ( _self . _resultTip , true , cartesian , info ) ;
}
} , Cesium . ScreenSpaceEventType . MOUSE _MOVE ) ;
}
remove ( ) {
if ( this . _markers . length == 0 ) {
return false ;
}
for ( let index = 0 ; index < this . _markers . length ; index ++ ) {
var element = this . _markers [ index ] ;
this . viewer . entities . remove ( element ) ;
}
this . _markers . length = 0 ;
this . viewer . entities . remove ( this . _resultTip ) ;
this . _resultTip = undefined ;
}
add ( ) {
this . createLightCamera ( ) ;
this . createShadowMap ( ) ;
this . createPostStage ( ) ;
this . drawFrustumOutline ( ) ;
this . drawSketch ( ) ;
}
update ( ) {
this . clear ( ) ;
this . add ( ) ;
}
clear ( ) {
if ( this . sketch ) {
this . viewer . entities . removeById ( this . sketch . id ) ;
this . sketch = null ;
}
if ( this . frustumOutline ) {
this . frustumOutline . destroy ( ) ;
this . frustumOutline = null ;
}
if ( this . postStage ) {
this . viewer . scene . postProcessStages . remove ( this . postStage ) ;
this . postStage = null ;
}
}
//创建相机
createLightCamera ( ) {
this . lightCamera = new Cesium . Camera ( this . viewer . scene ) ;
this . lightCamera . position = this . viewPosition ;
// if (this.viewPositionEnd) {
// let direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(this.viewPositionEnd, this.viewPosition, new Cesium.Cartesian3()), new Cesium.Cartesian3());
// this.lightCamera.direction = direction; // direction是相机面向的方向
// }
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
}
} ) ;
}
//创建阴影贴图
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 ,
2025-06-22 07:06:26 +00:00
fromLightSource : false ,
2025-03-11 08:25:45 +00:00
} ) ;
this . viewer . scene . shadowMap = this . shadowMap ;
2025-06-22 07:06:26 +00:00
// 启用地形阴影
this . viewer . scene . globe . shadows = Cesium . ShadowMode . ENABLED ;
2025-03-11 08:25:45 +00:00
}
//创建PostStage
createPostStage ( ) {
const fs = `
# define USE _CUBE _MAP _SHADOW true
uniform sampler2D colorTexture ;
uniform sampler2D depthTexture ;
varying vec2 v _textureCoordinates ;
uniform mat4 camera _projection _matrix ;
uniform mat4 camera _view _matrix ;
uniform samplerCube shadowMap _textureCube ;
uniform mat4 shadowMap _matrix ;
uniform vec4 shadowMap _lightPositionEC ;
uniform vec4 shadowMap _normalOffsetScaleDistanceMaxDistanceAndDarkness ;
uniform vec4 shadowMap _texelSizeDepthBiasAndNormalShadingSmooth ;
uniform float helsing _viewDistance ;
uniform vec4 helsing _visibleAreaColor ;
uniform vec4 helsing _invisibleAreaColor ;
struct zx _shadowParameters
{
vec3 texCoords ;
float depthBias ;
float depth ;
float nDotL ;
vec2 texelStepSize ;
float normalShadingSmooth ;
float darkness ;
} ;
float czm _shadowVisibility ( samplerCube shadowMap , zx _shadowParameters shadowParameters )
{
float depthBias = shadowParameters . depthBias ;
float depth = shadowParameters . depth ;
float nDotL = shadowParameters . nDotL ;
float normalShadingSmooth = shadowParameters . normalShadingSmooth ;
float darkness = shadowParameters . darkness ;
vec3 uvw = shadowParameters . texCoords ;
depth -= depthBias ;
float visibility = czm _shadowDepthCompare ( shadowMap , uvw , depth ) ;
return czm _private _shadowVisibility ( visibility , nDotL , normalShadingSmooth , darkness ) ;
}
vec4 getPositionEC ( ) {
return czm _windowToEyeCoordinates ( gl _FragCoord ) ;
}
vec3 getNormalEC ( ) {
return vec3 ( 1. ) ;
}
vec4 toEye ( vec2 uv , float depth ) {
vec2 xy = vec2 ( ( uv . x * 2. - 1. ) , ( uv . y * 2. - 1. ) ) ;
vec4 posInCamera = czm _inverseProjection * vec4 ( xy , depth , 1. ) ;
posInCamera = posInCamera / posInCamera . w ;
return posInCamera ;
}
vec3 pointProjectOnPlane ( vec3 planeNormal , vec3 planeOrigin , vec3 point ) {
vec3 v01 = point - planeOrigin ;
float d = dot ( planeNormal , v01 ) ;
return ( point - planeNormal * d ) ;
}
float getDepth ( vec4 depth ) {
float z _window = czm _unpackDepth ( depth ) ;
z _window = czm _reverseLogDepth ( z _window ) ;
float n _range = czm _depthRange . near ;
float f _range = czm _depthRange . far ;
return ( 2. * z _window - n _range - f _range ) / ( f _range - n _range ) ;
}
float shadow ( vec4 positionEC ) {
vec3 normalEC = getNormalEC ( ) ;
zx _shadowParameters shadowParameters ;
shadowParameters . texelStepSize = shadowMap _texelSizeDepthBiasAndNormalShadingSmooth . xy ;
shadowParameters . depthBias = shadowMap _texelSizeDepthBiasAndNormalShadingSmooth . z ;
shadowParameters . normalShadingSmooth = shadowMap _texelSizeDepthBiasAndNormalShadingSmooth . w ;
shadowParameters . darkness = shadowMap _normalOffsetScaleDistanceMaxDistanceAndDarkness . w ;
vec3 directionEC = positionEC . xyz - shadowMap _lightPositionEC . xyz ;
float distance = length ( directionEC ) ;
directionEC = normalize ( directionEC ) ;
float radius = shadowMap _lightPositionEC . w ;
if ( distance > radius )
{
return 2.0 ;
}
vec3 directionWC = czm _inverseViewRotation * directionEC ;
shadowParameters . depth = distance / radius - 0.0003 ;
shadowParameters . nDotL = clamp ( dot ( normalEC , - directionEC ) , 0. , 1. ) ;
shadowParameters . texCoords = directionWC ;
float visibility = czm _shadowVisibility ( shadowMap _textureCube , shadowParameters ) ;
return visibility ;
}
bool visible ( vec4 result )
{
result . x /= result . w ;
result . y /= result . w ;
result . z /= result . w ;
return result . x >= - 1. && result . x <= 1.
&& result . y >= - 1. && result . y <= 1.
&& result . z >= - 1. && result . z <= 1. ;
}
void main ( ) {
// 釉色 = 结构二维(颜色纹理, 纹理坐标)
gl _FragColor = texture2D ( colorTexture , v _textureCoordinates ) ;
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
float depth = getDepth ( texture2D ( depthTexture , v _textureCoordinates ) ) ;
// 视角 = (纹理坐标, 深度)
vec4 viewPos = toEye ( v _textureCoordinates , depth ) ;
// 世界坐标
vec4 wordPos = czm _inverseView * viewPos ;
// 虚拟相机中坐标
vec4 vcPos = camera _view _matrix * wordPos ;
float near = . 001 * helsing _viewDistance ;
float dis = length ( vcPos . xyz ) ;
if ( dis > near && dis < helsing _viewDistance ) {
// 透视投影
vec4 posInEye = camera _projection _matrix * vcPos ;
// 可视区颜色
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
if ( visible ( posInEye ) ) {
float vis = shadow ( viewPos ) ;
if ( vis > 0.3 ) {
gl _FragColor = mix ( gl _FragColor , helsing _visibleAreaColor , . 5 ) ;
} else {
gl _FragColor = mix ( gl _FragColor , helsing _invisibleAreaColor , . 5 ) ;
}
}
}
} ` ;
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 ) ;
}
//创建视锥线
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 //new Cesium.Color(0.0, 1.0, 0.0, 1.0)
) ,
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
} )
} )
) ;
}
//创建视网
drawSketch ( ) {
this . sketch = this . viewer . entities . add ( {
name : 'sketch' ,
position : this . viewPosition ,
orientation : Cesium . Transforms . headingPitchRollQuaternion (
this . viewPosition ,
Cesium . HeadingPitchRoll . fromDegrees ( this . viewHeading - this . horizontalViewAngle , this . viewPitch , 0.0 )
) ,
ellipsoid : {
radii : new Cesium . Cartesian3 (
this . viewDistance ,
this . viewDistance ,
this . viewDistance
) ,
// 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
}
} ) ;
}
}
//获取偏航角
function 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 ) ) ;
}
//获取俯仰角
function 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 ) ) ;
}