@geoblocks/cesium-post-process 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,10 +24,14 @@ const snowLine = new SnowLine(viewer, {
24
24
  altitude: 2000, // meters above the ellipsoid where the snow is half covering
25
25
  transition: 200, // height of the band over which the snow thins out, in meters
26
26
  maxSlope: 50, // steepest slope that holds snow, in degrees
27
- coverage: 1, // opacity of the snow, 0 to 1
27
+ coverage: 1, // opacity of the snow, 0 to 1; at 0 the stage is off
28
28
  patchiness: 0, // 0 to 1: at 1 the coverage is the share of the ground the snow covers, the noise leaving the rest bare
29
29
  waterLevel: 564, // meters above the ellipsoid: what lies within a quarter meter of it holds no snow, so a flat lake stays water
30
30
  sunDirection: sun, // the direction toward the sun the snow is shaded by, in world coordinates; Cesium's own sun if none
31
+ grain: 90, // the noise's largest scale, in meters, its finer ones a third and a ninth of it: a few meters for a garden
32
+ sceneLight: 0, // 0 to 1: how far the snow's brightness is the scene's own, its shadows and its dusk kept; 0 the sun's shading alone
33
+ steepCover: 0.35, // the thin cover the steep slopes keep, as a fraction of the coverage; 0 for none on a building's walls
34
+ origin: position, // the point of the world the noise is laid about; the camera's position when activated if none
31
35
  });
32
36
  snowLine.active = true;
33
37
 
@@ -77,6 +81,7 @@ const rainRings = new RainRings(viewer, {
77
81
  maxSlope: 30, // steepest slope that takes a ring, in degrees
78
82
  color: new Color(0.31, 0.39, 0.5, 0.6), // slate on a light ground, pale on a dark one; the alpha is how strongly it shows
79
83
  origin: position, // the point of the world the cells are laid about; the camera's position when activated if none
84
+ clock: () => performance.now() / 1000, // the time the rings run by, in seconds; one that stands still keeps them still (less motion)
80
85
  });
81
86
  rainRings.active = true;
82
87
 
package/local-frame.js CHANGED
@@ -1,4 +1,4 @@
1
- import {Matrix4} from '@cesium/core';
1
+ import {Cartesian3, FixedFrameTransforms, Matrix4} from '@cesium/core';
2
2
 
3
3
  /**
4
4
  * The local frame's coordinates from the eye's, for a shader that wants a pixel's place in a small frame (east-north-up about a point) to
@@ -13,3 +13,45 @@ import {Matrix4} from '@cesium/core';
13
13
  export function localFromEye(frame, viewMatrix, result = new Matrix4()) {
14
14
  return Matrix4.inverseTransformation(Matrix4.multiply(viewMatrix, frame, result), result);
15
15
  }
16
+
17
+ /**
18
+ * A local frame, east-north-up about an origin, for an effect that lays a pattern over the ground: the origin given, or the camera's
19
+ * position when the frame is first anchored.
20
+ */
21
+ export class LocalFrame {
22
+ /** @param {Cartesian3} [origin] */
23
+ constructor(origin) {
24
+ this.origin_ = origin && Cartesian3.clone(origin);
25
+ /** @type {Matrix4 | undefined} */
26
+ this.frame_ = undefined;
27
+ this.scratch_ = new Matrix4();
28
+ }
29
+
30
+ /** The point of the world the frame is laid about; undefined until anchored if none was given. */
31
+ get origin() {
32
+ return this.origin_;
33
+ }
34
+
35
+ set origin(value) {
36
+ this.origin_ = value && Cartesian3.clone(value);
37
+ this.frame_ = undefined;
38
+ }
39
+
40
+ /**
41
+ * The origin, the camera's position if there is none yet.
42
+ * @param {import('@cesium/engine').Scene} scene
43
+ */
44
+ anchor(scene) {
45
+ this.origin_ ??= Cartesian3.clone(scene.camera.positionWC);
46
+ return this.origin_;
47
+ }
48
+
49
+ /**
50
+ * The local frame's coordinates from the camera's eye's (localFromEye), anchored first.
51
+ * @param {import('@cesium/engine').Scene} scene
52
+ */
53
+ fromEye(scene) {
54
+ this.frame_ ??= FixedFrameTransforms.eastNorthUpToFixedFrame(this.anchor(scene), scene.ellipsoid, new Matrix4());
55
+ return localFromEye(this.frame_, scene.camera.viewMatrix, this.scratch_);
56
+ }
57
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geoblocks/cesium-post-process",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "license": "BSD-3-Clause",
5
5
  "type": "module",
6
6
  "repository": {
@@ -56,5 +56,5 @@
56
56
  "@cesium/core": ">=0.1.0",
57
57
  "@cesium/engine": ">=10"
58
58
  },
59
- "gitHead": "866feb087ad153c2303fdf12b670ec78f192f825"
59
+ "gitHead": "c9101c5e42f96f2ccc28cfb396dbe08c55e29a09"
60
60
  }
package/rain-rings.js CHANGED
@@ -1,10 +1,10 @@
1
- import {Cartesian3, Cartesian4, Color, FixedFrameTransforms, Matrix4, Math as CesiumMath} from '@cesium/core';
1
+ import {Cartesian3, Cartesian4, Color, Math as CesiumMath} from '@cesium/core';
2
2
  import {PostProcessStage} from '@cesium/engine';
3
3
  import {acquireTerrainDepth, releaseTerrainDepth} from './depth-test.js';
4
4
  import Effect from './effect.js';
5
5
  import {acquireFrames, releaseFrames} from './frame-clock.js';
6
6
  import {heightUniforms} from './height.js';
7
- import {localFromEye} from './local-frame.js';
7
+ import {LocalFrame} from './local-frame.js';
8
8
  import EyeFromDepth from './shaders/EyeFromDepth.js';
9
9
  import Hash from './shaders/Hash.js';
10
10
  import Height from './shaders/Height.js';
@@ -28,10 +28,11 @@ export default class RainRings extends Effect {
28
28
  /**
29
29
  * @param {import('@cesium/engine').CesiumWidget} viewer
30
30
  * @param {{intensity?: number, cell?: number, reach?: number, waterLevel?: number, maxSlope?: number, rate?: number,
31
- * color?: Color, origin?: Cartesian3}} [options] The share of the cells that have a drop, 0 to 1; the cell's size and the ring's growth
32
- * on the ground, in meters (the ring on water is clipped at 0.3 cell); the water's height above the ellipsoid, in meters, or undefined
33
- * for none; the steepest slope that takes a ring, in degrees; rings a second in a cell; the ring's color, its alpha how strongly it shows; the
34
- * point of the world the cells are laid about.
31
+ * color?: Color, origin?: Cartesian3, clock?: () => number}} [options] The share of the cells that have a drop, 0 to 1; the cell's size
32
+ * and the ring's growth on the ground, in meters (the ring on water is clipped at 0.3 cell); the water's height above the ellipsoid, in
33
+ * meters, or undefined for none; the steepest slope that takes a ring, in degrees; rings a second in a cell; the ring's color, its alpha how
34
+ * strongly it shows; the point of the world the cells are laid about; the time the rings run by, in seconds (the wall clock's by default;
35
+ * one that stands still keeps them still, for a viewer who asks for less motion).
35
36
  */
36
37
  constructor(viewer, options = {}) {
37
38
  super(viewer);
@@ -42,23 +43,9 @@ export default class RainRings extends Effect {
42
43
  this.maxSlope_ = options.maxSlope ?? 30;
43
44
  this.rate_ = options.rate ?? 1.6;
44
45
  this.color_ = options.color ?? new Color(0.31, 0.39, 0.5, 0.6);
45
- this.origin_ = options.origin && Cartesian3.clone(options.origin);
46
- /** @type {Matrix4 | undefined} */
47
- this.localFrame_ = undefined;
46
+ this.frame_ = new LocalFrame(options.origin);
47
+ this.clock_ = options.clock ?? (() => performance.now() / 1000);
48
48
  this.colorScratch_ = new Cartesian4();
49
- this.localScratch_ = new Matrix4();
50
- }
51
-
52
- /**
53
- * The local frame, east-north-up about the origin: the camera's position when the effect is activated, if none was given.
54
- * @private
55
- * @param {import('@cesium/engine').Scene} scene
56
- */
57
- frameAt_(scene) {
58
- this.origin_ ??= Cartesian3.clone(scene.camera.positionWC);
59
- const frame = this.localFrame_ ?? FixedFrameTransforms.eastNorthUpToFixedFrame(this.origin_, scene.ellipsoid, new Matrix4());
60
- this.localFrame_ = frame;
61
- return frame;
62
49
  }
63
50
 
64
51
  /**
@@ -70,14 +57,14 @@ export default class RainRings extends Effect {
70
57
  fragmentShader: EyeFromDepth + Height + Hash + Normal + RainRingsShader,
71
58
  uniforms: {
72
59
  ...heightUniforms(scene),
73
- localFromEye: () => localFromEye(this.frameAt_(scene), scene.camera.viewMatrix, this.localScratch_),
60
+ localFromEye: () => this.frame_.fromEye(scene),
74
61
  intensity: () => this.intensity_,
75
62
  cell: () => this.cell_,
76
63
  reach: () => this.reach_,
77
64
  waterLevel: () => this.waterLevel_ ?? NO_WATER,
78
65
  maxSlope: () => CesiumMath.toRadians(this.maxSlope_),
79
66
  rate: () => this.rate_,
80
- time: () => (performance.now() / 1000) % TIME_PERIOD,
67
+ time: () => this.clock_() % TIME_PERIOD,
81
68
  color: () => Cartesian4.fromColor(this.color_, this.colorScratch_),
82
69
  },
83
70
  });
@@ -91,7 +78,7 @@ export default class RainRings extends Effect {
91
78
  * @param {import('@cesium/engine').Scene} scene
92
79
  */
93
80
  activated_(scene) {
94
- this.frameAt_(scene);
81
+ this.frame_.anchor(scene);
95
82
  acquireTerrainDepth(scene);
96
83
  if (this.intensity_ > 0) {
97
84
  acquireFrames(scene, FRAME_RATE);
@@ -204,12 +191,11 @@ export default class RainRings extends Effect {
204
191
  * the effect is activated if none is given. Keep it within a few kilometers of what is looked at.
205
192
  */
206
193
  get origin() {
207
- return this.origin_;
194
+ return this.frame_.origin;
208
195
  }
209
196
 
210
197
  set origin(value) {
211
- this.origin_ = value && Cartesian3.clone(value);
212
- this.localFrame_ = undefined;
198
+ this.frame_.origin = value;
213
199
  this.viewer.scene.requestRender();
214
200
  }
215
201
  }
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from SnowLine.glsl.
2
- export default "// Snow above an altitude, on slopes gentle enough to hold it, shaded by the sun and by the\n// brightness of the scene so that the relief stays readable. The slope comes from a normal\n// rebuilt from the neighboring pixels and smoothed over the facets of the terrain. Noise anchored\n// to the ground breaks up the edge of the snow, which lasts lower on the slopes away from the sun.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\n// meters above the ellipsoid: half covered at the altitude, over a band transition meters high\nuniform float altitude;\nuniform float transition;\n// radians\nuniform float maxSlope;\n// 0 to 1\nuniform float coverage;\n// 0 to 1: how far the coverage thins the snow into patches, where 0 fades it\nuniform float patchiness;\n// meters above the ellipsoid, far above anything with no water: none lies within a quarter meter of it\nuniform float waterLevel;\n// the sun toward which the snow is shaded, in eye coordinates, when ownSun is 1; else Cesium's\nuniform vec3 sunEC;\nuniform float ownSun;\nin vec2 v_textureCoordinates;\n\nconst vec3 SNOW_SHADE = vec3(0.80, 0.87, 1.0);\nconst vec3 SNOW_SUN = vec3(1.0, 0.98, 0.94);\n// meters the snow line moves down on a slope facing away from the sun, and up on one facing it\nconst float ASPECT_SHIFT = 0.6;\n// meters the noise moves the snow line, as a fraction of the transition\nconst float NOISE_SHIFT = 0.8;\n// part of the snow that still covers the steep slopes at the limit, as a fraction of the coverage\nconst float STEEP_SNOW = 0.35;\n// half width of the slope limit, in radians (8 degrees), so that the snow thins out across the\n// facets of the terrain rather than stopping at their edges\nconst float SLOPE_EDGE = 0.14;\n// the coverage, moved by the noise, from which the snow begins and is whole when it is patches; how far the noise moves it\nconst vec2 PATCH = vec2(0.25, 0.6);\nconst float PATCH_NOISE = 1.6;\n// meters from the water's level within which a surface is water, and beyond which it is not\nconst vec2 WET = vec2(0.1, 0.25);\n\nfloat hash(vec3 p) {\n p = fract(p * 0.3183099 + 0.1);\n p *= 17.0;\n return fract(p.x * p.y * p.z * (p.x + p.y + p.z));\n}\n\nfloat valueNoise(vec3 p) {\n vec3 i = floor(p);\n vec3 f = fract(p);\n f = f * f * (3.0 - 2.0 * f);\n return mix(\n mix(mix(hash(i), hash(i + vec3(1, 0, 0)), f.x), mix(hash(i + vec3(0, 1, 0)), hash(i + vec3(1, 1, 0)), f.x), f.y),\n mix(mix(hash(i + vec3(0, 0, 1)), hash(i + vec3(1, 0, 1)), f.x), mix(hash(i + vec3(0, 1, 1)), hash(i + vec3(1, 1, 1)), f.x), f.y),\n f.z);\n}\n\n// 0 to 1, from a few octaves anchored to the ground\nfloat groundNoise(vec3 world) {\n return 0.55 * valueNoise(world / 90.0) + 0.3 * valueNoise(world / 30.0) + 0.15 * valueNoise(world / 10.0);\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n vec3 normal = smoothNormalAt(depthTexture, v_textureCoordinates, eye.xyz);\n float slope = acos(clamp(dot(normal, up), -1.0, 1.0));\n\n float sun = max(dot(normal, ownSun > 0.5 ? sunEC : czm_sunDirectionEC), 0.0);\n float noise = groundNoise((czm_inverseView * vec4(eye.xyz, 1.0)).xyz) - 0.5;\n // lower on the slopes away from the sun, irregular along the edge\n float shift = transition * (NOISE_SHIFT * noise + ASPECT_SHIFT * (0.5 - sun));\n float snowLine = altitude + shift;\n float snow = smoothstep(snowLine - 0.5 * transition, snowLine + 0.5 * transition, heightAt(eye.xyz));\n // opaque before the top of the band, so that the imagery does not show through the snow\n snow = smoothstep(0.0, 0.7, snow);\n // the steep slopes keep a thin, patchy cover, and the rock shows through\n float steep = smoothstep(maxSlope - SLOPE_EDGE, maxSlope + SLOPE_EDGE, slope);\n snow *= 1.0 - steep * (1.0 - STEEP_SNOW * smoothstep(0.35, 0.65, noise + 0.5));\n // the coverage an opacity, or, by the patchiness, the share of the ground the snow covers, the noise leaving it in patches\n float scattered = smoothstep(PATCH.x, PATCH.y, coverage + PATCH_NOISE * noise);\n // eye.w is 0 for the sky\n snow *= mix(coverage, scattered, patchiness) * eye.w;\n snow *= smoothstep(WET.x, WET.y, abs(heightAt(eye.xyz) - waterLevel));\n float brightness = czm_luminance(sceneColor.rgb);\n // blue in the shade, warm in the sun, with the relief of the scene kept in the brightness\n vec3 lit = mix(SNOW_SHADE, SNOW_SUN, sun) * clamp(0.68 + 0.25 * sun + 0.1 * brightness, 0.0, 1.0);\n out_FragColor = vec4(mix(sceneColor.rgb, lit, snow), sceneColor.a);\n}\n";
2
+ export default "// Snow above an altitude, on slopes gentle enough to hold it, shaded by the sun and by the\n// brightness of the scene so that the relief stays readable. The slope comes from a normal\n// rebuilt from the neighboring pixels and smoothed over the facets of the terrain. Noise anchored\n// to the ground breaks up the edge of the snow, which lasts lower on the slopes away from the sun.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\n// meters above the ellipsoid: half covered at the altitude, over a band transition meters high\nuniform float altitude;\nuniform float transition;\n// radians\nuniform float maxSlope;\n// 0 to 1\nuniform float coverage;\n// 0 to 1: how far the coverage thins the snow into patches, where 0 fades it\nuniform float patchiness;\n// meters: the noise's largest scale, its finer ones a third and a ninth of it\nuniform float grain;\n// 0 to 1: how far the snow's brightness is the scene's own (its shadows, the dusk), where 0 is the sun's shading\nuniform float sceneLight;\n// 0 to 1: the part of the snow that still covers the steep slopes at the limit, as a fraction of the coverage\nuniform float steepCover;\n// the local frame's coordinates from the eye's, made in double precision on the CPU (local-frame.js): the noise's place\nuniform mat4 localFromEye;\n// meters above the ellipsoid, far above anything with no water: none lies within a quarter meter of it\nuniform float waterLevel;\n// the sun toward which the snow is shaded, in eye coordinates, when ownSun is 1; else Cesium's\nuniform vec3 sunEC;\nuniform float ownSun;\nin vec2 v_textureCoordinates;\n\nconst vec3 SNOW_SHADE = vec3(0.80, 0.87, 1.0);\nconst vec3 SNOW_SUN = vec3(1.0, 0.98, 0.94);\n// meters the snow line moves down on a slope facing away from the sun, and up on one facing it\nconst float ASPECT_SHIFT = 0.6;\n// meters the noise moves the snow line, as a fraction of the transition\nconst float NOISE_SHIFT = 0.8;\n// half width of the slope limit, in radians (8 degrees), so that the snow thins out across the\n// facets of the terrain rather than stopping at their edges\nconst float SLOPE_EDGE = 0.14;\n// the coverage, moved by the noise, from which the snow begins and is whole when it is patches; how far the noise moves it\nconst vec2 PATCH = vec2(0.25, 0.6);\nconst float PATCH_NOISE = 1.6;\n// meters from the water's level within which a surface is water, and beyond which it is not\nconst vec2 WET = vec2(0.1, 0.25);\n\nfloat hash(vec3 p) {\n p = fract(p * 0.3183099 + 0.1);\n p *= 17.0;\n return fract(p.x * p.y * p.z * (p.x + p.y + p.z));\n}\n\nfloat valueNoise(vec3 p) {\n vec3 i = floor(p);\n vec3 f = fract(p);\n f = f * f * (3.0 - 2.0 * f);\n return mix(\n mix(mix(hash(i), hash(i + vec3(1, 0, 0)), f.x), mix(hash(i + vec3(0, 1, 0)), hash(i + vec3(1, 1, 0)), f.x), f.y),\n mix(mix(hash(i + vec3(0, 0, 1)), hash(i + vec3(1, 0, 1)), f.x), mix(hash(i + vec3(0, 1, 1)), hash(i + vec3(1, 1, 1)), f.x), f.y),\n f.z);\n}\n\n// 0 to 1, from a few octaves anchored to the ground, in the local frame: in world coordinates a float steps by half a meter\nfloat groundNoise(vec3 local) {\n return 0.55 * valueNoise(local / grain) + 0.3 * valueNoise(3.0 * local / grain) + 0.15 * valueNoise(9.0 * local / grain);\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n vec3 normal = smoothNormalAt(depthTexture, v_textureCoordinates, eye.xyz);\n float slope = acos(clamp(dot(normal, up), -1.0, 1.0));\n\n float sun = max(dot(normal, ownSun > 0.5 ? sunEC : czm_sunDirectionEC), 0.0);\n float noise = groundNoise((localFromEye * vec4(eye.xyz, 1.0)).xyz) - 0.5;\n // lower on the slopes away from the sun, irregular along the edge\n float shift = transition * (NOISE_SHIFT * noise + ASPECT_SHIFT * (0.5 - sun));\n float snowLine = altitude + shift;\n float snow = smoothstep(snowLine - 0.5 * transition, snowLine + 0.5 * transition, heightAt(eye.xyz));\n // opaque before the top of the band, so that the imagery does not show through the snow\n snow = smoothstep(0.0, 0.7, snow);\n // the steep slopes keep a thin, patchy cover, and the rock shows through\n float steep = smoothstep(maxSlope - SLOPE_EDGE, maxSlope + SLOPE_EDGE, slope);\n snow *= 1.0 - steep * (1.0 - steepCover * smoothstep(0.35, 0.65, noise + 0.5));\n // the coverage an opacity, or, by the patchiness, the share of the ground the snow covers, the noise leaving it in patches\n float scattered = smoothstep(PATCH.x, PATCH.y, coverage + PATCH_NOISE * noise);\n // eye.w is 0 for the sky\n snow *= mix(coverage, scattered, patchiness) * eye.w;\n snow *= smoothstep(WET.x, WET.y, abs(heightAt(eye.xyz) - waterLevel));\n float brightness = czm_luminance(sceneColor.rgb);\n // blue in the shade, warm in the sun, with the relief of the scene kept in the brightness; or as bright as the scene, its shadows and its\n // dusk kept on the snow\n float shaded = clamp(0.68 + 0.25 * sun + 0.1 * brightness, 0.0, 1.0);\n float scene = clamp(0.15 + 1.35 * brightness, 0.0, 1.0);\n vec3 lit = mix(SNOW_SHADE, SNOW_SUN, sun) * mix(shaded, scene, sceneLight);\n out_FragColor = vec4(mix(sceneColor.rgb, lit, snow), sceneColor.a);\n}\n";
package/snow-line.js CHANGED
@@ -3,6 +3,7 @@ import {PostProcessStage} from '@cesium/engine';
3
3
  import {acquireTerrainDepth, releaseTerrainDepth} from './depth-test.js';
4
4
  import Effect from './effect.js';
5
5
  import {heightUniforms} from './height.js';
6
+ import {LocalFrame} from './local-frame.js';
6
7
  import EyeFromDepth from './shaders/EyeFromDepth.js';
7
8
  import Height from './shaders/Height.js';
8
9
  import Normal from './shaders/Normal.js';
@@ -21,7 +22,7 @@ export default class SnowLine extends Effect {
21
22
  /**
22
23
  * @param {import('@cesium/engine').CesiumWidget} viewer
23
24
  * @param {{altitude?: number, transition?: number, maxSlope?: number, coverage?: number, patchiness?: number, waterLevel?: number,
24
- * sunDirection?: Cartesian3}} [options]
25
+ * sunDirection?: Cartesian3, grain?: number, sceneLight?: number, steepCover?: number, origin?: Cartesian3}} [options]
25
26
  */
26
27
  constructor(viewer, options = {}) {
27
28
  super(viewer);
@@ -32,6 +33,10 @@ export default class SnowLine extends Effect {
32
33
  this.patchiness_ = options.patchiness ?? 0;
33
34
  this.waterLevel_ = options.waterLevel;
34
35
  this.sunDirection_ = options.sunDirection && Cartesian3.normalize(options.sunDirection, new Cartesian3());
36
+ this.grain_ = options.grain ?? 90;
37
+ this.sceneLight_ = options.sceneLight ?? 0;
38
+ this.steepCover_ = options.steepCover ?? 0.35;
39
+ this.frame_ = new LocalFrame(options.origin);
35
40
  }
36
41
 
37
42
  /**
@@ -39,7 +44,7 @@ export default class SnowLine extends Effect {
39
44
  * @param {import('@cesium/engine').Scene} scene
40
45
  */
41
46
  createStage_(scene) {
42
- return new PostProcessStage({
47
+ const stage = new PostProcessStage({
43
48
  fragmentShader: EyeFromDepth + Height + Normal + SnowLineShader,
44
49
  uniforms: {
45
50
  ...heightUniforms(scene),
@@ -51,8 +56,15 @@ export default class SnowLine extends Effect {
51
56
  waterLevel: () => this.waterLevel_ ?? NO_WATER,
52
57
  sunEC: () => this.sunDirection_ ? Matrix4.multiplyByPointAsVector(scene.camera.viewMatrix, this.sunDirection_, sunScratch) : sunScratch,
53
58
  ownSun: () => this.sunDirection_ ? 1 : 0,
59
+ grain: () => this.grain_,
60
+ sceneLight: () => this.sceneLight_,
61
+ steepCover: () => this.steepCover_,
62
+ localFromEye: () => this.frame_.fromEye(scene),
54
63
  },
55
64
  });
65
+ // with no coverage the pass would only copy: off
66
+ stage.enabled = this.coverage_ > 0;
67
+ return stage;
56
68
  }
57
69
 
58
70
  /**
@@ -60,6 +72,7 @@ export default class SnowLine extends Effect {
60
72
  * @param {import('@cesium/engine').Scene} scene
61
73
  */
62
74
  activated_(scene) {
75
+ this.frame_.anchor(scene);
63
76
  acquireTerrainDepth(scene);
64
77
  }
65
78
 
@@ -108,7 +121,7 @@ export default class SnowLine extends Effect {
108
121
  }
109
122
 
110
123
  /**
111
- * Opacity of the snow, 0 to 1.
124
+ * Opacity of the snow, 0 (none: the stage is off) to 1.
112
125
  */
113
126
  get coverage() {
114
127
  return this.coverage_;
@@ -116,6 +129,9 @@ export default class SnowLine extends Effect {
116
129
 
117
130
  set coverage(value) {
118
131
  this.coverage_ = value;
132
+ if (this.stage_) {
133
+ this.stage_.enabled = value > 0;
134
+ }
119
135
  this.viewer.scene.requestRender();
120
136
  }
121
137
 
@@ -157,4 +173,56 @@ export default class SnowLine extends Effect {
157
173
  this.sunDirection_ = value && Cartesian3.normalize(value, new Cartesian3());
158
174
  this.viewer.scene.requestRender();
159
175
  }
176
+
177
+ /**
178
+ * The noise's largest scale, in meters, its finer ones a third and a ninth of it: the size of the patches and of the snow line's
179
+ * irregularities. A few meters for a garden, tens for a mountainside.
180
+ */
181
+ get grain() {
182
+ return this.grain_;
183
+ }
184
+
185
+ set grain(value) {
186
+ this.grain_ = value;
187
+ this.viewer.scene.requestRender();
188
+ }
189
+
190
+ /**
191
+ * How far the snow's brightness is the scene's own, 0 to 1: at 0 the snow is shaded by the sun alone and stays bright at dusk; at 1 it
192
+ * keeps the scene's shadows and darkens with its light, for a scene lit by a light of its own that goes down at night.
193
+ */
194
+ get sceneLight() {
195
+ return this.sceneLight_;
196
+ }
197
+
198
+ set sceneLight(value) {
199
+ this.sceneLight_ = value;
200
+ this.viewer.scene.requestRender();
201
+ }
202
+
203
+ /**
204
+ * The thin cover the steep slopes keep at the limit, as a fraction of the coverage, 0 to 1: the rock showing through on a mountain; 0
205
+ * for none on the walls of a building.
206
+ */
207
+ get steepCover() {
208
+ return this.steepCover_;
209
+ }
210
+
211
+ set steepCover(value) {
212
+ this.steepCover_ = value;
213
+ this.viewer.scene.requestRender();
214
+ }
215
+
216
+ /**
217
+ * The point of the world the noise is laid about, which keeps the snow's patches where they are as the camera moves; the camera's
218
+ * position when the effect is activated if none is given. Keep it within a few kilometers of what is looked at for a grain of a few meters.
219
+ */
220
+ get origin() {
221
+ return this.frame_.origin;
222
+ }
223
+
224
+ set origin(value) {
225
+ this.frame_.origin = value;
226
+ this.viewer.scene.requestRender();
227
+ }
160
228
  }