Cesium 地形开挖实现原理


地形裁剪是通过剔除裁剪面的组合空间范围内的片源实现

第一步:构建裁剪面,这里我们根据地理坐标的范围点实现裁剪面的创建(用户代码)

1)计算传入的点范围的顺序是逆时针还是顺时针 [isR=true]表示点的顺序是逆时针。

 1             var x1 = polygon[0].longitude;
 2 
 3             var y1 = polygon[0].latitude;
 4 
 5             var x2 = polygon[1].longitude;
 6 
 7             var y2 = polygon[1].latitude;
 8 
 9             var x3 = polygon[2].longitude;
10 
11             var y3 = polygon[2].latitude;
12 
13  
14 
15             var dirRes = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2);
16 
17             var isR = dirRes > 0;

 

逆时针则按照原本顺序存储点,否则从后往前依次存储点。

            

 1            var points = [];
 2 
 3             if (isR) {
 4 
 5                 for (var ii = 0; ii < polygon.length; ii++) {
 6 
 7                     points[ii] = Cesium.Cartesian3.fromDegrees(polygon[ii].longitude, polygon[ii].latitude);
 8 
 9                 }
10 
11             } else {
12 
13                 var count = 0;
14 
15                 for (var ii = polygon.length - 1; ii >= 0; ii--) {
16 
17                     points[count] = Cesium.Cartesian3.fromDegrees(polygon[ii].longitude, polygon[ii].latitude);
18 
19                     count++;
20 
21                 }
22 
23             }

 

 2)分别计算由三点确定的面(其中第三个点为原点,裁剪面经过原点),midpoint为两点直线连线的中点,up是单位向量,从原点(0,0,0)出发经过midpoint射线方向的单位向量。right为第一个点到第二点方向的单位向量。normal为垂直于裁剪面的法线向量。originCenteredPlane是法向量normal经过原点的面,distance是中点到originCenteredPlane面的距离,由distancenormal即可确定两点构成的裁剪面。

            

 1            var pointsLength = points.length;
 2 
 3             var clippingPlanes = [];
 4 
 5             for (var i = 0; i < pointsLength; ++i) {
 6 
 7                 var nextIndex = (i + 1) % pointsLength;
 8 
 9                 var midpoint = Cesium.Cartesian3.add(points[i], points[nextIndex], new Cesium.Cartesian3());
10 
11                 midpoint = Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint);
12 
13  
14 
15                 var up = Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3());
16 
17                 var right = Cesium.Cartesian3.subtract(points[nextIndex], midpoint, new Cesium.Cartesian3());
18 
19                 right = Cesium.Cartesian3.normalize(right, right);
20 
21  
22 
23                 var normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3());
24 
25                 normal = Cesium.Cartesian3.normalize(normal, normal);
26 
27  
28 
29                 var originCenteredPlane = new Cesium.Plane(normal, 0.0);
30 
31                 var distance = Cesium.Plane.getPointDistance(originCenteredPlane, midpoint);
32 
33  
34 
35                 clippingPlanes.push(new Cesium.ClippingPlane(normal, distance));
36 
37             }

 

 

第二步:Cesium内部实现裁剪面相关参数存储,裁剪面clippingPlanes的参数存储在纹理中,具体解析如下:

1)计算存储裁剪面相关参数所需的纹理宽度和高度大小,pixelsNeeded存储裁剪面参数所需的纹理的大小,ContextLimits.maximumTextureSize = 8192,纹理最大宽度限制。

 1 var pixelsNeeded = useFloatTexture ? this.length : this.length * 2;
 2 
 3  
 4 
 5 function computeTextureResolution(pixelsNeeded, result) {
 6 
 7         var maxSize = ContextLimits.maximumTextureSize;
 8 
 9         result.x = Math.min(pixelsNeeded, maxSize);
10 
11         result.y = Math.ceil(pixelsNeeded / result.x);
12 
13         return result;
14 
15     }

 

2)创建裁剪纹理

 1             requiredResolution.y *= 2;//分配所需空间的两倍,以避免频繁的纹理重新分配
 2 
 3  
 4 
 5             var sampler = new Sampler({
 6 
 7                 wrapS : TextureWrap.CLAMP_TO_EDGE,
 8 
 9                 wrapT : TextureWrap.CLAMP_TO_EDGE,
10 
11                 minificationFilter : TextureMinificationFilter.NEAREST,
12 
13                 magnificationFilter : TextureMagnificationFilter.NEAREST
14 
15             });
16 
17  
18 
19             if (useFloatTexture) {
20 
21                 clippingPlanesTexture = new Texture({
22 
23                     context : context,
24 
25                     width : requiredResolution.x,
26 
27                     height : requiredResolution.y,
28 
29                     pixelFormat : PixelFormat.RGBA,
30 
31                     pixelDatatype : PixelDatatype.FLOAT,
32 
33                     sampler : sampler,
34 
35                     flipY : false
36 
37                 });
38 
39                 this._float32View = new Float32Array(requiredResolution.x * requiredResolution.y * 4);
40 
41             } else {
42 
43                 clippingPlanesTexture = new Texture({
44 
45                     context : context,
46 
47                     width : requiredResolution.x,
48 
49                     height : requiredResolution.y,
50 
51                     pixelFormat : PixelFormat.RGBA,
52 
53                     pixelDatatype : PixelDatatype.UNSIGNED_BYTE,
54 
55                     sampler : sampler,
56 
57                     flipY : false
58 
59                 });
60 
61                 this._uint8View = new Uint8Array(requiredResolution.x * requiredResolution.y * 4);
62 
63             }
64 
65  

 

3)封装纹理数据

 1         function packPlanesAsFloats(clippingPlaneCollection, startIndex, endIndex) {
 2 
 3         var float32View = clippingPlaneCollection._float32View;
 4 
 5         var planes = clippingPlaneCollection._planes;
 6 
 7  
 8 
 9         var floatIndex = 0;
10 
11         for (var i = startIndex; i < endIndex; ++i) {
12 
13             var plane = planes[i];
14 
15             var normal = plane.normal;
16 
17  
18 
19             float32View[floatIndex] = normal.x;
20 
21             float32View[floatIndex + 1] = normal.y;
22 
23             float32View[floatIndex + 2] = normal.z;
24 
25             float32View[floatIndex + 3] = plane.distance;
26 
27  
28 
29             floatIndex += 4; // each plane is 4 floats
30 
31         }
32 
33     }
34 
35  
36 
37 packPlanesAsFloats(this, 0, this._planes.length);
38 
39  
40 
41 clippingPlanesTexture.copyFrom({
42 
43                 width : clippingPlanesTexture.width,
44 
45                 height : clippingPlanesTexture.height,
46 
47                 arrayBufferView : this._float32View
48 
49             });

 

第三步:构建globe时,向globe着色器代码中添加裁剪面着色器代码,部分代码省略,其中width为存储裁剪面纹理的宽度和高度

  1      function getClippingPlaneFloat(width, height) {
  2 
  3         var pixelWidth = 1.0 / width;
  4 
  5         var pixelHeight = 1.0 / height;
  6 
  7  
  8 
  9         var pixelWidthString = pixelWidth + '';
 10 
 11         if (pixelWidthString.indexOf('.') === -1) {
 12 
 13             pixelWidthString += '.0';
 14 
 15         }
 16 
 17         var pixelHeightString = pixelHeight + '';
 18 
 19         if (pixelHeightString.indexOf('.') === -1) {
 20 
 21             pixelHeightString += '.0';
 22 
 23         }
 24 
 25  
 26 
 27         var functionString =
 28 
 29             'vec4 getClippingPlane(sampler2D packedClippingPlanes, int clippingPlaneNumber, mat4 transform)\n' +
 30 
 31             '{\n' +
 32 
 33             '    int pixY = clippingPlaneNumber / ' + width + ';\n' +
 34 
 35             '    int pixX = clippingPlaneNumber - (pixY * ' + width + ');\n' +
 36 
 37             '    float u = (float(pixX) + 0.5) * ' + pixelWidthString + ';\n' + // sample from center of pixel
 38 
 39             '    float v = (float(pixY) + 0.5) * ' + pixelHeightString + ';\n' +
 40 
 41             '    vec4 plane = texture2D(packedClippingPlanes, vec2(u, v));\n' +
 42 
 43             '    return czm_transformPlane(plane, transform);\n' +
 44 
 45             '}\n';
 46 
 47         return functionString;
 48 
 49     }
 50 
 51  
 52 
 53  
 54 
 55 function clippingFunctionIntersect(clippingPlanesLength) {
 56 
 57         var functionString =
 58 
 59             'float clip(vec4 fragCoord, sampler2D clippingPlanes, mat4 clippingPlanesMatrix)\n' +
 60 
 61             '{\n' +
 62 
 63             '    bool clipped = true;\n' +
 64 
 65             '    vec4 position = czm_windowToEyeCoordinates(fragCoord);\n' +
 66 
 67             '    vec3 clipNormal = vec3(0.0);\n' +
 68 
 69             '    vec3 clipPosition = vec3(0.0);\n' +
 70 
 71             '    float clipAmount = 0.0;\n' +
 72 
 73             '    float pixelWidth = czm_metersPerPixel(position);\n' +
 74 
 75  
 76 
 77             '    for (int i = 0; i < ' + clippingPlanesLength + '; ++i)\n' +
 78 
 79             '    {\n' +
 80 
 81             '        vec4 clippingPlane = getClippingPlane(clippingPlanes, i, clippingPlanesMatrix);\n' +
 82 
 83  
 84 
 85             '        clipNormal = clippingPlane.xyz;\n' +
 86 
 87             '        clipPosition = -clippingPlane.w * clipNormal;\n' +
 88 
 89  
 90 
 91             '        float amount = dot(clipNormal, (position.xyz - clipPosition)) / pixelWidth;\n' +
 92 
 93             '        clipAmount = max(amount, clipAmount);\n' +
 94 
 95  
 96 
 97             '        clipped = clipped && (amount <= 0.0);\n' +
 98 
 99             '    }\n' +
100 
101  
102 
103             '    if (clipped)\n' +
104 
105             '    {\n' +
106 
107             '        discard;\n' +
108 
109             '    }\n' +
110 
111  
112 
113             '    return clipAmount;\n' +
114 
115             '}\n';
116 
117         return functionString;
118 
119     }

代码解析:

vec4 position = czm_windowToEyeCoordinates(fragCoord) 待检测的点

clipNormal = clippingPlane.xyz  垂直于裁剪面的法线向量

clipPosition = -clippingPlane.w * clipNormal; 法向量与裁剪面的交点

 float amount = dot(clipNormal, (position.xyz - clipPosition)) / pixelWidth; 待待测点与裁剪面上某一点构成的方向向量, dot(clipNormal, (position.xyz - clipPosition)) 方向向量在裁剪面法线方向上的投影长度(|clipNormal| |position.xyz - clipPosition| *cos∠(clipNormal, (position.xyz - clipPosition))),除以pixelWidth得到投影长度的单位长度。

 

clipAmount = max(amount, clipAmount);

clipped = clipped && (amount <= 0.0);

 

判断分量符号是否发生变化,如果最终结果改变则不再范围内,如果最终结果不变则在范围内。

注意:面的存储顺序是逆序时,判断代码如上。所以在做裁剪时外部需要传入逆时针存储的面。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM