引子

万众瞩目的可视域分析功能终于来了!上一篇做这个预告的时候,压根还没开始碰这块东西,还有点小忐忑呢,万一弄不出来不就打脸了么,不过好在我脸转的快没打着。

预期效果

效果还可以吧,除了上面星星点点的小痘痘。

实现原理

ShadowMap

顾名思义,ShadowMap就是阴影贴图。看Cesium的API,有一句话“Do not construct this directly”,看来官方是不希望我们动它,因为Cesium就是用它来实现阴影效果的。但是没办法,Cesium又不支持自定义光源,不动它我们就没办法实现可视域分析了(看过大佬们的博客,其实还是有其他方法的,不过这里暂不探讨了)。下面我们看下它构造:

参数 类型 描述
options Object 子属性:

属性 类型 默认值 描述
lightCamera Camera   光源相机对象。
enabled Boolean true 阴影贴图是否可用。
isPointLight Boolean false 光源是否为点光源。如果是点光源,则阴影不使用级联。
pointLightRadius Boolean 100.0 点光源的半径。
cascadesEnabled Boolean true 是否使用多个阴影贴图来覆盖视锥体的不同分区。
numberOfCascades Number 4 阴影贴图的级联数。支持的值为1和4。
maximumDistance Number 5000.0 生成级联阴影的最大距离。较低的值可提高阴影质量。
size Number 2048 每个阴影贴图的宽度和高度(以像素为单位)。
softShadows Boolean false 是否启用percentage-closer-filtering以生成更柔和的阴影。
darkness Number 0.3 阴影的黑暗度。
normalOffset Boolean true 是否将法线偏移应用于阴影。

通过上述构造参数可以很直观地了解ShadowMap了,其中重点要讲一下softShadows参数。描述中提到了percentage-closer-filtering,这个什么东东呢?等我从网上抄一段过来看看:Percentage Closer Filtering,简称PCF,是常用于柔化Shadow Map边缘,产生软阴影的一种技术,最早来源于1987年Computer Graphics上的论文,因为算法原理简单,实现快速而且并不会占用太多运算,所以广泛的应用于实时视频游戏的阴影计算中。Shadow Map的一个比较明显的缺点即是在生成的阴影边缘锯齿化很严重,而PCF则能有效地克服Shadow Map阴影边缘的锯齿。PCF通过在绘制阴影时,除了绘制该点阴影信息之外还对该点周围阴影情况进行多次采样并混合来实现锯齿的柔化,这也是抗锯齿最通用也是最简易的处理方式。用过和我一样方式实现可视域分析的童鞋都知道,锯齿情况很感人,所以能改善锯齿的一定要使用,当然仅靠这点还不够,还得想些其他方法,比如优化着色器等。本篇用到的片元着色器代码源于网络,作者并没有做太深的优化,我也是本着先用起来的态度拿过来了,等后期优化我会做些优化,到时候再更新文章吧。

Frustum

也就是视锥,它是相机的视觉表现效果。实际上视锥是不可见的,所以我们需要绘制出视锥的示意线,这里我们要使用FrustumOutlineGeometry来绘制,然后使用Primitive添加进来。下面来看一下FrustumOutlineGeometry的构造:

参数 类型 描述
options Object 子属性:

属性 类型 描述
frustum PerspectiveFrustum | OrthographicFrustum 视锥。
origin Cartesian3 视锥的起始点。
orientation Quaternion 视锥的方向。

我们看到第一个参数是frustum,也就是说要画视锥的边框线,还得先有视锥体才行。到了这里我们最直观的想法就是缺啥造啥呗,的确我们可以使用PerspectiveFrustum构造一个视锥体出来。但是细细想来,我们好像搞反了,应该是先相机再有视锥最后才有示意线。那我们换个思路,既然构造ShadowMap需要一个Camera,那么我们就直接利用这个Camera,查看API后我们发现Camera有一个frustum属性,可不,这就是视锥体啊。好了,视锥体有了,再为它附上起始点和方向,一个视锥示意线就搞定了。umn,那么相机怎么构造呢,相机中的视锥体又怎么设置呢?让我们再研究一下相机。

Camera

老规矩,先查看API,发现Camera的构造参数只有一个scene,看来要我们的重心要放在它的属性上了,我们挑几个我们需要用到的几个重点属性了解一下,请看下表:

属性 类型 默认值 描述
position Cartesian3   相机的起始点。
direction Cartesian3   相机的方向。
up Cartesian3   相机向上的方向。
right Cartesian3   相机向右的方向。
frustum PerspectiveFrustum   子属性:

属性 类型 默认值 描述
fov Number   视野(Field of View)的角度,单位是弧度。
aspectRatio Number   视锥的宽高比。
near Number 1.0 近平面的距离。
far Number 500000000.0 远平面的距离。

看API中给出的例子:

// 创建一个沿负z轴向下的,位于原点的,视野为60度的,宽高比为1:1的相机。
var camera = new Cesium.Camera(scene);
camera.position = new Cesium.Cartesian3();
camera.direction = Cesium.Cartesian3.negate(Cesium.Cartesian3.UNIT_Z, new Cesium.Cartesian3());
camera.up = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Y);
camera.frustum.fov = Cesium.Math.PI_OVER_THREE;
camera.frustum.near = 1.0;
camera.frustum.far = 2.0;

上述例子中给定了视锥的起点、向前的方向和向上的方向,就把视锥的方向确定下来了,当然你也可以使用setView方法,本篇例子正是使用这种方式。然后设置了视锥的视野角度、宽高比、近平面距离和远平面距离,把视锥的形状确定下来了。

视网

所谓的视网其实就是个网状的视觉草图示意线,没什么用,就是让它看起来又逼格一点。这里用的是EllipsoidGraphics绘制的实体对象,这个功能虽然比较简单,但也有坑,至少你在网上看到的好些文章中都是有瑕疵的。那么照例先看API,结构如下:

参数 类型 描述
options Object 子属性:

属性 类型 默认值 描述
show Property|Boolean true 椭球体的是否可见。
radii Property|Cartesian3   椭球体的半径。
innerRadii Property|Cartesian3   椭球体的内半径。
minimumClock Property|Number 0.0 椭球体的最小时钟角。
maximumClock Property|Number 2*PI 椭球体的最大时钟角。
minimumCone Property|Number 0.0 椭球体最小锥角。
maximumCone Property|Number PI 椭球体最大锥角。
heightReference Property|HeightReference HeightReference.NONE 相对于地形的位置。
fill Property|Boolean true 是否使用提供的材质填充椭球体。
material MaterialProperty|Color Color.WHITE 填充椭球体的材质。
outline Property|Boolean false 是否显示椭球体的边框线。
outlineColor Property|Color Color.BLACK 边框线的颜色。
outlineWidth Property|Number 1.0 边框线的宽度。
stackPartitions Property|Number 64 堆栈数量。
slicePartitions Property|Number 64 径向切片数量。
subdivisions Property|Number 128 指定每个轮廓环的采样数,以确定曲率的粒度。
shadows Property|ShadowMode ShadowMode.DISABLED 指定椭球体是投射还是接收来自光源的阴影。
distanceDisplayCondition Property|DistanceDisplayCondition   指定一个条件以确定椭球体将在距离相机的哪个位置时显示。

看了API清晰明了,基本不用多说了。要注意的几个属性,innerRadii是椭球的内半径,通俗点讲就是把一个球的心给挖了,挖多大呢,就是它说了算,另外如果想显示内切线条,这个值必须指定。当然最重要的四个属性就是minimumClock、maximumClock、minimumCone、maximumCone了,它们的作用就是截取球面上的一块补丁,让它看起来像是视网。

具体实现

做完以上功课了,下面开始具体实现。

类封装

// ViewShed.js/*** 可视域分析。** @author Helsing* @date 2020/08/28* @alias ViewShedStage* @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 ViewShedStage {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 ? 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.update();}add() {this.createLightCamera();this.createShadowMap();this.createPostStage();this.drawFrustumOutine();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;}}
}export default ViewShed;

常规的ES6封装和JSDoc注释,无需多讲。三个基本方法添加、更新、清除。

创建相机

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}});
}

上述采用了setView的方式确定相机的方向,你们可以看到我注释的部分,是采用direction的方式。

创建阴影贴图

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;
}

这个没什么好讲的。

创建PostStage

createPostStage() {const fs = glslconst 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})}));
}

上面有可能碰到的坑是最后添加Primitive的时候报错,可尝试appearance的flat属性设置为true,默认值为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));
}

上述两个方法是获取偏航角和俯仰角。额,你发现了,两个方法怎么那么像啊,我懒得整合了,你们看着办吧。

下面后处理中的GLSL代码是借鉴了网友的思路,核心代码挺简单的,看下注释就理解了,不做赘述了。

export default `#define USE_CUBE_MAP_SHADOW trueuniform 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(in vec2 uv,in 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(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){vec3 v01=point-planeOrigin;float d=dot(planeNormal,v01);return(point-planeNormal*d);}float getDepth(in 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(in 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(in 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);}}}}`;

小结

哎,不好意思做这个小结,实在没研究个啥出来。不过这个东西以前没搞过,仓促的做出来,仅限于从无到有吧,待我日后深入研究之后再好好写一篇。差点忘记说了,使用后处理的方式做的可视域分析,建议你在使用的时候开启深度检测和对数深度,否则效果不可描述,当然了,开了之后有点小卡。什么?你问我怎么开?就不告诉你,问度娘去!

PS

小伙伴们可以逛逛这个群854943530,纯技术交流哦,小白最爱。

Cesium深入浅出之可视域分析相关推荐

  1. vue3+SuperMap iClient3D for Cesium实现可视域分析功能

    本人小白一枚,文章如有问题还请各位大神评论区指出.整体实现是参考SuperMap iClient3D for Cesium的可视域分析功能源码~ 文章目录 前言 一.主要功能 二.使用步骤 1.HTM ...

  2. Cesium 可视域分析代码段(源码)补充

    很抱歉之前的博客里的代码段是贴的截图:关于可视域分析的实现_总要学点什么的博客-CSDN博客_可视域分析 功能优化后的完整demo: Cesium功能集 之 034-可视域分析(纯前端)完整版 现在将 ...

  3. Cesium可视域分析

    继续开始Cesium的学习之旅. 文章目录 一.简介 二.代码实现 2.1相关参数 2.2实现过程 一.简介 因为也没查到关于可视域分析的明确定义,所以就按自己的话来说一下了.对三维空间而言,可视域分 ...

  4. supermap iobjects学习——三维通视分析,可视域分析 (1)

    三维可视分析包括通视分析和可视域分析.前者用来判断两点之间是否通视,后者用于对观察点的可视范围进行分析. 涉及到的控件: sceneControl相关事件:Tracking,Tracked Scene ...

  5. 三维分析之可视域分析

    三维可视域分析是在场景的地形或模型数据表面,相对于某个观察点,基于一定的水平视角.垂直视角及指定范围半径,分析该区域内所有通视点的集合.分析结果用绿色区域表示在观察点处可见,红色区域表示在观察点处不可 ...

  6. 【cesium】可视域分析

    // 创建相机 createLightCamera () { this.lightCamera = new Cesium.Camera(this.viewer.scene); this.lightCa ...

  7. dataearth可视域分析_谁不知道前期分析要用ARCGIS?我就是不会用啊

    Q 你有没有遇到这样的情况? 身边的同事或同学经常说什么高程图.坡向图.坡度图.生态水文图.热力密度图.大数据算法,而自己却一无所知,心里感到特别的慌啊. 文末附ArcGIS最新版及超强数据神器 很多 ...

  8. dataearth可视域分析_在线三维实景建模及分析

    国科创(北京)信息技术有限公司的工程师可以快速地基于客户的需求开发无插件的在线三维实景可视化程序.构建精细化的在线三维场景,并且基于场景中的模型进行可视化的分析测量.用户可以基于场景模型实现测量距离. ...

  9. Cesium 可视域分析(未改源码)

    嘿嘿嘿,出来了. 此功能未修改cesium的源码,可动态调节夹角.颜色及距离等因素. 一下晒出部分代码: //创建一个点光源this._shadowMap = new Cesium.ShadowMap ...

最新文章

  1. 我用YOLOX露了一手,记录一下模型部署、优化及训练的实现全过程
  2. 从Java 8中的java.util.stream.Stream检索列表
  3. 如何优雅的导出Excel
  4. canal mysql多实例_canal搭建实例
  5. win7源码运行odoo8.0错误
  6. easyui Combotree 怎么加载数据 支持多选
  7. Ubuntu 20.04安装Ros Noetic及Ubuntu 18.04安装ROS Melodic(两版本详细填坑)
  8. python 生成器迭代器
  9. kindle touch救砖用,我只是搬运工,2020年4月6日
  10. RS485模块的介绍及引脚连线说明
  11. win10进程太多怎么优化_win10全能优化工具箱
  12. 靶点c语言,降脂治疗靶点:LDL-C是最好的吗?(上)
  13. STM32F103C8T6串口通信
  14. 会员卡券领取 小程序_新增卡券功能在哪 微信小程序内直接领取卡券方法
  15. res资源文件学习之res/values/attr.xml
  16. 量子计算与量子信息之量子计算概述
  17. LeetCode—494. 目标和(Target Sum)——分析及代码(Java)
  18. 李建忠讲23种设计模式笔记-上
  19. Java核心技术·卷二·第一章笔记
  20. 世界上第一第一台计算机叫什么,世界上第一台计算机叫什么

热门文章

  1. 大公司 Or 小公司?
  2. mace model convert
  3. 【Cocos游戏】《海贼王》500万用户神话:原因你也许不懂
  4. Vue+Flask前后端分离 Vue3跨域配置
  5. 平安养老险党委书记、董事长甘为民:做实、做多、做大养老三支柱
  6. sentry php使用,Sentry简介
  7. 关于.c .obj .cpp .exe区别
  8. 常见的几种SSL/TLS漏洞及攻击方式
  9. 如何搭建GFS分布式文件系统
  10. 优秀编程技术公众号推荐