Cesium实现水闸放水以及水面流速效果

不是cesium大佬,只是刚好需要我做这块就浅看了一下,用火星Mars3d的官方例子实现了一下,

项目是要用cesium,改的时候遇到蛮多问题

 添加河流的面,宽度这块设置的是固定的,河流每段的宽度差不多(如果是很不规则的话会有问题)河流旁边有地形的话,还是能看的

  let material = new FlowLineCustomMaterial({
    duration: 20000,
    trailImage: polysoil,
    repeat: new Cartesian2(50.0, 1.0)
  })
  viewer.entities.add({
    id: properties.id,
    properties: properties,
    polyline: {
      positions: positions,
      width: 50,
      material: material,//Color.fromCssColorString(color).withAlpha(alpha),
      // clampToGround:true,
      // scale: 0.1,
      scaleByDistance: new NearFarScalar(300, 1, 500000, 0.4), //设置随图缩放距离和比例
      distanceDisplayCondition: new DistanceDisplayCondition(0, 90000), //设置可见距离 10000米可见
    },
  })

FlowLineCustomMaterial的代码

import {defaultValue,Event,createPropertyDescriptor,defined,Property,Color,Cartesian2,Material} from "cesium";

import color1 from '@/assets/images/material/color1.png';

export class FlowLineCustomMaterial {
    /**
     * @param options(color 颜色(暂时不叠加),duration 重复时间,trailImage流动图片,repeat 重复次数(Cartesian2) )
     */
    constructor(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT)

        this._definitionChanged = new Event()
        this._color = undefined
        this._colorSubscription = undefined
        this._time = performance.now()
        this._repeat = undefined;

        this.color = options.color
        this.duration = options.duration
        this.trailImage = options.trailImage
        this.repeat = options.repeat
    }
}

Object.defineProperties(FlowLineCustomMaterial.prototype, {
    isConstant: {
        get: function () {
            return false
        },
    },

    definitionChanged: {
        get: function () {
            return this._definitionChanged
        },
    },

    color: createPropertyDescriptor('color'),
    repeat: createPropertyDescriptor('repeat')
})

FlowLineCustomMaterial.prototype.getType = function () {
    return 'PolylineTrail'
}

FlowLineCustomMaterial.prototype.getValue = function (time, result) {
    if (!defined(result)) {
        result = {}
    }

    result.color = Property.getValueOrClonedDefault(
        this._color,
        time,
        Color.WHITE,
        result.color
    )
    result.image = this.trailImage
    result.time =
        ((performance.now() - this._time) % this.duration) / this.duration
    result.repeat = Property.getValueOrClonedDefault(this._repeat, time,new Cartesian2(1.0,1.0), result.repeat);

    return result
}

FlowLineCustomMaterial.prototype.equals = function (other) {
    return (
        this === other ||
        (other instanceof FlowLineCustomMaterial &&
            Property.equals(this._color, other._color))
    )
}

Material.PolylineTrailType = 'PolylineTrail'
Material.PolylineTrailImage = color1
Material.PolylineTrailSource =
    'czm_material czm_getMaterial(czm_materialInput materialInput)\n\
                             {\n\
                               czm_material material = czm_getDefaultMaterial(materialInput);\n\
                               vec2 st = repeat * materialInput.st;\n\
                               vec4 colorImage = texture(image, vec2(fract(st.s - time), st.t));\n\
                               material.alpha = 0.8;\n\
                               material.diffuse = colorImage.rgb;\n\
                               return material;\n\
                             }'
// material.alpha = colorImage.a * color.a;?
// vec2 st = materialInput.st;//不设置重复
// material.diffuse = (colorImage.rgb+color.rgb)/2.0;//推按叠加color颜色


Material._materialCache.addMaterial(Material.PolylineTrailType, {
    fabric: {
        type: Material.PolylineTrailType,
        uniforms: {
            color: new Color(1.0, 0.0, 0.0, 0.5),
            image: Material.PolylineTrailImage,
            time: 0,
            repeat: new Cartesian2(1.0, 1.0)
        },
        source: Material.PolylineTrailSource,
    },
    translucent: function () {
        return true
    },
})

河面效果

阀门放水(是用的喷泉的例子效果,后面项目中要阀门那块是分流效果这块就注释了)

//放水粒子效果
let dynamicWaterPrimitive = DynamicWaterPrimitive.getInstance({viewer: globalProperties.$viewer})
dynamicWaterPrimitive.createFountainSingle({
        lng: 105.65447430631001,
        lat: 37.42931914904636, 
        height: 1172.0713235223845,
        heading: 300
})

DynamicWaterPrimitive

import {
  PrimitiveCollection,
  Primitive,
  GeometryInstance,
  PolygonHierarchy,
  EllipsoidSurfaceAppearance,
  Material,
  Cartesian3,
  ParticleSystem,
  Color,
  Cartesian2,
  CircleEmitter,
  HeadingPitchRoll,
  Transforms,
  PolygonGeometry,
  destroyObject,
} from "cesium";
import {nanoid} from 'nanoid'

import waterImage from "@/assets/images/material/water.png";
import waterNormals from "@/assets/images/material/waterNormals.jpg";
import waterGun from "@/assets/images/material/waterGun.png";
import waterdrink from "@/assets/images/material/waterdrink.png";
import DynamicHeightWaterPrimitive from "./DynamicHeightWaterPrimitive";

export default class DynamicWaterPrimitive{
  constructor(args) {
    this.primitives = new PrimitiveCollection();
    this.viewer = args.viewer;
    this.viewer.scene.primitives.add(this.primitives,0);//使用前先消除,确保仅有一个

    this.ParticleManager = {};

  };
}

DynamicWaterPrimitive.getInstance = function(args) {
  if (this.instance) {
    return this.instance;
  }
  if (typeof args === 'undefined' || typeof args.viewer === 'undefined') return  null;
  return this.instance = new DynamicWaterPrimitive(args);
}

DynamicWaterPrimitive.prototype.addDynamicWater = function(options) {
  this.primitives.add(this.createDynamicWater(options))
}

DynamicWaterPrimitive.prototype.createDynamicWater = function(options) {
  let {id = nanoid(),positionArray,height } = options;

  return  new Primitive({
    show: true,// 默认隐藏
    allowPicking: false,
    geometryInstances: new GeometryInstance({
      id: id,
      geometry: new PolygonGeometry({
        polygonHierarchy: new PolygonHierarchy(setPositionEC(positionArray,height)),
        extrudedHeight: 0,//注释掉此属性可以只显示水面
        perPositionHeight: true//注释掉此属性水面就贴地了
      })
    }),
    // 可以设置内置的水面shader
    appearance: new EllipsoidSurfaceAppearance({
      material: new Material({
        fabric: {
          type: 'Water',
          uniforms: {
            normalMap: waterNormals,
            frequency: 1000.0,
            animationSpeed: 0.01,
            amplitude: 10.0
          }
        }
      }),
      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.85;\n' +
        '#endif\n' +
        '}\n'
    })
  });
}

function setPositionEC(positons,height, ) {
  let waterc = []
  if (isNaN(Number(height))){
    return Cartesian3.fromDegreesArray(waterc)
  }

  for (let index = 0; index < positons.length; index++) {
    let position = positons[index]
    waterc.push(position)
    if ((index + 1 ) % 2 === 0 && index !== 0){
      waterc.push(height)
    }
  }
  return Cartesian3.fromDegreesArrayHeights(waterc)
}


DynamicWaterPrimitive.prototype.createDynamicHeightWater = function(options) {
  let {positions,height = 0,extrudedHeight = 0,targetHeight = 70,growNum = 0.05,grow,materialType} = options;

  this.primitives.add(new DynamicHeightWaterPrimitive({
    positions : positions,
    height : height,
    extrudedHeight : extrudedHeight,
    targetHeight : targetHeight,
    grow:grow,
    materialType:materialType,
    growNum:growNum,
    loadEndCallback:(primitive) => {
      // destroyObject(primitive)
      // this.primitives.remove(primitive);
      // this.primitives.add(this.addDynamicWater({
      //   positionArray:positions,height:targetHeight
      // }))
    }
  }))
}

DynamicWaterPrimitive.prototype.createFountain = function(options) {
  let {positionArray,} = options;

  if (typeof positionArray !== 'undefined' && positionArray.length > 0){
    positionArray.forEach(item => {
      this.primitives.add(this.createFountainParticleSystem({
        id:nanoid(),lng: item.lng, lat:  item.lat, height: item.height,heading: item.heading,roll: item.roll
      }))
    })
  }
}

DynamicWaterPrimitive.prototype.createFountainSingle = function(options) {
  options = {id:nanoid(),...options}
  this.primitives.add(this.createFountainParticleSystem(options))
}


//喷泉粒子
DynamicWaterPrimitive.prototype.createFountainParticleSystem = function (options) {
  let {lng, lat, height,heading = 0.0,pitch = 90,roll = 0.0,id} = options;
  let self = this;
  const gravityScratch = new Cartesian3();

  //重力参数
  function applyGravity(p, dt) {
    const position = p.position;

    Cartesian3.normalize(position, gravityScratch);
    Cartesian3.multiplyByScalar(
      gravityScratch,
      -8 * dt,
      gravityScratch
    );

    p.velocity = Cartesian3.add(
      p.velocity,
      gravityScratch,
      p.velocity
    );
  }

  let ps = new ParticleSystem({
    image: waterdrink,
    startColor: Color.LIGHTYELLOW.withAlpha(0.5),
    endColor: Color.LIGHTYELLOW.withAlpha(0.3),
    startScale: 2.0,
    endScale: 10.0,
    particleLife: 5.0,
    minimumSpeed: 10.0,
    maximumSpeed: 14.0,
    minimumParticleLife:1,
    maximumParticleLife:2,
    imageSize: new Cartesian2(2, 2),
    emissionRate: 30.0,
    lifetime: 12.0,
    emitter: new CircleEmitter(0.2),
    sizeInMeters: true,
    modelMatrix: Transforms.headingPitchRollToFixedFrame(Cartesian3.fromDegrees(lng, lat, height), HeadingPitchRoll.fromDegrees(heading, pitch, roll)),
    updateCallback: applyGravity,
  })
  if (!self.ParticleManager[id]){
    self.ParticleManager[id] = {
      options:options,
      particleSystem:ps,
    }
  }else if (!self.ParticleManager[id].particleSystem){
    self.ParticleManager[id].particleSystem = ps;
  }
  return ps
}


DynamicWaterPrimitive.prototype.clearParticle = function() {
  for(let key in this.ParticleManager){
    let manager = this.ParticleManager[key];
    this.primitives.remove(manager.particleSystem);
    this.ParticleManager[key].particleSystem = null;
  }
}

DynamicWaterPrimitive.prototype.addParticleFromCache = function() {
  for(let key in this.ParticleManager){
    let manager = this.ParticleManager[key];
    if (!manager.particleSystem){
      this.primitives.add(this.createFountainParticleSystem(manager.options))
    }
  }
}


DynamicWaterPrimitive.prototype.clearAll = function(options) {
  this.clearParticle()
  this.primitives.removeAll()
}

DynamicWaterPrimitive.prototype.destory = function(options) {
  this.viewer.scene.primitives.remove(this.primitives)
  this.instance = null;
}

 放个例子的效果图吧

说实话,效果一般般,没有火星的效果好看,没有想到更好的解决方案就先这样了~~

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要使用Cesium实现水闸放水效果,可以使用Cesium的ParticleSystem对象和WaterMaterial材质,结合场景中的水面模型来实现。以下是示例代码: ```javascript // 加载水面模型 var waterSurface = viewer.scene.primitives.add(Cesium.Model.fromGltf({ url: 'path/to/water-surface.gltf', modelMatrix: Cesium.Matrix4.multiply( Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(lon, lat)), Cesium.Matrix4.fromTranslation(new Cesium.Cartesian3(0, 0, 0)), new Cesium.Matrix4() ), scale: 100 })); // 定义水闸放水效果的粒子系统 var particleSystem = new Cesium.ParticleSystem({ image: 'path/to/particle.png', startColor: new Cesium.Color(1.0, 1.0, 1.0, 1.0), endColor: new Cesium.Color(1.0, 1.0, 1.0, 0.0), particleLife: 5.0, speed: 20.0, emissionRate: 1000.0, startScale: 1.0, endScale: 0.0, imageSize: new Cesium.Cartesian2(10, 10), emitter: new Cesium.CircleEmitter(0.2), emitterModelMatrix: Cesium.Matrix4.IDENTITY }); // 添加水闸放水效果的材质 waterSurface.readyPromise.then(function(model) { var node = model.getNode('WaterSurface'); if (Cesium.defined(node)) { var waterMaterial = new Cesium.WaterMaterial({ baseWaterColor: new Cesium.Color.fromCssColorString('#003366'), normalMap: 'path/to/normal-map.png', frequency: 100.0, animationSpeed: 0.01, amplitude: 0.1, specularIntensity: 0.5 }); node.material = waterMaterial; } }); // 将水闸放水效果的粒子系统添加到场景中 viewer.scene.primitives.add(particleSystem); // 绑定水闸放水效果的粒子系统到水面模型上 particleSystem.emitter = new Cesium.MeshEmitter(waterSurface, 'WaterSurface'); particleSystem.emitter.particleRadius = 0.05; particleSystem.emitter.minEmitTime = 0.1; particleSystem.emitter.maxEmitTime = 0.2; particleSystem.emitter.minInitialSpeed = -2.0; particleSystem.emitter.maxInitialSpeed = -5.0; particleSystem.emitter.minLife = 1.0; particleSystem.emitter.maxLife = 2.0; particleSystem.emitter.emitRate = 50.0; ``` 在上面的示例代码中,我们首先加载水面模型,并定义了一个粒子系统,用于模拟水闸放水效果。接着,我们添加了WaterMaterial材质,将其绑定到水面模型上,并设置了一些参数,用于调整材质的效果。最后,我们将粒子系统添加到场景中,并通过MeshEmitter将其绑定到水面模型上,设置了一些参数,用于调整粒子系统的效果。 需要注意的是,上述代码中的水面模型、粒子图片、法线贴图等资源需要根据实际情况进行替换或调整,并且水面模型需要包含一个名为“WaterSurface”的节点,用于绑定WaterMaterial材质。另外,上述代码只是一个简单示例,实际应用中还需要考虑一些细节问题,如水闸放水的位置、方向、流速等参数的调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

姗姗的鱼尾喵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值