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