|
@@ -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;
|
|
|
+ },
|
|
|
+
|
|
|
+};
|