// /**
|
// * @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 每个阴影贴图的大小。
|
// */
|
(function (window) {
|
'use strict';
|
function define_ViewShed() {
|
var ViewShed = {};
|
ViewShed.create = function(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;
|
};
|
|
ViewShed.add = function() {
|
this.createLightCamera();
|
this.createShadowMap();
|
// this.drawFrustumOutline(); //视锥线
|
this.drawSketch();
|
this.createPostStage();
|
};
|
|
ViewShed.update =function() {
|
this.clear();
|
this.add();
|
};
|
|
/**
|
* @method 更新终点坐标,从而实时更新绘制的实体的方向和半径
|
*
|
*/
|
ViewShed.updatePosition = function (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)
|
};
|
|
ViewShed.clear = function() {
|
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 创建相机
|
*/
|
ViewShed.createLightCamera = function()
|
{
|
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 创建阴影贴图
|
*/
|
ViewShed.createShadowMap = function()
|
{
|
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是做片元着色的,具体怎么实现的我也不太明白
|
*/
|
ViewShed.createPostStage = function()
|
{
|
const fs = '#define USE_CUBE_MAP_SHADOW true\n\
|
uniform sampler2D colorTexture;\n\
|
uniform sampler2D depthTexture;\n\
|
varying vec2 v_textureCoordinates;\n\
|
uniform mat4 camera_projection_matrix;\n\
|
uniform mat4 camera_view_matrix;\n\
|
uniform samplerCube shadowMap_textureCube;\n\
|
uniform mat4 shadowMap_matrix;\n\
|
uniform vec4 shadowMap_lightPositionEC;\n\
|
uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;\n\
|
uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;\n\
|
uniform float helsing_viewDistance; \n\
|
uniform vec4 helsing_visibleAreaColor;\n\
|
uniform vec4 helsing_invisibleAreaColor;\n\
|
struct zx_shadowParameters\n\
|
{\n\
|
vec3 texCoords;\n\
|
float depthBias;\n\
|
float depth;\n\
|
float nDotL;\n\
|
vec2 texelStepSize;\n\
|
float normalShadingSmooth;\n\
|
float darkness;\n\
|
};\n\
|
float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)\n\
|
{\n\
|
float depthBias = shadowParameters.depthBias;\n\
|
float depth = shadowParameters.depth;\n\
|
float nDotL = shadowParameters.nDotL;\n\
|
float normalShadingSmooth = shadowParameters.normalShadingSmooth;\n\
|
float darkness = shadowParameters.darkness;\n\
|
vec3 uvw = shadowParameters.texCoords;\n\
|
depth -= depthBias;\n\
|
float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);\n\
|
return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);\n\
|
}\n\
|
vec4 getPositionEC(){\n\
|
return czm_windowToEyeCoordinates(gl_FragCoord);\n\
|
}\n\
|
vec3 getNormalEC(){\n\
|
return vec3(1.0);\n\
|
}\n\
|
vec4 toEye(in vec2 uv,in float depth){\n\
|
vec2 xy=vec2((uv.x*2.0-1.0),(uv.y*2.0-1.0));\n\
|
vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.0);\n\
|
posInCamera=posInCamera/posInCamera.w;\n\
|
return posInCamera;\n\
|
}\n\
|
vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){\n\
|
vec3 v01=point-planeOrigin;\n\
|
float d=dot(planeNormal,v01);\n\
|
return(point-planeNormal*d);\n\
|
}\n\
|
float getDepth(in vec4 depth){\n\
|
float z_window=czm_unpackDepth(depth);\n\
|
z_window=czm_reverseLogDepth(z_window);\n\
|
float n_range=czm_depthRange.near;\n\
|
float f_range=czm_depthRange.far;\n\
|
return(2.0*z_window-n_range-f_range)/(f_range-n_range);\n\
|
}\n\
|
float shadow(in vec4 positionEC){\n\
|
vec3 normalEC=getNormalEC();\n\
|
zx_shadowParameters shadowParameters;\n\
|
shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;\n\
|
shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;\n\
|
shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;\n\
|
shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;\n\
|
vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;\n\
|
float distance=length(directionEC);\n\
|
directionEC=normalize(directionEC);\n\
|
float radius=shadowMap_lightPositionEC.w;\n\
|
if(distance>radius)\n\
|
{\n\
|
return 2.0;\n\
|
}\n\
|
vec3 directionWC=czm_inverseViewRotation*directionEC;\n\
|
shadowParameters.depth=distance/radius-0.0003;\n\
|
shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.0,1.0);\n\
|
shadowParameters.texCoords=directionWC;\n\
|
float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);\n\
|
return visibility;\n\
|
}\n\
|
bool visible(in vec4 result)\n\
|
{\n\
|
result.x/=result.w;\n\
|
result.y/=result.w;\n\
|
result.z/=result.w;\n\
|
return result.x>=-1.0&&result.x<=1.0\n\
|
&&result.y>=-1.0&&result.y<=1.0\n\
|
&&result.z>=-1.0&&result.z<=1.0;\n\
|
}\n\
|
void main(){\n\
|
// 釉色 = 结构二维(颜色纹理, 纹理坐标)\n\
|
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);\n\
|
// 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))\n\
|
float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));\n\
|
// 视角 = (纹理坐标, 深度)\n\
|
vec4 viewPos = toEye(v_textureCoordinates, depth);\n\
|
// 世界坐标\n\
|
vec4 wordPos = czm_inverseView * viewPos;\n\
|
// 虚拟相机中坐标\n\
|
vec4 vcPos = camera_view_matrix * wordPos;\n\
|
float near = 0.001 * helsing_viewDistance;\n\
|
float dis = length(vcPos.xyz);\n\
|
if(dis > near && dis < helsing_viewDistance){\n\
|
// 透视投影\n\
|
vec4 posInEye = camera_projection_matrix * vcPos;\n\
|
// 可视区颜色\n\
|
// vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);\n\
|
// vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);\n\
|
if(visible(posInEye)){\n\
|
float vis = shadow(viewPos);\n\
|
if(vis > 0.3){\n\
|
gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,0.5);\n\
|
} else{\n\
|
gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,0.5);\n\
|
}\n\
|
}\n\
|
}\n\
|
}\n\
|
';
|
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 创建视锥线
|
*/
|
ViewShed.drawFrustumOutline = function()
|
{
|
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)
|
*/
|
ViewShed.drawSketch = function()
|
{
|
this.sketch = this.viewer.entities.add({
|
name: 'sketch',
|
position: this.viewPosition,
|
orientation: new Cesium.CallbackProperty(()=> Cesium.Transforms.headingPitchRollQuaternion( //实体的方向
|
this.viewPosition,
|
Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - 90.0, this.viewPitch, 0.0))),
|
ellipsoid: { //椭球的半径
|
radii:new Cesium.CallbackProperty(() => 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, //轮廓的颜色
|
}
|
});
|
};
|
|
/**
|
* @method 获取偏航角
|
*/
|
ViewShed.getHeading = function(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 获取俯仰角
|
*/
|
ViewShed.getPitch = function(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));
|
};
|
return ViewShed;
|
}
|
if (typeof (ViewShed) === 'undefined') {
|
window.ViewShed = define_ViewShed();
|
} else {
|
console.log("ViewShed already defined.");
|
}
|
})(window);
|
|
|