Browse Source

新渲染方式加载

zhiyuan-007 4 months ago
parent
commit
8a83f45967

+ 8 - 7
package-lock.json

@@ -18,7 +18,7 @@
         "mitt": "^3.0.1",
         "sass": "^1.58.0",
         "sass-loader": "^13.2.0",
-        "three": "^0.169.0",
+        "three": "^0.146.0",
         "vue": "^3.3.4",
         "vue-router": "^4.2.4",
         "webpack": "^5.0.0"
@@ -5084,9 +5084,10 @@
       }
     },
     "node_modules/three": {
-      "version": "0.169.0",
-      "resolved": "https://registry.npmmirror.com/three/-/three-0.169.0.tgz",
-      "integrity": "sha512-Ed906MA3dR4TS5riErd4QBsRGPcx+HBDX2O5yYE5GqJeFQTPU+M56Va/f/Oph9X7uZo3W3o4l2ZhBZ6f6qUv0w=="
+      "version": "0.146.0",
+      "resolved": "https://registry.npmmirror.com/three/-/three-0.146.0.tgz",
+      "integrity": "sha512-1lvNfLezN6OJ9NaFAhfX4sm5e9YCzHtaRgZ1+B4C+Hv6TibRMsuBAM5/wVKzxjpYIlMymvgsHEFrrigEfXnb2A==",
+      "license": "MIT"
     },
     "node_modules/timezone-groups": {
       "version": "0.10.2",
@@ -9546,9 +9547,9 @@
       }
     },
     "three": {
-      "version": "0.169.0",
-      "resolved": "https://registry.npmmirror.com/three/-/three-0.169.0.tgz",
-      "integrity": "sha512-Ed906MA3dR4TS5riErd4QBsRGPcx+HBDX2O5yYE5GqJeFQTPU+M56Va/f/Oph9X7uZo3W3o4l2ZhBZ6f6qUv0w=="
+      "version": "0.146.0",
+      "resolved": "https://registry.npmmirror.com/three/-/three-0.146.0.tgz",
+      "integrity": "sha512-1lvNfLezN6OJ9NaFAhfX4sm5e9YCzHtaRgZ1+B4C+Hv6TibRMsuBAM5/wVKzxjpYIlMymvgsHEFrrigEfXnb2A=="
     },
     "timezone-groups": {
       "version": "0.10.2",

+ 1 - 1
package.json

@@ -20,7 +20,7 @@
     "mitt": "^3.0.1",
     "sass": "^1.58.0",
     "sass-loader": "^13.2.0",
-    "three": "^0.169.0",
+    "three": "^0.146.0",
     "vue": "^3.3.4",
     "vue-router": "^4.2.4",
     "webpack": "^5.0.0"

+ 14 - 6
pnpm-lock.yaml

@@ -36,8 +36,8 @@ importers:
         specifier: ^13.2.0
         version: 13.3.2(node-sass@8.0.0)(sass@1.67.0)(webpack@5.88.2)
       three:
-        specifier: ^0.169.0
-        version: 0.169.0
+        specifier: ^0.126.0
+        version: 0.126.0
       vue:
         specifier: ^3.3.4
         version: 3.3.4
@@ -720,6 +720,7 @@ packages:
   are-we-there-yet@3.0.1:
     resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+    deprecated: This package is no longer supported.
 
   arrify@1.0.1:
     resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
@@ -1006,6 +1007,7 @@ packages:
   gauge@4.0.4:
     resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+    deprecated: This package is no longer supported.
 
   gaze@1.1.3:
     resolution: {integrity: sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==}
@@ -1032,13 +1034,16 @@ packages:
 
   glob@7.1.7:
     resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==}
+    deprecated: Glob versions prior to v9 are no longer supported
 
   glob@7.2.3:
     resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+    deprecated: Glob versions prior to v9 are no longer supported
 
   glob@8.1.0:
     resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
     engines: {node: '>=12'}
+    deprecated: Glob versions prior to v9 are no longer supported
 
   globals@11.12.0:
     resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
@@ -1119,6 +1124,7 @@ packages:
 
   inflight@1.0.6:
     resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+    deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
 
   inherits@2.0.4:
     resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -1425,6 +1431,7 @@ packages:
   npmlog@6.0.2:
     resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
+    deprecated: This package is no longer supported.
 
   once@1.4.0:
     resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@@ -1558,6 +1565,7 @@ packages:
 
   rimraf@3.0.2:
     resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+    deprecated: Rimraf versions prior to v4 are no longer supported
     hasBin: true
 
   rollup@3.29.2:
@@ -1773,8 +1781,8 @@ packages:
     engines: {node: '>=10'}
     hasBin: true
 
-  three@0.169.0:
-    resolution: {integrity: sha512-Ed906MA3dR4TS5riErd4QBsRGPcx+HBDX2O5yYE5GqJeFQTPU+M56Va/f/Oph9X7uZo3W3o4l2ZhBZ6f6qUv0w==}
+  three@0.126.0:
+    resolution: {integrity: sha512-/MecvboUefStCkUfXLImoJxthN+FoLPcEP7pz1r1Dd9i8BPGGuj+S1sOPRvW4Z+ViZjP2oWWm1inNC/MT52ybA==}
 
   timezone-groups@0.10.2:
     resolution: {integrity: sha512-01G9JdlIybA9Njp0wJcGenXKWAw+woWbv6W/oMexWyPs7Nr/S2p2n1NRrMHbHaFzdf+PNNStQp1WILdnAGjYXQ==}
@@ -3120,7 +3128,7 @@ snapshots:
       fs.realpath: 1.0.0
       inflight: 1.0.6
       inherits: 2.0.4
-      minimatch: 3.0.8
+      minimatch: 3.1.2
       once: 1.4.0
       path-is-absolute: 1.0.1
     optional: true
@@ -4004,7 +4012,7 @@ snapshots:
       commander: 2.20.3
       source-map-support: 0.5.21
 
-  three@0.169.0: {}
+  three@0.126.0: {}
 
   timezone-groups@0.10.2: {}
 

+ 62 - 1
src/components/mapJK.vue

@@ -31,6 +31,8 @@ import SetPopupOptions from "../units/map/SetPopuopOptions";
 import AddScaleEvent from "../units/map/AddScaleEvent";
 import ViewShedAnalysisWidget from "../units/map/ViewShedAnalysis.js";
 import AddLightBallEvent from "../units/map/AddLightBallEvent.js";
+import AddThreeGridEvent from "../units/map/addThreeGridEvent.js";
+import LimitHeightAnalysis from "../units/map/LimitHeightAnalysis.js";
 
 export default {
   name: "mapJK",
@@ -48,6 +50,8 @@ export default {
     let m_handles = [];
     let viewShedAnalysisWidget = null;
     let addLightBallEvent = null;
+    let addThreeGridEvent = null;
+    let limitHeightAnalysisEvent = null;
     onMounted(() =>{
       bus.on('CreateMap',() =>{
         MapReady = $.Deferred();
@@ -131,6 +135,9 @@ export default {
           case "ThreeGrid":
             threeGrid(params);
             break;
+          case "LimitHeightAnalysis":
+            limitHeightAnalysis(params);
+            break;
         }
       }
       function setBackground(params){
@@ -342,6 +349,10 @@ export default {
       }
       function addLightBall(params){
         let status = params.status;
+        if(addLightBallEvent){
+          addLightBallEvent.clear();
+          addLightBallEvent = null;
+        }
         if(status === "hide"){
           return
         }
@@ -352,7 +363,57 @@ export default {
       }
       function threeGrid(params){
         let status = params.status;
-
+        if(addThreeGridEvent){
+          addThreeGridEvent.clear();
+          addThreeGridEvent = null;
+        }
+        if(status == "hide"){
+          return
+        }
+        addThreeGridEvent = new AddThreeGridEvent({
+          view:m_view
+        })
+        addThreeGridEvent.start();
+      }
+      function limitHeightAnalysis(params){
+        let status = params.status;
+        debugger
+        if(limitHeightAnalysisEvent){
+          limitHeightAnalysisEvent.clear();
+          limitHeightAnalysisEvent =null;
+        }
+        if(status == "hide"){
+          return
+        }
+        limitHeightAnalysisEvent = new LimitHeightAnalysis({
+          view:m_view,
+          map:m_map,
+          layerId:"WhiteMold",
+          rings:
+            [
+              [
+                2425.570123891455,
+                802.4959489723367
+              ],
+              [
+                3983.7870548734795,
+                796.6284491105638
+              ],
+              [
+                3953.6447199613026,
+                -126.47813961693197
+              ],
+              [
+                2399.753338035622,
+                -50.34620065126925
+              ],
+              [
+                2425.570123891455,
+                802.4959489723367
+              ]
+            ]
+        });
+        limitHeightAnalysisEvent.start()
       }
     })
   }

+ 12 - 1
src/config/basicTool.json

@@ -42,7 +42,7 @@
             "title":"WhiteMold",
             "visible": true,
             "opacity": 1,
-            "url": "https://cimweb.zjw.sh.cegn.cn:2008/MapServiceProxy/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3Mjg3ODIzNjAsImtleSI6IjA1MTk1NDE4Iiwic2VydmljZU5vIjoiRDkwMDMwMDA2MjAyMzA4MDEiLCJ1c2VybmFtZSI6InB0Z2wifQ.3fACafvHSV2LDRvXEY23xIrkogbUU1VzPiyGJEowUk4",
+            "url": "https://cimweb.zjw.sh.cegn.cn:2008/MapServiceProxy/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzE5OTg2MDQsImtleSI6IjA1MTk1NDE4Iiwic2VydmljZU5vIjoiRDkwMDMwMDA2MjAyMzA4MDEiLCJ1c2VybmFtZSI6InB0Z2wifQ.cxIPV-WaKXH9Jkwbhlhco7WJN5Jh09JtTHQnSMKLLHM",
             "token": ""
           }
         }
@@ -157,6 +157,17 @@
             "token": ""
           }
         }
+      },
+      {
+        "code": "1-13",
+        "title": "限高分析",
+        "data": {
+          "ActionName": "LimitHeightAnalysis",
+          "Parameters": {
+            "status": "show",
+            "token": ""
+          }
+        }
       }
     ]
   }

+ 1 - 2
src/units/map/AddLightBallEvent.js

@@ -14,8 +14,7 @@ class AddLightBallEvent{
             this.clear();
         }
         let subRenderClass = RenderNode.createSubclass(LightBallClass);
-        this.view.when(function (){
-            debugger
+        this.view.when(()=>{
             that.addLightBallEvent =  new subRenderClass({
                 view: that.view,
                 webgl

+ 39 - 0
src/units/map/LimitHeightAnalysis.js

@@ -0,0 +1,39 @@
+import RenderNode from '@arcgis/core/views/3d/webgl/RenderNode.js'
+import * as webgl from "@arcgis/core/views/3d/webgl.js";
+import {LimitHeightClass} from "../threejs/limitHeight.js";
+
+class LimitHeightAnalysis{
+    constructor(options) {
+        this.view = options.view;
+        this.m_map = options.map;
+        this.layerId = options.layerId || "WhiteMold";
+        this.rings = options.rings;
+        this.limitHeightAnalysis = null;
+    }
+    start(){
+        let that =this;
+        if(this.limitHeightAnalysis){
+            this.clear()
+        };
+        let subRenderClass =  RenderNode.createSubclass(LimitHeightClass);
+        let sceneLayer = this.m_map.layers.find(item => item.id === this.layerId);
+
+        this.view.when(()=>{
+            that.view.whenLayerView(sceneLayer).then((layerView) => {
+                that.limitHeightAnalysis = new subRenderClass({
+                    view: that.view,
+                    webgl,
+                    analyseLayerView : layerView,
+                    analyseLayer : sceneLayer,
+                    drawVertices:that.rings
+                })
+
+            });
+        })
+    }
+    clear(){
+
+    }
+
+}
+export default LimitHeightAnalysis;

+ 31 - 0
src/units/map/addThreeGridEvent.js

@@ -0,0 +1,31 @@
+import RenderNode from '@arcgis/core/views/3d/webgl/RenderNode.js';
+import * as webgl from "@arcgis/core/views/3d/webgl.js";
+import {ThreeGridClass} from "../threejs/threeGrid.js";
+
+class AddThreeGridEvent{
+    constructor(options){
+        this.view = options.view;
+        this.addThreeGridEvent = null;
+    }
+    start(){
+        let that = this;
+        if(this.addThreeGridEvent){
+            clear();
+        }
+        let subRenderClass = RenderNode.createSubclass(ThreeGridClass);
+        this.view.when(()=>{
+            that.addThreeGridEvent = new subRenderClass({
+                view:that.view,
+                webgl
+            })
+        })
+    }
+    clear(){
+        if(this.addThreeGridEvent){
+            this.addLightBallEvent.destroy();
+            this.addLightBallEvent = null;
+        }
+    }
+}
+
+export default AddThreeGridEvent;

+ 0 - 2
src/units/threejs/lightBall.js

@@ -1,7 +1,6 @@
 import * as THREE from 'three'
 export const LightBallClass = {
     constructor(options) {
-        debugger
         this.webgl = options.webgl;
         this.view = options.view;
         this.cube = null;
@@ -11,7 +10,6 @@ export const LightBallClass = {
 
 
     initialize() {
-        debugger
         this.clock = new THREE.Clock();
 
         this.renderer = new THREE.WebGLRenderer({

+ 235 - 0
src/units/threejs/limitHeight.js

@@ -0,0 +1,235 @@
+import * as THREE from 'three';
+
+export const LimitHeightClass = {
+    // 构造函数
+    constructor(options) {
+        this.webgl = options.webgl;
+        this.view = options.view;
+        this.limitH = 50;
+        this.analyseLayerView = options.analyseLayerView;
+        this.analyseLayer = options.analyseLayer;
+        this.drawVertices = options.drawVertices;
+        this.meshGroup = null;
+        this._camera = null;
+    },
+
+    // 初始化方法
+    initialize() {
+        this.clock = new THREE.Clock();
+
+        this.renderer = new THREE.WebGLRenderer({
+            context: this.view.context, // 使用已有的 WebGL 上下文
+            premultipliedAlpha: false,
+            antialias: true,
+            logarithmicDepthBuffer: true,
+            polygonOffset: true, // 启用 polygon offset 防止 z-fighting
+            alpha: true
+        });
+        this.renderer.shadowMap.enabled = true;
+        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+        this.renderer.setPixelRatio(window.devicePixelRatio);
+        this.renderer.setViewport(0, 0, this.view.width, this.view.height);
+        this.renderer.autoClearDepth = false; // 定义renderer是否清除深度缓存
+        this.renderer.autoClearStencil = false; // 定义renderer是否清除模板缓存
+        this.renderer.autoClearColor = false; // 定义renderer是否清除颜色缓存
+
+        let originalSetRenderTarget = this.renderer.setRenderTarget.bind(this.renderer);
+        this.renderer.setRenderTarget = function (target) {
+            originalSetRenderTarget(target);
+            this.state.viewport(new THREE.Vector4(0, 0, this.view.width, this.view.height));
+            if (target == null) {
+                this.view.bindRenderTarget();
+            }
+        }.bind(this.renderer);
+        // 支持裁切
+        this.renderer.localClippingEnabled = true;
+        this.scene = new THREE.Scene();
+        let cam = this.camera;
+        this._camera = new THREE.PerspectiveCamera(cam.fovY, cam.aspect, cam.near, cam.far);
+
+        // 设置场景中的光源
+        this.ambient = new THREE.AmbientLight(0xffffff, 1);
+        this.scene.add(this.ambient);
+        this.sun = new THREE.DirectionalLight(0xffffff, 0.5);
+        this.scene.add(this.sun);
+
+        this.start();
+        this.resetWebGLState();
+    },
+
+    // 启动函数,进行场景和物体的初始化
+    async start() {
+        if (this.analyseLayerView) {
+            let clipPanels = this.createClipPlane();
+            let meshMaterial = this.analysisRender();
+            meshMaterial.clippingPlanes = clipPanels;
+            this.meshGroup = new THREE.Group();
+            let posGroup = await this.analyseLayerView.getFeatureInfo();
+            this.createGeometry(posGroup);
+
+            // 给 meshGroup 中的每个 mesh 设置材质
+            this.meshGroup && this.meshGroup.children.forEach((mesh) => {
+                mesh.material = meshMaterial;
+            });
+            this.scene.add(this.meshGroup);
+        }
+    },
+
+    // 渲染函数
+    render(context) {
+        let cam = this.camera;
+        this._camera.position.set(cam.eye[0], cam.eye[1], cam.eye[2]);
+        this._camera.up.set(cam.up[0], cam.up[1], cam.up[2]);
+        this._camera.lookAt(new THREE.Vector3(cam.center[0], cam.center[1], cam.center[2]));
+        this._camera.projectionMatrix.fromArray(cam.projectionMatrix);
+
+        // 更新材质的 limit 值
+        if (this.meshGroup && this.meshGroup.children.length > 0 && this.meshGroup.children[0].material.uniforms) {
+            this.meshGroup.children.forEach((mesh) => {
+                mesh.material.uniforms.limit.value = this.limitH;
+                mesh.material.needsUpdate = true;
+            });
+        }
+
+        // 在渲染之前手动清除缓冲区(如果需要)
+        this.renderer.clearColor();
+        this.renderer.clearDepth();
+
+        // 渲染场景
+        this.renderer.state.reset();
+        this.renderer.state.setBlending(THREE.NoBlending);
+        this.renderer.render(this.scene, this._camera);
+
+        // 请求重新渲染
+        this.requestRender(this.view);
+        this.resetWebGLState();
+    },
+
+    // 创建几何体
+    createGeometry(posGroup) {
+        posGroup.forEach((object_pos) => {
+            const geometry = new THREE.BufferGeometry();
+            geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(object_pos), 3));
+            geometry.computeBoundingBox();
+            geometry.computeBoundingSphere();
+            geometry.computeVertexNormals();
+
+            const positions = geometry.attributes.position;
+            const normals = geometry.attributes.normal;
+            let uvs = [];
+
+            // 计算 UV 坐标
+            for (let i = 0; i < positions.count; i++) {
+                const normal = new THREE.Vector3(
+                    normals.getX(i),
+                    normals.getY(i),
+                    normals.getZ(i)
+                ).normalize();
+
+                const position = new THREE.Vector3(
+                    positions.getX(i),
+                    positions.getY(i),
+                    positions.getZ(i)
+                );
+                uvs.push((normal.x + 1) / 2, (normal.y + 1) / 2);
+            }
+
+            geometry.setAttribute('uv', new THREE.BufferAttribute(Float32Array.from(uvs), 2, false));
+
+            let material = new THREE.MeshPhongMaterial({
+                color: 'red',
+                transparent: false,
+                polygonOffset: true,
+                polygonOffsetFactor: -0.5,
+                polygonOffsetUnits: -0.5,
+            });
+
+            const mesh = new THREE.Mesh(geometry, material);
+            mesh.updateMatrix();
+            this.meshGroup.add(mesh);
+        });
+    },
+
+    // 根据多边形点创建裁切面
+    createClipPlane() {
+        let clipPanels = [];
+        for (let k = 0; k < this.drawVertices.length - 1; k++) {
+            let point1 = this.drawVertices[k];
+            let point2 = this.drawVertices[k + 1];
+            let base1 = [];
+            let base2 = [];
+            let top1 = [];
+            this.webgl.toRenderCoordinates(this.view, [point1[0], point1[1], 0], 0, this.view.spatialReference, base1, 0, 1);
+            this.webgl.toRenderCoordinates(this.view, [point2[0], point2[1], 0], 0, this.view.spatialReference, base2, 0, 1);
+            this.webgl.toRenderCoordinates(this.view, [point1[0], point1[1], 1000], 0, this.view.spatialReference, top1, 0, 1);
+
+            let plane = new THREE.Plane();
+            let p1_1 = new THREE.Vector3(base1[0], base1[1], base1[2]);
+            let p2_1 = new THREE.Vector3(top1[0], top1[1], top1[2]);
+            let p3_1 = new THREE.Vector3(base2[0], base2[1], base2[2]);
+            plane.setFromCoplanarPoints(p1_1, p2_1, p3_1);
+            clipPanels.push(plane);
+        }
+        return clipPanels;
+    },
+
+    // 创建材质并设置 shader
+    analysisRender() {
+        let material = new THREE.MeshBasicMaterial({
+            color: '#FF0000',
+            opacity: 0.8,
+            transparent: true,
+            side: THREE.DoubleSide,
+        });
+
+        material.onBeforeCompile = (shader) => {
+            const getFoot = `
+            uniform mat4 cameraMatrix;
+            uniform mat4 projectionMatrixInverse;
+            varying float depth;
+            varying vec2 depthUv;
+            varying vec4 vPosition;
+            #include <common>
+            `;
+
+            const begin_vertex = `
+            #include <worldpos_vertex>
+            vPosition= modelMatrix * vec4(transformed, 1.0 );
+            `;
+
+            const height_vary = `
+            uniform float opacity;
+            uniform float limit;
+            uniform float baseHeight;
+            varying vec4 vPosition;
+            float getHeight(vec3 point){
+                float distR = length(point);
+                float height = distR - 6378137.0;
+                return height - 0.1;
+            }
+            `;
+
+            const height_frag = `
+            float ph = getHeight(vPosition.xyz);
+            if(ph >= limit){
+                gl_FragColor = vec4(outgoingLight, diffuseColor.a);
+            } else {
+                discard;
+            }
+            `;
+
+            shader.vertexShader = shader.vertexShader.replace("#include <common>", getFoot);
+            shader.vertexShader = shader.vertexShader.replace("#include <worldpos_vertex>", begin_vertex);
+            shader.fragmentShader = shader.fragmentShader.replace('uniform float opacity;', height_vary);
+            shader.fragmentShader = shader.fragmentShader.replace('gl_FragColor = vec4( outgoingLight, diffuseColor.a );', height_frag);
+
+            shader.uniforms.limit = { value: 0.0 };
+            shader.uniforms.baseHeight = { value: 0.0 };
+            material.uniforms = shader.uniforms;
+        };
+
+        material.needsUpdate = true;
+        return material;
+    },
+
+};

+ 84 - 0
src/units/threejs/threeGrid.js

@@ -0,0 +1,84 @@
+import * as THREE from 'three';
+
+export const ThreeGridClass = {
+    constructor(options) {
+        this.webgl = options.webgl;
+        this.view = options.view;
+        this.extent = options.extent||{minX:-50000,maxX:50000,minY:-50000,maxY:50000}; // {minX, maxX, minY, maxY}
+        this.height = options.height ||10000; // 网格总高度
+        this.size = options.size ||2000; // 单个立方体的尺寸
+        this.mesh = null;
+        this._camera = null;
+    },
+
+    initialize() {
+        this.renderer = new THREE.WebGLRenderer({
+            context: this.webgl.context,
+            premultipliedAlpha: false,
+        });
+        this.renderer.setPixelRatio(window.devicePixelRatio);
+        this.renderer.setSize(this.view.width, this.view.height);
+        this.renderer.autoClear = false;
+
+        // 场景和相机
+        this.scene = new THREE.Scene();
+        this._camera = new THREE.PerspectiveCamera(75, this.view.width / this.view.height, 0.1, 10000);
+
+        // 创建实例化立方体网格
+        this.createInstancedMesh();
+        this.scene.add(this.mesh);
+
+        // 添加灯光(确保物体在场景中可以被看见)
+        const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
+        this.scene.add(ambientLight);
+
+        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
+        directionalLight.position.set(10, 10, 10);
+        this.scene.add(directionalLight);
+    },
+
+    createInstancedMesh() {
+        const geometry = new THREE.BoxGeometry(this.size, this.size, this.size);
+        const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.8 });
+
+        const xCount = Math.floor((this.extent.maxX - this.extent.minX) / this.size);
+        const yCount = Math.floor((this.extent.maxY - this.extent.minY) / this.size);
+        const zCount = Math.floor(this.height / this.size);
+        const count = xCount * yCount * zCount;
+
+        this.mesh = new THREE.InstancedMesh(geometry, material, count);
+        const dummy = new THREE.Object3D();
+        let index = 0;
+
+        for (let i = 0; i < xCount; i++) {
+            for (let j = 0; j < yCount; j++) {
+                for (let k = 0; k < zCount; k++) {
+                    const worldX = this.extent.minX + i * this.size + this.size / 2;
+                    const worldY = this.extent.minY + j * this.size + this.size / 2;
+                    const worldZ = k * this.size + this.size / 2;
+
+                    let renderPos = [];
+                    this.webgl.toRenderCoordinates(this.view, [worldX, worldY, worldZ], 0, this.view.spatialReference, renderPos, 0, 1);
+
+                    dummy.position.set(renderPos[0], renderPos[1], renderPos[2]);
+                    dummy.updateMatrix();
+                    this.mesh.setMatrixAt(index, dummy.matrix);
+
+                    index++;
+                }
+            }
+        }
+        this.mesh.instanceMatrix.needsUpdate = true;
+    },
+
+    render(context) {
+        this._camera.position.set(0, 0, 5000); // 确保相机距离场景足够远以看到整个场景
+        this._camera.lookAt(0, 0, 0);
+
+        this.renderer.state.reset();
+        this.renderer.setRenderTarget(null);
+        this.renderer.render(this.scene, this._camera);
+        this.requestRender();
+        this.resetWebGLState();
+    }
+}