@geoblocks/cesium-post-process 0.1.0 → 0.1.2

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/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/effect.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A screen-space effect: a post-process stage on a scene while active.
3
+ * Subclasses create their stage in `createStage_` and take their side effects
4
+ * in `activated_` and `deactivating_`.
5
+ */
6
+ export default class Effect {
7
+ /**
8
+ * @param {import('@cesium/engine').CesiumWidget} viewer
9
+ */
10
+ constructor(viewer) {
11
+ this.viewer = viewer;
12
+ /** @type {import('@cesium/engine').PostProcessStage | import('@cesium/engine').PostProcessStageComposite | undefined} */
13
+ this.stage_ = undefined;
14
+ }
15
+
16
+ get active() {
17
+ return this.stage_ !== undefined;
18
+ }
19
+
20
+ set active(active) {
21
+ const scene = this.viewer.scene;
22
+ if (active) {
23
+ if (this.stage_) {
24
+ return;
25
+ }
26
+ this.stage_ = this.createStage_(scene);
27
+ scene.postProcessStages.add(this.stage_);
28
+ this.activated_(scene);
29
+ } else {
30
+ if (!this.stage_) {
31
+ return;
32
+ }
33
+ this.deactivating_(scene);
34
+ // removing a stage also destroys it
35
+ scene.postProcessStages.remove(this.stage_);
36
+ this.stage_ = undefined;
37
+ }
38
+ scene.requestRender();
39
+ }
40
+
41
+ /**
42
+ * @param {import('@cesium/engine').Scene} _scene
43
+ * @return {import('@cesium/engine').PostProcessStage | import('@cesium/engine').PostProcessStageComposite}
44
+ */
45
+ createStage_(_scene) {
46
+ throw new Error(`${this.constructor.name} does not implement createStage_`);
47
+ }
48
+
49
+ /**
50
+ * Called once the stage is added to the scene.
51
+ * @param {import('@cesium/engine').Scene} _scene
52
+ */
53
+ activated_(_scene) {}
54
+
55
+ /**
56
+ * Called before the stage is removed from the scene.
57
+ * @param {import('@cesium/engine').Scene} _scene
58
+ */
59
+ deactivating_(_scene) {}
60
+
61
+ destroy() {
62
+ this.active = false;
63
+ }
64
+ }
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.0",
3
+ "version": "0.1.2",
4
4
  "license": "BSD-3-Clause",
5
5
  "type": "module",
6
6
  "repository": {
@@ -15,10 +15,15 @@
15
15
  "analog-video.js",
16
16
  "blur.js",
17
17
  "cesium-post-process.js",
18
+ "cloud-cover.js",
19
+ "cloud-map.js",
20
+ "cloud-noise.js",
21
+ "clouds.js",
18
22
  "color-isolation.js",
19
23
  "depth-test.js",
20
24
  "digital-video.js",
21
25
  "drone-display.js",
26
+ "effect.js",
22
27
  "focus.js",
23
28
  "frame-clock.js",
24
29
  "height.js",
@@ -37,6 +42,7 @@
37
42
  "tilt-shift.js",
38
43
  "toy.js",
39
44
  "valley-fog.js",
45
+ "weather-map.js",
40
46
  "white-model.js",
41
47
  "shaders/*.js"
42
48
  ],
@@ -47,5 +53,5 @@
47
53
  "@cesium/core": ">=0.1.0",
48
54
  "@cesium/engine": ">=10"
49
55
  },
50
- "gitHead": "430bbbb63bf3d60221d670f65d5e51fdb6d5b944"
56
+ "gitHead": "0abb7f3381fdf13b15d50047a46d3789f5f95a9e"
51
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 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 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 = 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 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";