mirror of
https://github.com/jiawanlong/Cesium-Examples.git
synced 2025-07-04 15:17:36 +00:00
523 lines
22 KiB
JavaScript
523 lines
22 KiB
JavaScript
/**
|
||
* 可视域分析。
|
||
*
|
||
* @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,
|
||
fromLightSource: false,
|
||
});
|
||
this.viewer.scene.shadowMap = this.shadowMap;
|
||
// 启用地形阴影
|
||
this.viewer.scene.globe.shadows = Cesium.ShadowMode.ENABLED;
|
||
}
|
||
|
||
//创建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));
|
||
}
|