Cesium深入浅出之可视域分析【转】

摘要:
吸引人的视觉领域分析功能终于到来了!但没有办法。铯不支持自定义光源。没有它,我们就无法实现可视化领域分析。MaximumDistanceNumber5000.0生成级联阴影的最大距离。黑暗数字0.3阴影的黑暗。Frustum也称为平截头体,是相机的视觉表示。原始笛卡尔3圆锥体的起点。让我们改变想法。由于ShadowMap的构建需要一个摄像头,我们可以直接使用这个摄像头。检查API后,我们发现Camera具有一个霜属性,称为视锥。aspectRatioNumber截头体的纵横比。InnerRadiiProperty |笛卡尔3椭球的内半径。

引子

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

预期效果

Cesium深入浅出之可视域分析【转】第1张

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

实现原理

ShadowMap

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

参数类型描述
optionsObject子属性:
属性类型默认值描述
lightCameraCamera 光源相机对象。
enabledBooleantrue阴影贴图是否可用。
isPointLightBooleanfalse光源是否为点光源。如果是点光源,则阴影不使用级联。
pointLightRadiusBoolean100.0点光源的半径。
cascadesEnabledBooleantrue是否使用多个阴影贴图来覆盖视锥体的不同分区。
numberOfCascadesNumber4阴影贴图的级联数。支持的值为1和4。
maximumDistanceNumber5000.0生成级联阴影的最大距离。较低的值可提高阴影质量。
sizeNumber2048每个阴影贴图的宽度和高度(以像素为单位)。
softShadowsBooleanfalse是否启用percentage-closer-filtering以生成更柔和的阴影。
darknessNumber0.3阴影的黑暗度。
normalOffsetBooleantrue是否将法线偏移应用于阴影。

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

Frustum

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

参数类型描述
optionsObject子属性:
属性类型描述
frustumPerspectiveFrustum | OrthographicFrustum视锥。
originCartesian3视锥的起始点。
orientationQuaternion视锥的方向。

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

Camera

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

属性类型默认值描述
positionCartesian3 相机的起始点。
directionCartesian3 相机的方向。
upCartesian3 相机向上的方向。
rightCartesian3 相机向右的方向。
frustumPerspectiveFrustum 子属性:
属性类型默认值描述
fovNumber 视野(Field of View)的角度,单位是弧度。
aspectRatioNumber 视锥的宽高比。
nearNumber1.0近平面的距离。
farNumber500000000.0远平面的距离。

 看API中给出的例子:

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

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

视网

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

参数类型描述
optionsObject子属性:
属性类型默认值描述
showProperty|Booleantrue椭球体的是否可见。
radiiProperty|Cartesian3 椭球体的半径。
innerRadiiProperty|Cartesian3 椭球体的内半径。
minimumClockProperty|Number0.0椭球体的最小时钟角。
maximumClockProperty|Number2*PI椭球体的最大时钟角。
minimumConeProperty|Number0.0椭球体最小锥角。
maximumConeProperty|NumberPI椭球体最大锥角。
heightReferenceProperty|HeightReferenceHeightReference.NONE相对于地形的位置。
fillProperty|Booleantrue是否使用提供的材质填充椭球体。
materialMaterialProperty|ColorColor.WHITE填充椭球体的材质。
outlineProperty|Booleanfalse是否显示椭球体的边框线。
outlineColorProperty|ColorColor.BLACK边框线的颜色。
outlineWidthProperty|Number1.0边框线的宽度。
stackPartitionsProperty|Number64堆栈数量。
slicePartitionsProperty|Number64径向切片数量。
subdivisionsProperty|Number128指定每个轮廓环的采样数,以确定曲率的粒度。
shadowsProperty|ShadowModeShadowMode.DISABLED指定椭球体是投射还是接收来自光源的阴影。
distanceDisplayConditionProperty|DistanceDisplayCondition 指定一个条件以确定椭球体将在距离相机的哪个位置时显示。

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

具体实现

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

类封装

复制代码
 1 // ViewShed.js
 2  
 3 /**
 4  * 可视域分析。
 5  *
 6  * @author Helsing
 7  * @date 2020/08/28
 8  * @alias ViewShedStage
 9  * @class
10  * @param {Cesium.Viewer} viewer Cesium三维视窗。
11  * @param {Object} options 选项。
12  * @param {Cesium.Cartesian3} options.viewPosition 观测点位置。
13  * @param {Cesium.Cartesian3} options.viewPositionEnd 最远观测点位置(如果设置了观测距离,这个属性可以不设置)。
14  * @param {Number} options.viewDistance 观测距离(单位`米`,默认值100)。
15  * @param {Number} options.viewHeading 航向角(单位`度`,默认值0)。
16  * @param {Number} options.viewPitch 俯仰角(单位`度`,默认值0)。
17  * @param {Number} options.horizontalViewAngle 可视域水平夹角(单位`度`,默认值90)。
18  * @param {Number} options.verticalViewAngle 可视域垂直夹角(单位`度`,默认值60)。
19  * @param {Cesium.Color} options.visibleAreaColor 可视区域颜色(默认值`绿色`)。
20  * @param {Cesium.Color} options.invisibleAreaColor 不可视区域颜色(默认值`红色`)。
21  * @param {Boolean} options.enabled 阴影贴图是否可用。
22  * @param {Boolean} options.softShadows 是否启用柔和阴影。
23  * @param {Boolean} options.size 每个阴影贴图的大小。
24  */
25 class ViewShedStage {
26  
27     constructor(viewer, options) {
28         this.viewer = viewer;
29         this.viewPosition = options.viewPosition;
30         this.viewPositionEnd = options.viewPositionEnd;
31         this.viewDistance = this.viewPositionEnd ? Cesium.Cartesian3.distance(this.viewPosition, this.viewPositionEnd) : (options.viewDistance || 100.0);
32         this.viewHeading = this.viewPositionEnd ? getHeading(this.viewPosition, this.viewPositionEnd) : (options.viewHeading || 0.0);
33         this.viewPitch = this.viewPositionEnd ? getPitch(this.viewPosition, this.viewPositionEnd) : (options.viewPitch || 0.0);
34         this.horizontalViewAngle = options.horizontalViewAngle || 90.0;
35         this.verticalViewAngle = options.verticalViewAngle || 60.0;
36         this.visibleAreaColor = options.visibleAreaColor || Cesium.Color.GREEN;
37         this.invisibleAreaColor = options.invisibleAreaColor || Cesium.Color.RED;
38         this.enabled = (typeof options.enabled === "boolean") ? options.enabled : true;
39         this.softShadows = (typeof options.softShadows === "boolean") ? options.softShadows : true;
40         this.size = options.size || 2048;
41  
42         this.update();
43     }
44  
45     add() {
46         this.createLightCamera();
47         this.createShadowMap();
48         this.createPostStage();
49         this.drawFrustumOutine();
50         this.drawSketch();
51     }
52  
53     update() {
54         this.clear();
55         this.add();
56     }
57  
58     clear() {
59         if (this.sketch) {
60             this.viewer.entities.removeById(this.sketch.id);
61             this.sketch = null;
62         }
63         if (this.frustumOutline) {
64             this.frustumOutline.destroy();
65             this.frustumOutline = null;
66         }
67         if (this.postStage) {
68             this.viewer.scene.postProcessStages.remove(this.postStage);
69             this.postStage = null;
70         }
71     }
72 }
73  
74 export default ViewShed;
复制代码

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

创建相机

复制代码
 1 createLightCamera() {
 2     this.lightCamera = new Cesium.Camera(this.viewer.scene);
 3     this.lightCamera.position = this.viewPosition;
 4     // if (this.viewPositionEnd) {
 5     //     let direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(this.viewPositionEnd, this.viewPosition, new Cesium.Cartesian3()), new Cesium.Cartesian3());
 6     //     this.lightCamera.direction = direction; // direction是相机面向的方向
 7     // }
 8     this.lightCamera.frustum.near = this.viewDistance * 0.001;
 9     this.lightCamera.frustum.far = this.viewDistance;
10     const hr = Cesium.Math.toRadians(this.horizontalViewAngle);
11     const vr = Cesium.Math.toRadians(this.verticalViewAngle);
12     const aspectRatio =
13         (this.viewDistance * Math.tan(hr / 2) * 2) /
14         (this.viewDistance * Math.tan(vr / 2) * 2);
15     this.lightCamera.frustum.aspectRatio = aspectRatio;
16     if (hr > vr) {
17         this.lightCamera.frustum.fov = hr;
18     } else {
19         this.lightCamera.frustum.fov = vr;
20     }
21     this.lightCamera.setView({
22         destination: this.viewPosition,
23         orientation: {
24             heading: Cesium.Math.toRadians(this.viewHeading || 0),
25             pitch: Cesium.Math.toRadians(this.viewPitch || 0),
26             roll: 0
27         }
28     });
29 }
复制代码

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

创建阴影贴图

复制代码
 1 createShadowMap() {
 2     this.shadowMap = new Cesium.ShadowMap({
 3         context: (this.viewer.scene).context,
 4         lightCamera: this.lightCamera,
 5         enabled: this.enabled,
 6         isPointLight: true,
 7         pointLightRadius: this.viewDistance,
 8         cascadesEnabled: false,
 9         size: this.size,
10         softShadows: this.softShadows,
11         normalOffset: false,
12         fromLightSource: false
13     });
14     this.viewer.scene.shadowMap = this.shadowMap;
15 }
复制代码

这个没什么好讲的。

创建PostStage

复制代码
 1 createPostStage() {
 2     const fs = glsl
 3     const postStage = new Cesium.PostProcessStage({
 4         fragmentShader: fs,
 5         uniforms: {
 6             shadowMap_textureCube: () => {
 7                 this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
 8                 return Reflect.get(this.shadowMap, "_shadowMapTexture");
 9             },
10             shadowMap_matrix: () => {
11                 this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
12                 return Reflect.get(this.shadowMap, "_shadowMapMatrix");
13             },
14             shadowMap_lightPositionEC: () => {
15                 this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
16                 return Reflect.get(this.shadowMap, "_lightPositionEC");
17             },
18             shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: () => {
19                 this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
20                 const bias = this.shadowMap._pointBias;
21                 return Cesium.Cartesian4.fromElements(
22                     bias.normalOffsetScale,
23                     this.shadowMap._distance,
24                     this.shadowMap.maximumDistance,
25                     0.0,
26                     new Cesium.Cartesian4()
27                 );
28             },
29             shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: () => {
30                 this.shadowMap.update(Reflect.get(this.viewer.scene, "_frameState"));
31                 const bias = this.shadowMap._pointBias;
32                 const scratchTexelStepSize = new Cesium.Cartesian2();
33                 const texelStepSize = scratchTexelStepSize;
34                 texelStepSize.x = 1.0 / this.shadowMap._textureSize.x;
35                 texelStepSize.y = 1.0 / this.shadowMap._textureSize.y;
36  
37                 return Cesium.Cartesian4.fromElements(
38                     texelStepSize.x,
39                     texelStepSize.y,
40                     bias.depthBias,
41                     bias.normalShadingSmooth,
42                     new Cesium.Cartesian4()
43                 );
44             },
45             camera_projection_matrix: this.lightCamera.frustum.projectionMatrix,
46             camera_view_matrix: this.lightCamera.viewMatrix,
47             helsing_viewDistance: () => {
48                 return this.viewDistance;
49             },
50             helsing_visibleAreaColor: this.visibleAreaColor,
51             helsing_invisibleAreaColor: this.invisibleAreaColor,
52         }
53     });
54     this.postStage = this.viewer.scene.postProcessStages.add(postStage);
55 } 
复制代码

创建视锥线

复制代码
 1 drawFrustumOutline() {
 2     const scratchRight = new Cesium.Cartesian3();
 3     const scratchRotation = new Cesium.Matrix3();
 4     const scratchOrientation = new Cesium.Quaternion();
 5     const position = this.lightCamera.positionWC;
 6     const direction = this.lightCamera.directionWC;
 7     const up = this.lightCamera.upWC;
 8     let right = this.lightCamera.rightWC;
 9     right = Cesium.Cartesian3.negate(right, scratchRight);
10     let rotation = scratchRotation;
11     Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
12     Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
13     Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
14     let orientation = Cesium.Quaternion.fromRotationMatrix(rotation, scratchOrientation);
15     
16     let instance = new Cesium.GeometryInstance({
17         geometry: new Cesium.FrustumOutlineGeometry({
18             frustum: this.lightCamera.frustum,
19             origin: this.viewPosition,
20             orientation: orientation
21         }),
22         id: Math.random().toString(36).substr(2),
23         attributes: {
24             color: Cesium.ColorGeometryInstanceAttribute.fromColor(
25                 Cesium.Color.YELLOWGREEN//new Cesium.Color(0.0, 1.0, 0.0, 1.0)
26             ),
27             show: new Cesium.ShowGeometryInstanceAttribute(true)
28         }
29     });
30 
31     this.frustumOutline = this.viewer.scene.primitives.add(
32         new Cesium.Primitive({
33             geometryInstances: [instance],
34             appearance: new Cesium.PerInstanceColorAppearance({
35                 flat: true,
36                 translucent: false
37             })
38         })
39     );
40 }
复制代码

上面有可能碰到的坑是最后添加Primitive的时候报错,可尝试appearance的flat属性设置为true,默认值为false。

创建视网

复制代码
 1 drawSketch() {
 2     this.sketch = this.viewer.entities.add({
 3         name: 'sketch',
 4         position: this.viewPosition,
 5         orientation: Cesium.Transforms.headingPitchRollQuaternion(
 6             this.viewPosition,
 7             Cesium.HeadingPitchRoll.fromDegrees(this.viewHeading - this.horizontalViewAngle, this.viewPitch, 0.0)
 8         ),
 9         ellipsoid: {
10             radii: new Cesium.Cartesian3(
11                 this.viewDistance,
12                 this.viewDistance,
13                 this.viewDistance
14             ),
15             // innerRadii: new Cesium.Cartesian3(2.0, 2.0, 2.0),
16             minimumClock: Cesium.Math.toRadians(-this.horizontalViewAngle / 2),
17             maximumClock: Cesium.Math.toRadians(this.horizontalViewAngle / 2),
18             minimumCone: Cesium.Math.toRadians(this.verticalViewAngle + 7.75),
19             maximumCone: Cesium.Math.toRadians(180 - this.verticalViewAngle - 7.75),
20             fill: false,
21             outline: true,
22             subdivisions: 256,
23             stackPartitions: 64,
24             slicePartitions: 64,
25             outlineColor: Cesium.Color.YELLOWGREEN
26         }
27     });
28 }
复制代码

上述注释的代码是内半径,设置之后可以显示球的中心向球面的发射线。

另外

复制代码
 1 function getHeading(fromPosition, toPosition) {
 2     let finalPosition = new Cesium.Cartesian3();
 3     let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
 4     Cesium.Matrix4.inverse(matrix4, matrix4);
 5     Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
 6     Cesium.Cartesian3.normalize(finalPosition, finalPosition);
 7     return Cesium.Math.toDegrees(Math.atan2(finalPosition.x, finalPosition.y));
 8 }
 9 
10 function getPitch(fromPosition, toPosition) {
11     let finalPosition = new Cesium.Cartesian3();
12     let matrix4 = Cesium.Transforms.eastNorthUpToFixedFrame(fromPosition);
13     Cesium.Matrix4.inverse(matrix4, matrix4);
14     Cesium.Matrix4.multiplyByPoint(matrix4, toPosition, finalPosition);
15     Cesium.Cartesian3.normalize(finalPosition, finalPosition);
16     return Cesium.Math.toDegrees(Math.asin(finalPosition.z));
17 }
复制代码

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

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

复制代码
  1 export default `
  2  #define USE_CUBE_MAP_SHADOW true
  3  uniform sampler2D colorTexture;
  4  uniform sampler2D depthTexture;
  5  varying vec2 v_textureCoordinates;
  6  uniform mat4 camera_projection_matrix;
  7  uniform mat4 camera_view_matrix;
  8  uniform samplerCube shadowMap_textureCube;
  9  uniform mat4 shadowMap_matrix;
 10  uniform vec4 shadowMap_lightPositionEC;
 11  uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
 12  uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
 13  uniform float helsing_viewDistance; 
 14  uniform vec4 helsing_visibleAreaColor;
 15  uniform vec4 helsing_invisibleAreaColor;
 16 
 17  struct zx_shadowParameters
 18  {
 19      vec3 texCoords;
 20      float depthBias;
 21      float depth;
 22      float nDotL;
 23      vec2 texelStepSize;
 24      float normalShadingSmooth;
 25      float darkness;
 26  };
 27 
 28  float czm_shadowVisibility(samplerCube shadowMap, zx_shadowParameters shadowParameters)
 29  {
 30      float depthBias = shadowParameters.depthBias;
 31      float depth = shadowParameters.depth;
 32      float nDotL = shadowParameters.nDotL;
 33      float normalShadingSmooth = shadowParameters.normalShadingSmooth;
 34      float darkness = shadowParameters.darkness;
 35      vec3 uvw = shadowParameters.texCoords;
 36      depth -= depthBias;
 37      float visibility = czm_shadowDepthCompare(shadowMap, uvw, depth);
 38      return czm_private_shadowVisibility(visibility, nDotL, normalShadingSmooth, darkness);
 39  }
 40 
 41  vec4 getPositionEC(){
 42      return czm_windowToEyeCoordinates(gl_FragCoord);
 43  }
 44 
 45  vec3 getNormalEC(){
 46      return vec3(1.);
 47  }
 48 
 49  vec4 toEye(in vec2 uv,in float depth){
 50      vec2 xy=vec2((uv.x*2.-1.),(uv.y*2.-1.));
 51      vec4 posInCamera=czm_inverseProjection*vec4(xy,depth,1.);
 52      posInCamera=posInCamera/posInCamera.w;
 53      return posInCamera;
 54  }
 55 
 56  vec3 pointProjectOnPlane(in vec3 planeNormal,in vec3 planeOrigin,in vec3 point){
 57      vec3 v01=point-planeOrigin;
 58      float d=dot(planeNormal,v01);
 59      return(point-planeNormal*d);
 60  }
 61 
 62  float getDepth(in vec4 depth){
 63      float z_window=czm_unpackDepth(depth);
 64      z_window=czm_reverseLogDepth(z_window);
 65      float n_range=czm_depthRange.near;
 66      float f_range=czm_depthRange.far;
 67      return(2.*z_window-n_range-f_range)/(f_range-n_range);
 68  }
 69 
 70  float shadow(in vec4 positionEC){
 71      vec3 normalEC=getNormalEC();
 72      zx_shadowParameters shadowParameters;
 73      shadowParameters.texelStepSize=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
 74      shadowParameters.depthBias=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
 75      shadowParameters.normalShadingSmooth=shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
 76      shadowParameters.darkness=shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
 77      vec3 directionEC=positionEC.xyz-shadowMap_lightPositionEC.xyz;
 78      float distance=length(directionEC);
 79      directionEC=normalize(directionEC);
 80      float radius=shadowMap_lightPositionEC.w;
 81      if(distance>radius)
 82      {
 83          return 2.0;
 84      }
 85      vec3 directionWC=czm_inverseViewRotation*directionEC;
 86      shadowParameters.depth=distance/radius-0.0003;
 87      shadowParameters.nDotL=clamp(dot(normalEC,-directionEC),0.,1.);
 88      shadowParameters.texCoords=directionWC;
 89      float visibility=czm_shadowVisibility(shadowMap_textureCube,shadowParameters);
 90      return visibility;
 91  }
 92 
 93  bool visible(in vec4 result)
 94  {
 95      result.x/=result.w;
 96      result.y/=result.w;
 97      result.z/=result.w;
 98      return result.x>=-1.&&result.x<=1.
 99      &&result.y>=-1.&&result.y<=1.
100      &&result.z>=-1.&&result.z<=1.;
101  }
102 
103  void main(){
104      // 釉色 = 结构二维(颜色纹理, 纹理坐标)
105      gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
106      // 深度 = 获取深度(结构二维(深度纹理, 纹理坐标))
107      float depth = getDepth(texture2D(depthTexture, v_textureCoordinates));
108      // 视角 = (纹理坐标, 深度)
109      vec4 viewPos = toEye(v_textureCoordinates, depth);
110      // 世界坐标
111      vec4 wordPos = czm_inverseView * viewPos;
112      // 虚拟相机中坐标
113      vec4 vcPos = camera_view_matrix * wordPos;
114      float near = .001 * helsing_viewDistance;
115      float dis = length(vcPos.xyz);
116      if(dis > near && dis < helsing_viewDistance){
117          // 透视投影
118          vec4 posInEye = camera_projection_matrix * vcPos;
119          // 可视区颜色
120          // vec4 helsing_visibleAreaColor=vec4(0.,1.,0.,.5);
121          // vec4 helsing_invisibleAreaColor=vec4(1.,0.,0.,.5);
122          if(visible(posInEye)){
123              float vis = shadow(viewPos);
124              if(vis > 0.3){
125                  gl_FragColor = mix(gl_FragColor,helsing_visibleAreaColor,.5);
126              } else{
127                  gl_FragColor = mix(gl_FragColor,helsing_invisibleAreaColor,.5);
128              }
129          }
130      }
131  }`;
复制代码

小结

没啥好总结的,实在没研究个啥出来。不过这个东西以前没搞过,仓促的做出来,仅限于从无到有吧,待日后深入研究吧。差点忘记说了,使用后处理的方式做的可视域分析,建议你在使用的时候开启深度检测和对数深度,否则效果出不来。

免责声明:文章转载自《Cesium深入浅出之可视域分析【转】》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇解决curl: (35) OpenSSL SSL_connect: Connection reset by peer in connection to raw.githubusercontent.com:443 错误C#使用FFMPEG推流,并且获取流保存在本地,随时取媒体进行播放!下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Libevent:6辅助函数以及类型

在头文件<event2/util.h>中定义了许多有用的函数和类型来帮助实现可移植的程序。Libevent在内部使用这些类型和函数。 一:基本类型 evutil_socket_t 除了Windows之外的大多数系统,socket就是一个整数,而且操作系统按照数值顺序对它们进行处理。而在Windows socket API中,socket是SO...

Android智能指针sp wp详解

研究Android的时候,经常会遇到sp、wp的东西,网上一搜,原来是android封装了c++中对象回收机制。说明:1. 如果一个类想使用智能指针,那么必须满足下面两个条件:a. 该类是虚基类RefBase的子类或间接子类b. 该类必须定义虚构造函数。如virtual ~MyClass(); 2. 本文以类BBinder来进行说明,其余类使用sp或wp...

C语言探索之旅 | 第一部分第五课:变量的世界(二),变量声明

作者 谢恩铭,公众号「程序员联盟」。 转载请注明出处。 原文:https://www.jianshu.com/p/8db33987cb49 《C语言探索之旅》全系列 内容简介 变量是什么? 给变量起个名字 变量的类型 声明变量 const 关键字 第一部分第六课预告 1. 变量是什么? 上一课我们学习的是 C语言探索之旅 | 第一部分第四课:变量...

C# 获取显示器的物理尺寸或分辨率

还是那个项目……还是那个领导……要求获取用户的显示器尺寸。一脸懵逼???还是照做…… 获取显示器的尺寸,有两种方法。第一种是通过查询注册表中,存储的指定显示器的相关信息;第二种是通过windows API 1、查询注册表中存储的显示器信息 /// <summary> ///获取显示器的相关硬件ID /// </summary> //...

Qt 访问网络

一、前言 Qt 中访问网络使用 QNetworkAccessManager,它的 API 是异步的,这样在访问网络的时候不需要启动一个线程,在线程里执行请求的代码。(但这一点在有时候需要阻塞时就是个麻烦了) 需要注意一点的是,请求响应的对象 QNetworkReply 需要我们自己手动的删除,一般都会在 QNetworkAccessManager::fin...

GeeTest 极验验证

 前台Html页面 <script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script> <script src="http://static.geetest.com/static/tools/gt.js"></script>...