import * as Cesium from "cesium"; import { WindLayer } from "../../kLayer/index.js"; /** * 创建风场 * @param {*} viewer * @param {*} controlColor */ async function createWind(viewer, windData, controlColor) { // 加载风场 // Create wind layer with options const windLayer = await new WindLayer(viewer, windData, { // ==================== 粒子相关 ==================== particlesTextureSize: 1500, // 粒子纹理尺寸,决定粒子总数(size * size) particleHeight: 2500, // 粒子高度(相对于地表) lineWidth: { min: 1.5, max: 2.5 }, // 粒子尾迹宽度范围 lineLength: { min: 300, max: 600 }, // 粒子尾迹长度范围 speedFactor: 1.0, // 粒子速度倍数 dropRate: 0.003, // 粒子掉落率 dropRateBump: 0.001, // 慢粒子额外掉落率 dynamic: true, // 是否启用动态动画 flipY: false, // 是否翻转 Y 坐标(视数据情况而定) // ==================== 风场数据相关 ==================== domain: undefined, // 可选:风速范围 domain = [min, max] displayRange: undefined, // 可选:可视化速度范围 displayRange = [min, max] uMax: undefined, // 可选:u 方向速度最大值(用于归一化) vMax: undefined, // 可选:v 方向速度最大值(用于归一化) // ==================== 颜色相关 ==================== colors: [ // 粒子颜色映射,可按速度渐变 "rgb(255,255,0)", "rgb(255,0,0)", "rgb(0,0,255)", // "rgb(0,255,255)", ], // ==================== 位置和范围 ==================== bounds: undefined, // 可选:指定风场边界 [xmin, ymin, xmax, ymax] projection: undefined, // 可选:投影函数,用于坐标转换 extent: undefined, // 可选:覆盖区域(Cesium.Rectangle) // ==================== 粒子纹理/形状 ==================== particleTexture: controlColor, // 可选:自定义粒子纹理 fadeOpacity: 0.996, // 粒子尾迹渐隐率(0~1,越小越快消失) // ==================== 性能优化 ==================== maxParticles: 50000000, // 最大粒子数量(默认由 particlesTextureSize 决定) skipRate: 1, // 粒子更新跳帧率(提升性能) // ==================== 其他 ==================== animation: true, // 是否开启动画 autoUpdate: true, // 是否自动更新风场 }); return windLayer; } /** * @description 相机飞行动画 * @param {Object} options 配置对象 * @param {Cesium.Viewer} options.viewer Cesium实例 * @param {Array} options.points 动画路径数组 * - lon {number} 经度 * - lat {number} 纬度 * - height {number} 高度 * - heading {number} 朝向角度(度) * - pitch {number} 俯仰角度(度) * - roll {number} 翻滚角度(度) * - duration {number} 飞行动画时长(秒) * @param {Function} [options.easingFunction] 缓动函数 */ function flyCameraPath({ viewer, points, easingFunction = Cesium.EasingFunction.LINEAR_NONE, }) { if (!viewer || !points || points.length === 0) { console.error("flyCameraPath: 参数错误,viewer或points缺失"); return; } // 生成动画步骤 const steps = []; for (let i = 0; i < points.length; i++) { const p = points[i]; // 基础飞行动画 steps.push({ destination: Cesium.Cartesian3.fromDegrees(p.lon, p.lat, p.height), duration: p.duration || 3, orientation: { heading: Cesium.Math.toRadians(p.heading || 0), pitch: Cesium.Math.toRadians(p.pitch || -90), roll: Cesium.Math.toRadians(p.roll || 0), }, maximumHeight: p.maximumHeight || p.height, }); } // 执行动画(递归) function runStep(index) { if (index >= steps.length) return; const step = steps[index]; viewer.camera.flyTo({ ...step, easingFunction, complete: () => runStep(index + 1), }); } runStep(0); } /** * 添加降雪效果 * @param {Viewer} viewer - 视图对象,通常是一个包含 DOM 元素和其他相关信息的对象。 * @see {@link https://sandcastle.cesium.com/?src=Particle%20System%20Weather.html|Cesium 天气示例} */ function addPrimitivesSnowCesium(viewer) { const scene = viewer.scene; // snow const snowParticleSize = 12.0; const snowRadius = 100000.0; const minimumSnowImageSize = new Cesium.Cartesian2( snowParticleSize, snowParticleSize ); const maximumSnowImageSize = new Cesium.Cartesian2( snowParticleSize * 2.0, snowParticleSize * 2.0 ); let snowGravityScratch = new Cesium.Cartesian3(); const snowUpdate = function (particle) { snowGravityScratch = Cesium.Cartesian3.normalize( particle.position, snowGravityScratch ); Cesium.Cartesian3.multiplyByScalar( snowGravityScratch, Cesium.Math.randomBetween(-30.0, -300.0), snowGravityScratch ); particle.velocity = Cesium.Cartesian3.add( particle.velocity, snowGravityScratch, particle.velocity ); const distance = Cesium.Cartesian3.distance( scene.camera.position, particle.position ); if (distance > snowRadius) { particle.endColor.alpha = 0.0; } else { particle.endColor.alpha = 1.0 / (distance / snowRadius + 0.1); } }; const data = scene.primitives.add( new Cesium.ParticleSystem({ modelMatrix: new Cesium.Matrix4.fromTranslation(scene.camera.position), minimumSpeed: -1.0, maximumSpeed: 0.0, lifetime: 15.0, emitter: new Cesium.SphereEmitter(snowRadius), startScale: 0.5, endScale: 1.0, image: "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, }) ); scene.skyAtmosphere.hueShift = -0.8; scene.skyAtmosphere.saturationShift = -0.7; scene.skyAtmosphere.brightnessShift = -0.33; scene.fog.density = 0.001; scene.fog.minimumBrightness = 0.8; return data; } /** * 添加降雨效果 * @param {Viewer} viewer - 视图对象,通常是一个包含 DOM 元素和其他相关信息的对象。 * @see {@link https://sandcastle.cesium.com/?src=Particle%20System%20Weather.html|Cesium 天气示例} */ function addPrimitivesRianCesium(viewer, size = 4) { // 先清空其他效果 const scene = viewer.scene; // rain const rainParticleSize = 15.0; const rainRadius = 100000.0; const rainImageSize = new Cesium.Cartesian2( rainParticleSize, rainParticleSize * size //图片大小 ); let rainGravityScratch = new Cesium.Cartesian3(); const rainUpdate = function (particle) { rainGravityScratch = Cesium.Cartesian3.normalize( particle.position, rainGravityScratch ); rainGravityScratch = Cesium.Cartesian3.multiplyByScalar( rainGravityScratch, -1050.0, rainGravityScratch ); particle.position = Cesium.Cartesian3.add( particle.position, rainGravityScratch, particle.position ); const distance = Cesium.Cartesian3.distance( scene.camera.position, particle.position ); if (distance > rainRadius) { particle.endColor.alpha = 0.0; } else { particle.endColor.alpha = Cesium.Color.BLUE.alpha / (distance / rainRadius + 0.1); } }; const data = scene.primitives.add( new Cesium.ParticleSystem({ modelMatrix: new Cesium.Matrix4.fromTranslation(scene.camera.position), speed: -1.0, lifetime: 15.0, emitter: new Cesium.SphereEmitter(rainRadius), startScale: 1.0, endScale: 0.0, image: "circular_particle.png", emissionRate: 9000.0, startColor: new Cesium.Color(0.27, 0.5, 0.7, 0.0), endColor: new Cesium.Color(0.27, 0.5, 0.7, 0.98), imageSize: rainImageSize, updateCallback: rainUpdate, }) ); scene.skyAtmosphere.hueShift = -0.97; scene.skyAtmosphere.saturationShift = 0.25; scene.skyAtmosphere.brightnessShift = -0.4; scene.fog.density = 0.00025; scene.fog.minimumBrightness = 0.01; return data; } // 加载glb模型 function addGlbModel(viewer) { if (!viewer || !devJson || !Array.isArray(devJson)) return; devJson.forEach((item) => { const { name, position } = item; if (!position || position.length < 3) return; const [lon, lat, height] = position; const modal = viewer.entities.add({ name: name, position: Cesium.Cartesian3.fromDegrees(lon, lat, height), model: { uri: "/modal/turbine.glb", // 模型路径 scale: 0.1, // 缩放比例 minimumPixelSize: 64, // 保证远距离也能显示 maximumScale: 200, // 最大缩放 runAnimations: true, // 如果模型有动画,自动播放 // 关键:直接调整模型的亮度和光照相关属性 color: Cesium.Color.WHITE, // 可以尝试给模型一个整体颜色乘数 colorBlendMode: Cesium.ColorBlendMode.MIX, // 颜色混合模式 silhouetteColor: Cesium.Color.WHITE, // 轮廓颜色 // 最有效的属性:直接放大光源的影响 luminanceAtZenith: 1.0, // 天顶亮度(增大此值可使模型更亮) }, label: { show: true, scale: 1, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, fillColor: Cesium.Color.WHITE, text: name, showBackground: true, backgroundColor: Cesium.Color.BLACK.withAlpha(0.5), pixelOffset: new Cesium.Cartesian2(0, -420), distanceDisplayCondition: new Cesium.DistanceDisplayCondition( 100, 10000 ), }, }); console.log(`加载模型`, modal); }); } /** * 绑定右键事件,输出当前相机完整参数 * @param {Cesium.Viewer} viewer */ function bindRightClickLogCamera(viewer) { const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction(function () { const camera = viewer.camera; const pos = camera.positionCartographic; const lon = Cesium.Math.toDegrees(pos.longitude).toFixed(6); const lat = Cesium.Math.toDegrees(pos.latitude).toFixed(6); const height = pos.height.toFixed(2); const heading = Cesium.Math.toDegrees(camera.heading).toFixed(2); const pitch = Cesium.Math.toDegrees(camera.pitch).toFixed(2); const roll = Cesium.Math.toDegrees(camera.roll).toFixed(2); console.log( `相机参数:经度=${lon}, 纬度=${lat}, 高度=${height}, heading=${heading}, pitch=${pitch}, roll=${roll}` ); }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); return handler; // 可以用 handler.destroy() 解绑 } export { createWind, flyCameraPath, addGlbModel, addPrimitivesSnowCesium, addPrimitivesRianCesium, bindRightClickLogCamera, };