SuperMap iClient3D for WebGL之闪电特效

  作者:nannan

目录

一、解决思路

1.1 打开场景

1.2 制作闪电特效

1.3 控制闪电动画

二、完整代码

三、最终成果


       小编有幸参加国网三维性能测评,在本次测评中,要求各厂商做出闪电特效功能。以往我们做天气特效大都是粒子系统,但是闪电特效使用粒子系统做出来效果不是很理想。非常感谢超图研究院三维研发人员的帮助,为我们提供了全新的解决方案。

一、解决思路

1.1 打开场景

var promise = viewer.scene.open("http://www.supermapol.com/realspace/services/3D-CBD-2/rest/realspace");

1.2 制作闪电特效

        制作闪电特效需要创建片段着色器。下面是代码思路:

(1)首先定义了一些辅助函数 hashhash2noise2fbm2 和 dseg 用于生成噪声和计算距离等操作。

 float hash(float x)
    {
    return fract(21654.6512 * sin(385.51 * x));
    }
    float hash(vec2 p)
    {
    return fract(1654.65157 * sin(15.5134763 * p.x + 45.5173247 * p.y + 5.21789));
    }
    vec2 hash2(vec2 p)
    {
    return vec2(hash(p * .754), hash(1.5743 * p + 4.5476351));
    }
    vec2 add = vec2(1.0, 0.0);
    vec2 noise2(vec2 x)
    {
    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f * f * (3.0 - 2.0 * f);
    vec2 res = mix(mix(hash2(p),
    hash2(p + add.xy), f.x),
    mix(hash2(p + add.yx), hash2(p + add.xx), f.x), f.y);
    return res;
    }
    vec2 fbm2(vec2 x)
    {
    vec2 r = vec2(0.0);
    float a = 1.0;
    for (int i = 0; i < 8; i++)
    {
    r += noise2(x) * a;
    x *= 2.;
    a *= .5;
    }
    return r;
    }
    float dseg(vec2 ba, vec2 pa)
    {
    float h = clamp(dot(pa, ba) / dot(ba, ba), -0.2, 1.);
    return length(pa - ba * h);
    }

(2)定义了三个 uniform 变量:colorTexture(颜色纹理)、fall_interval(下降间隔)、mix_factor(混合因子),以及一个 varying 变量 v_textureCoordinates(纹理坐标)。

uniform sampler2D colorTexture; 
uniform float fall_interval; 
uniform float mix_factor; 

(3)main 函数是片段着色器的入口点。在这里进行了如下操作:

  • 计算当前像素点的坐标 uv
  • 根据时间和位置计算一些参数,如 iTimeptgt 等。
  • 根据位置 p 计算出颜色值 c
  • 根据算法生成闪电效果的颜色 col
  • 循环计算闪电路径,更新颜色值 col
  • 最终根据混合因子 mix_factor 将计算得到的颜色值与输入的颜色纹理进行混合,并输出最终的颜色。
void main(void){
        vec2 uv = gl_FragCoord.xy; 
        float iTime = czm_frameNumber * fall_interval * clamp(fall_interval * 0.1, 0.01, 0.1); 
        vec2 p = uv / czm_viewport.zw; 
        vec2 d; 
        vec2 tgt = vec2(1., -1.); 
        float c = 0.; 
        if (p.y >= 0.) 
            c = (1. - (fbm2((p + .2) * p.y + .1 * iTime)).x) * p.y; 
        else 
            c = (1. - (fbm2(p + .2 + .1 * iTime)).x) * p.y * p.y; 
        vec3 col = vec3(0.); 
        vec3 col1 = c * vec3(.3, .5, 1.); 
        float mdist = 100000.; 
        float t = hash(floor(5. * iTime)); 
        tgt += 4. * hash2(tgt + t) - 1.5; 
        if (hash(t + 2.3) > .6) 
            for (int i = 0; i < 100; i++) {
                vec2 dtgt = tgt - p; 
                d = .05 * (vec2(-.5, -1.) + hash2(vec2(float(i), t))); 
                float dist = dseg(d, dtgt); 
                mdist = min(mdist, dist); 
                tgt -= d; 
                c = exp(-1.2 * dist) + exp(-55. * mdist); 
                col = c * vec3(.7, .8, 1.); 
            } 
            col += col1; 
        gl_FragColor = mix(texture2D(colorTexture, v_textureCoordinates), vec4(col, 0.0), mix_factor); 
    }

       这段代码实现了一个基于噪声和算法生成的闪电特效的片段着色器。它通过计算像素点的位置和时间,以及噪声函数的运算,生成了一种类似闪电的视觉效果,并将其与输入的颜色纹理进行混合,最终输出到屏幕上。

1.3 控制闪电动画

       控制闪电动画,我们需要在场景渲染的纹理或上一个后期处理阶段的输出上运行后期处理阶段,并把后处理阶段添加到集合中。

let LightningPostStage = new SuperMap3D.PostProcessStage({//在场景渲染的纹理或上一个后期处理阶段的输出上运行后期处理阶段。
                    fragmentShader: fragmentShaderSource,//执行此后处理阶段时要使用的片段着色器。
                    uniforms: {//一个对象,其属性将用于设置着色器制服。属性可以是常量值或函数。常量值也可以是用作纹理的 URI、数据 URI 或 HTML 元素。
                        mix_factor: 0.5,//混合系数0-1之间的数
                        fall_interval: 0.7,//0-1之间的数
                    }
                })

                const postProcessStage = viewer.scene.postProcessStages.add(LightningPostStage);//将后处理阶段添加到集合中。

二、完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>闪电</title>
    <link href="../../Build/SuperMap3D/Widgets/widgets.css" rel="stylesheet">
    <link href="./css/bootstrap.min.css" rel="stylesheet">
    <link href="./css/pretty.css" rel="stylesheet">
    <link href="./css/bootstrap-select.min.css" rel="stylesheet">
    <script src="./js/jquery.min.js"></script>
    <script src="./js/bootstrap.min.js"></script>
    <script src="./js/bootstrap-select.min.js"></script>
    <script src="./js/config.js"></script>
    <script type="text/javascript" src="../../Build/SuperMap3D/SuperMap3D.js"></script>

</head>

<body>
    <div id="Container"></div>
    <div id='loadingbar' class="spinner">
        <div class="spinner-container container1">
            <div class="circle1"></div>
            <div class="circle2"></div>
            <div class="circle3"></div>
            <div class="circle4"></div>
        </div>
        <div class="spinner-container container2">
            <div class="circle1"></div>
            <div class="circle2"></div>
            <div class="circle3"></div>
            <div class="circle4"></div>
        </div>
        <div class="spinner-container container3">
            <div class="circle1"></div>
            <div class="circle2"></div>
            <div class="circle3"></div>
            <div class="circle4"></div>
        </div>
    </div>

    <script>
        function onload(SuperMap3D) {
            // 通过config.js中的getEngineType,获取引擎类型(EngineType)用于设置启动方式
            var EngineType = getEngineType();
            var viewer = new SuperMap3D.Viewer('Container', {
                sceneModePicker: true,
                navigation: false,
                contextOptions: {
                    contextType: 2, // Webgl2:2 ; WebGPU:3
                },
            });
            window.viewer = viewer;

            viewer.scenePromise.then(function (scene) {
                init(SuperMap3D, scene, viewer);
            });
        }

        function init(SuperMap3D, scene, viewer) {
            viewer.resolutionScale = window.devicePixelRatio;

            var promise = viewer.scene.open("http://www.supermapol.com/realspace/services/3D-CBD-2/rest/realspace");
            SuperMap3D.when(promise, function () {
                // 开启雨景
                viewer.scene.postProcessStages.rain.enabled = true;
                viewer.scene.postProcessStages.rain.uniforms.angle = 0;
                viewer.scene.postProcessStages.rain.uniforms.speed = 14;

                const fragmentShaderSource = `
    float hash(float x)
    {
    return fract(21654.6512 * sin(385.51 * x));
    }
    float hash(vec2 p)
    {
    return fract(1654.65157 * sin(15.5134763 * p.x + 45.5173247 * p.y + 5.21789));
    }
    vec2 hash2(vec2 p)
    {
    return vec2(hash(p * .754), hash(1.5743 * p + 4.5476351));
    }
    vec2 add = vec2(1.0, 0.0);
    vec2 noise2(vec2 x)
    {
    vec2 p = floor(x);
    vec2 f = fract(x);
    f = f * f * (3.0 - 2.0 * f);
    vec2 res = mix(mix(hash2(p),
    hash2(p + add.xy), f.x),
    mix(hash2(p + add.yx), hash2(p + add.xx), f.x), f.y);
    return res;
    }
    vec2 fbm2(vec2 x)
    {
    vec2 r = vec2(0.0);
    float a = 1.0;
    for (int i = 0; i < 8; i++)
    {
    r += noise2(x) * a;
    x *= 2.;
    a *= .5;
    }
    return r;
    }
    float dseg(vec2 ba, vec2 pa)
    {
    float h = clamp(dot(pa, ba) / dot(ba, ba), -0.2, 1.);
    return length(pa - ba * h);
    }
    uniform sampler2D colorTexture; 
    uniform float fall_interval; 
    uniform float mix_factor; 
    varying vec2 v_textureCoordinates; 
    void main(void){
        vec2 uv = gl_FragCoord.xy; 
        float iTime = czm_frameNumber * fall_interval * clamp(fall_interval * 0.1, 0.01, 0.1); 
        vec2 p = uv / czm_viewport.zw; 
        vec2 d; 
        vec2 tgt = vec2(1., -1.); 
        float c = 0.; 
        if (p.y >= 0.) 
            c = (1. - (fbm2((p + .2) * p.y + .1 * iTime)).x) * p.y; 
        else 
            c = (1. - (fbm2(p + .2 + .1 * iTime)).x) * p.y * p.y; 
        vec3 col = vec3(0.); 
        vec3 col1 = c * vec3(.3, .5, 1.); 
        float mdist = 100000.; 
        float t = hash(floor(5. * iTime)); 
        tgt += 4. * hash2(tgt + t) - 1.5; 
        if (hash(t + 2.3) > .6) 
            for (int i = 0; i < 100; i++) {
                vec2 dtgt = tgt - p; 
                d = .05 * (vec2(-.5, -1.) + hash2(vec2(float(i), t))); 
                float dist = dseg(d, dtgt); 
                mdist = min(mdist, dist); 
                tgt -= d; 
                c = exp(-1.2 * dist) + exp(-55. * mdist); 
                col = c * vec3(.7, .8, 1.); 
            } 
            col += col1; 
        gl_FragColor = mix(texture2D(colorTexture, v_textureCoordinates), vec4(col, 0.0), mix_factor); 
    }`

                let LightningPostStage = new SuperMap3D.PostProcessStage({//在场景渲染的纹理或上一个后期处理阶段的输出上运行后期处理阶段。
                    fragmentShader: fragmentShaderSource,//执行此后处理阶段时要使用的片段着色器。
                    uniforms: {//一个对象,其属性将用于设置着色器制服。属性可以是常量值或函数。常量值也可以是用作纹理的 URI、数据 URI 或 HTML 元素。
                        mix_factor: 0.5,//混合系数0-1之间的数
                        fall_interval: 0.7,//0-1之间的数
                    }
                })

                const postProcessStage = viewer.scene.postProcessStages.add(LightningPostStage);//将后处理阶段添加到集合中。


                $('#loadingbar').remove();
            })

        }
        if (typeof SuperMap3D !== 'undefined') {
            window.startupCalled = true;
            onload(SuperMap3D);
        }
    </script>
</body>

</html>

三、最终成果

闪电特效

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: SuperMap iClient 3D for WebGL 是一款基于 WebGL 技术的三维地图开发工具,主要用于构建具有三维效果的地图应用程序。该工具提供了丰富的 API 接口和示例代码,可以帮助开发者快速上手,实现各种三维地图功能,如地形展示、建筑物模型、动态效果等。同时,SuperMap iClient 3D for WebGL 还支持多种数据格式,包括 SuperMap iServer 数据、OGC 标准数据、KML、GeoJSON 等,可以满足不同应用场景的需求。 ### 回答2: SuperMap iClient3D for WebGL是一个基于WebGL技术的三维地图开发框架。它提供了一系列的API和工具,使开发人员能够更加轻松地构建出高质量的三维地图应用。下面,我将从以下几个方面介绍SuperMap iClient3D for WebGL教程。 一、环境配置 SuperMap iClient3D for WebGL的开发需要先配置开发环境,包括安装Node.js、下载安装SuperMap iServer、下载安装SuperMap iClient3D资源包、创建Web应用程序。 二、基本概念 在学习SuperMap iClient3D for WebGL之前,需要了解一些基本概念。这些概念包括:场景(Scene)、层(Layer)、覆盖物(Overlay)等。在使用SuperMap iClient3D for WebGL时需要理解这些概念。 三、API使用 SuperMap iClient3D for WebGL提供了众多的API,使用这些API能够快速构建出一个三维地图应用。这些API包括:场景相关的API,如创建场景、设置场景属性等;层相关的API,如加载图层数据、隐藏、显示图层等;覆盖物相关的API,如创建点、线、面等覆盖物。 四、进阶应用 在掌握SuperMap iClient3D for WebGL的基础之后,可以通过进阶应用来进一步加深对这个框架的理解。进阶应用包括地球的三维测量、地球的动态效果、地球的热点区域等。 总之,SuperMap iClient3D for WebGL是一个非常实用的三维地图开发框架。通过学习其教程,开发人员可以更加方便、快速地构建出高质量的三维地图应用。 ### 回答3: SuperMap iClient 3D for WebGL是一款基于WebGL技术的开源GIS JavaScript库,用于构建高效、功能强大的3D GIS应用程序。这个库支持在Web浏览器里实现高质量的三维可视化效果,包括三维地形、建筑物、模型、标签等。现在,我来为你们讲解一下关于SuperMap iClient 3D for WebGL的教程。 一、前置知识 在接触SuperMap iClient 3D for WebGL之前,建议你先学习基本的WebGL知识和JavaScript语言,也应该具备计算机图形学基础和3D模型制作知识。 二、环境配置 在使用SuperMap iClient 3D for WebGL前,我们需要进行环境配置,在编写和运行项目时可以根据需要安装一系列依赖项,例如Node.js、npm包管理器、Gulp自动化工具、webpack工具和TypeScript编译器等。 三、基本使用 SuperMap iClient 3D for WebGL提供了丰富的API以实现3D可视化应用程序的开发,其中包括地图、图层、标注、工具、渲染等方面。同时,它还提供了多种数据格式的支持,如XYZ、OGC、JSON、KML、GML等。此外,SuperMap iClient 3D for WebGL还支持多种操作和交互方式,如平移、旋转、缩放、选取、编辑等,让用户可以更加自由地探索和编辑地图数据。 四、示例应用 SuperMap iClient 3D for WebGL提供了一系列的示例应用程序,可以帮助用户更好的理解其使用方法和特点,其中包括一些经典的三维场景展示和实时数据可视化等。例如,用户可以体验到城市三维建模、天气预报、热力图、空气质量监控等实用的功能。此外,SuperMap iClient 3D for WebGL也提供了一些模板和组件,供用户快速搭建自己的3D GIS应用程序。 总之,SuperMap iClient 3D for WebGL是一款非常优秀的基于WebGL技术的GIS JavaScript库,具有开源、高效、功能强大、易用等特点,可以为用户提供丰富多彩的三维GIS应用程序。当然,在使用前,建议用户先了解基本的WebGL和JavaScript知识,同时还需配置好开发环境。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值