@geoblocks/cesium-post-process 0.1.1 → 0.1.3

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
@@ -233,4 +233,6 @@ Its `snow`, from 0 to 1, turns the rain to snow in one population, so that nothi
233
233
 
234
234
  `SnowLine`, `ValleyFog`, `Precipitation`, `Clouds`, `Lightning`, `Toy`, `WhiteModel`, `TiltShift`, `Spotlight` and `MotionBlur` read the depth buffer. The post-process stages only get the terrain's depth when it is depth tested, so `globe.depthTestAgainstTerrain` is on while any of them is active and restored when the last one is deactivated: billboards and labels that show through the terrain can be hidden by it meanwhile.
235
235
 
236
+ The depth they read is the nearest of Cesium's frustums': with `scene.logarithmicDepthBuffer` off, Cesium splits the view into frustums of `farToNearRatio` (1000 by default) from the near plane, and only the nearest one's depth reaches the stages. What lies past the split has no depth there: `Toy` and `WhiteModel` paint their sky over it and draw no edge on it, `TiltShift` blurs it as the far background, `SnowLine` and `ValleyFog` take it for sky. Keep the logarithmic depth buffer on, or set `camera.frustum.near` so that one frustum holds the scene (a near plane of 0.3 m reaches 300 m).
237
+
236
238
  Each effect is a full-screen pass, about 1 to 5 ms per frame at full HD on an integrated GPU, on top of a scene that may already be near its budget: Chrome then stalls for hundreds of milliseconds at a time rather than slowing down evenly. If activating an effect brings that on, the cheapest lever is `scene.msaaSamples = 1` while it is active, restored when it is deactivated, as `@geoblocks/cesium-flyto` does for its flights: 4x multisampling costs more than the effect itself, and a moving picture hides the aliasing.
package/analog-video.js CHANGED
@@ -1,3 +1,4 @@
1
+ import {Math as CesiumMath} from '@cesium/core';
1
2
  import {PostProcessStage} from '@cesium/engine';
2
3
  import Effect from './effect.js';
3
4
  import {acquireFrames, releaseFrames} from './frame-clock.js';
@@ -48,7 +49,7 @@ export function signalAt(seconds, interference) {
48
49
  const weak = interference >= 1 ? 1 : interference * (0.75 + 0.25 * wander);
49
50
  // a tear lasts two frames: decided once for each pair
50
51
  const pair = Math.floor((seconds * FRAME_RATE) / 2) % 1000;
51
- const chance = (2 * TEAR_RATE / FRAME_RATE) * Math.min(Math.max((interference - 0.3) / 0.5, 0), 1);
52
+ const chance = (2 * TEAR_RATE / FRAME_RATE) * CesiumMath.clamp((interference - 0.3) / 0.5, 0, 1);
52
53
  const tear = interference < 1 && hash(pair, 11) < chance ? 0.2 + 0.6 * hash(pair, 13) : -1;
53
54
  return {weak, tear};
54
55
  }
package/cloud-map.js CHANGED
@@ -1,3 +1,4 @@
1
+ import {Math as CesiumMath} from '@cesium/core';
1
2
  import {imageData} from './weather-map.js';
2
3
 
3
4
  // The clouds' map, from a map of the rain: a radar sees the rain, "not the small cloud droplets and
@@ -44,7 +45,7 @@ function boxBlur(values, width, height, radius, rows) {
44
45
  }
45
46
  let sum = 0;
46
47
  for (let j = -radius; j <= radius; j++) {
47
- sum += line[Math.min(Math.max(j, 0), length - 1)];
48
+ sum += line[CesiumMath.clamp(j, 0, length - 1)];
48
49
  }
49
50
  for (let j = 0; j < length; j++) {
50
51
  values[start + j * step] = sum / (2 * radius + 1);
package/cloud-noise.js CHANGED
@@ -6,6 +6,7 @@
6
6
  // edges. And a square of texels that tiles over the clouds' map, for CloudsDensity.glsl to read
7
7
  // once rather than compute noise at each sample: red and green jitter the map's lookup, blue varies
8
8
  // its coverage. Each channel is stretched over the bytes' range.
9
+ import {Math as CesiumMath} from '@cesium/core';
9
10
 
10
11
  // cells of the base shape across the cube
11
12
  const BASE_CELLS = 4;
@@ -49,7 +50,7 @@ function perlin(period, seed) {
49
50
  const cx = c % period;
50
51
  const cy = Math.floor(c / period) % period;
51
52
  const cz = Math.floor(c / period ** 2);
52
- const theta = 2 * Math.PI * hash(cx, cy, cz, seed);
53
+ const theta = CesiumMath.TWO_PI * hash(cx, cy, cz, seed);
53
54
  const cosPhi = 2 * hash(cx, cy, cz, seed + 1) - 1;
54
55
  const sinPhi = Math.sqrt(1 - cosPhi * cosPhi);
55
56
  gradients.set([sinPhi * Math.cos(theta), sinPhi * Math.sin(theta), cosPhi], 3 * c);
package/clouds.js CHANGED
@@ -1,4 +1,4 @@
1
- import {Cartesian3, Ellipsoid, Matrix4, PixelDatatype, Rectangle} from '@cesium/core';
1
+ import {Cartesian3, Ellipsoid, Matrix4, PixelDatatype, Rectangle, Math as CesiumMath} from '@cesium/core';
2
2
  import {PostProcessStage, PostProcessStageComposite, TextureMagnificationFilter, TextureMinificationFilter} from '@cesium/engine';
3
3
  // @ts-expect-error the engine exports its 3D textures and samplers, but does not type them
4
4
  import {Sampler, Texture3D, TextureWrap} from '@cesium/engine';
@@ -341,7 +341,7 @@ export default class Clouds extends Effect {
341
341
  * @param {number} height meters
342
342
  */
343
343
  shadowExtentFor_(height) {
344
- return Math.min(Math.max(SHADOW_SPREAD * height, SHADOW_EXTENTS[0]), SHADOW_EXTENTS[1]);
344
+ return CesiumMath.clamp(SHADOW_SPREAD * height, SHADOW_EXTENTS[0], SHADOW_EXTENTS[1]);
345
345
  }
346
346
 
347
347
  /**
@@ -1,7 +1,6 @@
1
1
  import {PostProcessStage} from '@cesium/engine';
2
2
  import Effect from './effect.js';
3
3
  import ColorIsolationShader from './shaders/ColorIsolation.js';
4
- import Hsl from './shaders/Hsl.js';
5
4
 
6
5
  /**
7
6
  * Color isolation: one hue keeps its color, the rest of the scene turns gray.
@@ -21,7 +20,7 @@ export default class ColorIsolation extends Effect {
21
20
  /** @override */
22
21
  createStage_() {
23
22
  return new PostProcessStage({
24
- fragmentShader: Hsl + ColorIsolationShader,
23
+ fragmentShader: ColorIsolationShader,
25
24
  uniforms: {
26
25
  hue: () => this.hue_ / 360,
27
26
  range: () => this.range_ / 360,
package/digital-video.js CHANGED
@@ -1,3 +1,4 @@
1
+ import {Math as CesiumMath} from '@cesium/core';
1
2
  import {PostProcessStage} from '@cesium/engine';
2
3
  import Effect from './effect.js';
3
4
  import {acquireFrames, releaseFrames} from './frame-clock.js';
@@ -91,7 +92,7 @@ export default class DigitalVideo extends Effect {
91
92
  return;
92
93
  }
93
94
  const t = (now - episode.start) / episode.duration;
94
- this.severity_ = Math.max(0, Math.sin(Math.PI * Math.min(t, 1))) ** 0.7 * (0.4 + 0.6 * this.breakup_);
95
+ this.severity_ = Math.max(0, Math.sin(CesiumMath.PI * Math.min(t, 1))) ** 0.7 * (0.4 + 0.6 * this.breakup_);
95
96
  this.blackout_ = episode.blackout && Math.abs(t - 0.5) * episode.duration < BLACKOUT / 2 ? 1 : 0;
96
97
  }
97
98
 
package/drone-display.js CHANGED
@@ -139,7 +139,7 @@ export default class DroneDisplay extends Effect {
139
139
  throttle_() {
140
140
  const seconds = performance.now() / 1000 - this.startTime_;
141
141
  const wander = 2 * Math.sin(seconds * 0.3) * Math.sin(seconds * 0.13 + 1);
142
- return Math.round(Math.min(Math.max(CRUISE_THROTTLE + CLIMB_THROTTLE * this.verticalSpeed_ + wander, 0), 100));
142
+ return Math.round(CesiumMath.clamp(CRUISE_THROTTLE + CLIMB_THROTTLE * this.verticalSpeed_ + wander, 0, 100));
143
143
  }
144
144
 
145
145
  /**
package/infrared.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import {PostProcessStage} from '@cesium/engine';
2
2
  import Effect from './effect.js';
3
- import Hsl from './shaders/Hsl.js';
4
3
  import InfraredShader from './shaders/Infrared.js';
5
4
 
6
5
  /**
@@ -19,7 +18,7 @@ export default class Infrared extends Effect {
19
18
  /** @override */
20
19
  createStage_() {
21
20
  return new PostProcessStage({
22
- fragmentShader: Hsl + InfraredShader,
21
+ fragmentShader: InfraredShader,
23
22
  uniforms: {
24
23
  strength: () => this.strength_,
25
24
  },
package/jello.js CHANGED
@@ -1,4 +1,4 @@
1
- import {Cartesian4} from '@cesium/core';
1
+ import {Cartesian4, Math as CesiumMath} from '@cesium/core';
2
2
  import {PostProcessStage} from '@cesium/engine';
3
3
  import Effect from './effect.js';
4
4
  import {acquireFrames, releaseFrames} from './frame-clock.js';
@@ -18,7 +18,7 @@ const MAX_ROTATION = 0.008;
18
18
  // amplitude of the first sinusoid at full amount, per axis: x, y, rotation
19
19
  const MAX_AMPLITUDES = [MAX_SHIFT, 0.5 * MAX_SHIFT, MAX_ROTATION];
20
20
 
21
- const TAU = 2 * Math.PI;
21
+ const TAU = CesiumMath.TWO_PI;
22
22
  /** @type {('x' | 'y' | 'z' | 'w')[]} one sinusoid per component */
23
23
  const COMPONENTS = ['x', 'y', 'z', 'w'];
24
24
 
package/lightning.js CHANGED
@@ -1,4 +1,4 @@
1
- import {Cartesian3, Cartesian4, Cartographic, Event, Matrix4} from '@cesium/core';
1
+ import {Cartesian3, Cartesian4, Cartographic, Event, Matrix4, 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';
@@ -18,14 +18,14 @@ const MIN_DISTANCE = 300;
18
18
  const MIN_CHANNEL = 500;
19
19
  // longest time between two frames, in seconds, after a pause in requestRenderMode
20
20
  const MAX_STEP = 0.1;
21
- const TWO_PI = 2 * Math.PI;
21
+ const TWO_PI = CesiumMath.TWO_PI;
22
22
  // the branches of a channel (Lightning.glsl's BRANCHES), the share of them that exist, and how far down
23
23
  // the channel they leave it and at what angle from it, in radians, at most
24
24
  const BRANCHES = 5;
25
25
  const BRANCH_ODDS = 0.7;
26
26
  const BRANCH_ANGLE = [0.25, 0.75];
27
27
  // how far from the camera's heading a strike lands in front of it, in radians
28
- const AHEAD = Math.PI / 6;
28
+ const AHEAD = CesiumMath.PI / 6;
29
29
  // the radar's intensity, 0 to 1, over which a point goes from never to always struck: from about
30
30
  // 2.5 to 8 mm/h, the map being the square root of the rate over 10 mm/h
31
31
  const STORM = [0.5, 0.9];
@@ -46,7 +46,7 @@ const ABOVE_FADE = 200;
46
46
  * @param {number} x
47
47
  */
48
48
  function smoothstep(edge0, edge1, x) {
49
- const t = Math.min(Math.max((x - edge0) / (edge1 - edge0), 0), 1);
49
+ const t = CesiumMath.clamp((x - edge0) / (edge1 - edge0), 0, 1);
50
50
  return t * t * (3 - 2 * t);
51
51
  }
52
52
 
@@ -114,7 +114,7 @@ export default class Lightning extends Effect {
114
114
  }
115
115
  return strike;
116
116
  });
117
- this.above_ = Math.min(Math.max((scene.camera.positionCartographic.height - this.cloudBase_ + ABOVE_FADE) / (2 * ABOVE_FADE), 0), 1);
117
+ this.above_ = CesiumMath.clamp((scene.camera.positionCartographic.height - this.cloudBase_ + ABOVE_FADE) / (2 * ABOVE_FADE), 0, 1);
118
118
  const view = scene.camera.viewMatrix;
119
119
  for (let i = 0; i < MAX_STRIKES; i++) {
120
120
  const strike = this.strikes_[i];
package/motion-blur.js CHANGED
@@ -1,4 +1,4 @@
1
- import {Matrix4, PixelDatatype} from '@cesium/core';
1
+ import {Matrix4, PixelDatatype, Math as CesiumMath} from '@cesium/core';
2
2
  import {PostProcessStage, PostProcessStageComposite} from '@cesium/engine';
3
3
  import {acquireTerrainDepth, releaseTerrainDepth} from './depth-test.js';
4
4
  import Effect from './effect.js';
@@ -47,7 +47,7 @@ export default class MotionBlur extends Effect {
47
47
  // in requestRenderMode, the time since the previous frame is not the time of the motion
48
48
  exposureScale: () =>
49
49
  (this.strength_ * this.exposure_) /
50
- Math.min(Math.max((performance.now() - this.previousTime_) / 1000, 1 / 240), 1 / 10),
50
+ CesiumMath.clamp((performance.now() - this.previousTime_) / 1000, 1 / 240, 1 / 10),
51
51
  },
52
52
  textureScale: 0.5,
53
53
  pixelDatatype: PixelDatatype.FLOAT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geoblocks/cesium-post-process",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "license": "BSD-3-Clause",
5
5
  "type": "module",
6
6
  "repository": {
@@ -53,5 +53,5 @@
53
53
  "@cesium/core": ">=0.1.0",
54
54
  "@cesium/engine": ">=10"
55
55
  },
56
- "gitHead": "95e74403b31593a8d770f787bff6ae41e4e19dc9"
56
+ "gitHead": "5c6ee12b4e35f46ff733cf628da105f55121fbf3"
57
57
  }
package/precipitation.js CHANGED
@@ -1,4 +1,4 @@
1
- import {Cartesian3, Cartesian4} from '@cesium/core';
1
+ import {Cartesian3, Cartesian4, Math as CesiumMath} from '@cesium/core';
2
2
  import {PostProcessStage, PostProcessStageComposite} from '@cesium/engine';
3
3
  // @ts-expect-error the engine exports its textures, framebuffers and render states, but does not type them
4
4
  import {Framebuffer, RenderState, Texture} from '@cesium/engine';
@@ -45,7 +45,7 @@ const RAIN_EXPOSURE = 1 / 30;
45
45
  const SNOW_EXPOSURE = 1 / 90;
46
46
  // the longest trail, in cells: Precipitation.glsl looks one cell above each pixel's for it
47
47
  const MAX_TRAIL = 1;
48
- const TWO_PI = 2 * Math.PI;
48
+ const TWO_PI = CesiumMath.TWO_PI;
49
49
  // the layers of drops, 2 to 16 m away, a drop every 0.05 rad around at the horizon in the nearest
50
50
  // one, and the radius of its flakes in pixels, halved in each further layer (Precipitation.glsl's
51
51
  // LAYERS, NEAREST, and the rest of its drops)
@@ -74,7 +74,7 @@ const CLOUD_BASE_FADE = 50;
74
74
  * @param {number} x
75
75
  */
76
76
  function smoothstep(edge0, edge1, x) {
77
- const t = Math.min(Math.max((x - edge0) / (edge1 - edge0), 0), 1);
77
+ const t = CesiumMath.clamp((x - edge0) / (edge1 - edge0), 0, 1);
78
78
  return t * t * (3 - 2 * t);
79
79
  }
80
80
 
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from ColorIsolation.glsl.
2
- export default "// Color isolation: one hue keeps its color, the rest of the scene turns gray. After prod80's\n// ReShade Color Isolation (MIT), https://github.com/prod80/prod80-ReShade-Repository\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// the hue kept and the half width of the selection, 0 to 1 for the whole circle\nuniform float hue;\nuniform float range;\n// 0 to 1\nuniform float strength;\nin vec2 v_textureCoordinates;\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n vec3 color = clamp(sceneColor.rgb, 0.0, 1.0);\n float gray = dot(color, vec3(0.2126, 0.7152, 0.0722));\n float h = rgbToHsl(color).x;\n // a triangle around the hue, on the circle\n float r = 1.0 / max(range, 1e-3);\n float weight = max(1.0 - abs((h - hue) * r), 0.0)\n + max(1.0 - abs((h + 1.0 - hue) * r), 0.0)\n + max(1.0 - abs((h - 1.0 - hue) * r), 0.0);\n float w = clamp(weight, 0.0, 1.0);\n float keep = w * w * w * (w * (w * 6.0 - 15.0) + 10.0);\n vec3 isolated = mix(vec3(gray), color, keep);\n out_FragColor = vec4(mix(color, isolated, strength), sceneColor.a);\n}\n";
2
+ export default "// Color isolation: one hue keeps its color, the rest of the scene turns gray. After prod80's\n// ReShade Color Isolation (MIT), https://github.com/prod80/prod80-ReShade-Repository\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// the hue kept and the half width of the selection, 0 to 1 for the whole circle\nuniform float hue;\nuniform float range;\n// 0 to 1\nuniform float strength;\nin vec2 v_textureCoordinates;\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n vec3 color = clamp(sceneColor.rgb, 0.0, 1.0);\n float gray = czm_luminance(color);\n float h = czm_RGBToHSL(color).x;\n // a triangle around the hue, on the circle\n float r = 1.0 / max(range, 1e-3);\n float weight = max(1.0 - abs((h - hue) * r), 0.0)\n + max(1.0 - abs((h + 1.0 - hue) * r), 0.0)\n + max(1.0 - abs((h - 1.0 - hue) * r), 0.0);\n float w = clamp(weight, 0.0, 1.0);\n float keep = w * w * w * (w * (w * 6.0 - 15.0) + 10.0);\n vec3 isolated = mix(vec3(gray), color, keep);\n out_FragColor = vec4(mix(color, isolated, strength), sceneColor.a);\n}\n";
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from DepthEdges.glsl.
2
- export default "// The edges found in the depth, shared by Toy and WhiteModel, after EyeFromDepth: how far a pixel\n// lies, how large a pixel is there, the depth's neighbors around it, how sharply it bends between\n// them (a crease, not across a jump: an edge in front of something farther, nor the gentle bends\n// between the terrain's triangles), the outline on the near side of a jump; the sky's gradient.\n// its samplers read in high precision, whatever comes before (a phone's GPU's default is low)\nprecision highp sampler2D;\nuniform sampler2D depthTexture;\n\n// how much the depth bends, in pixels' sizes, to start and to end lighting an edge\nconst vec2 CREASE = vec2(0.6, 1.6);\n// how far the depth jumps, in pixels' sizes, to be an edge in front of something farther, not a crease\nuniform float jump;\n// the bevel's widest, in pixels: its neighbors read that far at the most\nconst float BEVEL = 8.0;\n// the sky's depth, far off\nconst float SKY = 1e9;\n\n// how far the pixel at uv lies, in meters: in an orthographic view past its near plane, small\n// numbers whose differences keep their precision (its camera thousands of meters back: some\n// phones' GPUs, Mali, lose it subtracting depths that large), in a perspective one from the camera\nfloat depthAt(vec2 uv) {\n if (czm_orthographicIn3D == 1.0) {\n float raw = texture(depthTexture, uv).r;\n return raw >= 1.0 ? SKY : raw * (czm_currentFrustum.y - czm_currentFrustum.x);\n }\n vec4 eye = eyeAt(depthTexture, uv);\n return eye.w == 0.0 ? SKY : -eye.z;\n}\n\n// a pixel's size in meters at a depth: an orthographic view's the same everywhere, a perspective's\n// growing with the depth\nfloat pixelAt(float depth) {\n return 2.0 / (czm_projection[0][0] * czm_viewport.z) * (czm_orthographicIn3D == 1.0 ? 1.0 : depth);\n}\n\n// the depths of the neighbors steps pixels away from uv, less its own c: right, left, up, down\nvec4 neighborsAt(vec2 uv, float c, float steps) {\n vec2 o = steps / czm_viewport.zw;\n return vec4(depthAt(uv + vec2(o.x, 0.0)), depthAt(uv - vec2(o.x, 0.0)), depthAt(uv + vec2(0.0, o.y)), depthAt(uv - vec2(0.0, o.y))) - c;\n}\n\n// how sharply the depth bends between neighbors n a span of meters apart, from 0 to 1, none across\n// a jump; convex where they lie farther than the middle, their sum positive\nfloat creaseOf(vec4 n, float span, float pixel) {\n float far = max(max(abs(n.x), abs(n.y)), max(abs(n.z), abs(n.w)));\n return smoothstep(CREASE.x, CREASE.y, (abs(n.x + n.y) + abs(n.z + n.w)) / span) * (1.0 - step(jump * pixel, far));\n}\n\n// 1 on the outline a pixel wide, on the near side of a jump: a neighbor much farther; n the\n// neighbors steps pixels away, those a pixel away when steps is 1\nfloat outlineAt(vec2 uv, float c, vec4 n, float steps, float pixel) {\n vec2 texel = 1.0 / czm_viewport.zw;\n float farther = steps > 1.0\n ? max(max(depthAt(uv + vec2(texel.x, 0.0)), depthAt(uv - vec2(texel.x, 0.0))), max(depthAt(uv + vec2(0.0, texel.y)), depthAt(uv - vec2(0.0, texel.y)))) - c\n : max(max(n.x, n.y), max(n.z, n.w));\n return step(jump * pixel, farther);\n}\n\n// the sky's gradient at uv, from the top of the screen to its bottom\nvec3 skyAt(vec2 uv, vec4 top, vec4 bottom) {\n return mix(bottom.rgb, top.rgb, smoothstep(0.0, 1.0, uv.y));\n}\n";
2
+ export default "// The edges found in the depth, shared by Toy and WhiteModel, after EyeFromDepth: how far a pixel\n// lies, how large a pixel is there, the depth's neighbors around it, how sharply it bends between\n// them (a crease, not across a jump: an edge in front of something farther, nor the gentle bends\n// between the terrain's triangles), the outline on the near side of a jump; the sky's gradient.\n// its samplers read in high precision, whatever comes before (a phone's GPU's default is low)\nprecision highp sampler2D;\nuniform sampler2D depthTexture;\n\n// how much the depth bends, in pixels' sizes, to start and to end lighting an edge\nconst vec2 CREASE = vec2(0.6, 1.6);\n// how far the depth jumps, in pixels' sizes, to be an edge in front of something farther, not a crease\nuniform float jump;\n// the bevel's widest, in pixels: its neighbors read that far at the most\nconst float BEVEL = 8.0;\n// the sky's depth, far off\nconst float SKY = 1e9;\n\n// how far the pixel at uv lies, in meters: in an orthographic view past its near plane, small\n// numbers whose differences keep their precision (its camera thousands of meters back: some\n// phones' GPUs, Mali, lose it subtracting depths that large), in a perspective one from the camera\nfloat depthAt(vec2 uv) {\n if (czm_orthographicIn3D == 1.0) {\n float raw = texture(depthTexture, uv).r;\n return raw >= 1.0 ? SKY : raw * (czm_currentFrustum.y - czm_currentFrustum.x);\n }\n vec4 eye = eyeAt(depthTexture, uv);\n return eye.w == 0.0 ? SKY : -eye.z;\n}\n\n// a pixel's size in meters at a depth: an orthographic view's the same everywhere, a perspective's\n// growing with the depth\nfloat pixelAt(float depth) {\n return 2.0 / (czm_projection[0][0] * czm_viewport.z) * (czm_orthographicIn3D == 1.0 ? 1.0 : depth);\n}\n\n// the depths of the neighbors steps pixels away from uv, less its own c: right, left, up, down\nvec4 neighborsAt(vec2 uv, float c, float steps) {\n vec2 o = steps / czm_viewport.zw;\n return vec4(depthAt(uv + vec2(o.x, 0.0)), depthAt(uv - vec2(o.x, 0.0)), depthAt(uv + vec2(0.0, o.y)), depthAt(uv - vec2(0.0, o.y))) - c;\n}\n\n// how sharply the depth bends between neighbors n a span of meters apart, from 0 to 1, none across\n// a jump; convex where they lie farther than the middle, their sum positive\nfloat creaseOf(vec4 n, float span, float pixel) {\n float far = max(max(abs(n.x), abs(n.y)), max(abs(n.z), abs(n.w)));\n return smoothstep(CREASE.x, CREASE.y, (abs(n.x + n.y) + abs(n.z + n.w)) / span) * (1.0 - step(jump * pixel, far));\n}\n\n// how far the surface beyond the pixel at uv lies behind it, in one direction o (a pixel's step): the two neighbours\n// that way extrapolated back to the pixel, linearly in an orthographic view, where the depth is affine on the screen,\n// in its reciprocal in a perspective one. A plane at any slope extrapolates exactly to the pixel, so a face seen at a\n// grazing angle is never a jump, only a nearer thing standing in front of a farther one. The nearer of the extrapolation\n// and the neighbour itself: beside a thin thing two pixels away, the extrapolation through it runs far behind the pixel\nfloat farSide(vec2 uv, vec2 o, float c) {\n float d1 = depthAt(uv + o);\n float d2 = depthAt(uv + 2.0 * o);\n if (d1 == SKY) {\n return SKY;\n }\n if (czm_orthographicIn3D == 1.0) {\n return min(d1, d2 == SKY ? d1 : 2.0 * d1 - d2) - c;\n }\n float r = d2 == SKY ? 1.0 / d1 : 2.0 / d1 - 1.0 / d2;\n return (r <= 0.0 ? d1 : min(d1, 1.0 / r)) - c;\n}\n\n// the outline, a pixel wide, on the near side of a jump: the farthest of the four directions' surfaces behind the pixel,\n// against the jump, anti-aliased over a quarter of it each way (n and steps, the creases' neighbours, unused here: the\n// outline reads its own)\nfloat outlineAt(vec2 uv, float c, vec4 n, float steps, float pixel) {\n vec2 texel = 1.0 / czm_viewport.zw;\n float farther = max(max(farSide(uv, vec2(texel.x, 0.0), c), farSide(uv, vec2(-texel.x, 0.0), c)),\n max(farSide(uv, vec2(0.0, texel.y), c), farSide(uv, vec2(0.0, -texel.y), c)));\n float limit = jump * pixel;\n return smoothstep(0.75 * limit, 1.25 * limit, farther);\n}\n\n// the sky's gradient at uv, from the top of the screen to its bottom\nvec3 skyAt(vec2 uv, vec4 top, vec4 bottom) {\n return mix(bottom.rgb, top.rgb, smoothstep(0.0, 1.0, uv.y));\n}\n";
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from DigitalVideo.glsl.
2
- export default "// A digital FPV video feed and its breakups: clean, slightly punchy colors, until a weak signal\n// breaks the picture into macroblocks, some displaced, strips shifted sideways and the color\n// channels split, and at worst a short black screen. A block keeps only its lowest frequencies,\n// as a codec that has thrown its detail away: a gradient between its corners, with fewer colors.\n// The displaced blocks and strips follow three.js's DigitalGlitch. Without the previous frames,\n// there are no freezes.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// how broken the picture is, 0 to 1, and 1 during a black screen\nuniform float severity;\nuniform float blackout;\n// the video frame number, for the random patterns\nuniform float frame;\nin vec2 v_textureCoordinates;\n\n// a macroblock, in CSS pixels\nconst float BLOCK = 16.0;\n\nvoid main() {\n vec2 size = czm_viewport.zw;\n vec2 pixel = gl_FragCoord.xy / czm_pixelRatio;\n vec2 uv = v_textureCoordinates;\n vec4 sceneColor = texture(colorTexture, uv);\n // clean: slightly crushed blacks, a little more color\n vec3 rgb = sceneColor.rgb;\n rgb = mix(vec3(dot(rgb, vec3(0.299, 0.587, 0.114))), rgb, 1.1);\n rgb = max(rgb - 0.02, 0.0) / 0.98;\n\n if (severity > 0.0) {\n // the pattern changes every few frames, as the decoder stutters\n float pattern = floor(frame / 3.0);\n // strips shifted sideways\n float strip = floor(pixel.y / (BLOCK * 2.0));\n if (hash(vec2(strip, pattern + 7.0)) < 0.25 * severity) {\n uv.x += (hash(vec2(strip, pattern + 11.0)) - 0.5) * 0.2 * severity;\n }\n // macroblocks: more of them as the signal gets worse; some show the picture displaced\n vec2 block = floor(uv * size / czm_pixelRatio / BLOCK);\n if (hash(block + pattern * 0.37) < 1.2 * severity) {\n vec2 displaced = hash(block + pattern + 3.0) < 0.3\n ? floor((vec2(hash(block + 5.0), hash(block + 9.0)) - 0.5) * 4.0)\n : vec2(0.0);\n vec2 cell = BLOCK * czm_pixelRatio / size;\n vec2 origin = (block + displaced) * cell;\n // the block's corners, and a gradient between them\n vec2 inset = 0.5 / size;\n vec3 c00 = texture(colorTexture, origin + inset).rgb;\n vec3 c10 = texture(colorTexture, origin + vec2(cell.x - inset.x, inset.y)).rgb;\n vec3 c01 = texture(colorTexture, origin + vec2(inset.x, cell.y - inset.y)).rgb;\n vec3 c11 = texture(colorTexture, origin + cell - inset).rgb;\n vec2 f = fract(uv * size / czm_pixelRatio / BLOCK);\n rgb = mix(mix(c00, c10, f.x), mix(c01, c11, f.x), f.y);\n // fewer colors\n float levels = mix(24.0, 6.0, severity);\n rgb = floor(rgb * levels + 0.5) / levels;\n } else if (uv != v_textureCoordinates) {\n rgb = texture(colorTexture, uv).rgb;\n }\n // the color channels split sideways\n vec2 split = vec2(3.0 * severity * czm_pixelRatio / size.x, 0.0);\n rgb.r = mix(rgb.r, texture(colorTexture, uv + split).r, severity);\n rgb.b = mix(rgb.b, texture(colorTexture, uv - split).b, severity);\n }\n out_FragColor = vec4(rgb * (1.0 - blackout), sceneColor.a);\n}\n";
2
+ export default "// A digital FPV video feed and its breakups: clean, slightly punchy colors, until a weak signal\n// breaks the picture into macroblocks, some displaced, strips shifted sideways and the color\n// channels split, and at worst a short black screen. A block keeps only its lowest frequencies,\n// as a codec that has thrown its detail away: a gradient between its corners, with fewer colors.\n// The displaced blocks and strips follow three.js's DigitalGlitch. Without the previous frames,\n// there are no freezes.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// how broken the picture is, 0 to 1, and 1 during a black screen\nuniform float severity;\nuniform float blackout;\n// the video frame number, for the random patterns\nuniform float frame;\nin vec2 v_textureCoordinates;\n\n// a macroblock, in CSS pixels\nconst float BLOCK = 16.0;\n\nvoid main() {\n vec2 size = czm_viewport.zw;\n vec2 pixel = gl_FragCoord.xy / czm_pixelRatio;\n vec2 uv = v_textureCoordinates;\n vec4 sceneColor = texture(colorTexture, uv);\n // clean: slightly crushed blacks, a little more color\n vec3 rgb = sceneColor.rgb;\n rgb = czm_saturation(rgb, 1.1);\n rgb = max(rgb - 0.02, 0.0) / 0.98;\n\n if (severity > 0.0) {\n // the pattern changes every few frames, as the decoder stutters\n float pattern = floor(frame / 3.0);\n // strips shifted sideways\n float strip = floor(pixel.y / (BLOCK * 2.0));\n if (hash(vec2(strip, pattern + 7.0)) < 0.25 * severity) {\n uv.x += (hash(vec2(strip, pattern + 11.0)) - 0.5) * 0.2 * severity;\n }\n // macroblocks: more of them as the signal gets worse; some show the picture displaced\n vec2 block = floor(uv * size / czm_pixelRatio / BLOCK);\n if (hash(block + pattern * 0.37) < 1.2 * severity) {\n vec2 displaced = hash(block + pattern + 3.0) < 0.3\n ? floor((vec2(hash(block + 5.0), hash(block + 9.0)) - 0.5) * 4.0)\n : vec2(0.0);\n vec2 cell = BLOCK * czm_pixelRatio / size;\n vec2 origin = (block + displaced) * cell;\n // the block's corners, and a gradient between them\n vec2 inset = 0.5 / size;\n vec3 c00 = texture(colorTexture, origin + inset).rgb;\n vec3 c10 = texture(colorTexture, origin + vec2(cell.x - inset.x, inset.y)).rgb;\n vec3 c01 = texture(colorTexture, origin + vec2(inset.x, cell.y - inset.y)).rgb;\n vec3 c11 = texture(colorTexture, origin + cell - inset).rgb;\n vec2 f = fract(uv * size / czm_pixelRatio / BLOCK);\n rgb = mix(mix(c00, c10, f.x), mix(c01, c11, f.x), f.y);\n // fewer colors\n float levels = mix(24.0, 6.0, severity);\n rgb = floor(rgb * levels + 0.5) / levels;\n } else if (uv != v_textureCoordinates) {\n rgb = texture(colorTexture, uv).rgb;\n }\n // the color channels split sideways\n vec2 split = vec2(3.0 * severity * czm_pixelRatio / size.x, 0.0);\n rgb.r = mix(rgb.r, texture(colorTexture, uv + split).r, severity);\n rgb.b = mix(rgb.b, texture(colorTexture, uv - split).b, severity);\n }\n out_FragColor = vec4(rgb * (1.0 - blackout), sceneColor.a);\n}\n";
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from Infrared.glsl.
2
- export default "// Black and white infrared film: foliage, which reflects the near infrared, turns bright, the sky\n// and water dark. The gray is the lightness raised or lowered by a weight per hue. After prod80's\n// ReShade Black & White (MIT), its Infrared preset, https://github.com/prod80/prod80-ReShade-Repository\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// 0 to 1\nuniform float strength;\nin vec2 v_textureCoordinates;\n\n// weights of the red, yellow, green, cyan, blue and magenta hues\nconst float RED = -1.35;\nconst float YELLOW = 2.35;\nconst float GREEN = 1.35;\nconst float CYAN = -1.35;\nconst float BLUE = -1.6;\nconst float MAGENTA = -1.07;\n// shape of the hue selections\nconst float CURVE = 1.5;\n\nfloat curve(float x, float k) {\n float s = sign(x - 0.5);\n float o = 0.5 * (1.0 + s);\n return o - 0.5 * s * pow(max(2.0 * (o - s * x), 0.0), k);\n}\n\n// how much of the hue centered at center there is in hue\nfloat hueWeight(float hue, float center) {\n return curve(max(1.0 - abs((hue - center) * 6.0), 0.0), CURVE);\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n vec3 hsl = rgbToHsl(clamp(sceneColor.rgb, 0.0, 1.0));\n float h = hsl.x;\n float weight = RED * (hueWeight(h, 0.0) + hueWeight(h, 1.0))\n + YELLOW * hueWeight(h, 1.0 / 6.0)\n + GREEN * hueWeight(h, 2.0 / 6.0)\n + CYAN * hueWeight(h, 3.0 / 6.0)\n + BLUE * hueWeight(h, 4.0 / 6.0)\n + MAGENTA * hueWeight(h, 5.0 / 6.0);\n float saturation = hsl.y * (1.0 - hsl.y) + hsl.y;\n float gray = clamp(hsl.z + hsl.z * weight * saturation * (1.0 - hsl.z), 0.0, 1.0);\n out_FragColor = vec4(mix(sceneColor.rgb, vec3(gray), strength), sceneColor.a);\n}\n";
2
+ export default "// Black and white infrared film: foliage, which reflects the near infrared, turns bright, the sky\n// and water dark. The gray is the lightness raised or lowered by a weight per hue. After prod80's\n// ReShade Black & White (MIT), its Infrared preset, https://github.com/prod80/prod80-ReShade-Repository\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// 0 to 1\nuniform float strength;\nin vec2 v_textureCoordinates;\n\n// weights of the red, yellow, green, cyan, blue and magenta hues\nconst float RED = -1.35;\nconst float YELLOW = 2.35;\nconst float GREEN = 1.35;\nconst float CYAN = -1.35;\nconst float BLUE = -1.6;\nconst float MAGENTA = -1.07;\n// shape of the hue selections\nconst float CURVE = 1.5;\n\nfloat curve(float x, float k) {\n float s = sign(x - 0.5);\n float o = 0.5 * (1.0 + s);\n return o - 0.5 * s * pow(max(2.0 * (o - s * x), 0.0), k);\n}\n\n// how much of the hue centered at center there is in hue\nfloat hueWeight(float hue, float center) {\n return curve(max(1.0 - abs((hue - center) * 6.0), 0.0), CURVE);\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n vec3 hsl = czm_RGBToHSL(clamp(sceneColor.rgb, 0.0, 1.0));\n float h = hsl.x;\n float weight = RED * (hueWeight(h, 0.0) + hueWeight(h, 1.0))\n + YELLOW * hueWeight(h, 1.0 / 6.0)\n + GREEN * hueWeight(h, 2.0 / 6.0)\n + CYAN * hueWeight(h, 3.0 / 6.0)\n + BLUE * hueWeight(h, 4.0 / 6.0)\n + MAGENTA * hueWeight(h, 5.0 / 6.0);\n float saturation = hsl.y * (1.0 - hsl.y) + hsl.y;\n float gray = clamp(hsl.z + hsl.z * weight * saturation * (1.0 - hsl.z), 0.0, 1.0);\n out_FragColor = vec4(mix(sceneColor.rgb, vec3(gray), strength), sceneColor.a);\n}\n";
@@ -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;\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\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, 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 // eye.w is 0 for the sky\n snow *= coverage * eye.w;\n float brightness = dot(sceneColor.rgb, vec3(0.2126, 0.7152, 0.0722));\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;\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\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, 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 // eye.w is 0 for the sky\n snow *= coverage * eye.w;\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";
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from Spotlight.glsl.
2
- export default "// A searchlight above the focus, pointing down: it throws a pool of warm light radius meters\n// wide on flat ground, with a soft penumbra, shades the relief under it and lights up the haze\n// in its beam, from SpotlightBeam.glsl; the rest of the scene darkens and loses its color. The\n// cone and the falloff follow three.js's SpotLight. Needs EyeFromDepth, Normal, Filmic and\n// SpotlightCone.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// the beam's haze and the log2 of the scene distance, at a fraction of the resolution\nuniform sampler2D beamTexture;\n// 0 to 1\nuniform float darkness;\n// brightness of the beam in the air, 0 to 1\nuniform float beam;\n// brightness of the light, 1 for the searchlight: the pool and the beam scale with it\nuniform float power;\nin vec2 v_textureCoordinates;\n\nconst vec3 LIGHT_COLOR = vec3(1.0, 0.93, 0.8);\nconst float INTENSITY = 1.4;\n// difference of the log2 of the distances, about 10 %, under which the beam's texels around a\n// pixel are interpolated\nconst float SOFT_DEPTH = 0.14;\n\n// light reaching a point fromLight away from the light: the cone around the axis with its\n// penumbra, and the inverse square, 1 at the focus, capped for what is close to the light\nfloat spotAt(vec3 fromLight, vec3 axis, float height, float coneCos, float penumbraCos) {\n float lightDistance = length(fromLight);\n float spot = smoothstep(coneCos, penumbraCos, dot(fromLight, -axis) / lightDistance);\n return spot * min(height * height / (lightDistance * lightDistance), 4.0);\n}\n\n// the beam's haze at the pixel, from the four texels of its lower resolution around it:\n// interpolated where they are all at the pixel's distance, else the one nearest to it, so that the\n// haze stops at the ridges in front of it rather than spilling onto them\nfloat beamAt(vec2 uv, float depth) {\n ivec2 size = textureSize(beamTexture, 0);\n vec2 position = uv * vec2(size) - 0.5;\n ivec2 base = ivec2(floor(position));\n vec2 f = fract(position);\n float interpolated = 0.0;\n float nearest = 0.0;\n float nearestGap = 1e38;\n float widestGap = 0.0;\n for (int i = 0; i < 4; i++) {\n ivec2 offset = ivec2(i & 1, i >> 1);\n vec2 texel = texelFetch(beamTexture, clamp(base + offset, ivec2(0), size - 1), 0).rg;\n float gap = abs(texel.g - depth);\n vec2 weights = mix(1.0 - f, f, vec2(offset));\n interpolated += weights.x * weights.y * texel.r;\n if (gap < nearestGap) {\n nearestGap = gap;\n nearest = texel.r;\n }\n widestGap = max(widestGap, gap);\n }\n return widestGap < SOFT_DEPTH ? interpolated : nearest;\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n Cone spot;\n if (!spotlightCone(spot)) {\n out_FragColor = sceneColor;\n return;\n }\n vec3 color = sceneColor.rgb;\n vec3 ambient = mix(color, vec3(dot(color, vec3(0.2126, 0.7152, 0.0722))), darkness) * (1.0 - darkness);\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n\n // eye.w is 0 for the sky, where the view ray goes on\n float sceneDistance = eye.w == 0.0 ? 1e30 : length(eye.xyz);\n float scattered = beam > 0.0 ? beamAt(v_textureCoordinates, log2(sceneDistance)) : 0.0;\n vec3 haze = LIGHT_COLOR * (power * beam * scattered);\n if (eye.w == 0.0) {\n out_FragColor = vec4(filmic(ambient + haze), sceneColor.a);\n return;\n }\n\n vec3 toLight = spot.light - eye.xyz;\n // the normal over wider steps where the surface is seen at a grazing angle, where the facets\n // of the terrain mesh would show as blotches; over fine ones elsewhere, for the relief\n vec3 coarse = normalize(normalAt(depthTexture, v_textureCoordinates, eye.xyz, 6.0 * czm_pixelRatio)\n + normalAt(depthTexture, v_textureCoordinates, eye.xyz, 18.0 * czm_pixelRatio));\n float grazing = smoothstep(0.7, 0.95, 1.0 - abs(dot(coarse, normalize(-eye.xyz))));\n vec3 normal = normalize(mix(smoothNormalAt(depthTexture, v_textureCoordinates, eye.xyz), coarse, grazing));\n float lambert = max(dot(normal, normalize(toLight)), 0.0);\n vec3 light = LIGHT_COLOR * (power * INTENSITY * spotAt(-toLight, up, spot.height, spot.coneCos, spot.penumbraCos) * lambert);\n out_FragColor = vec4(filmic(ambient + color * light + haze), sceneColor.a);\n}\n";
2
+ export default "// A searchlight above the focus, pointing down: it throws a pool of warm light radius meters\n// wide on flat ground, with a soft penumbra, shades the relief under it and lights up the haze\n// in its beam, from SpotlightBeam.glsl; the rest of the scene darkens and loses its color. The\n// cone and the falloff follow three.js's SpotLight. Needs EyeFromDepth, Normal, Filmic and\n// SpotlightCone.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// the beam's haze and the log2 of the scene distance, at a fraction of the resolution\nuniform sampler2D beamTexture;\n// 0 to 1\nuniform float darkness;\n// brightness of the beam in the air, 0 to 1\nuniform float beam;\n// brightness of the light, 1 for the searchlight: the pool and the beam scale with it\nuniform float power;\nin vec2 v_textureCoordinates;\n\nconst vec3 LIGHT_COLOR = vec3(1.0, 0.93, 0.8);\nconst float INTENSITY = 1.4;\n// difference of the log2 of the distances, about 10 %, under which the beam's texels around a\n// pixel are interpolated\nconst float SOFT_DEPTH = 0.14;\n\n// light reaching a point fromLight away from the light: the cone around the axis with its\n// penumbra, and the inverse square, 1 at the focus, capped for what is close to the light\nfloat spotAt(vec3 fromLight, vec3 axis, float height, float coneCos, float penumbraCos) {\n float lightDistance = length(fromLight);\n float spot = smoothstep(coneCos, penumbraCos, dot(fromLight, -axis) / lightDistance);\n return spot * min(height * height / (lightDistance * lightDistance), 4.0);\n}\n\n// the beam's haze at the pixel, from the four texels of its lower resolution around it:\n// interpolated where they are all at the pixel's distance, else the one nearest to it, so that the\n// haze stops at the ridges in front of it rather than spilling onto them\nfloat beamAt(vec2 uv, float depth) {\n ivec2 size = textureSize(beamTexture, 0);\n vec2 position = uv * vec2(size) - 0.5;\n ivec2 base = ivec2(floor(position));\n vec2 f = fract(position);\n float interpolated = 0.0;\n float nearest = 0.0;\n float nearestGap = 1e38;\n float widestGap = 0.0;\n for (int i = 0; i < 4; i++) {\n ivec2 offset = ivec2(i & 1, i >> 1);\n vec2 texel = texelFetch(beamTexture, clamp(base + offset, ivec2(0), size - 1), 0).rg;\n float gap = abs(texel.g - depth);\n vec2 weights = mix(1.0 - f, f, vec2(offset));\n interpolated += weights.x * weights.y * texel.r;\n if (gap < nearestGap) {\n nearestGap = gap;\n nearest = texel.r;\n }\n widestGap = max(widestGap, gap);\n }\n return widestGap < SOFT_DEPTH ? interpolated : nearest;\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n Cone spot;\n if (!spotlightCone(spot)) {\n out_FragColor = sceneColor;\n return;\n }\n vec3 color = sceneColor.rgb;\n vec3 ambient = czm_saturation(color, 1.0 - darkness) * (1.0 - darkness);\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n\n // eye.w is 0 for the sky, where the view ray goes on\n float sceneDistance = eye.w == 0.0 ? 1e30 : length(eye.xyz);\n float scattered = beam > 0.0 ? beamAt(v_textureCoordinates, log2(sceneDistance)) : 0.0;\n vec3 haze = LIGHT_COLOR * (power * beam * scattered);\n if (eye.w == 0.0) {\n out_FragColor = vec4(filmic(ambient + haze), sceneColor.a);\n return;\n }\n\n vec3 toLight = spot.light - eye.xyz;\n // the normal over wider steps where the surface is seen at a grazing angle, where the facets\n // of the terrain mesh would show as blotches; over fine ones elsewhere, for the relief\n vec3 coarse = normalize(normalAt(depthTexture, v_textureCoordinates, eye.xyz, 6.0 * czm_pixelRatio)\n + normalAt(depthTexture, v_textureCoordinates, eye.xyz, 18.0 * czm_pixelRatio));\n float grazing = smoothstep(0.7, 0.95, 1.0 - abs(dot(coarse, normalize(-eye.xyz))));\n vec3 normal = normalize(mix(smoothNormalAt(depthTexture, v_textureCoordinates, eye.xyz), coarse, grazing));\n float lambert = max(dot(normal, normalize(toLight)), 0.0);\n vec3 light = LIGHT_COLOR * (power * INTENSITY * spotAt(-toLight, up, spot.height, spot.coneCos, spot.penumbraCos) * lambert);\n out_FragColor = vec4(filmic(ambient + color * light + haze), sceneColor.a);\n}\n";
package/shaders/Super8.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from Super8.glsl.
2
- export default "// A Super 8 home movie: faded warm colors, halation around the highlights, heavy grain, the frame\n// drifting in the gate, flicker, light leaks, a strong vignette and the rounded corners of the\n// camera gate.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// the scene blurred, for the halation\nuniform sampler2D blurTexture;\n// seconds\nuniform float time;\n// 0 to 1\nuniform float fade;\nuniform float grain;\n// in CSS pixels\nuniform float weave;\n// 0 to 1\nuniform float flicker;\nuniform float lightLeaks;\nuniform float halation;\n// width over height of the visible frame, 0 for the whole canvas\nuniform float aspectRatio;\nin vec2 v_textureCoordinates;\n\n// Super 8 runs at 18 frames per second\nconst float FRAME_RATE = 18.0;\n// radius of the rounded corners, as a fraction of the frame height\nconst float CORNER = 0.05;\nconst vec3 LEAK = vec3(1.0, 0.45, 0.15);\n// size of the grain clumps, in CSS pixels, and how much they differ between the dye layers\nconst float GRAIN_SIZE = 2.0;\nconst float GRAIN_COLOR = 0.3;\n// light scattered back through the film base is red-orange\nconst vec3 HALATION = vec3(1.0, 0.35, 0.15);\n\nfloat luminance(vec3 color) {\n return dot(color, vec3(0.2126, 0.7152, 0.0722));\n}\n\nfloat hash(float n) {\n return fract(sin(n) * 43758.5453);\n}\n\n// smooth noise in [0, 1) over time\nfloat noise(float t) {\n float i = floor(t);\n float f = fract(t);\n return mix(hash(i), hash(i + 1.0), f * f * (3.0 - 2.0 * f));\n}\n\nvoid main() {\n vec2 size = czm_viewport.zw;\n // the time of the film frame, and its number for the noise\n float t = floor(time * FRAME_RATE) / FRAME_RATE;\n float frame = mod(floor(time * FRAME_RATE), 1000.0);\n\n // gate weave: the whole picture drifts by a pixel or two, from frame to frame\n vec2 drift = vec2(noise(t * 3.0), noise(t * 3.0 + 17.0)) * 2.0 - 1.0;\n vec2 uv = v_textureCoordinates + weave * czm_pixelRatio * drift / size;\n\n // the gate, with rounded corners, in pixels\n vec2 frameHalf = 0.5 * frameSize(aspectRatio) * size;\n float radius = CORNER * 2.0 * frameHalf.y;\n vec2 q = abs((v_textureCoordinates - 0.5) * size) - frameHalf + radius;\n float outside = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;\n float gate = 1.0 - smoothstep(-czm_pixelRatio, czm_pixelRatio, outside);\n // -0.5 to 0.5 inside the frame\n vec2 inFrame = (v_textureCoordinates - 0.5) * size / (2.0 * frameHalf);\n\n vec3 color = texture(colorTexture, uv).rgb;\n // faded film: less saturation, lifted blacks, a warm cast\n color = mix(color, vec3(luminance(color)), 0.4 * fade);\n color = mix(color, color * vec3(1.05, 0.95, 0.78) * 0.88 + vec3(0.09, 0.07, 0.05), fade);\n // halation: a warm glow bleeding around the highlights\n float glow = smoothstep(0.45, 0.9, luminance(texture(blurTexture, uv).rgb));\n color = 1.0 - (1.0 - color) * (1.0 - halation * glow * HALATION);\n // vignette\n color *= 1.0 - 0.6 * smoothstep(0.35, 0.85, length(inFrame));\n // a warm light leak, drifting along the right edge and coming and going\n vec2 leakAt = vec2(0.6, noise(time * 0.15) - 0.5);\n float leak = lightLeaks * smoothstep(0.3, 0.9, noise(time * 0.3 + 5.0)) * exp(-6.0 * dot(inFrame - leakAt, inFrame - leakAt));\n color = 1.0 - (1.0 - color) * (1.0 - leak * LEAK);\n // flicker: the brightness changes from frame to frame\n color *= 1.0 + flicker * (2.0 * hash(frame) - 1.0);\n // grain: clumps a few pixels wide, a new pattern with each film frame, a little different in\n // each dye layer, heaviest in the shadows and midtones and fading out in the highlights\n vec2 grainAt = gl_FragCoord.xy / (GRAIN_SIZE * czm_pixelRatio);\n float z = frame * 1.7 + 0.5;\n float mono = gradientNoise(grainAt, z);\n vec3 layers = vec3(gradientNoise(grainAt, z + 101.0), gradientNoise(grainAt, z + 211.0), gradientNoise(grainAt, z + 307.0));\n vec3 noise = mix(vec3(mono), layers, GRAIN_COLOR);\n float y = clamp(luminance(color), 0.0, 1.0);\n color += grain * 1.4 * noise * 2.0 * (1.0 - y) * (0.5 + y);\n\n out_FragColor = vec4(clamp(color, 0.0, 1.0) * gate, 1.0);\n}\n";
2
+ export default "// A Super 8 home movie: faded warm colors, halation around the highlights, heavy grain, the frame\n// drifting in the gate, flicker, light leaks, a strong vignette and the rounded corners of the\n// camera gate.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// the scene blurred, for the halation\nuniform sampler2D blurTexture;\n// seconds\nuniform float time;\n// 0 to 1\nuniform float fade;\nuniform float grain;\n// in CSS pixels\nuniform float weave;\n// 0 to 1\nuniform float flicker;\nuniform float lightLeaks;\nuniform float halation;\n// width over height of the visible frame, 0 for the whole canvas\nuniform float aspectRatio;\nin vec2 v_textureCoordinates;\n\n// Super 8 runs at 18 frames per second\nconst float FRAME_RATE = 18.0;\n// radius of the rounded corners, as a fraction of the frame height\nconst float CORNER = 0.05;\nconst vec3 LEAK = vec3(1.0, 0.45, 0.15);\n// size of the grain clumps, in CSS pixels, and how much they differ between the dye layers\nconst float GRAIN_SIZE = 2.0;\nconst float GRAIN_COLOR = 0.3;\n// light scattered back through the film base is red-orange\nconst vec3 HALATION = vec3(1.0, 0.35, 0.15);\n\nfloat hash(float n) {\n return fract(sin(n) * 43758.5453);\n}\n\n// smooth noise in [0, 1) over time\nfloat noise(float t) {\n float i = floor(t);\n float f = fract(t);\n return mix(hash(i), hash(i + 1.0), f * f * (3.0 - 2.0 * f));\n}\n\nvoid main() {\n vec2 size = czm_viewport.zw;\n // the time of the film frame, and its number for the noise\n float t = floor(time * FRAME_RATE) / FRAME_RATE;\n float frame = mod(floor(time * FRAME_RATE), 1000.0);\n\n // gate weave: the whole picture drifts by a pixel or two, from frame to frame\n vec2 drift = vec2(noise(t * 3.0), noise(t * 3.0 + 17.0)) * 2.0 - 1.0;\n vec2 uv = v_textureCoordinates + weave * czm_pixelRatio * drift / size;\n\n // the gate, with rounded corners, in pixels\n vec2 frameHalf = 0.5 * frameSize(aspectRatio) * size;\n float radius = CORNER * 2.0 * frameHalf.y;\n vec2 q = abs((v_textureCoordinates - 0.5) * size) - frameHalf + radius;\n float outside = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;\n float gate = 1.0 - smoothstep(-czm_pixelRatio, czm_pixelRatio, outside);\n // -0.5 to 0.5 inside the frame\n vec2 inFrame = (v_textureCoordinates - 0.5) * size / (2.0 * frameHalf);\n\n vec3 color = texture(colorTexture, uv).rgb;\n // faded film: less saturation, lifted blacks, a warm cast\n color = mix(color, vec3(czm_luminance(color)), 0.4 * fade);\n color = mix(color, color * vec3(1.05, 0.95, 0.78) * 0.88 + vec3(0.09, 0.07, 0.05), fade);\n // halation: a warm glow bleeding around the highlights\n float glow = smoothstep(0.45, 0.9, czm_luminance(texture(blurTexture, uv).rgb));\n color = 1.0 - (1.0 - color) * (1.0 - halation * glow * HALATION);\n // vignette\n color *= 1.0 - 0.6 * smoothstep(0.35, 0.85, length(inFrame));\n // a warm light leak, drifting along the right edge and coming and going\n vec2 leakAt = vec2(0.6, noise(time * 0.15) - 0.5);\n float leak = lightLeaks * smoothstep(0.3, 0.9, noise(time * 0.3 + 5.0)) * exp(-6.0 * dot(inFrame - leakAt, inFrame - leakAt));\n color = 1.0 - (1.0 - color) * (1.0 - leak * LEAK);\n // flicker: the brightness changes from frame to frame\n color *= 1.0 + flicker * (2.0 * hash(frame) - 1.0);\n // grain: clumps a few pixels wide, a new pattern with each film frame, a little different in\n // each dye layer, heaviest in the shadows and midtones and fading out in the highlights\n vec2 grainAt = gl_FragCoord.xy / (GRAIN_SIZE * czm_pixelRatio);\n float z = frame * 1.7 + 0.5;\n float mono = gradientNoise(grainAt, z);\n vec3 layers = vec3(gradientNoise(grainAt, z + 101.0), gradientNoise(grainAt, z + 211.0), gradientNoise(grainAt, z + 307.0));\n vec3 noise = mix(vec3(mono), layers, GRAIN_COLOR);\n float y = clamp(czm_luminance(color), 0.0, 1.0);\n color += grain * 1.4 * noise * 2.0 * (1.0 - y) * (0.5 + y);\n\n out_FragColor = vec4(clamp(color, 0.0, 1.0) * gate, 1.0);\n}\n";
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from TiltShift.glsl.
2
- export default "// Tilt-shift: a narrow band of sharpness around the focal distance and the blurred image further\n// off, with the punchy colors of miniature photographs. Distances compare as ratios, like a lens:\n// sharp within range factors of two of the focal distance, fully blurred at twice that. An\n// orthographic camera has no distance to compare: a band across the screen, as a miniature's fake,\n// sharp within range of the view's height above and below the focus, whatever stands there.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\nuniform sampler2D blurTexture;\nuniform sampler2D depthTexture;\n// in eye coordinates; w is 0 for what is in the middle of the screen\nuniform vec4 focus;\n// in factors of two of the focal distance, or shares of the view's height (orthographic)\nuniform float range;\n// 0 to 1\nuniform float saturation;\nin vec2 v_textureCoordinates;\n\nvoid main() {\n vec4 target = focus.w > 0.0 ? focus : eyeAt(depthTexture, vec2(0.5));\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n float fromCamera = eye.w == 0.0 ? 1e30 : length(eye.xyz);\n // the orthographic view's height: its projection's y scale is 2 over it\n float off = czm_orthographicIn3D == 1.0\n ? abs(eye.y - target.y) * czm_projection[1][1] / 2.0\n : abs(log2(fromCamera / length(target.xyz)));\n // no blur without a focus in front of the camera (the sky in the middle, a focus behind)\n float blur = target.w > 0.0 && target.z < 0.0 ? smoothstep(range, 2.0 * range, off) : 0.0;\n vec4 sceneColor = mix(texture(colorTexture, v_textureCoordinates), texture(blurTexture, v_textureCoordinates), blur);\n\n vec3 color = sceneColor.rgb;\n color = mix(vec3(dot(color, vec3(0.2126, 0.7152, 0.0722))), color, 1.0 + saturation);\n color = mix(color, smoothstep(0.0, 1.0, color), saturation);\n out_FragColor = vec4(clamp(color, 0.0, 1.0), sceneColor.a);\n}\n";
2
+ export default "// Tilt-shift: a narrow band of sharpness around the focal distance and the blurred image further\n// off, with the punchy colors of miniature photographs. Distances compare as ratios, like a lens:\n// sharp within range factors of two of the focal distance, fully blurred at twice that. An\n// orthographic camera has no distance to compare: a band across the screen, as a miniature's fake,\n// sharp within range of the view's height above and below the focus, whatever stands there.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\nuniform sampler2D blurTexture;\nuniform sampler2D depthTexture;\n// in eye coordinates; w is 0 for what is in the middle of the screen\nuniform vec4 focus;\n// in factors of two of the focal distance, or shares of the view's height (orthographic)\nuniform float range;\n// 0 to 1\nuniform float saturation;\nin vec2 v_textureCoordinates;\n\nvoid main() {\n vec4 target = focus.w > 0.0 ? focus : eyeAt(depthTexture, vec2(0.5));\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n float fromCamera = eye.w == 0.0 ? 1e30 : length(eye.xyz);\n // the orthographic view's height: its projection's y scale is 2 over it\n float off = czm_orthographicIn3D == 1.0\n ? abs(eye.y - target.y) * czm_projection[1][1] / 2.0\n : abs(log2(fromCamera / length(target.xyz)));\n // no blur without a focus in front of the camera (the sky in the middle, a focus behind)\n float blur = target.w > 0.0 && target.z < 0.0 ? smoothstep(range, 2.0 * range, off) : 0.0;\n vec4 sceneColor = mix(texture(colorTexture, v_textureCoordinates), texture(blurTexture, v_textureCoordinates), blur);\n\n vec3 color = sceneColor.rgb;\n color = czm_saturation(color, 1.0 + saturation);\n color = mix(color, smoothstep(0.0, 1.0, color), saturation);\n out_FragColor = vec4(clamp(color, 0.0, 1.0), sceneColor.a);\n}\n";
package/shaders/Toy.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from Toy.glsl.
2
- export default "// A toy's look, as an isometric game's, after DepthEdges: the edges lit where the surface bends\n// outward and shaded where it bends inward, rounded as molded plastic close up; an outline along\n// the jumps, on their near side, a darker shade of the color; the colors a little richer and\n// warmer; the sky, if asked, a soft gradient.\n// its samplers read in high precision, whatever comes before (a phone's GPU's default is low)\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// how much brighter a convex edge gets, how much darker a concave one\nuniform float light;\nuniform float shade;\n// the edges' bevel, in meters, a pixel at the least; how much darker the outline, 0 for none\nuniform float bevel;\nuniform float outline;\n// the colors' saturation, and a factor each of red, green and blue\nuniform float saturation;\nuniform vec3 warm;\n// the sky's gradient, from the top of the screen to its bottom; w 0 to keep the sky\nuniform vec4 skyTop;\nuniform vec4 skyBottom;\nin vec2 v_textureCoordinates;\n\nvoid main() {\n vec2 uv = v_textureCoordinates;\n vec4 sceneColor = texture(colorTexture, uv);\n float c = depthAt(uv);\n if (c == SKY) {\n out_FragColor = skyTop.w > 0.0 ? vec4(skyAt(uv, skyTop, skyBottom), 1.0) : sceneColor;\n return;\n }\n float pixel = pixelAt(c);\n // the neighbors a bevel's width away: the bend measured over it, strongest on the edge and fading\n // to nothing a bevel off, a rounded edge's highlight\n float steps = clamp(bevel / pixel, 1.0, BEVEL);\n vec4 n = neighborsAt(uv, c, steps);\n float crease = creaseOf(n, steps * pixel, pixel);\n vec3 color = sceneColor.rgb * (n.x + n.y + n.z + n.w > 0.0 ? 1.0 + light * crease : 1.0 - shade * crease);\n if (outline > 0.0) {\n color *= 1.0 - outline * outlineAt(uv, c, n, steps, pixel);\n }\n color = mix(vec3(dot(color, vec3(0.2126, 0.7152, 0.0722))), color, saturation) * warm;\n out_FragColor = vec4(clamp(color, 0.0, 1.0), sceneColor.a);\n}\n";
2
+ export default "// A toy's look, as an isometric game's, after DepthEdges: the edges lit where the surface bends\n// outward and shaded where it bends inward, rounded as molded plastic close up; an outline along\n// the jumps, on their near side, a darker shade of the color; the colors a little richer and\n// warmer; the sky, if asked, a soft gradient.\n// its samplers read in high precision, whatever comes before (a phone's GPU's default is low)\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// how much brighter a convex edge gets, how much darker a concave one\nuniform float light;\nuniform float shade;\n// the edges' bevel, in meters, a pixel at the least; how much darker the outline, 0 for none\nuniform float bevel;\nuniform float outline;\n// the colors' saturation, and a factor each of red, green and blue\nuniform float saturation;\nuniform vec3 warm;\n// the sky's gradient, from the top of the screen to its bottom; w 0 to keep the sky\nuniform vec4 skyTop;\nuniform vec4 skyBottom;\nin vec2 v_textureCoordinates;\n\nvoid main() {\n vec2 uv = v_textureCoordinates;\n vec4 sceneColor = texture(colorTexture, uv);\n float c = depthAt(uv);\n if (c == SKY) {\n out_FragColor = skyTop.w > 0.0 ? vec4(skyAt(uv, skyTop, skyBottom), 1.0) : sceneColor;\n return;\n }\n float pixel = pixelAt(c);\n // the neighbors a bevel's width away: the bend measured over it, strongest on the edge and fading\n // to nothing a bevel off, a rounded edge's highlight\n float steps = clamp(bevel / pixel, 1.0, BEVEL);\n vec3 color = sceneColor.rgb;\n // the creases' neighbours read only when they light or shade: a scene drawing its own edges asks for neither\n if (light != 0.0 || shade != 0.0) {\n vec4 n = neighborsAt(uv, c, steps);\n float crease = creaseOf(n, steps * pixel, pixel);\n color *= n.x + n.y + n.z + n.w > 0.0 ? 1.0 + light * crease : 1.0 - shade * crease;\n }\n if (outline > 0.0) {\n color *= 1.0 - outline * outlineAt(uv, c, vec4(0.0), steps, pixel);\n }\n color = czm_saturation(color, saturation) * warm;\n out_FragColor = vec4(clamp(color, 0.0, 1.0), sceneColor.a);\n}\n";
@@ -1,2 +1,2 @@
1
1
  // This file is automatically rebuilt from WhiteModel.glsl.
2
- export default "// A white model's look, as an architect's plaster model or clay render, after DepthEdges: every\n// color turned to its value and the values pressed into a narrow range of whites (an accent's hue\n// kept, if asked; the darkest left black, a section's poché), so the light and the shapes alone\n// tell the surfaces apart; darker where surfaces meet, an ambient obscurance from the depth; the\n// creases lit and shaded, a fine outline along the jumps; the sky, if asked, a pale gradient.\n// its samplers read in high precision, whatever comes before (a phone's GPU's default is low)\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// how much brighter a convex edge gets, how much darker a concave one\nuniform float light;\nuniform float shade;\n// the edges' bevel, in meters, a pixel at the least; how much darker the outline, 0 for none\nuniform float bevel;\nuniform float outline;\n// the colors' saturation, 0 for white, and a factor each of red, green and blue: the plaster's tint\nuniform float saturation;\nuniform vec3 warm;\n// the darkest and the lightest values, the scene's black and white; the value under which a color\n// keeps it, 0 for none\nuniform vec2 values;\nuniform float poche;\n// how much darker a corner gets, and how wide its shadow is, in meters\nuniform float occlusion;\nuniform float radius;\n// the sky's gradient, from the top of the screen to its bottom; w 0 to keep the sky\nuniform vec4 skyTop;\nuniform vec4 skyBottom;\nin vec2 v_textureCoordinates;\n\nconst vec3 LUMA = vec3(0.2126, 0.7152, 0.0722);\n// the darkest colors' values, under the poché's, kept as they are rather than pressed into the\n// whites, fading into them over these shares of it: an architect's poché, solid black, or a text's\nconst vec2 POCHE = vec2(0.6, 1.4);\n// the occlusion: its widest, in pixels; its samples, on a spiral turned by the golden angle, the\n// spiral itself turned from pixel to pixel; how steeply above the surface a sample rises to count\n// (the normal from the depth is not exact), and how far from the pixel, in meters, a pixel and a\n// half at the least (the depth's own steps on a flat wall not counted); how many pixels wide the\n// disc is to show, fading in (zoomed out, its samples fall within the depth's noise)\nconst float RING = 48.0;\nconst int SAMPLES = 12;\nconst float GOLDEN = 2.39996;\nconst float BIAS = 0.15;\nconst float NEAREST = 0.05;\nconst vec2 SHOWN = vec2(3.0, 8.0);\n\n// how much the surfaces around uv close in over it, from 0 to 1, as the Alchemy ambient obscurance\n// (McGuire et al. 2011): the share of the samples around it, in a disc of the radius, that rise\n// above its surface (its normal from the depth's slopes), each the more as it rises steeper, none\n// farther than the radius; a corner's foot about a half, a flat floor none. p its eye coordinates, n\n// their slopes' normal\nfloat occludedAt(vec2 uv, vec3 p, vec3 n, float pixel) {\n float reach = min(radius, RING * pixel);\n float shown = smoothstep(SHOWN.x, SHOWN.y, reach / pixel);\n if (shown == 0.0) {\n return 0.0;\n }\n // turned toward the eye\n vec3 eye = czm_orthographicIn3D == 1.0 ? vec3(0.0, 0.0, 1.0) : normalize(-p);\n n = dot(n, eye) < 0.0 ? -n : n;\n float nearest = max(NEAREST, 1.5 * pixel);\n float spin = 6.2832 * fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));\n float sum = 0.0;\n for (int i = 0; i < SAMPLES; i++) {\n float a = float(i) * GOLDEN + spin;\n vec2 o = vec2(cos(a), sin(a)) * (float(i) + 0.5) / float(SAMPLES) * reach / pixel / czm_viewport.zw;\n vec4 q = eyeAt(depthTexture, uv + o);\n vec3 v = q.xyz - p;\n float d = length(v);\n if (q.w > 0.0 && d > nearest) {\n sum += max(0.0, dot(v / d, n) - BIAS) * (1.0 - smoothstep(0.5 * reach, reach, d));\n }\n }\n return shown * sum / float(SAMPLES);\n}\n\nvoid main() {\n vec2 uv = v_textureCoordinates;\n // the surface's normal from the depth's slopes, read before any pixel returns, in step with its\n // neighbors\n vec3 p = eyeAt(depthTexture, uv).xyz;\n vec3 normal = normalize(cross(dFdx(p), dFdy(p)));\n vec4 sceneColor = texture(colorTexture, uv);\n float c = depthAt(uv);\n if (c == SKY) {\n out_FragColor = skyTop.w > 0.0 ? vec4(skyAt(uv, skyTop, skyBottom), 1.0) : sceneColor;\n return;\n }\n float pixel = pixelAt(c);\n // the color's value pressed between the darkest and the lightest, but the darkest's; its hue as\n // saturated as asked, scaled down rather than clipped where a channel would pass 1\n float value = dot(sceneColor.rgb, LUMA);\n float kept = poche > 0.0 ? 1.0 - smoothstep(POCHE.x * poche, POCHE.y * poche, value) : 0.0;\n float pressed = mix(mix(values.x, values.y, value), value, kept);\n vec3 hue = sceneColor.rgb * pressed / max(value, 1e-4);\n vec3 color = mix(vec3(pressed), hue / max(1.0, max(hue.r, max(hue.g, hue.b))), saturation);\n float steps = clamp(bevel / pixel, 1.0, BEVEL);\n vec4 n = neighborsAt(uv, c, steps);\n float crease = creaseOf(n, steps * pixel, pixel);\n color *= n.x + n.y + n.z + n.w > 0.0 ? 1.0 + light * crease : 1.0 - shade * crease;\n if (occlusion > 0.0) {\n color *= 1.0 - occlusion * occludedAt(uv, p, normal, pixel);\n }\n if (outline > 0.0) {\n color *= 1.0 - outline * outlineAt(uv, c, n, steps, pixel);\n }\n out_FragColor = vec4(clamp(color * warm, 0.0, 1.0), sceneColor.a);\n}\n";
2
+ export default "// A white model's look, as an architect's plaster model or clay render, after DepthEdges: every\n// color turned to its value and the values pressed into a narrow range of whites (an accent's hue\n// kept, if asked; the darkest left black, a section's poché), so the light and the shapes alone\n// tell the surfaces apart; darker where surfaces meet, an ambient obscurance from the depth; the\n// creases lit and shaded, a fine outline along the jumps; the sky, if asked, a pale gradient.\n// its samplers read in high precision, whatever comes before (a phone's GPU's default is low)\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// how much brighter a convex edge gets, how much darker a concave one\nuniform float light;\nuniform float shade;\n// the edges' bevel, in meters, a pixel at the least; how much darker the outline, 0 for none\nuniform float bevel;\nuniform float outline;\n// the colors' saturation, 0 for white, and a factor each of red, green and blue: the plaster's tint\nuniform float saturation;\nuniform vec3 warm;\n// the darkest and the lightest values, the scene's black and white; the value under which a color\n// keeps it, 0 for none\nuniform vec2 values;\nuniform float poche;\n// how much darker a corner gets, and how wide its shadow is, in meters\nuniform float occlusion;\nuniform float radius;\n// the sky's gradient, from the top of the screen to its bottom; w 0 to keep the sky\nuniform vec4 skyTop;\nuniform vec4 skyBottom;\nin vec2 v_textureCoordinates;\n\n// the darkest colors' values, under the poché's, kept as they are rather than pressed into the\n// whites, fading into them over these shares of it: an architect's poché, solid black, or a text's\nconst vec2 POCHE = vec2(0.6, 1.4);\n// the occlusion: its widest, in pixels; its samples, on a spiral turned by the golden angle, the\n// spiral itself turned from pixel to pixel; how steeply above the surface a sample rises to count\n// (the normal from the depth is not exact), and how far from the pixel, in meters, a pixel and a\n// half at the least (the depth's own steps on a flat wall not counted); how many pixels wide the\n// disc is to show, fading in (zoomed out, its samples fall within the depth's noise)\nconst float RING = 48.0;\nconst int SAMPLES = 12;\nconst float GOLDEN = 2.39996;\nconst float BIAS = 0.15;\nconst float NEAREST = 0.05;\nconst vec2 SHOWN = vec2(3.0, 8.0);\n\n// how much the surfaces around uv close in over it, from 0 to 1, as the Alchemy ambient obscurance\n// (McGuire et al. 2011): the share of the samples around it, in a disc of the radius, that rise\n// above its surface (its normal from the depth's slopes), each the more as it rises steeper, none\n// farther than the radius; a corner's foot about a half, a flat floor none. p its eye coordinates, n\n// their slopes' normal\nfloat occludedAt(vec2 uv, vec3 p, vec3 n, float pixel) {\n float reach = min(radius, RING * pixel);\n float shown = smoothstep(SHOWN.x, SHOWN.y, reach / pixel);\n if (shown == 0.0) {\n return 0.0;\n }\n // turned toward the eye\n vec3 eye = czm_orthographicIn3D == 1.0 ? vec3(0.0, 0.0, 1.0) : normalize(-p);\n n = dot(n, eye) < 0.0 ? -n : n;\n float nearest = max(NEAREST, 1.5 * pixel);\n float spin = 6.2832 * fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));\n float sum = 0.0;\n for (int i = 0; i < SAMPLES; i++) {\n float a = float(i) * GOLDEN + spin;\n vec2 o = vec2(cos(a), sin(a)) * (float(i) + 0.5) / float(SAMPLES) * reach / pixel / czm_viewport.zw;\n vec4 q = eyeAt(depthTexture, uv + o);\n vec3 v = q.xyz - p;\n float d = length(v);\n if (q.w > 0.0 && d > nearest) {\n sum += max(0.0, dot(v / d, n) - BIAS) * (1.0 - smoothstep(0.5 * reach, reach, d));\n }\n }\n return shown * sum / float(SAMPLES);\n}\n\nvoid main() {\n vec2 uv = v_textureCoordinates;\n // the surface's normal from the depth's slopes, read before any pixel returns, in step with its\n // neighbors\n vec3 p = eyeAt(depthTexture, uv).xyz;\n vec3 normal = normalize(cross(dFdx(p), dFdy(p)));\n vec4 sceneColor = texture(colorTexture, uv);\n float c = depthAt(uv);\n if (c == SKY) {\n out_FragColor = skyTop.w > 0.0 ? vec4(skyAt(uv, skyTop, skyBottom), 1.0) : sceneColor;\n return;\n }\n float pixel = pixelAt(c);\n // the color's value pressed between the darkest and the lightest, but the darkest's; its hue as\n // saturated as asked, scaled down rather than clipped where a channel would pass 1\n float value = czm_luminance(sceneColor.rgb);\n float kept = poche > 0.0 ? 1.0 - smoothstep(POCHE.x * poche, POCHE.y * poche, value) : 0.0;\n float pressed = mix(mix(values.x, values.y, value), value, kept);\n vec3 hue = sceneColor.rgb * pressed / max(value, 1e-4);\n vec3 color = mix(vec3(pressed), hue / max(1.0, max(hue.r, max(hue.g, hue.b))), saturation);\n float steps = clamp(bevel / pixel, 1.0, BEVEL);\n if (light != 0.0 || shade != 0.0) {\n vec4 n = neighborsAt(uv, c, steps);\n float crease = creaseOf(n, steps * pixel, pixel);\n color *= n.x + n.y + n.z + n.w > 0.0 ? 1.0 + light * crease : 1.0 - shade * crease;\n }\n if (occlusion > 0.0) {\n color *= 1.0 - occlusion * occludedAt(uv, p, normal, pixel);\n }\n if (outline > 0.0) {\n color *= 1.0 - outline * outlineAt(uv, c, vec4(0.0), steps, pixel);\n }\n out_FragColor = vec4(clamp(color * warm, 0.0, 1.0), sceneColor.a);\n}\n";
package/toy.js CHANGED
@@ -16,6 +16,9 @@ const skyBottomScratch = new Cartesian4();
16
16
  * outward and shaded where it bends inward, rounded close up, the silhouettes
17
17
  * outlined in a darker shade of their color, the colors a little richer and
18
18
  * warmer, the sky a soft gradient if asked.
19
+ * The edges are found in the depth of the nearest of Cesium's frustums only: with `scene.logarithmicDepthBuffer` off,
20
+ * Cesium splits the view at `farToNearRatio` times the near plane, and what lies past the split has no depth here, so
21
+ * the sky gradient is painted over it; keep the logarithmic depth, or a near plane that keeps the scene in one frustum.
19
22
  */
20
23
  export default class Toy extends Effect {
21
24
  /**
package/weather-map.js CHANGED
@@ -1,8 +1,8 @@
1
- import {Cartesian4} from '@cesium/core';
1
+ import {Cartesian4, Math as CesiumMath} from '@cesium/core';
2
2
  // @ts-expect-error the engine exports its textures, but does not type them
3
3
  import {Texture} from '@cesium/engine';
4
4
 
5
- const TWO_PI = 2 * Math.PI;
5
+ const TWO_PI = CesiumMath.TWO_PI;
6
6
  // the max map's texels are blocks of this many pixels of the map across
7
7
  const POOL = 4;
8
8
 
@@ -154,9 +154,9 @@ export default class WeatherMap {
154
154
  this.data_ = imageData(image);
155
155
  Cartesian4.fromElements(
156
156
  rectangle.west / TWO_PI + 0.5,
157
- rectangle.south / Math.PI + 0.5,
157
+ rectangle.south / CesiumMath.PI + 0.5,
158
158
  TWO_PI / (rectangle.east - rectangle.west),
159
- Math.PI / (rectangle.north - rectangle.south),
159
+ CesiumMath.PI / (rectangle.north - rectangle.south),
160
160
  this.bounds,
161
161
  );
162
162
  }
@@ -233,8 +233,8 @@ export default class WeatherMap {
233
233
  sample_(image, u, v) {
234
234
  const {width, height, data} = image;
235
235
  // the image's rows go down from the north
236
- const x = Math.min(Math.max(u * width - 0.5, 0), width - 1);
237
- const y = Math.min(Math.max((1 - v) * height - 0.5, 0), height - 1);
236
+ const x = CesiumMath.clamp(u * width - 0.5, 0, width - 1);
237
+ const y = CesiumMath.clamp((1 - v) * height - 0.5, 0, height - 1);
238
238
  const x0 = Math.floor(x);
239
239
  const y0 = Math.floor(y);
240
240
  const x1 = Math.min(x0 + 1, width - 1);
package/shaders/Hsl.js DELETED
@@ -1,2 +0,0 @@
1
- // This file is automatically rebuilt from Hsl.glsl.
2
- export default "// Hue (0 to 1, red at 0), saturation and lightness of a color.\nvec3 rgbToHsl(vec3 color) {\n float maxc = max(color.r, max(color.g, color.b));\n float minc = min(color.r, min(color.g, color.b));\n float lightness = 0.5 * (maxc + minc);\n float delta = maxc - minc;\n if (delta < 1e-5) {\n return vec3(0.0, 0.0, lightness);\n }\n float saturation = lightness > 0.5 ? delta / (2.0 - maxc - minc) : delta / (maxc + minc);\n float hue = maxc == color.r ? (color.g - color.b) / delta + (color.g < color.b ? 6.0 : 0.0)\n : maxc == color.g ? (color.b - color.r) / delta + 2.0\n : (color.r - color.g) / delta + 4.0;\n return vec3(hue / 6.0, saturation, lightness);\n}\n";