Cesium材质特效

news2024/9/29 21:18:17

文章目录

  • 0.引言
  • 1.视频材质
  • 2.分辨率尺度
  • 3.云
  • 4.雾
  • 5.动态水面
  • 6.雷达扫描
  • 7.流动线
  • 8.电子围栏
  • 9.粒子烟花
  • 10.粒子火焰
  • 11.粒子天气

0.引言

现有的gis开发方向较流行的是webgis开发,其中Cesium是一款开源的WebGIS库,主要用于实时地球和空间数据的可视化和分析。它提供了丰富的地图显示和数据可视化功能,并能实现三维可视化开发。本文将使用一些特殊的材质,如视频材质、自定义材质和Cesium内置的一些特殊效果类、粒子系统等实现一些特效场景的模拟,包括云、雾、动态水面、雷达扫描、流动线、电子围栏、粒子烟花、粒子火焰及粒子天气等。

1.视频材质

对于通过Entity方式和Primitive方式创建的几何实体,下面介绍如何给几何实体贴上一个特殊的材质,即视频材质。
视频资源网址: https://cesium.com/public/SandcastleSampleData/big-buck-bunny_trailer.mp4
(1)实现代码
  在这里插入图片描述

6_1_视频材质.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
    <meta charset="UTF-8">  
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  
    <meta name="viewport" content="width=device-width, initial-scale=1.0">  
    <title>视频</title>  
    <script src="./Build/Cesium/Cesium.js"></script>  
    <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
    <style>  
        html,  
        body,  
        #cesiumContainer {  
            width: 100%;  
            height: 100%;  
            margin: 0;  
            padding: 0;  
        }  
  
        .toolbar {  
            position: absolute;  
            top: 10px;  
            left: 20px;  
            background-color: rgb(0, 0, 0, 0);  
        }  
    </style>  
</head>  
  
<body>  
    <div id="cesiumContainer">  
    </div>  
  
    <div class="toolbar">  
        <select id="dropdown" onchange="change()">  
            <option value="edit1">视频材质</option>  
            <option value="edit2">视频重复</option>  
        </select>  
    </div>  
  
    <video id="myVideo" muted="true" autoplay="true" loop="true" style="display: none;">  
        <source src="./vedio/big-buck-bunny_trailer.mp4" type="video/mp4">  
    </video>  
  
    <script>  
        Cesium.Ion.defaultAccessToken = '你的token';  
        var viewer = new Cesium.Viewer("cesiumContainer", {  
            timeline: false,  
            animation: false,  
            fullscreenButton: false,  
        });  
  
        //viewer.scene.globe.depthTestAgainstTerrain = false;  
  
        const videoElement = document.getElementById("myVideo");  
        //将视频元素与模拟时钟同步  
        let synchronizer = new Cesium.VideoSynchronizer({  
            clock: viewer.clock,  
            element: videoElement  
        });  
        viewer.clock.shouldAnimate = true;  
  
        var sphere = viewer.entities.add({  
            position: Cesium.Cartesian3.fromDegrees(104, 39, 2200),  
            ellipsoid: {  
                radii: new Cesium.Cartesian3(1000, 1000, 1000),  
                material: videoElement,  
            },  
        });  
        //相机视角锁定sphere  
        viewer.trackedEntity = sphere;  
  
        //改变视频重复个数  
        var isRepeat = false;  
        sphere.ellipsoid.material.repeat = new Cesium.CallbackProperty(  
            function (result) {  
                if (isRepeat) {  
                    result.x = 8;  
                    result.y = 8;  
                } else {  
                    result.x = 1;  
                    result.y = 1;  
                }  
                return result;  
            },  
            false  
        );  
  
        var dropdown = document.getElementById('dropdown');  
        function change() {  
            switch (dropdown.value) {  
                case 'edit1':  
                    isRepeat = false;  
                    break;  
                case 'edit2':  
                    isRepeat = true;  
                    break;  
                default:  
                    break;  
            }  
        }  
    </script>  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述
   在这里插入图片描述

2.分辨率尺度

在Cesium中,可以通过viewer.resolutionScale获取或者设置渲染分辨率的缩放比例。当该属性值小于1.0时,可以改善性能不佳的设备的显示效果,而当该属性值大于1.0时,将以更快的速度呈现分辨率,并缩小比例,从而提高视觉保真度。例如,如果窗口的尺寸为640像素×480像素,则将viewer.resolutionScale的值设置为0.5,会导致场景以320像素×240像素渲染,之后设置为2.0,会导致场景以1280像素×960像素渲染。
(1)实现代码
  在这里插入图片描述

6_2_分辨率尺度.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
    <meta charset="UTF-8">  
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  
    <meta name="viewport" content="width=device-width, initial-scale=1.0">  
    <title>材质特效篇_分辨率尺度</title>  
    <script src="./Build/Cesium/Cesium.js"></script>  
    <link rel="stylesheet" href="./Build/Cesium//Widgets/widgets.css">  
    <style>  
        html,  
        body,  
        #cesiumContainer {  
            width: 100%;  
            height: 100%;  
            margin: 0;  
            padding: 0;  
            overflow: hidden;  
        }  
        .toolbar {  
            position: absolute;  
            top: 10px;  
            left: 20px;  
            background-color: rgba(0, 0, 0, 0.6);  
        }  
  
    </style>  
  
</head>  
  
<body>  
    <div id="cesiumContainer">  
    </div>  
    <div class="toolbar">  
        <label style="color: white;">分辨率尺度</label> <br />  
        <input type="range" max="2" step="0.1" oninput="change()" id="R" value="1">  
        <input type="text" style="width:70px; " id="resolutionValue" value="1" onchange="change2()">  
  
    </div>  
    <script>  
        Cesium.Ion.defaultAccessToken = '你的token';  
        var viewer = new Cesium.Viewer("cesiumContainer", {  
            animation: false, //是否显示动画工具  
            timeline: false,  //是否显示时间轴工具  
            fullscreenButton: false,  //是否显示全屏按钮工具  
        });  
    var tileset = viewer.scene.primitives.add(  
        new Cesium.Cesium3DTileset({  
            url: './倾斜摄影/大雁塔3DTiles/tileset.json',  
        }));  
  
    viewer.zoomTo(tileset);  
    function change() {  
        //拿到滑动条当前值  
        var resolutionScale = Number(R.value);  
        //将值约束在0.1和2.0之间  
        resolutionScale = Cesium.Math.clamp(resolutionScale, 0.1, 2.0);  
        //文本框显示当前值  
        resolutionValue.value = resolutionScale;  
        //修改分辨率尺度  
        viewer.resolutionScale = resolutionScale;  
    }  
    function change2() {  
        var resolutionScale = Number(resolutionValue.value);  
        //将值约束在0.1和2.0之间  
        resolutionScale = Cesium.Math.clamp(resolutionScale, 0.1, 2.0);  
        R.value = resolutionScale;  
        change();  
    }  
    </script>  
  
</body>  
  
</html>

(2)结果显示
调整前:
  在这里插入图片描述
调整后:
  在这里插入图片描述

3.云

在模拟实际场景时,可以通过CloudCollection类在场景中渲染云,同时支持手动修改云的大小、亮度等来模拟积云。基本思路为先使用CloudCollection类创建一个云集合,然后在云集合中添加定义的不同样式的云。
(1)实现代码
  在这里插入图片描述

6_3_云.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
  <meta charset="utf-8" />  
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
  <meta name="viewport"  
    content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />  
  <meta name="description" content="Fog post process">  
  <meta name="cesium-sandcastle-labels" content="Showcases, Post Processing">  
  <title>材质特效篇_云</title>  
  <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
  <script type="text/javascript" src="./Build/Cesium/Cesium.js"></script>  
  
</head>  
<style>  
  html,  
  body,  
  #cesiumContainer {  
    width: 100%;  
    height: 100%;  
    margin: 0;  
    padding: 0;  
    overflow: hidden;  
  }  
  
  .toolbar {  
    position: absolute;  
    top: 10px;  
    left: 20px;  
    color: white;  
    background-color: rgba(0, 0, 0, 0.6);  
  }  
</style>  
  
<body>  
  <div id="cesiumContainer"></div>  
  <div class="toolbar">  
    <label>X轴尺寸</label> <br />  
    <input type="range" min="5" max="50" step="1" oninput="changeScale()" id="ScaleX" value="25">  
    <input type="text" style="width:70px; " id="ScaleXValue" value="25" onchange="changeScaleX()"> <br>  
    <label>Y轴尺寸</label> <br />  
    <input type="range" min="5" max="50" step="1" oninput="changeScale()" id="ScaleY" value="12">  
    <input type="text" style="width:70px; " id="ScaleYValue" value="12" onchange="changeScaleY()"> <br>  
    <label>亮度</label> <br />  
    <input type="range" min="0" max="1" step="0.01" oninput="changeBrightness()" id="Brightness" value="1">  
    <input type="text" style="width:70px; " id="BrightnessValue" value="1" onchange="changeBrightnessValue()"> <br>  
  </div>  
  
  <script>  
    Cesium.Ion.defaultAccessToken = '你的token';  
    var viewer = new Cesium.Viewer("cesiumContainer", {  
      animation: false, //是否显示动画工具  
      timeline: false,  //是否显示时间轴工具  
      fullscreenButton: false,  //是否显示全屏按钮工具  
    });  
  
    //创建并添加云集合  
    var clouds = viewer.scene.primitives.add(  
      new Cesium.CloudCollection({  
        noiseDetail: 16.0,  
      })  
    );  
  
    //添加云  
    var cloud = clouds.add({  
      position: Cesium.Cartesian3.fromDegrees(114.39264, 30.52252, 100),  
      scale: new Cesium.Cartesian2(25, 12),  
      slice: 0.36,  
      brightness: 1,  
    })  
  
    //设置相机位置及方向  
    viewer.camera.lookAt(  
      Cesium.Cartesian3.fromDegrees(114.39264, 30.52252, 100),  
      new Cesium.Cartesian3(30, 30, -10)  
    );  
  
    var ScaleX = document.getElementById('ScaleX'); //X轴尺寸  
    var ScaleXValue = document.getElementById('ScaleXValue'); //ScaleX滑动条值  
    var ScaleY = document.getElementById('ScaleY'); //Y轴尺寸  
    var ScaleYValue = document.getElementById('ScaleYValue'); //ScaleY滑动条值  
    var Brightness = document.getElementById('Brightness'); //亮度  
    var BrightnessValue = document.getElementById('BrightnessValue'); //亮度滑动条值  
  
    //Scale滑动条  
    function changeScale() {  
      //拿到scaleX滑动条当前值  
      var sX = Number(ScaleX.value);  
      //文本框显示当前值  
      ScaleXValue.value = sX;  
  
      //拿到scaleY滑动条当前值  
      var sY = Number(ScaleY.value);  
      //x轴旋转文本框显示当前值  
      ScaleYValue.value = sY;  
  
      //修改云的比例  
      cloud.scale = new Cesium.Cartesian2(sX, sY);  
    }  
    //ScaleX文本框  
    function changeScaleX() {  
      //拿到scaleX文本框的值并赋值给滑动条  
      ScaleX.value = Number(ScaleXValue.value);  
  
      changeScale();  
    }  
    //ScaleY文本框  
    function changeScaleY() {  
      //拿到scaleY文本框的值并赋值给滑动条  
      ScaleY.value = Number(ScaleYValue.value);  
      changeScale();  
    }  
  
    //Brightness滑动条  
    function changeBrightness() {  
      //拿到Brightness滑动条滑动条当前值  
      var brightness = Number(Brightness.value);  
      //文本框显示当前值  
      BrightnessValue.value = brightness;  
  
      //修改云的亮度  
      cloud.brightness = brightness;  
    }  
    //Brightness文本框  
    function changeBrightnessValue() {  
      //拿到文本框的值并赋值给滑动条  
      Brightness.value = Number(BrightnessValue.value);  
      changeBrightness();  
    }  
  
  </script>  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述

4.雾

Cesium在1.46版本之后新增了场景后处理功能。所谓场景后处理,我们可以将其理解为一个不断叠加的过程。例如,我们拍了一张照片,拍完之后觉得该照片亮度不够,于是我们在该照片的基础上进行了亮度的调整,得到了一张新照片,然后觉得新照片不够好看,又在新照片的基础上添加了滤镜,此后我们可能还会进行多次处理,直到最后得到的照片满足我们的要求为止,这个过程就类似于场景后处理,即我们在绘制场景时可能会不断地对场景进行一些处理,将最终符合我们要求的处理结果绘制到屏幕上。下面通过Cesium的场景后处理功能来实现雾的效果。
(1)实现代码
  在这里插入图片描述

6_4_雾效果.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
  <meta charset="utf-8" />  
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
  <meta name="viewport"  
    content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />  
  <meta name="description" content="Fog post process">  
  <meta name="cesium-sandcastle-labels" content="Showcases, Post Processing">  
  <title>材质特效篇_雾效果</title>  
  <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
  <script type="text/javascript" src="./Build/Cesium/Cesium.js"></script>  
  
</head>  
<style>  
  html,  
  body,  
  #cesiumContainer {  
    width: 100%;  
    height: 100%;  
    margin: 0;  
    padding: 0;  
    overflow: hidden;  
  }  
</style>  
  
<body>  
  <div id="cesiumContainer"></div>  
  <script>  
    Cesium.Ion.defaultAccessToken = '你的token';  
    var viewer = new Cesium.Viewer("cesiumContainer", {  
      animation: false, //是否显示动画工具  
      timeline: false,  //是否显示时间轴工具  
      fullscreenButton: false,  //是否显示全屏按钮工具  
    });  
  
    var tileset = viewer.scene.primitives.add(  
      new Cesium.Cesium3DTileset({  
        url: './倾斜摄影/大雁塔3DTiles/tileset.json',  
      }));  
    viewer.zoomTo(tileset);  
  
    var fragmentShaderSource =  
      `//计算每个渲染顶点和视点(相机)的距离  
      float getDistance(sampler2D depthTexture, vec2 texCoords)  
      {  
          float depth = czm_unpackDepth(texture2D(depthTexture, texCoords));  
          if (depth == 0.0) {  
              return czm_infinity;  
          }  
          vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);  
          return -eyeCoordinate.z / eyeCoordinate.w;  
      }  
      //按距离进行插值  
      float interpolateByDistance(vec4 nearFarScalar, float distance)  
      {  
          float startDistance = nearFarScalar.x;  
          float startValue = nearFarScalar.y;  
          float endDistance = nearFarScalar.z;  
          float endValue = nearFarScalar.w;  
          float t = clamp((distance - startDistance) / (endDistance - startDistance), 0.0, 1.0);  
          return mix(startValue, endValue, t);  
      }  
      //计算透明度  
      vec4 alphaBlend(vec4 sourceColor, vec4 destinationColor)  
      {  
          return sourceColor * vec4(sourceColor.aaa, 1.0) + destinationColor * (1.0 - sourceColor.a);  
      }  
  
      uniform sampler2D colorTexture; //颜色纹理 内置变量  
      uniform sampler2D depthTexture; //深度纹理  内置变量  
      varying vec2 v_textureCoordinates;  //屏幕采样点坐标 内置变量  
  
      uniform vec4 fogByDistance; //自定义属性 外部变量  
      uniform vec4 fogColor;  //自定义属性 外部变量  
      void main(void)  
      {  
          float distance = getDistance(depthTexture, v_textureCoordinates);  
          vec4 sceneColor = texture2D(colorTexture, v_textureCoordinates);  
          float blendAmount = interpolateByDistance(fogByDistance, distance);  
          vec4 finalFogColor = vec4(fogColor.rgb, fogColor.a * blendAmount);  
          gl_FragColor = alphaBlend(finalFogColor, sceneColor);  
      }`;  
  
    var postProcessStage = new Cesium.PostProcessStage({  
      //片源着色器  
      fragmentShader: fragmentShaderSource,  
      uniforms: {  
        fogByDistance: new Cesium.Cartesian4(0, 0, 600, 1.0), //距离  
        fogColor: Cesium.Color.WHITE, //颜色  
      },  
    })  
    viewer.scene.postProcessStages.add(postProcessStage);  
  
  </script>  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述

5.动态水面

模拟水面效果也是Cesium场景中常见的功能,例如,有的项目可能通过绘制实体面,并设置材质为淡蓝色来模拟水面。但是,在实际生活中,水面往往不是静止的而是动态的,下面通过修改水面的材质来实现动态水面的效果。动态水面的具体实现思路为:先准备一张水面纹理图片,然后通过Primitive方式创建一个矩形实体,使用EllipsoidSurfaceAppearance定义一个水面材质,并给矩形实体设置该材质,即可实现简单的动态水面效果。
(1)实现代码
  在这里插入图片描述

6_5_动态水面.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
    <meta charset="UTF-8">  
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  
    <meta name="viewport" content="width=device-width, initial-scale=1.0">  
    <title>材质特效篇_动态水面</title>  
    <script src="./Build/Cesium/Cesium.js"></script>  
    <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
    <style>  
        html,  
        body,  
        #cesiumContainer {  
            width: 100%;  
            height: 100%;  
            margin: 0;  
            padding: 0;  
            overflow: hidden;  
        }  
  
    </style>  
  
</head>  
  
<body>  
    <div id="cesiumContainer">  
    </div>  
    <script>  
        Cesium.Ion.defaultAccessToken = '你的token';  
        var viewer = new Cesium.Viewer("cesiumContainer", {  
            animation: false, //是否显示动画工具  
            timeline: false,  //是否显示时间轴工具  
            fullscreenButton: false,  //是否显示全屏按钮工具  
            terrainProvider: Cesium.createWorldTerrain()  
        });  
        viewer.scene.globe.depthTestAgainstTerrain = true;//开启深度检测后 会有高程遮挡效果  
    var rectangle = new Cesium.GeometryInstance({  
        geometry: new Cesium.RectangleGeometry({  
            rectangle: Cesium.Rectangle.fromDegrees(95.0, 39.0, 100.0, 42.0),  
            height: 3500.0  
        })  
    });  
    //定义外观  
    var rectangleAppearance = new Cesium.EllipsoidSurfaceAppearance({  
        aboveGround: true,  
        material: new Cesium.Material({  
            fabric:  
            {  
                type: 'Water',        //材质类型  
                uniforms: {  
                    //baseWaterColor: new Cesium.Color.fromBytes(24, 173, 247, 100),//基础颜色  
                    normalMap: './RasterImage/图片/动态水面.jpg',        //法线纹理贴图  
                    frequency: 100.0,        //波的数量  
                    animationSpeed: 0.01,        //水波震动速度  
                    amplitude: 10.0                //振幅大小  
                },  
            }  
        }),  
        //重写shader,修改水面的透明度  
        fragmentShaderSource: 'varying vec3 v_positionMC;\n' +  
            'varying vec3 v_positionEC;\n' +  
            'varying vec2 v_st;\n' +  
            'void main()\n' +  
            '{\n' +  
            'czm_materialInput materialInput;\n' +  
            'vec3 normalEC = normalize(czm_normal3D * czm_geodeticSurfaceNormal(v_positionMC, vec3(0.0), vec3(1.0)));\n' +  
            '#ifdef FACE_FORWARD\n' +  
            'normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);\n' +  
            '#endif\n' +  
            'materialInput.s = v_st.s;\n' +  
            'materialInput.st = v_st;\n' +  
            'materialInput.str = vec3(v_st, 0.0);\n' +  
            'materialInput.normalEC = normalEC;\n' +  
            'materialInput.tangentToEyeMatrix = czm_eastNorthUpToEyeCoordinates(v_positionMC, materialInput.normalEC);\n' +  
            'vec3 positionToEyeEC = -v_positionEC;\n' +  
            'materialInput.positionToEyeEC = positionToEyeEC;\n' +  
            'czm_material material = czm_getMaterial(materialInput);\n' +  
            '#ifdef FLAT\n' +  
            'gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n' +  
            '#else\n' +  
            'gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);\n' +  
            'gl_FragColor.a=0.55;\n' +  
            '#endif\n' +  
            '}\n'  
    });  
    var addRectangleGeometry = new Cesium.Primitive({  
        geometryInstances: rectangle,  
        appearance: rectangleAppearance  
    })  
    viewer.scene.primitives.add(addRectangleGeometry);  
    viewer.camera.flyTo({  
        destination: Cesium.Cartesian3.fromDegrees(108, 42, 6000000),  
    })  
    </script>  
  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述
   在这里插入图片描述

6.雷达扫描

使用飞机或无人机沿着飞行路线进行雷达扫描的效果在实际应用中是很常见的。在Cesium中实现雷达扫描效果的方法有很多,可以通过对Entity实体贴纹理并对材质进行不断的旋转来实现,或者通过着色器重写Entity实体的材质shader来实现。比较而言,前者对于新手来说更容易实现,下面通过第一种方法来模拟雷达扫描效果。
(1)实现代码
  在这里插入图片描述

6_6_雷达扫描.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
    <meta charset="UTF-8">  
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  
    <meta name="viewport" content="width=device-width, initial-scale=1.0">  
    <title>材质特效篇_雷达扫描</title>  
    <script src="./Build/Cesium/Cesium.js"></script>  
    <link rel="stylesheet" href="./Build/Cesium//Widgets/widgets.css">  
    <style>  
        html,  
        body,  
        #cesiumContainer {  
            width: 100%;  
            height: 100%;  
            margin: 0;  
            padding: 0;  
            overflow: hidden;  
        }  
  
    </style>  
  
</head>  
  
<body>  
    <div id="cesiumContainer">  
    </div>  
    <script>  
        Cesium.Ion.defaultAccessToken = '你的token';  
        var viewer = new Cesium.Viewer("cesiumContainer", {  
            animation: false, //是否显示动画工具  
            timeline: false,  //是否显示时间轴工具  
            fullscreenButton: false,  //是否显示全屏按钮工具  
        });  
    var rotation = 0; //纹理旋转角度  
    var amount = 4;        //旋转变化量  
    var rader = {  
        position: Cesium.Cartesian3.fromDegrees(114.40372, 30.52252),  
        ellipse: {  
            semiMajorAxis: 300.0,  
            semiMinorAxis: 300.0,  
            //指定材质  
            material: new Cesium.ImageMaterialProperty({  
                image: './RasterImage/图片/color.png',  
                color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),  
            }),  
    // 不设置高度则无法渲染外框线  
    height: 0.0,  
    //heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,  
    //外边框  
    outline: true,  
    outlineWidth: 2,  
    outlineColor: new Cesium.Color(1.0, 1.0, 0.0, 1.0),  

    //纹理旋转角度通过CallbackProperty回调  
    stRotation: new Cesium.CallbackProperty(function () {  
        rotation += amount;  
        if (rotation >= 360 || rotation <= -360) {  
            rotation = 0;  
        }  
        //度数转弧度  
        return Cesium.Math.toRadians(rotation);  
    }, false)  
    }  
    }  
    //将rader添加进entity集合  
    viewer.entities.add(rader)  
    var point = viewer.entities.add({  
        position: Cesium.Cartesian3.fromDegrees(114.40372, 30.52252),  
        point: {  
            pixelSize: 10,  
            color: Cesium.Color.RED,  
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND  
        }  
    });  
    viewer.camera.setView({  
        destination: Cesium.Cartesian3.fromDegrees(114.40372, 30.52252, 2000)  
    });  
    </script>  
  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述
   在这里插入图片描述

7.流动线

Cesium中有许多封装好的内置纹理,如条纹、颜色、虚线、棋盘、水面等,但是这些内置纹理大多是静态的,并不能满足我们在实际开发中的需求,这时就需要我们通过自定义材质来达到特定的纹理效果。
自定义材质可以通过现有的内置材质派生,也可以使用Fabric和GLSL来自定义。但是在实际开发中,为了减少代码冗余,我们通常将常用的自定义材质封装成一个个Material材质类以便复用,下面将介绍如何封装一个自定义流动线材质类。
(1)实现代码
  在这里插入图片描述

6_7_流动线.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
  <meta charset="UTF-8">  
  <meta http-equiv="X-UA-Compatible" content="IE=edge">  
  <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  <title>材质特效篇_流动线</title>  
  <script src="./Build/Cesium/Cesium.js"></script>  
  <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
  <script src="./Build/js/jquery.min.js"></script>  
  <style>  
    html,  
    body,  
    #cesiumContainer {  
      width: 100%;  
      height: 100%;  
      margin: 0;  
      padding: 0;  
      overflow: hidden;  
    }  
  </style>  
</head>  
  
<body>  
  <div id="cesiumContainer">  
  </div>  
  
  <script>  
    Cesium.Ion.defaultAccessToken =  
      '你的token';  
    var viewer = new Cesium.Viewer("cesiumContainer", {  
      animation: true, //是否显示动画工具  
      timeline: true, //是否显示时间轴工具  
      fullscreenButton: false, //是否显示全屏按钮工具  
    });  
    /* console.log('selectionIndicator',viewer.selectionIndicator);  
    $(".cesium-viewer-selectionIndicatorContainer").css('display','none'); */  
  
    viewer.scene.fxaa = false  
    viewer.scene.postProcessStages.fxaa.enabled = false;  
  
    var supportsImageRenderingPixelated = viewer.cesiumWidget._supportsImageRenderingPixelated;  
    if (supportsImageRenderingPixelated) {  
      var vtxf_dpr = window.devicePixelRatio;  
      while (vtxf_dpr >= 2.0) {  
        vtxf_dpr /= 2.0;  
      }  
      viewer.resolutionScale = vtxf_dpr;  
    }  
  
    //创建构造函数  
    function PolylineTrailLinkMaterialProperty(color, duration) {  
      this._definitionChanged = new Cesium.Event();  
      this._color = undefined;  
      this._colorSubscription = undefined;  
      this.color = color;  
      this.duration = duration;  
      this._time = (new Date()).getTime();  
    }  
  
    //Object.defineProperties() 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。  
    //Object.defineProperties(obj, props)  
    //obj:在其上定义或修改属性的对象   props:要定义其可枚举属性或修改的属性描述符的对象。  
    Object.defineProperties(PolylineTrailLinkMaterialProperty.prototype, {  
      isConstant: {  
        get: function () {  
          return false;  
        }  
      },  
      definitionChanged: {  
        get: function () {  
          return this._definitionChanged;  
        }  
      },  
      color: Cesium.createPropertyDescriptor('color')  
    });  
  
    PolylineTrailLinkMaterialProperty.prototype.getType = function (time) {  
      return 'PolylineTrailLink';  
    }  
    PolylineTrailLinkMaterialProperty.prototype.getValue = function (time, result) {  
      if (!Cesium.defined(result)) {  
        result = {};  
      }  
      result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, Cesium.Color.WHITE, result.color);  
      result.image = Cesium.Material.PolylineTrailLinkImage;  
      result.time = (((new Date()).getTime() - this._time) % this.duration) / this.duration;  
      return result;  
    }  
    PolylineTrailLinkMaterialProperty.prototype.equals = function (other) {  
      return this === other || (other instanceof PolylineTrailLinkMaterialProperty &amp;&amp; Property.equals(this._color,  
        other._color))  
    };  
  
    Cesium.PolylineTrailLinkMaterialProperty = PolylineTrailLinkMaterialProperty;  
    //纹理类型  
    Cesium.Material.PolylineTrailLinkType = 'PolylineTrailLink';  
    //纹理图片  
    Cesium.Material.PolylineTrailLinkImage = "./RasterImage/图片/color.png";  
    //纹理资源  
    Cesium.Material.PolylineTrailLinkSource =  
      "czm_material czm_getMaterial(czm_materialInput materialInput)\n\  
            {\n\  
                float time = czm_frameNumber/100.0;\n\  
                czm_material material = czm_getDefaultMaterial(materialInput);\n\  
                vec2 st = materialInput.st;\n\  
                vec4 colorImage = texture2D(image, vec2(fract(3.0*st.s - time), st.s));\n\  
                material.alpha = colorImage.a * color.a;\n\  
                material.diffuse = (colorImage.rgb+color.rgb)/2.0;\n\  
                return material;\n\  
            }";  
    //time越小,速度越慢  
    //colorImage控制纹理  
    //fract中 3.0是纹理个数  -time是逆时针 +time是顺时针  
    //alpha 透明度  
    //diffuse 颜色  
  
    /* "czm_material czm_getMaterial(czm_materialInput materialInput)\n\  
    {\n\  
        czm_material material = czm_getDefaultMaterial(materialInput);\n\  
        vec2 st = materialInput.st;\n\  
        vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));\n\  
        material.alpha = colorImage.a * color.a;\n\  
        material.diffuse = (colorImage.rgb+color.rgb)/2.0;\n\  
        return material;\n\  
    }" */  
  
    //添加自定义材质  
    Cesium.Material._materialCache.addMaterial(Cesium.Material.PolylineTrailLinkType, {  
      fabric: {  
        //纹理类型  
        type: Cesium.Material.PolylineTrailLinkType,  
        //传递给着色器的外部属性  
        uniforms: {  
          color: new Cesium.Color(0.0, 0.0, 0.0, 1),  
          image: Cesium.Material.PolylineTrailLinkImage,  
          time: 0  
        },  
        //纹理资源  
        source: Cesium.Material.PolylineTrailLinkSource  
      },  
      //是否透明  
      translucent: function (material) {  
        return true;  
      }  
    })  
  
    var line = viewer.entities.add({  
      name: 'PolylineTrailLink',  
      polyline: {  
        positions: Cesium.Cartesian3.fromDegreesArray([  
          118.286419, 31.864436,  
          119.386419, 31.864436,  
          119.386419, 32.864436,  
          118.686419, 32.864436,  
        ]),  
        width: 10,  
        //设置材质为自定义材质  
        material: new Cesium.PolylineTrailLinkMaterialProperty(  
          Cesium.Color.fromBytes(255, 0, 0).withAlpha(0.8),  
          /* 1000 */  
        ),  
      }  
    });  
    viewer.flyTo(line)  
  </script>  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述
   在这里插入图片描述

8.电子围栏

下面封装一个自定义电子围栏材质类,能够对Entity墙体贴动态材质,实现电子围栏效果。封装自定义电子围栏材质类的流程和封装自定义流动线材质类的流程一样。
(1)实现代码
  在这里插入图片描述

6_8_电子围栏.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
    <meta charset="UTF-8">  
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  
    <meta name="viewport" content="width=device-width, initial-scale=1.0">  
    <title>材质特效篇_电子围栏</title>  
    <script src="./Build/Cesium/Cesium.js"></script>  
    <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
    <style>  
        html,  
        body,  
        #cesiumContainer {  
            width: 100%;  
            height: 100%;  
            margin: 0;  
            padding: 0;  
            overflow: hidden;  
        }  
    </style>  
</head>  
  
<body>  
    <div id="cesiumContainer">  
    </div>  
  
    <script>  
        Cesium.Ion.defaultAccessToken = '你的token';  
        var viewer = new Cesium.Viewer("cesiumContainer", {  
            animation: false, //是否显示动画工具  
            timeline: false,  //是否显示时间轴工具  
            fullscreenButton: false,  //是否显示全屏按钮工具  
        });  
  
        function DynamicWallMaterialProperty(color, duration) {  
            this._definitionChanged = new Cesium.Event();  
            this._color = undefined;  
            this._colorSubscription = undefined;  
            this.color = color;  
            this.duration = duration;  
            this._time = (new Date()).getTime();  
        }  
  
        Object.defineProperties(DynamicWallMaterialProperty.prototype, {  
            isConstant: {  
                get: function () {  
                    return false;  
                }  
            },  
            definitionChanged: {  
                get: function () {  
                    return this._definitionChanged;  
                }  
            },  
            color: Cesium.createPropertyDescriptor('color')  
        });  
  
        DynamicWallMaterialProperty.prototype.getType = function (time) {  
            return 'DynamicWall';  
        }  
        DynamicWallMaterialProperty.prototype.getValue = function (time, result) {  
            if (!Cesium.defined(result)) {  
                result = {};  
            }  
            result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, Cesium.Color.WHITE, result.color);  
            result.image = Cesium.Material.DynamicWallImage;  
            result.time = (((new Date()).getTime() - this._time) % this.duration) / this.duration;  
            return result;  
        }  
        DynamicWallMaterialProperty.prototype.equals = function (other) {  
            return this === other || (other instanceof DynamicWallMaterialProperty &amp;&amp; Property.equals(this._color, other._color))  
        };  
  
        Cesium.DynamicWallMaterialProperty = DynamicWallMaterialProperty;  
        Cesium.Material.DynamicWallType = 'DynamicWall';  
        Cesium.Material.DynamicWallImage = "./RasterImage/图片/color.png";//图片  
        Cesium.Material.DynamicWallSource =  
            `czm_material czm_getMaterial(czm_materialInput materialInput)  
            {  
                float time = czm_frameNumber/100.0;  
                czm_material material = czm_getDefaultMaterial(materialInput);  
                vec2 st = materialInput.st;  
                vec4 colorImage = texture2D(image, vec2(fract(1.0*st.t - time), st.t));  
                material.alpha = colorImage.a * color.a;  
                material.diffuse = (colorImage.rgb+color.rgb)/2.0;  
                return material;  
            }`      //由上到下  
  
        //添加自定义材质  
        Cesium.Material._materialCache.addMaterial(Cesium.Material.DynamicWallType, {  
            fabric: {  
                //纹理类型  
                type: Cesium.Material.DynamicWallType,  
                //传递给着色器的外部属性  
                uniforms: {  
                    color: new Cesium.Color(0.0, 0.0, 0.0, 1),  
                    image: Cesium.Material.DynamicWallImage,  
                    time: 0  
                },  
                //纹理资源  
                source: Cesium.Material.DynamicWallSource  
            },  
            //是否透明  
            translucent: function (material) {  
                return true;  
            }  
        })  
  
        var dynamicWall = viewer.entities.add({  
            wall: {  
                positions: Cesium.Cartesian3.fromDegreesArrayHeights([  
                    118.286419, 31.864436, 20000.0,  
                    119.386419, 31.864436, 20000.0,  
                    119.386419, 32.864436, 20000.0,  
                    118.286419, 32.864436, 20000.0,  
                    118.286419, 31.864436, 20000.0,  
                ]),  
                material: new Cesium.DynamicWallMaterialProperty(Cesium.Color.fromBytes(255, 200, 10).withAlpha(0.8), 3000),  
            }  
        })  
        viewer.flyTo(dynamicWall)  
  
    </script>  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述
   在这里插入图片描述

9.粒子烟花

粒子系统表示三维计算机图形学中用于模拟一些特定模糊现象的技术,而这些现象用其他传统的渲染技术难以实现其真实感的物理运动规律。经常使用粒子系统模拟的现象有烟花、火焰、雨水及雪花等。简而言之,粒子系统就是一种用于模拟真实现象的图形技术,是由一个个的小图像集合而成的,从远处看会形成一个“复杂”的场景来模拟一些现象。
Cesium粒子系统不仅是多个小图像的直接集合,而且允许控制单个粒子的寿命、速度、位置等属性,也正是由于粒子的各种属性可以控制,才能够模拟各种复杂的场景。粒子系统效果在电影和电子游戏中应用广泛。下面使用粒子系统模拟烟花爆炸效果。
(1)实现代码
  在这里插入图片描述

6_9_粒子烟花.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
  <meta charset="utf-8" />  
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
  <meta name="description" content="Particle system fireworks.">  
  <meta name="cesium-sandcastle-labels" content="Beginner, Showcases">  
  <title>材质特效篇_粒子烟花</title>  
  <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
  <script src="./Build/Cesium/Cesium.js"></script>  
  
</head>  
<style>  
  html,  
  body,  
  #cesiumContainer {  
    width: 100%;  
    height: 100%;  
    margin: 0;  
    padding: 0;  
    overflow: hidden;  
  }  
</style>  
  
<body>  
  
  <div id="cesiumContainer"></div>  
  <script>  
    Cesium.Ion.defaultAccessToken = '你的token';  
    var viewer = new Cesium.Viewer("cesiumContainer", {  
      animation: false, //是否显示动画工具  
      timeline: false,  //是否显示时间轴工具  
      fullscreenButton: false,  //是否显示全屏按钮工具  
      shouldAnimate: true,  //必须开启,自动播放动画  
    });  
  
    /* Cesium.Math.setRandomNumberSeed(315); */  
  
    //东北天到指定原点变换矩阵,将粒子系统从模型坐标转换为世界坐标  
    const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(  
      Cesium.Cartesian3.fromDegrees(114.39664, 30.52052)  
    );  
    //粒子发射器高度  
    const emitterInitialLocation = new Cesium.Cartesian3(0.0, 0.0, 100.0);  
  
    //粒子贴图  
    var particleCanvas;  
    //绘制图形  
    function getImage() {  
      if (!Cesium.defined(particleCanvas)) {  
        particleCanvas = document.createElement("canvas");  
        particleCanvas.width = 20;  
        particleCanvas.height = 20;  
        const context2D = particleCanvas.getContext("2d");  
        context2D.beginPath();  
        //圆心x 圆心y 半径 起始角度 终止角度 逆时针  
        context2D.arc(10, 10, 8, 0, Cesium.Math.TWO_PI, true);  
        context2D.closePath();  
        context2D.fillStyle = "rgba(255, 255, 255, 1)";  
        context2D.fill();  
      }  
      return particleCanvas;  
    }  
  
    /* var radar = viewer.entities.add({  
    rectangle: {  
        coordinates: Cesium.Rectangle.fromDegrees(114.40072, 30.51952, 114.40572, 30.52452),  
        material: new Cesium.ImageMaterialProperty({  
            //image: new Cesium.CallbackProperty(drawCanvas, false),  
            image:getImage(),  
            //transparent: true  
        }),  
    }  
    }); */  
    /* const minimumExplosionSize = 30.0; //最小爆炸尺寸  
    const maximumExplosionSize = 100.0;  //最大爆炸尺寸 */  
    var particlePixelSize = new Cesium.Cartesian2(7.0, 7.0);  //粒子大小  
    var burstNum = 400.0;  //爆炸粒子个数  
    var lifetime = 10.0;    //粒子系统发射粒子的时间  
    var numberOfFireworks = 20.0;  //烟花个数  
  
    //创建烟花函数  
    function createFirework(offset, color, bursts) {  
      var position = Cesium.Cartesian3.add(  
        emitterInitialLocation,  
        offset,  
        new Cesium.Cartesian3()  
      );  
      //从发射位置创建表示转换的Matrix4  
      var emitterModelMatrix = Cesium.Matrix4.fromTranslation(position);  
  
      //随机设置烟花的生命周期  
      /* const size = Cesium.Math.randomBetween(  
        minimumExplosionSize,  
        maximumExplosionSize  
      );  
      const normalSize =  
        (size - minimumExplosionSize) /  
        (maximumExplosionSize - minimumExplosionSize);  
      const minLife = 0.3;  
      const maxLife = 1.0;  
      const life = normalSize * (maxLife - minLife) + minLife; */  
  
      viewer.scene.primitives.add(  
        new Cesium.ParticleSystem({  
          image: getImage(),  //粒子贴图  
          startColor: color,  //粒子在其生命初期的颜色  
          endColor: color.withAlpha(0.0),//粒子在其生命结束的颜色  
          //particleLife: life, //粒子生命周期  
          particleLife: 1, //粒子生命周期  
          speed: 100.0, //粒子扩散速度  
          imageSize: particlePixelSize, //粒子像素大小  
          emissionRate: 0,  //每秒要发射的粒子数  
          emitter: new Cesium.SphereEmitter(0.1), //系统粒子发射器  
          bursts: bursts, //粒子爆炸,ParticleBurst 的数组  
          lifetime: lifetime, //粒子系统发射粒子的时间  
          //updateCallback: force,  //每帧都要调用一次回调函数以更新粒子  
          modelMatrix: modelMatrix, //将粒子系统从模型转换为世界坐标的4x4转换矩阵。  
          emitterModelMatrix: emitterModelMatrix,//在粒子系统局部坐标系内转换粒子系统发射器的4x4转换矩阵  
          loop: true //粒子循环爆发  
        })  
      );  
    }  
  
    //粒子发射器偏移量范围  
    var xMin = -100.0;  
    var xMax = 100.0;  
    var yMin = -80.0;  
    var yMax = 100.0;  
    var zMin = -50.0;  
    var zMax = 50.0;  
  
    //设置随机颜色选项数组  
    var colorOptions = [  
      {  
        minimumRed: 0.75,  
        green: 0.0,  
        minimumBlue: 0.8,  
        alpha: 1.0,  
      },  
      {  
        red: 0.0,  
        minimumGreen: 0.75,  
        minimumBlue: 0.8,  
        alpha: 1.0,  
      },  
      {  
        red: 0.0,  
        green: 0.0,  
        minimumBlue: 0.8,  
        alpha: 1.0,  
      },  
      {  
        minimumRed: 0.75,  
        minimumGreen: 0.75,  
        blue: 0.0,  
        alpha: 1.0,  
      },  
    ];  
  
    //创建烟花  
    for (let i = 0; i < numberOfFireworks; ++i) {  
      var x = Cesium.Math.randomBetween(xMin, xMax);  
      var y = Cesium.Math.randomBetween(yMin, yMax);  
      var z = Cesium.Math.randomBetween(zMin, zMax);  
      var offset = new Cesium.Cartesian3(x, y, z);  
      //使用提供的选项创建随机颜色  
      var color = Cesium.Color.fromRandom(  
        colorOptions[i % colorOptions.length]  
      );  
      //粒子爆炸,ParticleBurst 的数组,在周期时间发射粒子爆发  
      var bursts = [];  
      for (let j = 0; j < 3; ++j) {  
        bursts.push(  
          new Cesium.ParticleBurst({  
            time: Cesium.Math.nextRandomNumber() * lifetime, //粒子系统生命周期开始后以秒为单位的时间,将发生爆发  
            minimum: burstNum, //爆发中发射的最小粒子数。  
            maximum: burstNum, //爆发中发射的最大粒子数。  
          })  
        );  
      }  
      //传参,创建烟花  
      createFirework(offset, color, bursts);  
    }  
  
    viewer.scene.camera.setView({  
      destination:  
        Cesium.Cartesian3.fromDegrees(114.39664, 30.52052, 2000)  
    })  
  
  </script>  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述
   在这里插入图片描述

10.粒子火焰

下面使用Cesium粒子系统模拟火焰燃烧效果。
(1)实现代码
  在这里插入图片描述

6_10_粒子火焰.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
  <meta charset="utf-8" />  
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
  <meta name="description" content="Particle system fireworks.">  
  <meta name="cesium-sandcastle-labels" content="Beginner, Showcases">  
  <title>材质特效篇_粒子火焰</title>  
  <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
  <script src="./Build/Cesium/Cesium.js"></script>  
  
</head>  
<style>  
  html,  
  body,  
  #cesiumContainer {  
    width: 100%;  
    height: 100%;  
    margin: 0;  
    padding: 0;  
    overflow: hidden;  
  }  
</style>  
  
<body>  
  
  <div id="cesiumContainer"></div>  
  
  <script>  
    Cesium.Ion.defaultAccessToken = '你的token';  
    var viewer = new Cesium.Viewer("cesiumContainer", {  
      animation: false, //是否显示动画工具  
      timeline: false,  //是否显示时间轴工具  
      fullscreenButton: false,  //是否显示全屏按钮工具  
      shouldAnimate: true,  //必须开启 自动播放动画  
    });  
  
    // 加载飞机模型  
    var entity = viewer.entities.add({  
      model: {  
        uri: './3D格式数据/glb/Cesium_Air.glb',  
        minimumPixelSize: 64  
      },  
      position: Cesium.Cartesian3.fromDegrees(114.39264, 30.52252, 100)  
    });  
    //视角追踪模型  
    viewer.trackedEntity = entity;  
  
    //计算把粒子系统从模型坐标系转到世界坐标系指定原点的矩阵  
    const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(  
      Cesium.Cartesian3.fromDegrees(114.39264, 30.52252, 100)  
    );  
  
    //计算模型坐标系的平移矩阵  
    function computeEmitterModelMatrix() {  
      //定义粒子发射器的方向、俯仰角以及翻滚角  
      var hpr = Cesium.HeadingPitchRoll.fromDegrees(0.0, 0.0, 0.0, new Cesium.HeadingPitchRoll());  
      //定义一个由平移,旋转和缩放定义的仿射变换  
      var trs = new Cesium.TranslationRotationScale();  
      //火焰位置  
      //平移  
      trs.translation = Cesium.Cartesian3.fromElements(2.5, 4.0, 1.0, new Cesium.Cartesian3());  
      //旋转  
      trs.rotation = Cesium.Quaternion.fromHeadingPitchRoll(hpr, new Cesium.Quaternion());  
      return Cesium.Matrix4.fromTranslationRotationScale(trs, new Cesium.Matrix4());  
    }  
  
    var particleSystem = new Cesium.ParticleSystem({  
      image: './RasterImage/图片/fire.png',  
      startScale: 1.0,  //开始比例  
      endScale: 4.0,  //结束比例  
      particleLife: 1.0,   //粒子生命周期  
      speed: 5.0, //粒子速度  
      imageSize: new Cesium.Cartesian2(20, 20),   //粒子图形尺寸  
      emissionRate: 5.0,  //每秒发射粒子个数  
      lifetime: 16.0,   //粒子系统发射粒子的时间  
      modelMatrix: modelMatrix,  //将粒子系统从模型转换为世界坐标的4x4转换矩阵  
      emitterModelMatrix: computeEmitterModelMatrix() //在粒子系统局部坐标系内转换粒子系统发射器的4x4转换矩阵  
    })  
    viewer.scene.primitives.add(particleSystem);  
  
  </script>  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述
   在这里插入图片描述

11.粒子天气

常见的粒子特效还有雨、雪等粒子天气特效,下面使用Cesium粒子系统模拟天气特效,包括下雨天与下雪天两种情况。
(1)实现代码
  在这里插入图片描述

6_11_粒子天气.html
<!DOCTYPE html>
<html lang="en">  
  
<head>  
  <meta charset="utf-8" />  
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
  <meta name="description" content="Particle system fireworks.">  
  <meta name="cesium-sandcastle-labels" content="Beginner, Showcases">  
  <title>材质特效篇_粒子天气</title>  
  <link rel="stylesheet" href="./Build/Cesium/Widgets/widgets.css">  
  <script src="./Build/Cesium/Cesium.js"></script>  
  
</head>  
<style>  
  html,  
  body,  
  #cesiumContainer {  
    width: 100%;  
    height: 100%;  
    margin: 0;  
    padding: 0;  
    overflow: hidden;  
  }  
  
  .toolbar {  
    position: absolute;  
    top: 10px;  
    left: 20px;  
    background-color: rgb(0, 0, 0, 0);  
  }  
</style>  
  
<body>  
  
  <div id="cesiumContainer"></div>  
  <div class="toolbar">  
    <select id="dropdown" onchange="change()">  
      <option value="snow"></option>  
      <option value="rain"></option>  
      <option value="null">null</option>  
    </select>  
  </div>  
  
  <script>  
    Cesium.Ion.defaultAccessToken = '你的token';  
    var viewer = new Cesium.Viewer("cesiumContainer", {  
      animation: false, //是否显示动画工具  
      timeline: false,  //是否显示时间轴工具  
      fullscreenButton: false,  //是否显示全屏按钮工具  
      shouldAnimate: true,  //必须开启  
      terrainProvider: Cesium.createWorldTerrain(),  
    });  
  
    //粒子特效位置  
    var position = new Cesium.Cartesian3.fromDegrees(114.39664, 30.52052, 2000);  
    var modelMatrix = new Cesium.Matrix4.fromTranslation(position)  
  
    //模拟下雪天粒子特效常量定义  
    const snowRadius = 100000.0; //下雪的范围半径  
    const minimumSnowImageSize = new Cesium.Cartesian2(10, 10); //雪花最小尺寸  
    const maximumSnowImageSize = new Cesium.Cartesian2(20, 20); //雪花最大尺寸  
  
    //创建Cartesian3对象,用于在回调函数中实时更新粒子位置  
    var snowGravityScratch = new Cesium.Cartesian3();  
    //粒子更新回调函数  
    function snowUpdate (particle) {  
      //计算提供的笛卡尔坐标系的标准化形式  
      Cesium.Cartesian3.normalize(  
        particle.position,  //要标准化的笛卡尔坐标  
        snowGravityScratch  //结果存储对象  
      );  
      //将提供的笛卡尔分量乘以标准的标量  
      Cesium.Cartesian3.multiplyByScalar(  
        snowGravityScratch, //要缩放的笛卡尔坐标  
        //要与之相乘的标量,负值代表粒子位置下降即粒子从上往下落  
        Cesium.Math.randomBetween(-30.0, -300.0),  
        snowGravityScratch  //结果存储对象  
      );  
      //粒子位置根据snowGravityScratch变化  
      Cesium.Cartesian3.add(  
        particle.position,  
        snowGravityScratch,  
        particle.position  
      );  
    };  
  
    // 雨  
    const rainRadius = 100000.0; //下雨的范围半径  
    const rainImageSize = new Cesium.Cartesian2(20, 35); //15,30分别代表宽高  
  
    var rainGravityScratch = new Cesium.Cartesian3();  
    //粒子更新回调函数  
    function rainUpdate (particle) {  
      //计算提供的笛卡尔坐标系的标准化形式  
      Cesium.Cartesian3.normalize(  
        particle.position,  //要标准化的笛卡尔坐标  
        rainGravityScratch  //结果存储对象  
      );  
      //将提供的笛卡尔分量乘以标准的标量  
      Cesium.Cartesian3.multiplyByScalar(  
        rainGravityScratch,  //要缩放的笛卡尔坐标  
        -1000.0,             //要与之相乘的标量,雨比雪下落速度快的多 所以这个值负的多点  
        rainGravityScratch   //结果存储对象  
      );  
      //粒子位置根据rainGravityScratch变化  
      Cesium.Cartesian3.add(  
        particle.position,  
        rainGravityScratch,  
        particle.position  
      );  
    };  
  
    //粒子系统-雪配置项  
    var snowOption = {  
      modelMatrix: modelMatrix, //将粒子系统从模型转换为世界坐标的4x4转换矩阵。  
      lifetime: 15.0, //粒子系统发射粒子的时间(以秒为单位)  
      emitter: new Cesium.SphereEmitter(snowRadius),  //该系统的粒子发射器  
      startScale: 0.5,  //在粒子寿命开始时应用于粒子图像的初始比例  
      endScale: 1.0,  //在粒子寿命结束时应用于粒子图像的最终比例。  
      image: "./RasterImage/图片/snowflake_particle.png", //粒子贴图  
      emissionRate: 7000.0, //每秒要发射的粒子数  
      startColor: Cesium.Color.WHITE.withAlpha(0.0),  //粒子在其生命初期的颜色。  
      endColor: Cesium.Color.WHITE.withAlpha(1.0),  //粒子寿命结束时的颜色。  
      minimumImageSize: minimumSnowImageSize, //设置宽度的最小范围,以高度为单位,在该范围上可以随机缩放粒子图像的尺寸(以像素为单位)  
      maximumImageSize: maximumSnowImageSize, //设置最大宽度边界,以高度为单位,在该边界以下可以随机缩放粒子图像的尺寸(以像素为单位)  
      updateCallback: snowUpdate, //每帧都要调用一次回调函数以更新粒子  
    }  
  
    //粒子系统-雨配置项  
    var rainOption = {  
      modelMatrix: modelMatrix,//将粒子系统从模型转换为世界坐标的4x4转换矩阵。  
      lifetime: 15.0,//粒子系统发射粒子的时间(以秒为单位)  
      emitter: new Cesium.SphereEmitter(rainRadius),//该系统的粒子发射器  
      startScale: 1.0,//在粒子寿命开始时应用于粒子图像的初始比例  
      endScale: 0.0,//在粒子寿命结束时应用于粒子图像的最终比例。  
      image: "./RasterImage/图片/circular_particle.png",//粒子贴图  
      emissionRate: 9000.0,//每秒要发射的粒子数  
      startColor: new Cesium.Color(1, 1, 1, 0.0),//粒子在其生命初期的颜色。  
      endColor: new Cesium.Color(1.0, 1.0, 1.0, 0.98),//粒子寿命结束时的颜色。  
      imageSize: rainImageSize,//粒子贴图尺寸  
      updateCallback: rainUpdate,//每帧都要调用一次回调函数以更新粒子  
    }  
  
    //默认下雪天  
    viewer.scene.primitives.add(new Cesium.ParticleSystem(snowOption));  
  
    //下拉框回调函数  
    var dropdown = document.getElementById('dropdown');  
    function change() {  
      switch (dropdown.value) {  
        case 'snow':  
          viewer.scene.primitives.removeAll();  
          viewer.scene.primitives.add(new Cesium.ParticleSystem(snowOption));  
          break;  
        case 'rain':  
          viewer.scene.primitives.removeAll();  
          viewer.scene.primitives.add(new Cesium.ParticleSystem(rainOption));  
          break;  
        case 'null':  
          viewer.scene.primitives.removeAll();  
          break;  
        default:  
          break;  
      }  
    }  
  
    //设置相机视角  
    /* viewer.scene.camera.setView({  
      destination:  
        Cesium.Cartesian3.fromDegrees(114.39664, 30.40052, 10000),  
      orientation: {  
        heading: 4.731089976107251,  
        pitch: -0.32003481981370063,  
      },  
    }) */  
  
    //设置相机初始位置  
    viewer.scene.camera.setView({  
      destination: new Cesium.Cartesian3(-2318006.190591779, 5016113.738321363,3239729.8052793955),  
      orientation: {  
        heading: 5.0433812878480655,  
        pitch: -0.25943108890985744,  
        roll: 0.000002292722656171975  
      },  
      duration: 0.0  
    });  
  
  </script>  
</body>  
  
</html>

(2)结果显示
  在这里插入图片描述
  在这里插入图片描述


cesium文章涉及数据

参考资料:
[1] 郭明强. 《WebGIS之Cesium三维软件开发》; 2023-04-01 [accessed 2024-01-27].
[2] WaqarLeaver. Cesium开源water材质和粒子效果示例代码研究; 2021-05-30 [accessed 2024-01-27].
[3] GIS兵墩墩. C2——cesium流动特效; 2020-11-04 [accessed 2024-01-27].
[4] 那那那那那么长的哲尘. Cesium实现流动线/动态纹理; 2024-01-11 [accessed 2024-01-27].

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1413858.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序(十八)组件通信(父传子)

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.组件属性变量的定义 2.组件属性变量的默认状态 3.组件属性变量的传递方法 解释一下为什么是父传子&#xff0c;因为组件是页面的一部分&#xff0c;数据是从页面传递到组件的&#xff0c;所以是父传子&#xf…

opencv#32 可分离滤波

滤波的可分离性 就是将一个线性滤波变成多个线性滤波&#xff0c;这里面具体所指的是变成x方向的线性滤波和y方向的线性滤波。无论先做x方向的滤波还是y方向滤波&#xff0c;两者的叠加结果是一致的&#xff0c;这个性质取决于滤波操作是并行的&#xff0c;也就是每一个图像在滤…

IS-IS:05 ISIS开销值和协议优先级

IS-IS 协议为路由器的每个 IS-IS 接口定义并维护了一个 level-1 开销值和一个 level-2开销值。开销值可以在接口上或者全局上手动配置&#xff0c;也可以使用 auto-cost自动计算确定。 修改接口cost&#xff1a; int g0/0/0 isis cost 50修改全局cost&#xff1a; isis cir…

如何用AI设计立体图标?

立体图标&#xff0c;顾名思义就是要有立体的效果的&#xff0c;有种3D的感觉。平面的图标已经渐渐满足不了人们的需求&#xff0c;立体图标将会越来越受欢迎&#xff0c;所以你一定要会制作立体图标才行。立体图标的设计同样可以用AI来做&#xff0c;下面就分享用AI制作立体图…

林浩然矩阵江湖历险记

林浩然矩阵江湖历险记 Lin Haoran’s Matrix Adventures 在那充满神秘色彩的矩阵世界里&#xff0c;林浩然面对的挑战是驯服一个具有六个个性元素的23矩阵——“小三儿”。这个矩阵由两行三列组成&#xff0c;每一个元素都像是棋盘上的一枚棋子&#xff0c;它们紧密排列在一起&…

计算机机器视觉——构建数字识别项目(OpenCV入门实践)

项目简介 ---我们的项目是使用OpenCV来识别图片中的数字。我们将使用一个预训练的模型&#xff0c;将图片中的数字转换为对应的数字标签。为了实现这个功能&#xff0c;我们需要完成以下步骤&#xff1a; ——安装必要的软件包和库 ——————准备数据集 ————————训…

TensorFlow2实战-系列教程1:回归问题预测

&#x1f9e1;&#x1f49b;&#x1f49a;TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 1、环境测试 import tensorflow as tf import numpy as np tf.__version__打印结果 ‘…

【开源】基于JAVA的房屋出售出租系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 房屋销售模块2.2 房屋出租模块2.3 预定意向模块2.4 交易订单模块 三、系统展示四、核心代码4.1 查询房屋求租单4.2 查询卖家的房屋求购单4.3 出租意向预定4.4 出租单支付4.5 查询买家房屋销售交易单 五、免责说明 一、摘…

前端怎么监听手机键盘是否弹起

摘要&#xff1a; 开发移动端中&#xff0c;经常会遇到一些交互需要通过判断手机键盘是否被唤起来做的&#xff0c;说到判断手机键盘弹起和收起&#xff0c;应该都知道&#xff0c;安卓和ios判断手机键盘是否弹起的写法是有所不同的&#xff0c;下面讨论总结一下两端的区别以及…

专业120+总分400+海南大学838信号与系统考研高分经验海大电子信息与通信

今年专业838信号与系统120&#xff0c;总分400&#xff0c;顺利上岸海南大学&#xff0c;这一年的复习起起伏伏&#xff0c;但是最后还是坚持下来的&#xff0c;吃过的苦都是值得&#xff0c;总结一下自己的复习经历&#xff0c;希望对大家复习有帮助。首先我想先强调一下专业课…

嵌入式学习第十一天

1.数组和指针的关系: 1.一维数组和指针的关系: int a[5] {1, 2, 3, 4, 5}; int *p NULL; p &a[0]; p a; 数组的数组名a是指向数组第一个元素的一个指针常量 a &a[0] a 的类型可以理解为 int * 有两种情况除…

《动手学深度学习(PyTorch版)》笔记4.4

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过。…

SpringBoot自定义全局异常处理器

文章目录 一、介绍二、实现1. 定义全局异常处理器2. 自定义异常类 三、使用四、疑问 一、介绍 Springboot框架提供两个注解帮助我们十分方便实现全局异常处理器以及自定义异常。 ControllerAdvice 或 RestControllerAdvice&#xff08;推荐&#xff09;ExceptionHandler 二、…

软件设计师——计算机网络(四)

&#x1f4d1;前言 本文主要是【计算机网络】——软件设计师——计算机网络的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1…

架构整洁之道-设计原则

4 设计原则 通常来说&#xff0c;要想构建一个好的软件系统&#xff0c;应该从写整洁的代码开始做起。这就是SOLID设计原则所要解决的问题。 SOLID原则的主要作用就是告诉我们如何将数据和函数组织成为类&#xff0c;以及如何将这些类链接起来成为程序。请注意&#xff0c;这里…

chroot: failed to run command ‘/bin/bash’: No such file or directory

1. 问题描述及原因分析 在busybox的环境下&#xff0c;执行 cd rootfs chroot .报错如下&#xff1a; chroot: failed to run command ‘/bin/bash’: No such file or directory根据报错应该rootfs文件系统中缺少/bin/bash&#xff0c;进入查看确实默认是sh&#xff0c;换成…

vertica10.0.0单点安装_ubuntu18.04

ubuntu的软件包格式为deb&#xff0c;而rpm格式的包归属于红帽子Red Hat。 由于项目一直用的vertica-9.3.1-4.x86_64.RHEL6.rpm&#xff0c;未进行其他版本适配&#xff0c;而官网又下载不到vertica-9.3.1-4.x86_64.deb&#xff0c;尝试通过alian命令将rpm转成deb&#xff0c;但…

【GitHub项目推荐--30 天学会XXX】【转载】

30 天学会 React 这个项目是《30 天 React 挑战》&#xff0c;是在 30 天内学习 React 的分步指南。它需要你学习 React 之前具备 HTML、CSS 和 JavaScript 知识储备。 除了 30 天学会 React&#xff0c;开发者还发布过 30 天学会 JavaScript 等项目。 开源地址&#xff1a;…

解读BEVFormer,新一代CV工作的基石

文章出处 BEVFormer这篇文章很有划时代的意义&#xff0c;改变了许多视觉领域工作的pipeline[2203.17270] BEVFormer: Learning Birds-Eye-View Representation from Multi-Camera Images via Spatiotemporal Transformers (arxiv.org)https://arxiv.org/abs/2203.17270 BEV …

数论Leetcode204. 计数质数、Leetcode858. 镜面反射、Leetcode952. 按公因数计算最大组件大小

Leetcode204. 计数质数 题目 给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 代码 class Solution:def countPrimes(self, n: int) -> int:if n < 2:return 0prime_arr [1 for _ in range(n)]prime_arr[0], prime_arr[1] 0, 0ls list()for i in…