mirror of
				https://github.com/jiawanlong/Cesium-Examples.git
				synced 2025-11-04 09:14:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			416 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			416 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const Common = `
 | 
						|
        uniform sampler2D heightMap;
 | 
						|
        uniform float heightScale;
 | 
						|
        uniform float maxElevation;
 | 
						|
        uniform float minElevation;
 | 
						|
        uniform sampler2D iChannel0;
 | 
						|
        uniform float iTime;
 | 
						|
 | 
						|
        uniform float coast2water_fadedepth;
 | 
						|
        uniform float large_waveheight; // change to adjust the "heavy" waves
 | 
						|
        uniform float large_wavesize;  // factor to adjust the large wave size
 | 
						|
        uniform float small_waveheight;  // change to adjust the small random waves
 | 
						|
        uniform float small_wavesize;   // factor to ajust the small wave size
 | 
						|
        uniform float water_softlight_fact;  // range [1..200] (should be << smaller than glossy-fact)
 | 
						|
        uniform float water_glossylight_fact; // range [1..200]
 | 
						|
        uniform float particle_amount;
 | 
						|
        uniform float WATER_LEVEL; // Water level (range: 0.0 - 2.0)
 | 
						|
        vec3 watercolor = vec3(0.0, 0.60, 0.66); // 'transparent' low-water color (RGB)
 | 
						|
        vec3 watercolor2 = vec3(0.0,0.0,0.5); // deep-water color (RGB, should be darker than the low-water color)
 | 
						|
        vec3 water_specularcolor = vec3(1.3, 1.3, 0.9);    // specular Color (RGB) of the water-highlights
 | 
						|
        vec3 light;
 | 
						|
 | 
						|
        // calculate random value
 | 
						|
        float hash(float n) {
 | 
						|
            return fract(sin(n) * 43758.5453123);
 | 
						|
        }
 | 
						|
 | 
						|
        // 2d noise function
 | 
						|
        float noise1(in vec2 x) {
 | 
						|
            vec2 p = floor(x);
 | 
						|
            vec2 f = smoothstep(0.0, 1.0, fract(x));
 | 
						|
            float n = p.x + p.y * 57.0;
 | 
						|
            return mix(mix(hash(n + 0.0), hash(n + 1.0), f.x), mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y);
 | 
						|
        }
 | 
						|
 | 
						|
        float noise(vec2 p) {
 | 
						|
            return textureLod(iChannel0, p * vec2(1. / 256.), 0.0).x;
 | 
						|
        }
 | 
						|
 | 
						|
        float height_map(vec2 p) {
 | 
						|
            float f = texture(heightMap,p).r;
 | 
						|
            return clamp(f, 0., 10.);
 | 
						|
        }
 | 
						|
 | 
						|
        const mat2 m = mat2(0.72, -1.60, 1.60, 0.72);
 | 
						|
 | 
						|
        float water_map(vec2 p, float height) {
 | 
						|
            vec2 p2 = p * large_wavesize;
 | 
						|
            vec2 shift1 = 0.001 * vec2(iTime * 160.0 * 2.0, iTime * 120.0 * 2.0);
 | 
						|
            vec2 shift2 = 0.001 * vec2(iTime * 190.0 * 2.0, -iTime * 130.0 * 2.0);
 | 
						|
 | 
						|
        // coarse crossing 'ocean' waves...
 | 
						|
            float f = 0.6000 * noise(p);
 | 
						|
            f += 0.2500 * noise(p * m);
 | 
						|
            f += 0.1666 * noise(p * m * m);
 | 
						|
            float wave = sin(p2.x * 0.622 + p2.y * 0.622 + shift2.x * 4.269) * large_waveheight * f * height * height;
 | 
						|
 | 
						|
            p *= small_wavesize;
 | 
						|
            f = 0.;
 | 
						|
            float amp = 1.0, s = .5;
 | 
						|
            for(int i = 0; i < 9; i++) {
 | 
						|
                p = m * p * .947;
 | 
						|
                f -= amp * abs(sin((noise(p + shift1 * s) - .5) * 2.));
 | 
						|
                amp = amp * .59;
 | 
						|
                s *= -1.329;
 | 
						|
            }
 | 
						|
 | 
						|
            return wave + f * small_waveheight;
 | 
						|
        }
 | 
						|
 | 
						|
        float nautic(vec2 p) {
 | 
						|
            p *= 18.;
 | 
						|
            float f = 0.;
 | 
						|
            float amp = 1.0, s = .5;
 | 
						|
            for(int i = 0; i < 3; i++) {
 | 
						|
                p = m * p * 1.2;
 | 
						|
                f += amp * abs(smoothstep(0., 1., noise(p + iTime * s)) - .5);
 | 
						|
                amp = amp * .5;
 | 
						|
                s *= -1.227;
 | 
						|
            }
 | 
						|
            return pow(1. - f, 5.);
 | 
						|
        }
 | 
						|
 | 
						|
        float particles(vec2 p) {
 | 
						|
            p *= 200.;
 | 
						|
            float f = 0.;
 | 
						|
            float amp = 1.0, s = 1.5;
 | 
						|
            for(int i = 0; i < 3; i++) {
 | 
						|
                p = m * p * 1.2;
 | 
						|
                f += amp * noise(p + iTime * s);
 | 
						|
                amp = amp * .5;
 | 
						|
                s *= -1.227;
 | 
						|
            }
 | 
						|
            return pow(f * .35, 7.) * particle_amount;
 | 
						|
        }
 | 
						|
 | 
						|
        float test_shadow(vec2 xy, float height) {
 | 
						|
            vec3 r0 = vec3(xy, height);
 | 
						|
            vec3 rd = normalize(light - r0);
 | 
						|
 | 
						|
            float hit = 1.0;
 | 
						|
            float t = 0.001;
 | 
						|
            for(int j = 1; j < 25; j++) {
 | 
						|
                vec3 p = r0 + t * rd;
 | 
						|
                float h = height_map(p.xy);
 | 
						|
                float height_diff = p.z - h;
 | 
						|
                if(height_diff < 0.0) {
 | 
						|
                    return 0.0;
 | 
						|
                }
 | 
						|
                t += 0.01 + height_diff * .02;
 | 
						|
                hit = min(hit, 2. * height_diff / t); // soft shaddow   
 | 
						|
            }
 | 
						|
            return hit;
 | 
						|
        }
 | 
						|
`;
 | 
						|
 | 
						|
export default class Erosion extends Cesium.Primitive {
 | 
						|
  constructor(options) {
 | 
						|
    super();
 | 
						|
    this.viewer = options.viewer;
 | 
						|
    this.extent = options.extent;
 | 
						|
    this.maxElevation = options.maxElevation;
 | 
						|
    this.minElevation = options.minElevation;
 | 
						|
    this.heightMap = options.canvas;
 | 
						|
    this.noise = options.noise;
 | 
						|
 | 
						|
    this.coast2water_fadedepth = 0.1;
 | 
						|
    this.large_waveheight = 0.5; // change to adjust the "heavy" waves
 | 
						|
    this.large_wavesize = 4; // factor to adjust the large wave size
 | 
						|
    this.small_waveheight = 0.9; // change to adjust the small random waves
 | 
						|
    this.small_wavesize = 0.12; // factor to ajust the small wave size
 | 
						|
    this.water_softlight_fact = 36; // range [1..200] (should be << smaller than glossy-fact)
 | 
						|
    this.water_glossylight_fact = 120; // range [1..200]
 | 
						|
    this.particle_amount = 70;
 | 
						|
    this.WATER_LEVEL = 0.34;
 | 
						|
    this._showLines = false;
 | 
						|
 | 
						|
    this.resolution = Cesium.defaultValue(
 | 
						|
      options.resolution,
 | 
						|
      new Cesium.Cartesian2(1024, 1024)
 | 
						|
    );
 | 
						|
  }
 | 
						|
  createCommand(context) {
 | 
						|
    const rectangle = new Cesium.RectangleGeometry({
 | 
						|
      ellipsoid: Cesium.Ellipsoid.WGS84,
 | 
						|
      rectangle: Cesium.Rectangle.fromDegrees(...this.extent),
 | 
						|
      vertexFormat: Cesium.VertexFormat.POSITION_AND_ST,
 | 
						|
      granularity: Cesium.Math.toRadians(0.0001), // 调整这个参数以增加顶点密度
 | 
						|
      height: this.minElevation,
 | 
						|
    });
 | 
						|
    const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
 | 
						|
    const attributeLocations =
 | 
						|
      Cesium.GeometryPipeline.createAttributeLocations(geometry);
 | 
						|
 | 
						|
    const va = Cesium.VertexArray.fromGeometry({
 | 
						|
      context: context,
 | 
						|
      geometry: geometry,
 | 
						|
      attributeLocations: attributeLocations,
 | 
						|
    });
 | 
						|
    const vs = `
 | 
						|
        in vec4 position;
 | 
						|
        in vec2 st;
 | 
						|
        out vec2 v_st;
 | 
						|
 | 
						|
        const float PI = 3.141592653589793;
 | 
						|
        const float earthRadius = 6378137.0; // WGS84 椭球体的平均半径
 | 
						|
        const float angularVelocity = 180.0 / PI;
 | 
						|
 | 
						|
        const float RADII_X = 6378137.0;
 | 
						|
        const float RADII_Y = 6378137.0;
 | 
						|
        const float RADII_Z = 6356752.314245;
 | 
						|
 | 
						|
        vec3 worldToGeographic(vec3 worldPosition) {
 | 
						|
            // 步骤1: 世界坐标到ECEF坐标
 | 
						|
            vec3 ecef = worldPosition;  // 假设世界坐标已经是ECEF
 | 
						|
 | 
						|
            // 步骤2: ECEF到地理坐标
 | 
						|
            float l = length(ecef.xy);
 | 
						|
            float e2 = 1.0 - (RADII_Z * RADII_Z) / (RADII_X * RADII_X);
 | 
						|
            float u = atan(ecef.z * RADII_X / (l * RADII_Z));
 | 
						|
            float lat = atan((ecef.z + e2 * RADII_Z * pow(sin(u), 3.0)) / 
 | 
						|
                            (l - e2 * RADII_X * pow(cos(u), 3.0)));
 | 
						|
            float lon = atan(ecef.y, ecef.x);
 | 
						|
            float N = RADII_X / sqrt(1.0 - e2 * sin(lat) * sin(lat));
 | 
						|
            float alt = l / cos(lat) - N;
 | 
						|
 | 
						|
            // 将弧度转换为度
 | 
						|
            lat = degrees(lat);
 | 
						|
            lon = degrees(lon);
 | 
						|
 | 
						|
            return vec3(lon, lat, alt);
 | 
						|
        }
 | 
						|
 | 
						|
        vec3 geo2cartesian(vec3 geo){
 | 
						|
            float cosLat=cos(geo.y);
 | 
						|
            float snX=cosLat*cos(geo.x);
 | 
						|
            float snY=cosLat*sin(geo.x);
 | 
						|
            float snZ=sin(geo.y);
 | 
						|
            vec3 sn=normalize(vec3(snX,snY,snZ));
 | 
						|
            vec3 radiiSquared=vec3(40680631.59076899*1000000.,40680631.59076899*1000000.,40408299.98466144*1000000.);
 | 
						|
            vec3 sk=radiiSquared*sn;
 | 
						|
            float gamma=sqrt(dot(sn,sk));
 | 
						|
            sk=sk/gamma;
 | 
						|
            sn=sn*geo.z;
 | 
						|
            return sk+sn;
 | 
						|
        }
 | 
						|
 | 
						|
        vec3 deg2cartesian(vec3 deg) {
 | 
						|
            vec2 radGeo=radians(deg.xy);
 | 
						|
            vec3 geo=vec3(radGeo.xy,deg.z);
 | 
						|
            return geo2cartesian(geo);
 | 
						|
        }
 | 
						|
 | 
						|
        void main() {
 | 
						|
            float normalizedHeight = 0.0;
 | 
						|
 | 
						|
            vec2 uv = st;
 | 
						|
            float deepwater_fadedepth = 0.5 + coast2water_fadedepth;
 | 
						|
 | 
						|
            float height = height_map(uv);
 | 
						|
            vec3 col;
 | 
						|
 | 
						|
            float waveheight = clamp(WATER_LEVEL * 3. - 1.5, 0., 1.);
 | 
						|
            float level = WATER_LEVEL + .2 * water_map(uv * 15. + vec2(iTime * .1), waveheight);
 | 
						|
 | 
						|
            if(height <= level) {
 | 
						|
                normalizedHeight = level;
 | 
						|
            }else{
 | 
						|
                normalizedHeight = height; // 减少边缘拉伸的割裂感
 | 
						|
            }
 | 
						|
 | 
						|
            float heightOffset = (maxElevation - minElevation) * normalizedHeight;
 | 
						|
            
 | 
						|
            // 将顶点位置从模型空间转换到世界空间
 | 
						|
            vec4 worldPosition = czm_model * position;
 | 
						|
            
 | 
						|
            // 将世界坐标转换为经纬度和高度
 | 
						|
            vec3 llh = worldToGeographic(worldPosition.xyz);
 | 
						|
            
 | 
						|
            // 将调整后的经纬度和高度转换回笛卡尔坐标
 | 
						|
            vec3 adjustedCartesian = deg2cartesian(vec3(llh.xy,minElevation+heightOffset));
 | 
						|
            
 | 
						|
            gl_Position = czm_projection * czm_view * vec4(adjustedCartesian,1.0);
 | 
						|
            v_st = st;
 | 
						|
        }
 | 
						|
      `;
 | 
						|
    const fs = `
 | 
						|
        in vec2 v_st;
 | 
						|
 | 
						|
        void main(){
 | 
						|
            light = vec3(-0., .0, 2.8); // position of the sun
 | 
						|
            vec2 uv = v_st;
 | 
						|
 | 
						|
            float deepwater_fadedepth = 0.5 + coast2water_fadedepth;
 | 
						|
 | 
						|
            float height = height_map(uv);
 | 
						|
            vec3 col;
 | 
						|
 | 
						|
            float waveheight = clamp(WATER_LEVEL * 3. - 1.5, 0., 1.);
 | 
						|
            float level = WATER_LEVEL + .2 * water_map(uv * 15. + vec2(iTime * .1), waveheight);
 | 
						|
            if(height <= level) {
 | 
						|
                vec2 dif = vec2(.0, .01);
 | 
						|
                vec2 pos = uv * 15. + vec2(iTime * .01);
 | 
						|
                float h1 = water_map(pos - dif, waveheight);
 | 
						|
                float h2 = water_map(pos + dif, waveheight);
 | 
						|
                float h3 = water_map(pos - dif.yx, waveheight);
 | 
						|
                float h4 = water_map(pos + dif.yx, waveheight);
 | 
						|
                vec3 normwater = normalize(vec3(h3 - h4, h1 - h2, .125)); // norm-vector of the 'bumpy' water-plane
 | 
						|
                uv += normwater.xy * .002 * (level - height);
 | 
						|
 | 
						|
                col = vec3(1.0);
 | 
						|
 | 
						|
                float coastfade = clamp((level - height) / coast2water_fadedepth, 0., 1.);
 | 
						|
                float coastfade2 = clamp((level - height) / deepwater_fadedepth, 0., 1.);
 | 
						|
                float intensity = col.r * .2126 + col.g * .7152 + col.b * .0722;
 | 
						|
                watercolor = mix(watercolor * intensity, watercolor2, smoothstep(0., 1., coastfade2));
 | 
						|
 | 
						|
                vec3 r0 = vec3(uv, WATER_LEVEL);
 | 
						|
                vec3 rd = normalize(light - r0); // ray-direction to the light from water-position
 | 
						|
                float grad = dot(normwater, rd); // dot-product of norm-vector and light-direction
 | 
						|
                float specular = pow(grad, water_softlight_fact);  // used for soft highlights                          
 | 
						|
                float specular2 = pow(grad, water_glossylight_fact); // used for glossy highlights
 | 
						|
                float gradpos = dot(vec3(0., 0., 1.), rd);
 | 
						|
                float specular1 = smoothstep(0., 1., pow(gradpos, 5.));  // used for diffusity (some darker corona around light's specular reflections...)                          
 | 
						|
                float watershade = test_shadow(uv, level);
 | 
						|
                watercolor *= 2.2 + watershade;
 | 
						|
                watercolor += (.2 + .8 * watershade) * ((grad - 1.0) * .5 + specular) * .25;
 | 
						|
                watercolor /= (1. + specular1 * 1.25);
 | 
						|
                watercolor += watershade * specular2 * water_specularcolor;
 | 
						|
                watercolor += watershade * coastfade * (1. - coastfade2) * (vec3(.5, .6, .7) * nautic(uv) + vec3(1., 1., 1.) * particles(uv));
 | 
						|
 | 
						|
                col = mix(col, watercolor, coastfade);
 | 
						|
    
 | 
						|
                float alpha = clamp(coastfade,0.1,0.6);
 | 
						|
                out_FragColor = vec4(col,1.0);
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
  
 | 
						|
      `;
 | 
						|
    const shaderProgram = Cesium.ShaderProgram.fromCache({
 | 
						|
      context: context,
 | 
						|
      vertexShaderSource: Common + vs,
 | 
						|
      fragmentShaderSource: Common + fs,
 | 
						|
      attributeLocations: attributeLocations,
 | 
						|
    });
 | 
						|
    const texture = new Cesium.Texture({
 | 
						|
      context: context,
 | 
						|
      width: 2048.0,
 | 
						|
      height: 2048.0,
 | 
						|
      pixelFormat: Cesium.PixelFormat.RGBA,
 | 
						|
      pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
 | 
						|
      flipY: true,
 | 
						|
      sampler: new Cesium.Sampler({
 | 
						|
        minificationFilter: Cesium.TextureMinificationFilter.LINEAR,
 | 
						|
        magnificationFilter: Cesium.TextureMagnificationFilter.LINEAR,
 | 
						|
        wrapS: Cesium.TextureWrap.REPEAT,
 | 
						|
        wrapT: Cesium.TextureWrap.REPEAT,
 | 
						|
      }),
 | 
						|
      source: this.heightMap,
 | 
						|
    });
 | 
						|
    const noise = new Cesium.Texture({
 | 
						|
      context: context,
 | 
						|
      width: 512.0,
 | 
						|
      height: 512.0,
 | 
						|
      pixelFormat: Cesium.PixelFormat.RGBA,
 | 
						|
      pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
 | 
						|
      flipY: true,
 | 
						|
      sampler: new Cesium.Sampler({
 | 
						|
        minificationFilter: Cesium.TextureMinificationFilter.LINEAR,
 | 
						|
        magnificationFilter: Cesium.TextureMagnificationFilter.LINEAR,
 | 
						|
        wrapS: Cesium.TextureWrap.REPEAT,
 | 
						|
        wrapT: Cesium.TextureWrap.REPEAT,
 | 
						|
      }),
 | 
						|
      source: this.noise,
 | 
						|
    });
 | 
						|
    const uniformMap = {
 | 
						|
      heightMap: () => {
 | 
						|
        return texture;
 | 
						|
      },
 | 
						|
      heightScale: () => 1.0,
 | 
						|
      minElevation: () => this.minElevation,
 | 
						|
      maxElevation: () => this.maxElevation,
 | 
						|
      iTime: () => this.time,
 | 
						|
      iChannel0: () => noise,
 | 
						|
      coast2water_fadedepth: () => this.coast2water_fadedepth,
 | 
						|
      large_waveheight: () => this.large_waveheight, // change to adjust the "heavy" waves
 | 
						|
      large_wavesize: () => this.large_wavesize, // factor to adjust the large wave size
 | 
						|
      small_waveheight: () => this.small_waveheight, // change to adjust the small random waves
 | 
						|
      small_wavesize: () => this.small_wavesize, // factor to ajust the small wave size
 | 
						|
      water_softlight_fact: () => this.water_softlight_fact, // range [1..200] (should be << smaller than glossy-fact)
 | 
						|
      water_glossylight_fact: () => this.water_glossylight_fact, // range [1..200]
 | 
						|
      particle_amount: () => this.particle_amount,
 | 
						|
      WATER_LEVEL: () => this.WATER_LEVEL,
 | 
						|
    };
 | 
						|
    const renderState = Cesium.RenderState.fromCache({
 | 
						|
      depthTest: { enabled: true },
 | 
						|
      depthMask: { enabled: true },
 | 
						|
      blending: Cesium.BlendingState.ALPHA_BLEND,
 | 
						|
      cull: {
 | 
						|
        enabled: false,
 | 
						|
      },
 | 
						|
    });
 | 
						|
    this.drawCommand = new Cesium.DrawCommand({
 | 
						|
      modelMatrix: this.modelMatrix,
 | 
						|
      vertexArray: va,
 | 
						|
      primitiveType: Cesium.PrimitiveType.TRIANGLES, //TRIANGLES LINES
 | 
						|
      shaderProgram: shaderProgram,
 | 
						|
      uniformMap: uniformMap,
 | 
						|
      renderState: renderState,
 | 
						|
      pass: Cesium.Pass.OPAQUE,
 | 
						|
    });
 | 
						|
  }
 | 
						|
  set showLines(value) {
 | 
						|
    this._showLines = value;
 | 
						|
    this.drawCommand.primitiveType = this._showLines
 | 
						|
      ? Cesium.PrimitiveType.LINES
 | 
						|
      : Cesium.PrimitiveType.TRIANGLES;
 | 
						|
  }
 | 
						|
  get showLines() {
 | 
						|
    return this._showLines;
 | 
						|
  }
 | 
						|
  async update(frameState) {
 | 
						|
    const now = performance.now();
 | 
						|
    this.deltaTime = (now - this.lastUpdateTime) / 1000.0; // 转换为秒
 | 
						|
    this.lastUpdateTime = now;
 | 
						|
    this.time = now / 1000;
 | 
						|
    this.frame += 0.02;
 | 
						|
    if (!this.drawCommand) {
 | 
						|
      this.createCommand(frameState.context);
 | 
						|
    }
 | 
						|
    frameState.commandList.push(this.drawCommand);
 | 
						|
  }
 | 
						|
  destroy() {
 | 
						|
    super.destroy();
 | 
						|
    const commondList = [this.drawCommand];
 | 
						|
    commondList.forEach((drawCommand) => {
 | 
						|
      if (drawCommand) {
 | 
						|
        const va = drawCommand.vertexArray,
 | 
						|
          sp = drawCommand.shaderProgram;
 | 
						|
        if (!va.isDestroyed()) va.destroy();
 | 
						|
        if (!sp.isDestroyed || !sp.isDestroyed()) {
 | 
						|
          sp.destroy();
 | 
						|
        }
 | 
						|
        drawCommand.isDestroyed = function returnTrue() {
 | 
						|
          return true;
 | 
						|
        };
 | 
						|
        drawCommand.uniformMap = undefined;
 | 
						|
        drawCommand.renderState = Cesium.RenderState.removeFromCache(
 | 
						|
          drawCommand.renderState
 | 
						|
        );
 | 
						|
      }
 | 
						|
    });
 | 
						|
    this.drawCommand = null;
 | 
						|
  }
 | 
						|
} |