基于three.js 和ArcGIS JS API 防空盾/电屏蔽

简介

   ArcGIS API for JavaScript  是ESRI 推出面向 WEB 端 GIS   API,可构建引人注目的web 地图应用程序,通过交互式用户体验和令人惊叹的2D和3D可视化来释放地理时空大数据的潜力。同时提供了一个轻量级接口来访问SceneView的WebGL上下文,因此可以创建与内置层相同的方式与场景交互的自定义可视化。开发人员可以直接编写WebGL代码,也可以与第三方WebGL库集成

基于ArcGIS JS API 和 three.js 防空盾/电屏蔽扩展类electricShieldRenderer

define([
    'dojo/_base/declare',
    "esri/geometry/geometryEngine",
    "esri/geometry/Extent",
    "esri/views/3d/externalRenderers",
    "esri/geometry/Polygon",
    "esri/geometry/Point",
    "esri/geometry/support/webMercatorUtils"
], function (
    declare,
    geometryEngine,
    Extent,
    externalRenderers,
    Polygon,
    Point,
    webMercatorUtils
) {
    var THREE = window.THREE;
    var electricShieldRenderer= declare([], {
        constructor: function (view, points, options) {
            this.view = view;
            this.electricShieldobject3d = [];
            const OPTIONS = {
                speed: 0.02,
                radius: 1,
                altitude: 0,
            }
            this.options = this.extend({}, OPTIONS, options, {
                points: points
            });
        },

        setup: function (context) {
            this.renderer = new THREE.WebGLRenderer({
                context: context.gl, // 可用于将渲染器附加到已有的渲染环境(RenderingContext)中
                premultipliedAlpha: false, // renderer是否假设颜色有 premultiplied alpha. 默认为true
            });
            this.renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比。通常用于避免HiDPI设备上绘图模糊
            this.renderer.setViewport(0, 0, this.view.width, this.view.height); // 视口大小设置

            // Make sure it does not clear anything before rendering
            this.renderer.autoClear = false;
            this.renderer.autoClearDepth = false;
            this.renderer.autoClearColor = false;
            this.renderer.autoClearStencil = false;

            // The ArcGIS JS API renders to custom offscreen buffers, and not to the default framebuffers.
            // We have to inject this bit of code into the three.js runtime in order for it to bind those
            // buffers instead of the default ones.
            var originalSetRenderTarget = this.renderer.setRenderTarget.bind(this.renderer);
            this.renderer.setRenderTarget = function (target) {
                originalSetRenderTarget(target);
                if (target == null) {
                    context.bindRenderTarget();
                }
            };
            this.scene = new THREE.Scene();
            this.camera = new THREE.PerspectiveCamera();

            const axesHelper = new THREE.AxesHelper(1);
            axesHelper.position.copy(1000000, 100000, 100000);
            this.scene.add(axesHelper);

            this._setupScene(context);


        },
        _setupScene: function (context) {
            var scope = this;
            scope.material = scope.getMaterial()
            const {
                altitude,
                radius
            } = scope.options;
            scope.options.points.forEach((point) => {
                const geometry = new THREE.SphereBufferGeometry(point.radius, 50, 50, 0, Math.PI * 2);
                const object3d = new THREE.Mesh(geometry, scope.material);
                const position = scope.coordinateToVector3([point.x, point.y], point.altitude);
                object3d.position.copy(position);
                object3d.rotation.x = Math.PI / 2;
                scope.scene.add(object3d);
                scope.electricShieldobject3d.push(object3d)
            })
            context.resetWebGLState();
        },
        getMaterial: function () {
            var scope = this;
            var ElectricShield = {
                uniforms: {
                    time: {
                        type: "f",
                        value: 0
                    },
                    color: {
                        type: "c",
                        value: new THREE.Color(scope.options.color)
                    },
                    opacity: {
                        type: "f",
                        value: 1
                    }
                },
                vertexShaderSource: "\n  precision lowp float;\n  precision lowp int;\n  "
                    .concat(
                        THREE.ShaderChunk.fog_pars_vertex,
                        "\n  varying vec2 vUv;\n  void main() {\n    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n    vUv = uv;\n    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    "
                    )
                    .concat(THREE.ShaderChunk.fog_vertex, "\n  }\n"),
                fragmentShaderSource: `
                #if __VERSION__ == 100
                 #extension GL_OES_standard_derivatives : enable
                #endif
                uniform vec3 color;
                uniform float opacity;
                uniform float time;
                varying vec2 vUv;
                #define pi 3.1415926535
                #define PI2RAD 0.01745329252
                #define TWO_PI (2. * PI)
                float rands(float p){
                    return fract(sin(p) * 10000.0);
                }
                float noise(vec2 p){
                    float t = time / 20000.0;
                    if(t > 1.0) t -= floor(t);
                    return rands(p.x * 14. + p.y * sin(t) * 0.5);
                }
                vec2 sw(vec2 p){
                    return vec2(floor(p.x), floor(p.y));
                }
                vec2 se(vec2 p){
                    return vec2(ceil(p.x), floor(p.y));
                }
                vec2 nw(vec2 p){
                    return vec2(floor(p.x), ceil(p.y));
                }
                vec2 ne(vec2 p){
                    return vec2(ceil(p.x), ceil(p.y));
                }
                float smoothNoise(vec2 p){
                    vec2 inter = smoothstep(0.0, 1.0, fract(p));
                    float s = mix(noise(sw(p)), noise(se(p)), inter.x);
                    float n = mix(noise(nw(p)), noise(ne(p)), inter.x);
                    return mix(s, n, inter.y);
                }
                float fbm(vec2 p){
                    float z = 2.0;
                    float rz = 0.0;
                    vec2 bp = p;
                    for(float i = 1.0; i < 6.0; i++){
                    rz += abs((smoothNoise(p) - 0.5)* 2.0) / z;
                    z *= 2.0;
                    p *= 2.0;
                    }
                    return rz;
                }
                void main() {
                    vec2 uv = vUv;
                    vec2 uv2 = vUv;
                    if (uv.y < 0.5) {
                    discard;
                    }
                    uv *= 4.;
                    float rz = fbm(uv);
                    uv /= exp(mod(time * 2.0, pi));
                    rz *= pow(15., 0.9);
                    gl_FragColor = mix(vec4(color, opacity) / rz, vec4(color, 0.1), 0.2);
                    if (uv2.x < 0.05) {
                    gl_FragColor = mix(vec4(color, 0.1), gl_FragColor, uv2.x / 0.05);
                    }
                    if (uv2.x > 0.95){
                    gl_FragColor = mix(gl_FragColor, vec4(color, 0.1), (uv2.x - 0.95) / 0.05);
                    }
                }`
            };
            let material = new THREE.ShaderMaterial({
                uniforms: ElectricShield.uniforms,
                defaultAttributeValues: {},
                vertexShader: ElectricShield.vertexShaderSource,
                fragmentShader: ElectricShield.fragmentShaderSource,
                blending: THREE.AdditiveBlending,
                depthWrite: !1,
                shading: THREE.FlatShading,
                opacity: 0.1,
                depthTest: !0,
                side: THREE.DoubleSide,
                transparent: !0,
                wireframe: scope.options.wireframe === undefined ? false : scope.options.wireframe,
            });
            return material;
        },
        setMaterialColor: function (rgb) {
            if (!this.electricShieldobject3d.length) {
                return
            }
            this.electricShieldobject3d.forEach((item) => {
                item.material.color.set(rgb);
            })
        },
        setwireframe: function () {
            if (!this.electricShieldobject3d.length) {
                return
            }
            this.electricShieldobject3d.forEach((item) => {
                item.material.wireframe = !item.material.wireframe;
            })
        },
        setopacity: function (opacity) {
            if (!this.electricShieldobject3d.length) {
                return
            }
            this.electricShieldobject3d.forEach((item) => {
                item.material.opacity = opacity;
            })
        },
        setaltitude: function (altitude) {
            if (!this.electricShieldobject3d.length) {
                return
            }
            this.electricShieldobject3d.forEach((item) => {
                item.position.z = altitude;
            })
        },

        setscaleZ: function (scaleZ) {
            if (!this.electricShieldobject3d.length) {
                return
            }
            this.electricShieldobject3d.forEach((item) => {
                item.scale.z = scaleZ;
            })
        },

        coordinateToVector3: function (coord, z = 0) {
            const p = coord;
            let transform = new THREE.Matrix4();
            let transformation = new Array(16);
            // const z = point.z === undefined ? 0 : point.z
            transform.fromArray(
                externalRenderers.renderCoordinateTransformAt(
                    this.view,
                    [p[0], p[1], z],
                    this.view.spatialReference,
                    transformation
                )
            );
            let vector3 = new THREE.Vector3(
                transform.elements[12],
                transform.elements[13],
                transform.elements[14]
            );
            return vector3;
        },
        extend: function (dest) { // (Object[, Object, ...]) ->
            for (let i = 1; i < arguments.length; i++) {
                const src = arguments[i];
                for (const k in src) {
                    dest[k] = src[k];
                }
            }
            return dest;
        },

        render: function (context) {
            const cam = context.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.electricShieldobject3d.map((item) => {
                item.material.uniforms.time.value += this.options.speed;
            })
            this.camera.projectionMatrix.fromArray(cam.projectionMatrix);
            this.renderer.state.reset();
            this.renderer.render(this.scene, this.camera);
            externalRenderers.requestRender(this.view);
            context.resetWebGLState();
        }
    });
    return electricShieldRenderer;
});

防空盾/电屏蔽扩展类electricShieldRenderer 调用示例

const points = [{
                        "name": "上海",
                        "x": 13520199.85,
                        "y": 3654099.00,
                        "radius": 800000,
                        "altitude": 0
                    }]
                const options = {
                        "color": "#FF0000",
                        "wireframe": false,
                        "speed": 0.01
                    }
                const rwRender = new electricShieldRenderer(view, points, options);
                externalRenderers.add(view, rwRender);

效果图:
基于three.js 和ArcGIS JS API 防空盾/电屏蔽

防空盾/电屏蔽渲染效果图

基于three.js 和ArcGIS JS API 防空盾/电屏蔽

完整代码下载地址

基于three.js 和ArcGIS JS API 防空盾/电屏蔽

联系方式

qq: 314984468

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ISpaceART

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值