@geoblocks/cesium-post-process 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +29 -0
- package/README.md +235 -2
- package/analog-video.js +138 -0
- package/blur.js +39 -0
- package/cesium-post-process.js +23 -0
- package/color-isolation.js +69 -0
- package/depth-test.js +37 -0
- package/digital-video.js +108 -0
- package/drone-display.js +170 -0
- package/focus.js +25 -0
- package/frame-clock.js +61 -0
- package/height.js +23 -0
- package/infrared.js +40 -0
- package/jello.js +146 -0
- package/lens-distortion.js +68 -0
- package/lightning.js +321 -0
- package/motion-blur.js +117 -0
- package/package.json +49 -4
- package/precipitation.js +493 -0
- package/shaders/Alpenglow.js +2 -0
- package/shaders/AnalogVideo.js +2 -0
- package/shaders/Beam.js +2 -0
- package/shaders/CloudsBlur.js +2 -0
- package/shaders/CloudsComposite.js +2 -0
- package/shaders/CloudsDensity.js +2 -0
- package/shaders/CloudsDepth.js +2 -0
- package/shaders/CloudsMarch.js +2 -0
- package/shaders/CloudsResolve.js +2 -0
- package/shaders/CloudsShadow.js +2 -0
- package/shaders/CloudsShadowMap.js +2 -0
- package/shaders/CloudsSlab.js +2 -0
- package/shaders/ColorIsolation.js +2 -0
- package/shaders/DepthEdges.js +2 -0
- package/shaders/DigitalVideo.js +2 -0
- package/shaders/DroneDisplay.js +2 -0
- package/shaders/EyeFromDepth.js +2 -0
- package/shaders/Filmic.js +2 -0
- package/shaders/Flashlight.js +2 -0
- package/shaders/Fog.js +2 -0
- package/shaders/Frame.js +2 -0
- package/shaders/Hash.js +2 -0
- package/shaders/Height.js +2 -0
- package/shaders/Hsl.js +2 -0
- package/shaders/Infrared.js +2 -0
- package/shaders/Jello.js +2 -0
- package/shaders/LensDistortion.js +2 -0
- package/shaders/Lightning.js +2 -0
- package/shaders/MotionBlur.js +2 -0
- package/shaders/MotionBlurVelocity.js +2 -0
- package/shaders/Noise.js +2 -0
- package/shaders/Normal.js +2 -0
- package/shaders/Phase.js +2 -0
- package/shaders/Precipitation.js +2 -0
- package/shaders/PrecipitationShaftBlur.js +2 -0
- package/shaders/PrecipitationShafts.js +2 -0
- package/shaders/PrecipitationWind.js +2 -0
- package/shaders/RainHaze.js +2 -0
- package/shaders/Shockwave.js +2 -0
- package/shaders/SkyTable.js +2 -0
- package/shaders/SnowLine.js +2 -0
- package/shaders/SpeedLines.js +2 -0
- package/shaders/Spotlight.js +2 -0
- package/shaders/SpotlightBeam.js +2 -0
- package/shaders/SpotlightBeamBlur.js +2 -0
- package/shaders/SpotlightCone.js +2 -0
- package/shaders/Super8.js +2 -0
- package/shaders/Technicolor.js +2 -0
- package/shaders/TiltShift.js +2 -0
- package/shaders/Toy.js +2 -0
- package/shaders/Trails.js +2 -0
- package/shaders/ValleyFog.js +2 -0
- package/shaders/WeatherMap.js +2 -0
- package/shaders/WhiteModel.js +2 -0
- package/snow-line.js +108 -0
- package/speed-lines.js +71 -0
- package/spotlight.js +193 -0
- package/strikes.js +30 -0
- package/super8.js +164 -0
- package/technicolor.js +56 -0
- package/tilt-shift.js +124 -0
- package/toy.js +192 -0
- package/valley-fog.js +105 -0
- package/white-model.js +105 -0
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from DroneDisplay.glsl.
|
|
2
|
+
export default "// A drone's OSD, as ArduPilot draws it on an HD video system: 50 columns and 18 rows of 12 x 18\n// pixel character cells over the whole picture, with the readouts near its edges, here in the\n// blocky white glyphs of an analog chip with their black outline, and some in color. The layout\n// follows a fixed-wing plane's display; the glyphs are drawn here, not copied from a font.\n// - The horizon: 9 dots of 2 x 2 pixels around the middle, as ArduPilot's draw_horizon: pitch (at\n// most 20 degrees) moves it a row per 5 degrees, and roll tilts it at its angle on the screen;\n// each dot is at one of 9 heights in its cell, or past 45 degrees of roll, one per row at one of\n// 9 places across its cell. A dot more than 4 cells from the middle is not drawn, and upside\n// down the horizon blinks. It is a symbol, not lined up with the real horizon.\n// - The flight mode, top left: FBWA, RTL in failsafe; the heading, top middle.\n// - Top right, the link bar, green, orange then red as the link quality drops, and the battery,\n// its symbol filled in green in 7 steps, and its voltage.\n// - The right column: the altitude since the display turned on, under a mountain with a green\n// dot; the vertical speed, with one or two green arrows; the throttle.\n// - The air speed, bottom middle, after its pale blue label.\n// - The FAILSAFE warning, blinking red in the lower part of the middle while the control link is\n// lost.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// 0 to 1\nuniform float opacity;\n// degrees, negative looking down\nuniform float pitch;\n// degrees, positive to the right\nuniform float roll;\n// degrees, 0 to 360\nuniform float heading;\n// meters, since the display turned on\nuniform float altitude;\n// m/s, positive climbing\nuniform float verticalSpeed;\n// km/h\nuniform float speed;\n// volts\nuniform float voltage;\n// 0 to 99\nuniform float linkQuality;\n// percent\nuniform float throttle;\n// 1 while the control link is lost, 0 otherwise\nuniform float failsafe;\n// 1 or 0, twice per second: the blinking of the warning and of the horizon upside down\nuniform float blink;\nin vec2 v_textureCoordinates;\n\nconst float COLUMNS = 50.0;\nconst float ROWS = 18.0;\n// a cell, in pixels\nconst float CELL_WIDTH = 12.0;\nconst float CELL_HEIGHT = 18.0;\n// the middle cell, where the horizon is\nconst int MIDDLE_COLUMN = 24;\nconst int MIDDLE_ROW = 8;\n// ArduPilot's horizon: its limit in pitch, rows per degree of it (16 rows per 80 degrees), and\n// rows per column at 45 degrees of roll, for the line to tilt at its angle\nconst float MAX_PITCH = 20.0;\nconst float ROWS_PER_DEGREE = 0.2;\nconst float RATIO = CELL_WIDTH / CELL_HEIGHT;\n// battery: volts full and empty of a 3S, and the steps of its symbol's fill (full, 5 to 1, empty)\nconst float FULL_VOLTS = 12.6;\nconst float EMPTY_VOLTS = 10.5;\nconst float BATTERY_STEPS = 6.0;\n// the right column: the units in its last column, right of the numbers\nconst int UNIT_COLUMN = 41;\n// the link bar: its first column, and its length in cells\nconst int LINK_COLUMN = 36;\nconst float LINK_CELLS = 6.0;\n\nconst vec3 WHITE = vec3(1.0);\nconst vec3 GREEN = vec3(0.2, 0.85, 0.35);\nconst vec3 ORANGE = vec3(1.0, 0.6, 0.1);\nconst vec3 RED = vec3(0.95, 0.15, 0.15);\nconst vec3 PALE_BLUE = vec3(0.55, 0.75, 0.95);\n\n// the glyphs, at 12 x 18 pixels: one row of 12 bits each, the leftmost bit highest; the table is\n// written by scripts/osd-font.js from its pixel drawings\nconst int DOT = 10;\nconst int SPACE = 11;\nconst int MINUS = 12;\nconst int VOLT = 13;\nconst int DEGREE = 14;\nconst int PERCENT = 15;\n// the units of the altitude, the vertical speed and the air speed, and the air speed's label\nconst int ALTITUDE = 16;\nconst int METERS_PER_SECOND = 17;\nconst int KILOMETERS_PER_HOUR = 18;\nconst int AIR_SPEED = 19;\n// the vertical speed's arrows\nconst int UP = 20;\nconst int UP_TWO = 21;\nconst int DOWN = 22;\nconst int DOWN_TWO = 23;\n// the letters of the flight modes and the warning\nconst int LETTER_F = 24;\nconst int LETTER_B = 25;\nconst int LETTER_W = 26;\nconst int LETTER_A = 27;\nconst int LETTER_R = 28;\nconst int LETTER_T = 29;\nconst int LETTER_L = 30;\nconst int LETTER_I = 31;\nconst int LETTER_E = 32;\nconst int LETTER_S = 33;\n// the warning's row and first column: FAILSAFE, centered under the middle\nconst int WARNING_ROW = 12;\nconst int WARNING_COLUMN = 21;\n\nconst int FONT[612] = int[612](\n 0, 0, 0, 983040, 1671264, 1474704, 1474704, 1474704, 1474704, 1474704, 1474704, 1474704, 1671264, 983040, 0, 0, 0, 0, // 0\n 0, 0, 0, 917504, 1704000, 1179840, 1704000, 655424, 655424, 655424, 655424, 1769536, 1114336, 2031616, 0, 0, 0, 0, // 1\n 0, 0, 0, 983040, 1671264, 1474704, 1998864, 426000, 884768, 1769536, 1441920, 1540224, 1081584, 2064384, 0, 0, 0, 0, // 2\n 0, 0, 0, 2031616, 1147104, 1998864, 163856, 950288, 622688, 950288, 163856, 1998864, 1147104, 2031616, 0, 0, 0, 0, // 3\n 0, 0, 0, 2064384, 1474704, 1474704, 1474704, 1474704, 1081584, 1998864, 163856, 163856, 163856, 229376, 0, 0, 0, 0, // 4\n 0, 0, 0, 2064384, 1081584, 1540224, 1507456, 1147104, 1998864, 163856, 1998864, 1474704, 1671264, 983040, 0, 0, 0, 0, // 5\n 0, 0, 0, 983040, 1638496, 1507456, 1507456, 1147104, 1474704, 1474704, 1474704, 1474704, 1671264, 983040, 0, 0, 0, 0, // 6\n 0, 0, 0, 2064384, 1081584, 1998864, 426000, 360480, 852000, 720960, 655424, 655424, 655424, 917504, 0, 0, 0, 0, // 7\n 0, 0, 0, 983040, 1671264, 1474704, 1474704, 1474704, 1671264, 1474704, 1474704, 1474704, 1671264, 983040, 0, 0, 0, 0, // 8\n 0, 0, 0, 983040, 1671264, 1474704, 1474704, 1474704, 1605744, 950288, 163856, 950288, 622688, 983040, 0, 0, 0, 0, // 9\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 983040, 589920, 589920, 983040, 0, 0, 0, 0, // .\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // space\n 0, 0, 0, 0, 0, 0, 0, 2064384, 1081584, 2064384, 0, 0, 0, 0, 0, 0, 0, 0, // -\n 0, 0, 0, 0, 0, 0, 3899392, 2785552, 2785552, 3047696, 3506336, 1376416, 1769536, 917504, 0, 0, 0, 0, // v, volts\n 0, 0, 1966080, 3342528, 2949408, 2949408, 3342528, 1966080, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // °, degrees\n 0, 0, 0, 0, 7979008, 4932360, 5161744, 8224800, 1769536, 3653760, 7225624, 5915160, 7585792, 0, 0, 0, 0, 0, // %\n 1835008, 3539072, 7061824, 14508584, 11973652, 6543362, 12611584, 0, 4128768, 2195936, 2785616, 2785616, 2785616, 4161536, 0, 0, 0, 0, // a mountain over m, the altitude; the shader adds a green dot inside\n 0, 4128768, 2195936, 2785616, 2785616, 2785616, 4161536, 0, 983040, 1638496, 1507456, 1769536, 1900576, 1245376, 1966080, 0, 0, 0, // m over s, meters per second\n 14680064, 12567552, 11036024, 10135124, 11183444, 11183444, 16769024, 917504, 655424, 720960, 622688, 688208, 688208, 1015808, 0, 0, 0, 0, // km over h, kilometers per hour\n 0, 8257536, 14410304, 11445524, 9070424, 11191632, 11175248, 16744448, 15757312, 2022914, 7575750, 1400490, 13849290, 1547910, 16576512, 0, 0, 0, // Air over Spd, the air speed\n 0, 0, 0, 0, 0, 0, 917504, 1769536, 3244256, 6341104, 4211704, 8372224, 0, 0, 0, 0, 0, 0, // an arrow up\n 0, 0, 917504, 1769536, 3244256, 6341104, 4211704, 8372224, 0, 917504, 1769536, 3244256, 6341104, 4211704, 8372224, 0, 0, 0, // two arrows up\n 0, 0, 0, 0, 0, 0, 8372224, 4211704, 6341104, 3244256, 1769536, 917504, 0, 0, 0, 0, 0, 0, // an arrow down\n 0, 0, 8372224, 4211704, 6341104, 3244256, 1769536, 917504, 0, 8372224, 4211704, 6341104, 3244256, 1769536, 917504, 0, 0, 0, // two arrows down\n 0, 0, 0, 2064384, 1081584, 1540224, 1310848, 1507456, 1114336, 1507456, 1310848, 1310848, 1310848, 1835008, 0, 0, 0, 0, // F\n 0, 0, 0, 2031616, 1147104, 1474704, 1474704, 1474704, 1147104, 1474704, 1474704, 1474704, 1147104, 2031616, 0, 0, 0, 0, // B\n 0, 0, 0, 3899392, 2785552, 2785552, 2785552, 3047696, 2785616, 2785616, 2785616, 2785616, 3506336, 2031616, 0, 0, 0, 0, // W\n 0, 0, 0, 983040, 1671264, 1474704, 1474704, 1474704, 1081584, 1474704, 1474704, 1474704, 1474704, 2064384, 0, 0, 0, 0, // A\n 0, 0, 0, 2031616, 1147104, 1474704, 1474704, 1474704, 1147104, 1409184, 1474704, 1474704, 1474704, 2064384, 0, 0, 0, 0, // R\n 0, 0, 0, 4161536, 2130416, 3899456, 655424, 655424, 655424, 655424, 655424, 655424, 655424, 917504, 0, 0, 0, 0, // T\n 0, 0, 0, 1835008, 1310848, 1310848, 1310848, 1310848, 1310848, 1310848, 1310848, 1540224, 1081584, 2064384, 0, 0, 0, 0, // L\n 0, 0, 0, 2031616, 1114336, 1769536, 655424, 655424, 655424, 655424, 655424, 1769536, 1114336, 2031616, 0, 0, 0, 0, // I\n 0, 0, 0, 2064384, 1081584, 1540224, 1310848, 1507456, 1114336, 1507456, 1310848, 1540224, 1081584, 2064384, 0, 0, 0, 0, // E\n 0, 0, 0, 1015808, 1605744, 1540224, 1310848, 1507456, 1671264, 950288, 163856, 1998864, 1147104, 2031616, 0, 0, 0, 0 // S\n);\n\nint digit(float value, float power) {\n return int(mod(floor(value / power), 10.0));\n}\n\n// 10 to the n, exactly: pow may be off by a little on the GPU\nfloat powerOfTen(int n) {\n return n == 3 ? 1000.0 : n == 2 ? 100.0 : n == 1 ? 10.0 : 1.0;\n}\n\n// a number right-aligned to end at the column last: its whole part, and its tenths after a dot,\n// and a minus before it when negative; -1 left of it\nint numberAt(float value, bool tenths, int column, int last) {\n float scaled = floor(abs(value) * (tenths ? 10.0 : 1.0) + 0.5);\n float whole = tenths ? floor(scaled / 10.0) : scaled;\n int digits = whole >= 1000.0 ? 4 : whole >= 100.0 ? 3 : whole >= 10.0 ? 2 : 1;\n int i = last - column;\n if (tenths) {\n if (i == 0) {\n return digit(scaled, 1.0);\n }\n if (i == 1) {\n return DOT;\n }\n i -= 2;\n }\n if (i >= 0 && i < digits) {\n return digit(whole, powerOfTen(i));\n }\n return i == digits && value < 0.0 && scaled > 0.0 ? MINUS : -1;\n}\n\n// the glyph in this cell and its color, or -1\nint glyphAt(int column, int row, out vec3 color) {\n color = WHITE;\n // the flight mode, top left\n if (row == 1 && column >= 3 && column <= 6) {\n int i = column - 3;\n if (failsafe > 0.5) {\n return i == 0 ? LETTER_R : i == 1 ? LETTER_T : i == 2 ? LETTER_L : -1;\n }\n return i == 0 ? LETTER_F : i == 1 ? LETTER_B : i == 2 ? LETTER_W : LETTER_A;\n }\n // the heading, top middle\n if (row == 1 && column >= 23 && column <= 26) {\n return column == 26 ? DEGREE : numberAt(mod(floor(heading + 0.5), 360.0), false, column, 25);\n }\n // the battery voltage, top right after its symbol: 12.0v\n if (row == 2 && column > LINK_COLUMN && column <= UNIT_COLUMN) {\n return column == UNIT_COLUMN ? VOLT : numberAt(clamp(voltage, 0.0, 99.9), true, column, UNIT_COLUMN - 1);\n }\n // the right column: the altitude, the vertical speed after its arrows, the throttle\n bool right = column >= UNIT_COLUMN - 6 && column <= UNIT_COLUMN;\n if (row == MIDDLE_ROW && right) {\n return column == UNIT_COLUMN ? ALTITUDE : numberAt(clamp(altitude, -999.0, 9999.0), false, column, UNIT_COLUMN - 1);\n }\n if (row == MIDDLE_ROW + 1 && right) {\n if (column == UNIT_COLUMN) {\n return METERS_PER_SECOND;\n }\n float climb = min(abs(verticalSpeed), 99.9);\n int width = climb >= 9.95 ? 4 : 3;\n if (column == UNIT_COLUMN - 1 - width) {\n color = GREEN;\n bool up = verticalSpeed > 0.0;\n return climb < 0.05 ? -1 : climb < 5.0 ? (up ? UP : DOWN) : (up ? UP_TWO : DOWN_TWO);\n }\n return numberAt(climb, true, column, UNIT_COLUMN - 1);\n }\n if (row == MIDDLE_ROW + 3 && right) {\n return column == UNIT_COLUMN ? PERCENT : numberAt(clamp(throttle, 0.0, 100.0), false, column, UNIT_COLUMN - 1);\n }\n // the warning\n if (failsafe > 0.5 && blink > 0.5 && row == WARNING_ROW && column >= WARNING_COLUMN && column < WARNING_COLUMN + 8) {\n int i = column - WARNING_COLUMN;\n color = RED;\n return i == 0 ? LETTER_F : i == 1 ? LETTER_A : i == 2 ? LETTER_I : i == 3 ? LETTER_L : i == 4 ? LETTER_S : i == 5 ? LETTER_A : i == 6 ? LETTER_F : LETTER_E;\n }\n // the air speed, bottom middle: its label, whole km/h and the unit\n if (row == 16 && column >= 20 && column <= 25) {\n if (column == 20) {\n color = PALE_BLUE;\n return AIR_SPEED;\n }\n return column == 25 ? KILOMETERS_PER_HOUR : numberAt(clamp(speed, 0.0, 999.0), false, column, 24);\n }\n return -1;\n}\n\n// 2 where the glyph is drawn, 1 on its black outline, 0 elsewhere; x and y in the cell's pixels.\n// A row of the table holds the glyph in its low 12 bits and the outline in the 12 above, the\n// leftmost pixel highest\nfloat glyphInk(int glyph, int x, int y) {\n if (glyph < 0) {\n return 0.0;\n }\n int row = FONT[glyph * int(CELL_HEIGHT) + y] >> (int(CELL_WIDTH) - 1 - x);\n return (row & 1) == 1 ? 2.0 : ((row >> int(CELL_WIDTH)) & 1) == 1 ? 1.0 : 0.0;\n}\n\n// 2 inside the box, 1 on its outline a pixel around, 0 elsewhere; in pixels\nfloat boxInk(vec2 p, vec2 low, vec2 high) {\n if (all(greaterThanEqual(p, low)) && all(lessThan(p, high))) {\n return 2.0;\n }\n return all(greaterThanEqual(p, low - 1.0)) && all(lessThan(p, high + 1.0)) ? 1.0 : 0.0;\n}\n\n// what is drawn over what is under it, its outline under what is drawn\nvoid paint(inout float ink, inout vec3 fill, float layer, vec3 color) {\n if (layer > ink) {\n ink = layer;\n fill = color;\n }\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n // the grid as large as fits the screen, centered, from its top left, in pixels\n vec2 size = czm_viewport.zw;\n vec2 grid = vec2(COLUMNS * CELL_WIDTH, ROWS * CELL_HEIGHT);\n float scale = min(size.x / grid.x, size.y / grid.y);\n vec2 fromCorner = vec2(gl_FragCoord.x - czm_viewport.x, czm_viewport.y + size.y - gl_FragCoord.y) - 0.5 * (size - grid * scale);\n vec2 p = floor(fromCorner / scale);\n int column = int(floor(p.x / CELL_WIDTH));\n int row = int(floor(p.y / CELL_HEIGHT));\n if (column < 0 || column >= int(COLUMNS) || row < 0 || row >= int(ROWS)) {\n out_FragColor = sceneColor;\n return;\n }\n vec2 inCell = p - vec2(float(column) * CELL_WIDTH, float(row) * CELL_HEIGHT);\n\n vec3 glyphColor;\n int glyph = glyphAt(column, row, glyphColor);\n float ink = 0.0;\n vec3 fill = WHITE;\n paint(ink, fill, glyphInk(glyph, int(inCell.x), int(inCell.y)), glyphColor);\n\n // the horizon, as ArduPilot's draw_horizon in rows and columns from the middle cell, with the\n // pitch (positive nose down) raising it: a dot per column, at its height in the cell in ninths,\n // or past 45 degrees of roll a dot per row, at its place across the cell in ninths\n bool nearMiddle = abs(column - MIDDLE_COLUMN) <= 5 && abs(row - MIDDLE_ROW) <= 5;\n if (nearMiddle && (abs(roll) < 90.0 || blink > 0.5)) {\n float level = clamp(-pitch, -MAX_PITCH, MAX_PITCH) * ROWS_PER_DEGREE;\n float ky = sin(radians(roll));\n float kx = cos(radians(roll));\n bool steep = abs(ky) >= abs(kx);\n for (int k = -4; k <= 4; k++) {\n float along = steep ? (float(k) - level) / RATIO * kx / ky + 0.5 : RATIO * float(k) * ky / kx + level + 0.5;\n float cell = floor(along);\n float ninth = floor((along - cell) * 9.0);\n if (abs(cell) > 4.0) {\n continue;\n }\n vec2 center = steep\n ? vec2(float(MIDDLE_COLUMN) + cell, float(MIDDLE_ROW - k)) * vec2(CELL_WIDTH, CELL_HEIGHT) + vec2(2.0 + ninth, 9.0)\n : vec2(float(MIDDLE_COLUMN + k), float(MIDDLE_ROW) - cell) * vec2(CELL_WIDTH, CELL_HEIGHT) + vec2(6.0, 17.0 - 2.0 * ninth);\n float d = length(p + 0.5 - center);\n paint(ink, fill, d < 1.0 ? 2.0 : d < 2.0 ? 1.0 : 0.0, WHITE);\n }\n }\n\n // the link bar, top right: as long as the link quality, its color by it; full and red in\n // failsafe\n if (row == 1) {\n float quality = failsafe > 0.5 ? 99.0 : clamp(linkQuality, 0.0, 99.0);\n vec3 color = failsafe > 0.5 || quality < 40.0 ? RED : quality < 70.0 ? ORANGE : GREEN;\n float left = float(LINK_COLUMN) * CELL_WIDTH;\n float bar = floor(LINK_CELLS * CELL_WIDTH * (quality + 1.0) / 100.0);\n if (bar > 0.0) {\n paint(ink, fill, boxInk(p, vec2(left, CELL_HEIGHT + 7.0), vec2(left + bar, CELL_HEIGHT + 11.0)), color);\n }\n }\n\n // the battery symbol, top right before its voltage: a white frame with a cap, filled in green\n // from the bottom in steps as the voltage drops, black inside above the fill\n if (column == LINK_COLUMN && row == 2) {\n float charge = clamp((voltage - EMPTY_VOLTS) / (FULL_VOLTS - EMPTY_VOLTS), 0.0, 1.0);\n float fillTop = 17.0 - 13.0 * ceil(charge * BATTERY_STEPS) / BATTERY_STEPS;\n paint(ink, fill, max(boxInk(inCell, vec2(2.0, 3.0), vec2(10.0, 18.0)), boxInk(inCell, vec2(4.0, 1.0), vec2(8.0, 3.0))), WHITE);\n if (all(greaterThanEqual(inCell, vec2(3.0, 4.0))) && all(lessThan(inCell, vec2(9.0, 17.0)))) {\n ink = inCell.y >= fillTop ? 2.0 : 1.0;\n fill = GREEN;\n }\n }\n\n // the green dot inside the altitude's mountain\n if (glyph == ALTITUDE) {\n paint(ink, fill, all(greaterThanEqual(inCell, vec2(3.0, 4.0))) && all(lessThan(inCell, vec2(5.0, 6.0))) ? 2.0 : 0.0, GREEN);\n }\n\n vec3 color = ink == 2.0 ? fill : ink == 1.0 ? vec3(0.0) : sceneColor.rgb;\n out_FragColor = vec4(mix(sceneColor.rgb, color, ink > 0.0 ? opacity : 0.0), sceneColor.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from EyeFromDepth.glsl.
|
|
2
|
+
export default "// Eye coordinates of the pixel at uv, from the depth texture; w is 0 for the sky. With a\n// logarithmic depth buffer, czm_readDepth's perspective depth is about 1 beyond a few meters,\n// so the distance is decoded from the logarithmic depth directly. An orthographic camera's depth\n// is linear between the near and far planes Cesium fits to the scene each frame, not the\n// projection's, and its rays are parallel; Cesium gives it no inverse projection (zero).\n\n// A sampler's default precision is low: some phones' GPUs (Mali) then read the depth as a half float,\n// steps of centimeters a few hundred meters away.\nprecision highp sampler2D;\n\nvec4 eyeAt(sampler2D depthTexture, vec2 uv) {\n float raw = texture(depthTexture, uv).r;\n if (czm_orthographicIn3D == 1.0) {\n vec2 xy = (2.0 * uv - 1.0 - czm_projection[3].xy) / vec2(czm_projection[0][0], czm_projection[1][1]);\n return raw >= 1.0 ? vec4(0.0, 0.0, -1.0, 0.0) : vec4(xy, -mix(czm_currentFrustum.x, czm_currentFrustum.y, raw), 1.0);\n }\n // the pixel's view ray, through the near plane\n vec4 ray = czm_inverseProjection * vec4(2.0 * uv - 1.0, -1.0, 1.0);\n ray.xyz /= ray.w;\n if (raw >= 1.0) {\n return vec4(normalize(ray.xyz), 0.0);\n }\n#ifdef LOG_DEPTH\n float viewDepth = exp2(raw * czm_log2FarDepthFromNearPlusOne) - 1.0 + czm_currentFrustum.x;\n#else\n vec4 eye = czm_inverseProjection * vec4(2.0 * uv - 1.0, 2.0 * raw - 1.0, 1.0);\n float viewDepth = -eye.z / eye.w;\n#endif\n return vec4(ray.xyz * (viewDepth / -ray.z), 1.0);\n}\n\n// the distance to the pixel at uv, in meters, and the sky as far as a given distance\nfloat eyeDistance(sampler2D depthTexture, vec2 uv, float skyDistance) {\n vec4 eye = eyeAt(depthTexture, uv);\n return eye.w == 0.0 ? skyDistance : length(eye.xyz);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Filmic.glsl.
|
|
2
|
+
export default "// a filmic roll-off (ACES, Narkowicz's fit) for the effects that add light to the picture: Cesium\n// has already tone mapped it, so their light would clip to flat white without it\nvec3 filmic(vec3 x) {\n return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Flashlight.glsl.
|
|
2
|
+
export default "// A torch held to the right of and below the eye, pointing where the camera looks: a narrow\n// hotspot and a wide, dim spill around it, fading with distance, shading the relief they fall\n// on; and the hotspot's beam in the haze, raymarched through its shaft, which enters the picture\n// from the corner and ends on the bright circle it makes; the rest of the scene darkens and loses\n// its color. Without a target, it stays on toward the sky.\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\n// brightness of the LED, 1 for a pocket torch: the pool and the beam scale with it\nuniform float power;\n// half angle of the cone of the spill, radians\nuniform float angle;\n// meters: distance at which the light has fallen to a half\nuniform float range;\n// 0 to 1\nuniform float darkness;\n// brightness of the beam in the air, 0 to 1\nuniform float beam;\n// half angle of the hotspot, radians, the bright core of the pool and the beam's shaft in the\n// air, which is that light on its way; and its radius at the torch, meters\nuniform float beamAngle;\nuniform float beamWidth;\n// how far the beam lights the haze, meters\nuniform float beamReach;\n// brightness of the lit haze\nuniform float beamDensity;\n// Henyey-Greenstein asymmetry of the haze, 0 to 1: it scatters mostly forward, so the beam is\n// brightest looking along it\nuniform float beamAnisotropy;\nin vec2 v_textureCoordinates;\n\n// the cool white of an LED torch\nconst vec3 LIGHT_COLOR = vec3(0.7, 0.85, 1.0);\nconst float INTENSITY = 1.4;\n// shading of a surface facing away from the torch, relative to one facing it: a torch held at\n// chest height lights the ground at a grazing angle, where Lambert's law alone leaves it dark\nconst float WRAP = 0.4;\n// the torch, in eye coordinates: to the right of, below and a little behind the eye, so that its\n// beam enters the picture from the corner\nconst vec3 HAND = vec3(0.6, -0.4, 0.2);\n// the beam converges on the middle of the screen this far ahead, meters\nconst float AIM = 20.0;\n// brightness of the spill outside the hotspot, relative to it\nconst float SPILL = 0.3;\n// the beam in the haze is the hot shaft of the reflector, not the pool's wide cone: a cone from\n// a virtual source behind the hand, as far as its width at the torch and its angle put it, so\n// that a wide shaft looks almost parallel; a hot core over this fraction of its angle, the edge\n// soft outside it, crisp\nconst float BEAM_CORE = 0.8;\n// the distance from the torch at which the lit haze has fallen to a quarter, meters: short, so\n// that the shaft is as bright at its root, which the view rays cross over a few centimeters, as\n// at its far end, which they follow for meters\nconst float BEAM_REFERENCE = 1.0;\n// the haze also dims the light on its way, per meter\nconst float BEAM_EXTINCTION = 0.02;\n// the lit haze fades in over the first meters of the view ray, so that the root of the beam does\n// not stand as a wall of light in the corner of the picture\nconst vec2 BEAM_NEAR_FADE = vec2(0.5, 2.0);\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\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 // the axis points from what the torch lights back to it, as the beam expects\n vec3 axis = normalize(HAND - vec3(0.0, 0.0, -AIM));\n float coneCos = cos(angle);\n float coreCos = cos(beamAngle);\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 = 0.0;\n if (beam > 0.0) {\n Beam shaft;\n shaft.apex = HAND + axis * (beamWidth / tan(beamAngle));\n shaft.forward = -axis;\n shaft.edgeCos = cos(beamAngle);\n shaft.coreCos = cos(beamAngle * BEAM_CORE);\n shaft.hotCore = 1.0;\n shaft.light = HAND;\n shaft.reference = BEAM_REFERENCE;\n shaft.core = BEAM_REFERENCE;\n shaft.reach = beamReach;\n shaft.extinction = BEAM_EXTINCTION;\n shaft.anisotropy = beamAnisotropy;\n shaft.spacing = 2.0;\n shaft.nearFade = BEAM_NEAR_FADE;\n shaft.dust = 0.0;\n shaft.dustScale = 1.0;\n scattered = beamDensity * beamAlong(normalize(eye.xyz), sceneDistance, shaft);\n }\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 fromLight = eye.xyz - HAND;\n float lightDistance = length(fromLight);\n vec3 direction = fromLight / lightDistance;\n float cosine = dot(direction, -axis);\n // 1 in the core, SPILL at the edge of the cone, 0 outside\n float core = smoothstep(coreCos, 1.0, cosine);\n float spill = smoothstep(coneCos, coreCos, cosine);\n float cone = max(core, SPILL * spill);\n float falloff = 1.0 / (1.0 + lightDistance * lightDistance / (range * range));\n\n // the normal over wider steps than the spotlight's: at the grazing angles of a torch held at\n // chest height, the facets of the terrain mesh would show as dark blotches on the ground\n vec3 normal = normalize(normalAt(depthTexture, v_textureCoordinates, eye.xyz, 6.0 * czm_pixelRatio)\n + normalAt(depthTexture, v_textureCoordinates, eye.xyz, 18.0 * czm_pixelRatio));\n float shade = mix(WRAP, 1.0, max(dot(normal, -direction), 0.0));\n vec3 light = LIGHT_COLOR * (power * INTENSITY * cone * falloff * shade);\n out_FragColor = vec4(filmic(ambient + color * light + haze), sceneColor.a);\n}\n";
|
package/shaders/Fog.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Fog.glsl.
|
|
2
|
+
export default "// What ValleyFog's fog and the rain's and the snow's haze share: the sky as a ray this long, and\n// their light, lit by the sun as Cesium's fog, turning around sunset to a moonlit blue as bright as\n// the scene's fog.minimumBrightness, rather than a flat gray. Needs Height.\n\n// the sky is a ray this long, in meters\nconst float SKY_DISTANCE = 50000.0;\n// tint of the fog at night, scaled by fog.minimumBrightness\nconst vec3 NIGHT = vec3(0.45, 0.55, 0.85);\n\n// 0 at night to 1 by day, from the sun's height at the camera\nfloat daylight() {\n return smoothstep(-0.1, 0.3, dot(up, czm_sunDirectionEC));\n}\n\n// a fog's color by day, turning to the moonlit blue at night\nvec3 byDaylight(vec3 color, float day) {\n return mix(NIGHT * czm_fogMinimumBrightness, color, day);\n}\n";
|
package/shaders/Frame.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Frame.glsl.
|
|
2
|
+
export default "// Size of the visible frame of the given width over height, centered in the canvas, in texture\n// coordinates: bars above and below when the canvas is narrower than the ratio, on the sides when\n// it is wider; the whole canvas for a ratio of 0.\nvec2 frameSize(float aspectRatio) {\n if (aspectRatio <= 0.0) {\n return vec2(1.0);\n }\n float canvas = czm_viewport.z / czm_viewport.w;\n return aspectRatio < canvas ? vec2(aspectRatio / canvas, 1.0) : vec2(1.0, canvas / aspectRatio);\n}\n";
|
package/shaders/Hash.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Hash.glsl.
|
|
2
|
+
export default "// random in [0, 1) for a point, without sin, which GPUs compute poorly for large numbers\nfloat hash(vec2 p) {\n vec3 p3 = fract(p.xyx * 0.1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\n// per pixel noise in [0, 1), \"interleaved gradient noise\" (Jimenez 2014): a dither that\n// spreads the sample positions of a blur between neighbouring pixels\nfloat pixelNoise(vec2 pixel) {\n return fract(52.9829189 * fract(dot(pixel, vec2(0.06711056, 0.00583715))));\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Height.glsl.
|
|
2
|
+
export default "// Height above the ellipsoid of a point in eye coordinates, computed relative to the camera: in\n// world coordinates, single precision would round the position to about half a meter. The\n// uniforms come from the CPU in double precision (height.js).\nuniform float cameraHeight;\nuniform vec3 up;\nuniform float radius;\n\nfloat heightAt(vec3 eye) {\n // the camera is at the origin of eye coordinates; away from it horizontally, the ground falls\n // below the horizontal by the Earth's curvature\n float rise = dot(eye, up);\n float across = dot(eye, eye) - rise * rise;\n return cameraHeight + rise + across / (2.0 * radius);\n}\n\n// the part below a height of the ray from the camera to a point in eye coordinates, as shares of\n// it, from its start to its end, the height taken as linear along it; none when both ends are above\nvec2 belowHeight(vec3 rayEnd, float height) {\n float h0 = cameraHeight;\n float h1 = heightAt(rayEnd);\n if (min(h0, h1) >= height) {\n return vec2(0.0);\n }\n return vec2(h0 > height ? (h0 - height) / (h0 - h1) : 0.0, h1 > height ? (height - h0) / (h1 - h0) : 1.0);\n}\n";
|
package/shaders/Hsl.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
package/shaders/Jello.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Jello.glsl.
|
|
2
|
+
export default "// The jello of a vibrating camera with a rolling shutter: the sensor reads its rows one after\n// another, so each row sees the camera at a different moment of its shake, and straight edges\n// wobble. After Readout's readout pass (stoatworks-labs, MIT): row r of H is read\n// `readout * (1 - r / H)` before the frame, the camera's pose is taken at the middle of the row's\n// exposure, and the shake is a few sinusoids in x, y and rotation. The phases come from the CPU,\n// in double precision; the picture is magnified by zoom so that its edges do not show.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// the shake: frequencies in Hz, phases at the frame's time in radians, amplitudes in picture\n// heights (x, y) and radians (rotation)\nuniform vec4 shakeHz;\nuniform vec4 phaseX;\nuniform vec4 phaseY;\nuniform vec4 phaseRotation;\nuniform vec4 amplitudeX;\nuniform vec4 amplitudeY;\nuniform vec4 amplitudeRotation;\nuniform float zoom;\nin vec2 v_textureCoordinates;\n\n// seconds to read all the rows, top down, and to expose each\nconst float READOUT = 0.02;\nconst float EXPOSURE = 0.004;\n\nvoid main() {\n vec2 size = czm_viewport.zw;\n // the row, from the top, and how long before the frame it was read, at the middle of its exposure\n float row = floor((1.0 - v_textureCoordinates.y) * size.y);\n float tau = READOUT * (1.0 - row / size.y) + 0.5 * EXPOSURE;\n vec4 arg = czm_twoPi * shakeHz * tau;\n vec2 shift = vec2(dot(amplitudeX, sin(phaseX - arg)), dot(amplitudeY, sin(phaseY - arg)));\n float rotation = dot(amplitudeRotation, sin(phaseRotation - arg));\n\n // centred, in picture heights, so that a rotation is not a shear\n float aspect = size.x / size.y;\n vec2 p = vec2((v_textureCoordinates.x - 0.5) * aspect, v_textureCoordinates.y - 0.5);\n float c = cos(rotation);\n float s = sin(rotation);\n p = (vec2(p.x * c - p.y * s, p.x * s + p.y * c) + shift) / zoom;\n out_FragColor = texture(colorTexture, vec2(p.x / aspect + 0.5, p.y + 0.5));\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from LensDistortion.glsl.
|
|
2
|
+
export default "// The barrel distortion and dark corners of a wide-angle lens, like those of FPV cameras: straight\n// lines bend outward away from the middle. The corners stay in place and the middle is\n// magnified, so the picture keeps filling the screen.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// 0 to 1\nuniform float distortion;\nuniform float vignette;\nin vec2 v_textureCoordinates;\n\n// strongest distortion: the middle magnified by 1 + this, relative to the corners\nconst float MAX_DISTORTION = 0.5;\n\nvoid main() {\n vec2 aspect = vec2(czm_viewport.z / czm_viewport.w, 1.0);\n // from the middle, 1 at the corners\n vec2 fromMiddle = (v_textureCoordinates - 0.5) * aspect / (0.5 * length(aspect));\n float r2 = dot(fromMiddle, fromMiddle);\n float k = distortion * MAX_DISTORTION;\n vec2 uv = 0.5 + (v_textureCoordinates - 0.5) * (1.0 + k * r2) / (1.0 + k);\n vec4 sceneColor = texture(colorTexture, uv);\n float dark = 1.0 - vignette * smoothstep(0.3, 1.0, r2);\n out_FragColor = vec4(sceneColor.rgb * dark, sceneColor.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Lightning.glsl.
|
|
2
|
+
export default "// Lightning, cloud-to-ground strikes: a branching channel from the cloud base to the ground, with 2 to 4\n// return strokes tens of milliseconds apart, down one channel, and a flash that follows their flicker, as\n// in the descriptions of the lightning flash (Rakov and Uman, \"Lightning: Physics and Effects\", 2003). The\n// channel is drawn over the picture in screen space as a hot core in a halo, at least a pixel and a half\n// wide: a channel is centimeters wide, and a thousand meters away it would not show at its true width. It\n// is a line from the cloud to the ground that wanders to either side, by value noise of the position along\n// it in four octaves, pinned at both ends, with branches that leave it downward at an angle and wander the\n// same way, fading toward their ends. The distance to it is the pixel's across the line, less the wander,\n// over the slope of the wander, so that the width stays the same where it zigzags: a function of the\n// position along the line, a handful of noise lookups for each channel, not a polyline for each pixel.\n// The channel is hidden behind nearer terrain. The flash lights each pixel in proportion to its own color,\n// less with its distance to the strike, and the sky toward the cloud above it. Seen from above the cloud\n// base, the channel is hidden in the cloud, and the flash lights the cloud over the strike from inside, as\n// a glow. Needs EyeFromDepth and Hash.\nprecision highp sampler2D;\nconst int STRIKES = 4;\nconst int BRANCHES = 5;\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\n// the top of each strike's channel, at the cloud base, in eye coordinates, and its seed; and its foot\n// and its age in seconds, negative when there is no strike in the slot\nuniform vec4 strikeTop[STRIKES];\nuniform vec4 strikeBottom[STRIKES];\n// the branches of each strike's channel, from the CPU: where along the channel they leave it, the cosine\n// and the sine of their angle from it, and their length as a share of the channel's, 0 for none\nuniform vec4 strikeBranches[STRIKES * BRANCHES];\n// the glow of each strike in the clouds, in eye coordinates, and the map's intensity at the strike,\n// 0 for none; and 0 below the cloud base to 1 above it, where the glow replaces the channel\nuniform vec4 strikeGlow[STRIKES];\nuniform float above;\nin vec2 v_textureCoordinates;\n\nconst int MAX_STROKES = 4;\n// how far the main channel and the branches wander to either side, as a fraction of their length\nconst float MAIN_WANDER = 0.7;\nconst float BRANCH_WANDER = 0.6;\n// the brightness of the branches against the main channel's\nconst float BRANCH_BRIGHTNESS = 0.5;\n// how far from the line between its ends a channel reaches, as a fraction of its length: the main one's\n// wander, then its branches; and the widest halo in pixels. Pixels farther than that skip the channel\nconst float MAIN_REACH = 0.35;\nconst float BRANCH_REACH = 0.5;\nconst float MAX_HALO = 80.0;\nconst float MAX_SLOPE = 2.5;\n// the channel's flicker under which it is not drawn\nconst float FAINT = 0.01;\n// the width of the core and of the halo, in meters: scaled to pixels by the distance, the core to no\n// less than a pixel and a half\nconst float CORE_WIDTH = 1.0;\nconst float HALO_WIDTH = 25.0;\n// how bright the halo is against the core\nconst float HALO = 0.35;\n// the channel is white at its core and blue around it, from the nitrogen lines of a 30000 K plasma, with a\n// violet cast from the hydrogen line of the water in a storm's air (Kieu et al., 2021)\nconst vec3 HALO_COLOR = vec3(0.68, 0.66, 1.0);\nconst vec3 FLASH_COLOR = vec3(0.8, 0.82, 1.0);\n// the extinction of the light of a strike per meter, in red, green and blue: the scattering of the air goes\n// with the wavelength to the minus 4, so far strikes turn yellow, then red, as at sunset\nconst vec3 EXTINCTION = vec3(1.0, 1.9, 3.4) * 2e-5;\n// the distance, in meters, at which the flash on the land has fallen to half\nconst float FLASH_REACH = 3000.0;\n// the sky is a ray this long\nconst float SKY_DISTANCE = 20000.0;\n// time constants of the channel's flicker and of the flash, in seconds\nconst float BOLT_DECAY = 0.05;\nconst float FLASH_DECAY = 0.15;\n// the glow in the clouds above a strike, seen from above: as wide as this, in meters, and as bright\nconst float GLOW_RADIUS = 4000.0;\nconst float GLOW_BRIGHTNESS = 0.9;\n// seen from above, most of the way to the glow is through thin air: its light is dimmed by at most\n// this many meters of the air near the ground, about the height over which the air thins by e\nconst float GLOW_AIR = 8000.0;\n\n// noise of one coordinate, in [-0.5, 0.5), and its slope: linear between the random values of the whole\n// numbers, not smoothed, so that the path is made of straight segments with sharp turns, as a bolt is\nvec2 valueNoise(float x, float seed) {\n float cell = floor(x);\n float low = hash(vec2(cell, seed));\n float high = hash(vec2(cell + 1.0, seed));\n return vec2(mix(low, high, fract(x)) - 0.5, high - low);\n}\n\n// four octaves of it along a channel, from 4 segments to 34: the wander at s and its slope\nvec2 wander(float s, float seed) {\n vec2 sum = vec2(0.0);\n float amplitude = 1.0;\n float frequency = 4.0;\n for (int i = 0; i < 4; i++) {\n sum += amplitude * valueNoise(s * frequency, seed + 7.0 * float(i)) * vec2(1.0, frequency);\n amplitude *= 0.45;\n frequency *= 2.1;\n }\n return sum;\n}\n\n// how far a channel is from its line at s, in its lengths, and the slope of that: the wander held to 0\n// at the start, by a smoothstep over the first quarter of a branch, and at both ends of the main\n// channel, by a sine, so that it leaves the cloud and reaches the ground upright\nvec2 offsetAt(float s, float seed, bool main) {\n vec2 w = wander(s, seed);\n float envelope = sin(czm_pi * s);\n float slope = czm_pi * cos(czm_pi * s);\n if (!main) {\n float u = clamp(4.0 * s, 0.0, 1.0);\n envelope = u * u * (3.0 - 2.0 * u);\n slope = 24.0 * u * (1.0 - u);\n }\n return vec2(w.x * envelope, w.y * envelope + w.x * slope);\n}\n\n// the distance in pixels from p to a channel that leaves a along dir, len pixels long, and the share s\n// of the way along it where p is nearest\nfloat channelDistance(vec2 p, vec2 a, vec2 dir, float len, float amplitude, float seed, bool main, out float s) {\n vec2 r = p - a;\n float along = dot(r, dir);\n s = clamp(along / len, 0.0, 1.0);\n vec2 offset = offsetAt(s, seed, main);\n float across = dot(r, vec2(-dir.y, dir.x)) - amplitude * len * offset.x;\n // the estimate holds near the channel only: past a slope of a few, the halo of a steep segment would\n // stretch sideways\n float slope = clamp(amplitude * offset.y, -MAX_SLOPE, MAX_SLOPE);\n // past the ends, the distance to the end\n return length(vec2(across / sqrt(1.0 + slope * slope), along - s * len));\n}\n\n// the core and the halo of the channel at a distance in pixels\nvec2 channelProfile(float d, float core, float halo) {\n return vec2(exp(-d * d / (core * core)), HALO * exp(-d / halo));\n}\n\n// the light of a strike seen from this far, in each color\nvec3 transmittance(float d) {\n return exp(-EXTINCTION * d);\n}\n\n// the flicker of the channel and the light of the flash, at an age in seconds: each stroke lights it up\n// at once and fades, the first one the brightest, and a dimmer current keeps the channel lit between them\nvoid strokes(float age, float seed, out float bolt, out float flash) {\n bolt = 0.3 * exp(-age / 0.3);\n flash = 0.0;\n float count = 2.0 + floor(3.0 * hash(vec2(seed, 91.0)));\n float start = 0.0;\n for (int i = 0; i < MAX_STROKES; i++) {\n if (float(i) >= count) {\n break;\n }\n float strength = i == 0 ? 1.0 : 0.35 + 0.5 * hash(vec2(seed, 50.0 + float(i)));\n float since = age - start;\n if (since >= 0.0) {\n bolt += strength * exp(-since / BOLT_DECAY);\n flash += strength * exp(-since / FLASH_DECAY);\n }\n start += 0.04 + 0.08 * hash(vec2(seed, 70.0 + float(i)));\n }\n}\n\nvec2 screenPoint(vec3 eye) {\n vec4 clip = czm_projection * vec4(eye, 1.0);\n return (0.5 * clip.xy / clip.w + 0.5) * czm_viewport.zw;\n}\n\n// the distance from p to the segment ab\nfloat segmentDistance(vec2 p, vec2 a, vec2 b) {\n vec2 ab = b - a;\n return length(p - a - ab * clamp(dot(p - a, ab) / dot(ab, ab), 0.0, 1.0));\n}\n\nvoid main() {\n vec2 uv = v_textureCoordinates;\n vec4 sceneColor = texture(colorTexture, uv);\n vec3 color = sceneColor.rgb;\n vec4 eye = eyeAt(depthTexture, uv);\n float sceneDistance = eye.w > 0.0 ? length(eye.xyz) : 1e9;\n vec3 pixelEye = eye.w > 0.0 ? eye.xyz : eye.xyz * SKY_DISTANCE;\n vec2 pixel = uv * czm_viewport.zw;\n // the focal length in pixels\n float focal = 0.5 * czm_projection[1][1] * czm_viewport.w;\n\n vec3 light = vec3(0.0);\n vec3 channel = vec3(0.0);\n for (int k = 0; k < STRIKES; k++) {\n float age = strikeBottom[k].w;\n if (age < 0.0) {\n continue;\n }\n vec3 top = strikeTop[k].xyz;\n vec3 bottom = strikeBottom[k].xyz;\n float seed = strikeTop[k].w;\n float bolt;\n float flash;\n strokes(age, seed, bolt, flash);\n\n // the land around the foot of the strike, and the sky toward the cloud above it\n float reach = distance(pixelEye, bottom) / FLASH_REACH;\n float sky = eye.w > 0.0 ? 0.0 : pow(max(dot(eye.xyz, normalize(top)), 0.0), 6.0);\n vec3 seen = transmittance(length(bottom));\n light += FLASH_COLOR * seen * flash * (color * 1.2 + 0.08) / (1.0 + reach * reach);\n light += FLASH_COLOR * seen * flash * sky * 0.5;\n\n // seen from above, the flash lights the cloud over the strike from inside: a glow around the\n // point of the view ray nearest to it, hidden behind nearer terrain\n vec4 glow = strikeGlow[k];\n float along = dot(glow.xyz, normalize(eye.xyz));\n if (above > 0.0 && glow.w > 0.0 && along > 0.0 && sceneDistance > along * 0.98) {\n float across = length(glow.xyz - normalize(eye.xyz) * along);\n light += FLASH_COLOR * transmittance(min(along, GLOW_AIR)) * flash * glow.w * above * GLOW_BRIGHTNESS * exp(-across * across / (GLOW_RADIUS * GLOW_RADIUS));\n }\n\n // the channel, when both its ends are in front of the camera, and the pixel is near it\n if (top.z > -1.0 || bottom.z > -1.0) {\n continue;\n }\n vec2 a = screenPoint(top);\n vec2 b = screenPoint(bottom);\n float len = distance(a, b);\n // the halo's widest reach, around the middle of the channel: pixels farther than that skip the channel\n float margin = 3.0 * clamp(HALO_WIDTH * focal / length(0.5 * (top + bottom)), 4.0, MAX_HALO);\n // the branches reach further from the line than the main channel's wander does\n float fromLine = segmentDistance(pixel, a, b);\n if (bolt < FAINT || len < 2.0 || fromLine > BRANCH_REACH * len + margin) {\n continue;\n }\n vec2 dir = (b - a) / len;\n float s = clamp(dot(pixel - a, dir) / len, 0.0, 1.0);\n float boltDistance = length(mix(top, bottom, s));\n float core = max(1.5, CORE_WIDTH * focal / boltDistance);\n float halo = clamp(HALO_WIDTH * focal / boltDistance, 4.0, MAX_HALO);\n vec2 profile = vec2(0.0);\n if (fromLine < MAIN_REACH * len + margin) {\n profile = channelProfile(channelDistance(pixel, a, dir, len, MAIN_WANDER, seed, true, s), core, halo);\n }\n for (int j = 0; j < BRANCHES; j++) {\n vec4 branch = strikeBranches[k * BRANCHES + j];\n float branchLen = len * branch.w;\n if (branchLen <= 0.0) {\n continue;\n }\n float at = branch.x;\n vec2 branchDir = vec2(branch.y * dir.x - branch.z * dir.y, branch.z * dir.x + branch.y * dir.y);\n // nowhere near the branch, which stays within its length and wander of its middle, and the main\n // channel's wander of its line\n vec2 onLine = a + dir * len * at;\n if (distance(pixel, onLine + branchDir * 0.5 * branchLen) > (0.5 + 0.5 * BRANCH_WANDER) * branchLen + MAIN_REACH * len + margin) {\n continue;\n }\n vec2 base = onLine + vec2(-dir.y, dir.x) * MAIN_WANDER * len * offsetAt(at, seed, true).x;\n float along;\n float branchDistance = channelDistance(pixel, base, branchDir, branchLen, BRANCH_WANDER, seed + 5.0 * float(j + 1), false, along);\n profile = max(profile, BRANCH_BRIGHTNESS * (1.0 - 0.7 * along) * channelProfile(branchDistance, core, halo));\n }\n // hidden behind terrain nearer than the channel at this height\n if (sceneDistance > boltDistance * 0.98 - 30.0) {\n channel += (1.0 - above) * bolt * transmittance(boltDistance) * (profile.x + profile.y * HALO_COLOR);\n }\n }\n out_FragColor = vec4(color + light + channel, sceneColor.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from MotionBlur.glsl.
|
|
2
|
+
export default "// Camera motion blur, after \"A Reconstruction Filter for Plausible Motion Blur\" (McGuire et al.,\n// I3D 2012). Only the camera moves, so each pixel's motion follows from its depth and the\n// reprojection into the previous frame, computed once per pixel into the velocity buffer of\n// MotionBlurVelocity.glsl. The blur gathers jittered samples along the pixel's motion, weighted\n// by depth so that near terrain and far ridges or sky do not smear into each other.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// xy: half the blur, as a fraction of the viewport; z: the distance from the camera\nuniform sampler2D velocityTexture;\nin vec2 v_textureCoordinates;\n\nconst int SAMPLES = 16;\n// depth difference, relative to the depth, over which a sample goes from in front to behind\nconst float SOFT_DEPTH = 0.1;\n\n\n// xy: half the blur of the pixel at uv, in pixels; z: its distance from the camera\nvec3 halfBlurAt(vec2 uv) {\n vec3 velocity = texture(velocityTexture, uv).xyz;\n return vec3(velocity.xy * czm_viewport.zw, velocity.z);\n}\n\n// 1 when b is in front of a, 0 when it is SOFT_DEPTH behind\nfloat inFront(float a, float b) {\n return clamp(1.0 - (b - a) / (SOFT_DEPTH * min(a, b)), 0.0, 1.0);\n}\n\n// the blur of a pixel with this half blur covers a sample this far away; the extents are kept\n// above 0 for samples without blur\nfloat cone(float gap, vec2 halfBlur) {\n return clamp(1.0 - gap / max(length(halfBlur), 1e-3), 0.0, 1.0);\n}\n\n// both pixels are blurred over the gap between them\nfloat cylinder(float gap, vec2 halfBlur) {\n float extent = max(length(halfBlur), 1e-3);\n return 1.0 - smoothstep(0.95 * extent, 1.05 * extent, gap);\n}\n\nvoid main() {\n vec3 center = halfBlurAt(v_textureCoordinates);\n vec2 halfBlur = center.xy;\n vec4 centerColor = texture(colorTexture, v_textureCoordinates);\n float extent = length(halfBlur);\n if (extent < 0.5) {\n out_FragColor = centerColor;\n return;\n }\n\n float weight = 1.0 / extent;\n vec4 color = centerColor * weight;\n // the jitter turns the banding of few samples into fine noise\n float jitter = pixelNoise(gl_FragCoord.xy) - 0.5;\n for (int i = 0; i < SAMPLES; i++) {\n float t = mix(-1.0, 1.0, (float(i) + jitter + 1.0) / float(SAMPLES + 1));\n vec2 uv = v_textureCoordinates + t * halfBlur / czm_viewport.zw;\n vec3 other = halfBlurAt(uv);\n float gap = abs(t) * extent;\n float front = inFront(center.z, other.z);\n float behind = inFront(other.z, center.z);\n // a sample in front counts when its own blur reaches this pixel, one behind when this\n // pixel's blur reaches it, and both when they are blurred together\n float alpha =\n front * cone(gap, other.xy) +\n behind * cone(gap, halfBlur) +\n 2.0 * cylinder(gap, other.xy) * cylinder(gap, halfBlur);\n weight += alpha;\n color += alpha * texture(colorTexture, uv);\n }\n out_FragColor = color / weight;\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from MotionBlurVelocity.glsl.
|
|
2
|
+
export default "// The velocity buffer of the motion blur, at a fraction of the resolution: each pixel's half\n// blur, as a fraction of the viewport, and its distance from the camera. Computed once per\n// pixel here rather than once per sample in the gather. Needs EyeFromDepth.\nprecision highp sampler2D;\nuniform sampler2D depthTexture;\n// current eye coordinates to the previous frame's clip coordinates, computed in double\n// precision on the CPU\nuniform mat4 reprojection;\n// exposure time over the time since the previous frame: the blur spans the motion during the\n// exposure, whatever the frame rate\nuniform float exposureScale;\nin vec2 v_textureCoordinates;\n\n// longest blur, as a fraction of the viewport height\nconst float MAX_BLUR = 0.05;\n\nvoid main() {\n // the sky is a direction: only the camera rotation moves it\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n vec4 previous = reprojection * eye;\n vec2 motion = previous.w > 0.0 ? v_textureCoordinates - (previous.xy / previous.w * 0.5 + 0.5) : vec2(0.0);\n motion *= exposureScale;\n // the clamp is resolution independent: the ratio to the viewport height is the same at any scale\n vec2 pixels = motion * czm_viewport.zw;\n float extent = length(pixels);\n float maxExtent = MAX_BLUR * czm_viewport.w;\n if (extent > maxExtent) {\n motion *= maxExtent / extent;\n }\n out_FragColor = vec4(0.5 * motion, eye.w == 0.0 ? 1e30 : length(eye.xyz), 1.0);\n}\n";
|
package/shaders/Noise.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Noise.glsl.
|
|
2
|
+
export default "// random gradient in [-1, 1] at a lattice point\nvec3 gradient(vec3 p) {\n p = fract(p * vec3(0.1031, 0.1030, 0.0973));\n p += dot(p, p.yxz + 33.33);\n return fract((p.xxy + p.yxx) * p.zyx) * 2.0 - 1.0;\n}\n\n// gradient noise, about -0.7 to 0.7. After prod80's ReShade Film Grain (MIT), with gradients\n// from a hash instead of a texture\nfloat gradientNoise(vec3 p) {\n vec3 i = floor(p);\n vec3 f = fract(p);\n vec3 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);\n return mix(\n mix(\n mix(dot(gradient(i), f), dot(gradient(i + vec3(1.0, 0.0, 0.0)), f - vec3(1.0, 0.0, 0.0)), u.x),\n mix(dot(gradient(i + vec3(0.0, 1.0, 0.0)), f - vec3(0.0, 1.0, 0.0)), dot(gradient(i + vec3(1.0, 1.0, 0.0)), f - vec3(1.0, 1.0, 0.0)), u.x),\n u.y),\n mix(\n mix(dot(gradient(i + vec3(0.0, 0.0, 1.0)), f - vec3(0.0, 0.0, 1.0)), dot(gradient(i + vec3(1.0, 0.0, 1.0)), f - vec3(1.0, 0.0, 1.0)), u.x),\n mix(dot(gradient(i + vec3(0.0, 1.0, 1.0)), f - vec3(0.0, 1.0, 1.0)), dot(gradient(i + vec3(1.0, 1.0, 1.0)), f - vec3(1.0, 1.0, 1.0)), u.x),\n u.y),\n u.z);\n}\n\n// random gradient in [-1, 1] at a lattice point of the plane, for a seed\nvec2 gradient(vec2 p, float seed) {\n vec3 p3 = fract(vec3(p, seed) * vec3(0.1031, 0.1030, 0.0973));\n p3 += dot(p3, p3.yxz + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy) * 2.0 - 1.0;\n}\n\n// gradient noise over the plane, about -0.7 to 0.7, a new pattern for each seed: half the\n// hashes of the volume's, for a pattern that only has to change by whole frames\nfloat gradientNoise(vec2 p, float seed) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);\n return mix(\n mix(dot(gradient(i, seed), f), dot(gradient(i + vec2(1.0, 0.0), seed), f - vec2(1.0, 0.0)), u.x),\n mix(dot(gradient(i + vec2(0.0, 1.0), seed), f - vec2(0.0, 1.0)), dot(gradient(i + vec2(1.0, 1.0), seed), f - vec2(1.0, 1.0)), u.x),\n u.y);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Normal.glsl.
|
|
2
|
+
export default "// normal at the pixel from its neighbors at this distance in pixels: on each axis the nearer\n// side, so that the edges of the scene do not bend it\nprecision highp sampler2D;\nvec3 normalAt(sampler2D depthTexture, vec2 uv, vec3 center, float distance) {\n vec2 step = distance / czm_viewport.zw;\n vec3 right = eyeAt(depthTexture, uv + vec2(step.x, 0.0)).xyz - center;\n vec3 left = center - eyeAt(depthTexture, uv - vec2(step.x, 0.0)).xyz;\n vec3 above = eyeAt(depthTexture, uv + vec2(0.0, step.y)).xyz - center;\n vec3 below = center - eyeAt(depthTexture, uv - vec2(0.0, step.y)).xyz;\n vec3 dx = dot(right, right) < dot(left, left) ? right : left;\n vec3 dy = dot(above, above) < dot(below, below) ? above : below;\n vec3 normal = normalize(cross(dx, dy));\n // toward the camera\n return normal * sign(dot(normal, -center));\n}\n\n// the terrain mesh is made of flat triangles, and so are the normals from the depth buffer:\n// averaged over several distances, the facets blend into a smoother surface\nvec3 smoothNormalAt(sampler2D depthTexture, vec2 uv, vec3 center) {\n return normalize(normalAt(depthTexture, uv, center, 2.0 * czm_pixelRatio) + normalAt(depthTexture, uv, center, 6.0 * czm_pixelRatio));\n}\n";
|
package/shaders/Phase.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Phase.glsl.
|
|
2
|
+
export default "// Henyey-Greenstein's phase function, normalized: 1 / (4 pi) for light scattered alike in all\n// directions, which the haze and the clouds share.\nfloat henyeyGreenstein(float cosTheta, float g) {\n float g2 = g * g;\n return (1.0 - g2) / (4.0 * czm_pi * pow(1.0 + g2 - 2.0 * g * cosTheta, 1.5));\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Precipitation.glsl.
|
|
2
|
+
export default "// Precipitation, rain or snow: streaks of falling drops in four layers, nearer ones larger, faster\n// and more opaque, in one pass over the scene, after the composite rainfall of ToyShop (Tatarchuk\n// 2006). The drops are laid out on the sphere of view directions, around the direction they fall\n// toward: drops falling along parallel lines appear on great circles through it, so in its Mercator\n// coordinates (the angle around it, and log(tan(theta / 2)) of the angle theta from it) the drops\n// of a layer, at one distance, are a regular grid scrolling at a constant rate: their size and\n// speed on the sphere both go with sin(theta). The streaks fall down the screen at the horizon and\n// radiate from below the camera when it looks down. The drops are hidden behind what is nearer than\n// their layer, and the rain veils the distance in an overcast haze, whose light the drops refract,\n// and in shafts of heavier rain hanging below the cloud base. The wind comes in gusts. The snow,\n// from 0 to 1, turns the rain to snow in one population: the drops slow down from 9 m/s to 1 m/s,\n// which shortens their streak, and grow from thin sharp lines into soft round flakes that flutter,\n// the nearest layer out of focus as in Andrew Baldwin's \"Just snow\", in a thicker, whiter haze.\n// Where it rains comes from the map: the shafts and the share of each ray in the rain from the\n// shafts' pass, the drops from the map's intensity at the camera, read on the CPU. The haze's length\n// below the cloud base is this pass's, from each pixel's depth, sharp at the ridges. Seen from\n// above, where Clouds draws the clouds, they hide the rain under them: no shafts nor haze.\n// Needs EyeFromDepth, Hash, Height, PrecipitationWind, Fog, Phase and RainHaze.\nprecision highp sampler2D;\nconst int LAYERS = 4;\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\n// the shafts' opacity, the ray's extinction, the share of it in the rain and the share of the rain\n// in the sun, from PrecipitationShafts.glsl at a fraction of the resolution\nuniform sampler2D shaftTexture;\n// 1 when Clouds draws the clouds, 0 otherwise\nuniform float clouds;\n// 1 when their shadow map lights the haze, 0 otherwise\nuniform float shadowed;\n// the map's intensity at the camera, 0 to 1\nuniform float localIntensity;\n// from the CPU, as they change with the snow: the length of a drop's trail, its motion over the\n// exposure, in cells, and the scroll of the grid along the fall, in cells, wrapped at PERIOD, which\n// has to follow the speed without a jump\nuniform float trail;\nuniform float offset;\n// what the layers share, from the CPU rather than computed for each pixel: the cells around and\n// along the fall per radian, the number of cells around, and the distance in meters; and the\n// brightness, how far the largest drop's shape reaches around its trail in pixels, the radius of a\n// streak and the radius of the largest flake in pixels\nuniform vec4 layerShape[LAYERS];\nuniform vec4 layerLook[LAYERS];\nin vec2 v_textureCoordinates;\n\n// cells along the fall, for the random drops to repeat seamlessly as they scroll\nconst float PERIOD = 1000.0;\n// the nearest layer is 2 m away, each further layer twice as far; at the horizon, a drop every\n// 0.05 rad around\nconst float NEAREST = 2.0;\n// share of the cells with a drop, at full intensity\nconst float DENSITY = 0.6;\n// a drop is a capsule from a point in its cell, its trail within a cell: the cells are taller than\n// wide in the rain, square in the snow\n// how far a flake flutters across its cell, in cells, and a streak wavers in the gusts\nconst float FLUTTER = 0.12;\nconst float WAVER = 0.08;\n// the drops refract the haze's light, whiter than they would, as the milk film crews add to the\n// water, for rain that shows; the flakes scatter it, near white\nconst float RAIN_MILK = 0.25;\nconst float SNOW_MILK = 0.35;\n// the shafts' shade of the haze: darker in the rain, lighter in the snow\nconst float RAIN_SHAFT_SHADE = 0.75;\nconst float SNOW_SHAFT_SHADE = 1.1;\n// the shafts' resolution, precipitation.js's SHAFT_SCALE\nconst float SHAFT_SCALE = 0.125;\n\n// the shafts' opacity, the ray's extinction, the share of it in the rain and the share of the rain\n// in the sun, blurred by PrecipitationShaftBlur.glsl and upsampled bilinearly from their texture,\n// which is read at its nearest texel\nvec4 shafts() {\n // the size Cesium gives the texture\n vec2 size = ceil(czm_viewport.zw * SHAFT_SCALE);\n vec2 p = v_textureCoordinates * size - 0.5;\n vec2 i = floor(p);\n vec2 f = p - i;\n vec2 center = (i + 0.5) / size;\n vec2 texel = 1.0 / size;\n vec4 bottom = mix(texture(shaftTexture, center), texture(shaftTexture, center + vec2(texel.x, 0.0)), f.x);\n vec4 top = mix(texture(shaftTexture, center + vec2(0.0, texel.y)), texture(shaftTexture, center + texel), f.x);\n return mix(bottom, top, f.y);\n}\n\n// the opacity at the pixel of the drop with its head in the cell `back` cells above its own, if it\n// has one: f is the pixel's place in its own cell. The drop morphs from a thin, sharp, long streak\n// to a round, soft, short flake with the snow: its radius and softness, brightness and flutter.\nfloat drop(vec4 look, float index, float count, float density, float streak, float amplitude, vec2 reach, vec2 pixelCells, vec2 cell, vec2 f, float back) {\n vec2 id = vec2(mod(cell.x, count), mod(cell.y - back, PERIOD) + PERIOD * index);\n float h = hash(id);\n if (h >= density) {\n return 0.0;\n }\n // the drop's random values, from the one hash: uniform in [0, 1) for the cells with a drop; its\n // head somewhere in the middle of its cell, and the pixel's place around its trail\n h /= density;\n vec2 head = vec2(0.25 + 0.5 * fract(h * 13.7), 0.25 + 0.5 * fract(h * 71.3));\n vec2 toHead = vec2(f.x, f.y + back) - head;\n // most drops are nowhere near the pixel: the rest of the shape is for the few that are\n if (abs(toHead.x) > amplitude + reach.x || toHead.y < -reach.y || toHead.y > streak + reach.y) {\n return 0.0;\n }\n // wavering in the gusts or fluttering across the cell at its own rate\n float rate = mix(1.7, 1.0 + 2.0 * fract(h * 5.1), snow);\n toHead.x -= amplitude * sin(rate * time + czm_twoPi * fract(h * 7.3));\n float t = streak > 0.0 ? clamp(toHead.y / streak, 0.0, 1.0) : 0.0;\n // the distance to the trail, in pixels\n float distanceToTrail = length(vec2(toHead.x, toHead.y - t * streak) / pixelCells);\n float radius = mix(look.z, look.w * (0.6 + 0.8 * fract(h * 3.7)), snow);\n // the nearest layer's flakes out of focus, soft and wide; the others sharp\n float soft = mix(0.5, index == 0.0 ? 0.8 * radius : 0.5, snow);\n float coverage = 1.0 - smoothstep(radius - soft, radius + soft, distanceToTrail);\n // fading out along a streak, not along a flake\n float taper = mix(1.0, 4.0 * t * (1.0 - t), streak);\n return look.x * (0.55 + 0.45 * fract(h * 37.1)) * coverage * taper;\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n // the pixel's view direction and distance, the sky far away, and the angle a pixel covers\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n vec3 view = normalize(eye.xyz);\n float sceneDistance = eye.w == 0.0 ? SKY_DISTANCE : length(eye.xyz);\n float pixel = 2.0 / (czm_projection[1][1] * czm_viewport.w);\n\n // the local frame at the camera, in world coordinates\n vec3 upWorld = upWC();\n vec3 eastWorld = eastWC();\n vec3 northWorld = cross(upWorld, eastWorld);\n float tilt = gustingTilt();\n\n // none above the cloud base; as hard as it rains at the camera\n float raining = 1.0 - smoothstep(cloudBase - 50.0, cloudBase + 50.0, cameraHeight);\n\n // the haze over the part of the ray below the cloud base, as rainy as the shafts' pass found it,\n // from the rain's to the snow's color; from above, hidden by the clouds where Clouds draws them\n vec2 below = belowHeight(view * sceneDistance, cloudBase);\n vec4 sampled = shafts();\n vec3 shaft = sampled.xyz * (1.0 - clouds * (1.0 - raining));\n // the rain's, in its bands, and the clear air's and the mist's over the share of the ray in the\n // rain, thinning out with the height, summed in optical depth\n float rainLength = (below.y - below.x) * sceneDistance;\n float h0 = heightAt(view * sceneDistance * below.x);\n float h1 = heightAt(view * sceneDistance * below.y);\n float layers = layerDepth(CLEAR_AIR, h0, h1, rainLength) + layerDepth(MIST, h0, h1, rainLength);\n float haze = rainHaze(rainLength * shaft.y * (1.0 + RAIN_BANDS) * rainExtinction(intensity, snow) + shaft.z * layers);\n // a rain cell seen from a dry spot is still under its own clouds\n float overcast = max(localIntensity, min(shaft.y * (1.0 + RAIN_BANDS), 1.0));\n // the sun through the gaps of the clouds, from their shadow map, or as much as the overcast lets\n float sunlit = shadowed > 0.0 ? sampled.w : 1.0 - overcast;\n vec3 hazeColor = rainHazeColor(view, daylight(), snow, overcast, sunlit);\n vec3 color = mix(sceneColor.rgb, hazeColor, haze);\n\n color = mix(color, min(mix(RAIN_SHAFT_SHADE, SNOW_SHAFT_SHADE, snow) * hazeColor, vec3(1.0)), shaft.x);\n float local = intensity * localIntensity;\n // in eye coordinates: the direction the drops fall toward, tilted toward the east by the wind,\n // and north across it\n vec3 fall = czm_viewRotation * (sin(tilt) * eastWorld - cos(tilt) * upWorld);\n vec3 north = czm_viewRotation * northWorld;\n vec3 across = normalize(north - dot(north, fall) * fall);\n vec3 dropColor = min(hazeColor + mix(RAIN_MILK, SNOW_MILK, snow), vec3(1.0));\n\n float cosTheta = dot(view, fall);\n float sinTheta = sqrt(max(1.0 - cosTheta * cosTheta, 0.0));\n // none toward the poles, where the cells shrink below a pixel\n if (raining > 0.0 && local > 0.0 && sinTheta > 0.01) {\n float around = atan(dot(view, cross(fall, across)), dot(view, across));\n float along = log(sinTheta / max(1.0 + cosTheta, 1e-6));\n float density = DENSITY * local;\n float streak = trail;\n float amplitude = mix(WAVER, FLUTTER, snow);\n // a pixel in radians, around the pole of the fall, where cells shrink\n float pixelOverSin = pixel / sinTheta;\n float brightness = raining * (0.5 + 0.5 * local);\n for (int i = 0; i < LAYERS; i++) {\n vec4 shape = layerShape[i];\n // hidden behind what is nearer than the layer\n float depthFade = smoothstep(0.5, 1.0, sceneDistance / shape.w);\n // a pixel in cells; the layer fades out as its cells get a few pixels wide\n vec2 pixelCells = pixelOverSin * shape.xy;\n float fade = brightness * depthFade * (1.0 - smoothstep(0.15, 0.4, pixelCells.x));\n if (fade <= 0.0) {\n continue;\n }\n float index = float(i);\n vec2 p = vec2(around * shape.x, along * shape.y + offset + 0.37 * index);\n vec2 cell = floor(p);\n vec2 f = p - cell;\n vec4 look = layerLook[i];\n vec2 reach = look.y * pixelCells;\n // the drops with their head in the pixel's cell, and in the cell above, whose trail or soft\n // edge may reach into it\n float alpha = drop(look, index, shape.z, density, streak, amplitude, reach, pixelCells, cell, f, 0.0);\n if (f.y <= streak) {\n alpha = 1.0 - (1.0 - alpha) * (1.0 - drop(look, index, shape.z, density, streak, amplitude, reach, pixelCells, cell, f, 1.0));\n }\n color = mix(color, dropColor, alpha * fade);\n }\n }\n out_FragColor = vec4(color, sceneColor.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from PrecipitationShaftBlur.glsl.
|
|
2
|
+
export default "// The shafts' opacity, the ray's extinction, the share of it in the rain and the share of the rain\n// in the sun, blurred over the neighboring texels at\n// their own resolution: they fade out around the ridges in front of them rather than stopping at\n// their outline. A 3 x 3 binomial kernel here costs a 64th of the reads the same blur would take\n// at the full resolution.\nprecision highp sampler2D;\nuniform sampler2D shaftTexture;\nin vec2 v_textureCoordinates;\n\nvoid main() {\n // czm_viewport is this pass's texture, the size of the shafts'\n vec2 texel = 1.0 / czm_viewport.zw;\n vec4 sum = vec4(0.0);\n for (int y = -1; y <= 1; y++) {\n for (int x = -1; x <= 1; x++) {\n float weight = (x == 0 ? 2.0 : 1.0) * (y == 0 ? 2.0 : 1.0);\n sum += weight * texture(shaftTexture, v_textureCoordinates + vec2(float(x), float(y)) * texel);\n }\n }\n out_FragColor = sum / 16.0;\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from PrecipitationShafts.glsl.
|
|
2
|
+
export default "// The shafts of the rain or the snow, at a fraction of the resolution: their opacity along each\n// view ray, for Precipitation.glsl to shade, and the ray's mean extinction, as a share of the\n// extinction where the map is full, for its haze, which it computes at its own resolution, sharp at\n// the ridges. Both are over the part of the ray below the cloud base, where it rains, found from the\n// scene's depth before it is sampled:\n// from far above, a pixel shows the rain in the column under it. Along that part, patches of noise\n// on the ground under each sample, more of them as it rains harder, slanting with the wind from the\n// cloud base and drifting with it, where the map has rain. Not dithered: at this resolution the\n// dither shows as dots, where the patches are too soft to band. Needs EyeFromDepth, Noise, Height,\n// PrecipitationWind, WeatherMap, Fog, Phase, RainHaze and CloudsShadow. With clouds in the scene, it also finds\n// the share of the rain along the ray that the sun lights through their gaps, from their shadow map:\n// the haze's light shafts (Wronski 2014's front-to-back march, on the clouds' shadow map as\n// Hillaire 2016, sec. 5.9).\nprecision highp sampler2D;\nuniform sampler2D depthTexture;\n// 1 when the clouds' shadow map is there to read, 0 otherwise\nuniform float shadowed;\nin vec2 v_textureCoordinates;\n\n// samples along the part of each ray below the cloud base, as far as SHAFT_DISTANCE from its\n// start, size of the patches in meters, extinction inside them per meter, about 6 km of visibility\nconst int SHAFT_STEPS = 12;\nconst float SHAFT_DISTANCE = 20000.0;\nconst float SHAFT_SIZE = 2500.0;\n// snow hides more, about 2.5 km\nconst vec2 SHAFT_EXTINCTION = vec2(0.0006, 0.0015);\n// the wind's speed, from its tilt of the drops' fall at 9 m/s, also in the snow: a drift changing\n// with the snow would move the shafts\nconst float DRIFT_VELOCITY = 9.0;\n// the shafts thin out over this height below the cloud base, into the clouds\nconst float SHAFT_TOP = 1000.0;\n\nvoid main() {\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n vec3 rayEnd = eye.w == 0.0 ? eye.xyz * SKY_DISTANCE : eye.xyz;\n // the part of the ray below the cloud base, and the part of it that is sampled\n vec2 below = belowHeight(rayEnd, cloudBase);\n float t0 = below.x;\n float t1 = below.y;\n float opacity = 0.0;\n float share = 0.0;\n float wet = 0.0;\n float lit = 1.0;\n if (t1 > t0) {\n float rayLength = length(rayEnd);\n float sampled = min(t1, t0 + SHAFT_DISTANCE / rayLength);\n // the ray in world coordinates, once rather than for each sample\n vec3 rayWC = czm_inverseViewRotation * rayEnd;\n vec3 upWorld = upWC();\n vec3 eastWorld = eastWC();\n float threshold = mix(0.4, 0.0, intensity);\n // the gusts sway the shafts, but they drift with the steady wind\n float slant = tan(gustingTilt());\n float drift = DRIFT_VELOCITY * speed * tan(radians(wind)) * time;\n float raining = 0.0;\n float sunlit = 0.0;\n float density = 0.0;\n for (int i = 0; i < SHAFT_STEPS; i++) {\n float t = mix(t0, sampled, (float(i) + 0.5) / float(SHAFT_STEPS));\n float h = heightAt(rayEnd * t);\n vec3 ground = czm_viewerPositionWC + rayWC * t - upWorld * h;\n float local = mapAt(ground);\n ground -= eastWorld * ((cloudBase - h) * slant + drift);\n float patches = gradientNoise(ground / SHAFT_SIZE);\n // in bands drifting with the shafts, the noise being about -0.7 to 0.7\n float extinction = rainExtinction(intensity * local, snow) * (1.0 + RAIN_BANDS * patches / 0.7);\n raining += extinction;\n // the sun through the clouds above the sample, weighted by how much it scatters there\n vec3 sampleWC = czm_viewerPositionWC + rayWC * t;\n sunlit += extinction * (shadowed > 0.0 ? exp(-shadowDepth(sampleWC)) : 1.0);\n wet += smoothstep(0.0, 0.15, local);\n float top = clamp((cloudBase - h) / SHAFT_TOP, 0.0, 1.0);\n density += local * top * smoothstep(threshold - 0.3, threshold + 0.3, patches);\n }\n // the whole part below the cloud base is as rainy as its sampled part, its extinction as a\n // share of the heaviest bands', and the share of it in the rain\n share = raining / (float(SHAFT_STEPS) * (1.0 + RAIN_BANDS) * rainExtinction(intensity, snow));\n wet /= float(SHAFT_STEPS);\n lit = raining > 0.0 ? sunlit / raining : 1.0;\n float depth = mix(SHAFT_EXTINCTION.x, SHAFT_EXTINCTION.y, snow) * (0.5 + 0.5 * intensity) * density * (sampled - t0) * rayLength / float(SHAFT_STEPS);\n opacity = 1.0 - exp(-depth);\n }\n out_FragColor = vec4(opacity, share, wet, lit);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from PrecipitationWind.glsl.
|
|
2
|
+
export default "// What the passes of the precipitation share: the local frame at the camera and the wind, in gusts.\n// Needs Height.\n// seconds, wrapped at an hour\nuniform float time;\n// tilt of the fall toward the east, in degrees\nuniform float wind;\n// speed of the drops, 1 at their terminal velocity\nuniform float speed;\n// meters above the ellipsoid\nuniform float cloudBase;\n// 0 to 1\nuniform float intensity;\n// share of the precipitation that is snow, 0 to 1\nuniform float snow;\n\n// the gusts: the wind's tilt varies by this share, over periods of 18 and 7.2 s that divide the\n// hour the time wraps at\nconst float GUSTS = 0.35;\n\n// up and east at the camera, in world coordinates\nvec3 upWC() {\n return czm_inverseViewRotation * up;\n}\n\nvec3 eastWC() {\n return normalize(cross(vec3(0.0, 0.0, 1.0), upWC()));\n}\n\n// the wind's tilt, in radians, in gusts\nfloat gustingTilt() {\n float gust = 1.0 + GUSTS * (0.6 * sin(czm_twoPi * time / 18.0) + 0.4 * sin(czm_twoPi * time / 7.2 + 1.3));\n return radians(wind) * gust;\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from RainHaze.glsl.
|
|
2
|
+
export default "// The haze of the rain or the snow, which Precipitation and Clouds share: its extinction, its\n// opacity over an optical depth, and its color. The rain has no color of its own: its extinction\n// is the same at all wavelengths for drops above 0.2 mm (Montero-Martinez et al. 2025), so the\n// haze is the light of the sky and the sun it scatters toward the eye, L_in = (1 - T) * (the light\n// scattered toward the eye) (Hillaire 2020): the clear sky of Cesium's atmosphere at the horizon,\n// warm toward the sun and blue away from it, turning under thick clouds to the gray of an\n// overcast sky, brighter overhead, and the sun's forward glow through thin clouds. A moonlit blue\n// at night, as ValleyFog's. Needs Height, Fog and Phase.\nprecision highp sampler2D;\n\n// the light intensity of the sky's atmosphere, which is not the scene's atmosphere's: 50 rather than\n// 10 by default\nuniform float skyLightIntensity;\n// the clear sky toward the horizon and overhead, from SkyTable.glsl\nuniform sampler2D skyTable;\n\n// the rain rate, in mm/h, at intensity 1, the map's intensity being sqrt(R / RAIN_RATE), as the\n// weather demo draws it from MeteoSwiss' radar\nconst float RAIN_RATE = 10.0;\n// the extinction per meter of the rain, 0.11 R^0.85 per km at R mm/h, added to the clear air's, 0.1\n// per km (Montero-Martinez et al. 2025): 39 km of visibility in dry air, 11.5 km at 2.5 mm/h, 4.4 km\n// at 10 mm/h\nconst vec2 RAIN_LAW = vec2(0.00011, 0.85);\n// layers of haze thinning out with the height, their extinction per meter at sea level and their\n// scale height in meters, summed in optical depth with the rain's: the clear air's, 0.1 per km at\n// the ground, over an aerosol's scale height of 1 to 2 km; and a mist lying low, 1 per km at the\n// Swiss plateau's 500 m, that fills the valleys and leaves the ridges above it. Both scale heights\n// and the mist's extinction are not from a source\nconst vec2 CLEAR_AIR = vec2(0.0001, 1200.0);\nconst vec2 MIST = vec2(0.0035, 400.0);\n// the rain's extinction drifts in bands, by this share around its mean: sigma_rain *=\n// mix(0.6, 1.4, noise) (Wronski 2014, \"procedural Perlin noise animated by wind\")\nconst float RAIN_BANDS = 0.4;\n// visibility in meters at intensity 0 and 1 in the snow\nconst vec2 SNOW_VISIBILITY = vec2(20000.0, 500.0);\n// the haze never quite hides the sky and the far terrain\nconst float MAX_HAZE = 0.9;\n// the overcast sky at the horizon by day, a brighter gray under snow clouds; overhead, it is three\n// times as bright, the CIE's overcast sky, L / L_zenith = (1 + 2 sin(elevation)) / 3 (CIE 2003),\n// and darker under thick clouds, by up to this share: the report's 0.6 left the haze under the\n// heaviest rain at a quarter of the brightness, darker than the scene it veils, which the overcast\n// only dims by half\nconst vec3 RAIN_HAZE = vec3(0.6, 0.64, 0.68);\nconst vec3 SNOW_HAZE = vec3(0.7, 0.72, 0.76);\nconst float THICK_CLOUDS = 0.3;\n// the asymmetry of the light scattered by the rain and by the snow, from the haze's 0.7 to the\n// rain's 0.9 to 1 (arXiv 1403.2977)\nconst vec2 HAZE_ASYMMETRY = vec2(0.9, 0.75);\n// the sky toward the horizon is a ray this long, out of the atmosphere\nconst float SKY_RAY = 1e7;\n// the clear sky at the horizon is read this far above it, in radians, 5 degrees: right at it,\n// Cesium's atmosphere turns a pale cyan that its sky, interpolated from the vertices of its dome,\n// never shows\nconst float SKY_ELEVATION = 0.087;\n// the sky table's texels toward the horizon, besides the one overhead, SkyTable.glsl's\nconst float SKY_TABLE = 64.0;\n\n// the extinction per meter, from the rain's to the snow's, as hard as it rains: 3.912 / visibility\n// is the extinction that leaves 2 % of the light. None where it does not rain, rising from 0 at the\n// edge of the rain: the dry air is Cesium's fog's\nfloat rainExtinction(float intensity, float snow) {\n float rain = max(RAIN_LAW.x * pow(RAIN_RATE * intensity * intensity, RAIN_LAW.y), 1e-9);\n float snowfall = 3.912 / (SNOW_VISIBILITY.x * pow(SNOW_VISIBILITY.y / SNOW_VISIBILITY.x, intensity));\n return smoothstep(0.0, 0.15, intensity) * rain * pow(snowfall / rain, snow);\n}\n\n// the optical depth of a layer of haze along a straight ray this long from height h0 to h1, its\n// height linear along it: the line integral of exponential height fog,\n// (a / b) exp(-b h0) (1 - exp(-b dh)) / dh per unit of length (Quilez), with the first terms of its\n// Taylor series where the ray is level, as Flax's ExponentialHeightFog.hlsl\nfloat layerDepth(vec2 layer, float h0, float h1, float rayLength) {\n float x = (h1 - h0) / layer.y;\n float level = abs(x) < 1e-3 ? 1.0 - 0.5 * x : (1.0 - exp(-x)) / x;\n return layer.x * rayLength * exp(-h0 / layer.y) * level;\n}\n\n// the haze's opacity over an optical depth in the rain\nfloat rainHaze(float opticalDepth) {\n return MAX_HAZE * (1.0 - exp(-opticalDepth));\n}\n\n// the clear sky in a direction in world coordinates from a point at a height, as the sky shader of Cesium's atmosphere\n// renders it without high dynamic range, its inner radius lowered as SkyAtmosphereCommon.glsl\n// lowers it: from the ellipsoid's surface, a ray toward the horizon would cross the densest air\n// for hundreds of kilometers and turn a dim yellow\nvec3 clearSkyFrom(vec3 originWC, float height, vec3 directionWC, vec3 lightWC) {\n czm_ray ray = czm_ray(originWC, directionWC);\n float radiiDifference = czm_ellipsoidRadii.x - czm_ellipsoidRadii.z;\n float adjust = 0.5 * radiiDifference * clamp((height - 0.25 * czm_ellipsoidRadii.x) / (0.75 * czm_ellipsoidRadii.x), 0.0, 1.0);\n float innerRadius = length(originWC) - height - 0.25 * radiiDifference - adjust;\n vec3 rayleigh;\n vec3 mie;\n float opacity;\n czm_computeScattering(ray, SKY_RAY, lightWC, innerRadius, rayleigh, mie, opacity);\n vec3 color = czm_computeAtmosphereColor(ray, lightWC, rayleigh, mie, opacity).rgb * skyLightIntensity / czm_atmosphereLightIntensity;\n return czm_inverseGamma(czm_pbrNeutralTonemapping(color));\n}\n\n// the clear sky seen from the camera\nvec3 clearSky(vec3 directionWC, vec3 lightWC) {\n return clearSkyFrom(czm_viewerPositionWC, czm_eyeHeight, directionWC, lightWC);\n}\n\n// the clear sky seen from under the camera at a height, or from the camera if it is lower: from\n// far above, the camera sees the black of space overhead, not the sky the clouds and the haze see,\n// whose color divided by its luminance would turn to any hue\nvec3 clearSkyBelow(float height, vec3 directionWC, vec3 lightWC) {\n float h = min(czm_eyeHeight, height);\n return clearSkyFrom(czm_viewerPositionWC - normalize(czm_viewerPositionWC) * (czm_eyeHeight - h), h, directionWC, lightWC);\n}\n\n// the clear sky toward a view direction in eye coordinates, at its azimuth and at an elevation\n// above the horizon, in radians, lit by the scene's light, as the sky's atmosphere is lit by\n// default: the scene's atmosphere is lit from overhead by default, and would turn the horizon cyan\n// at any time of day\nvec3 skyToward(vec3 view, float elevation) {\n vec3 level = view - dot(view, up) * up;\n level = dot(level, level) > 1e-6 ? normalize(level) : normalize(cross(up, vec3(1.0, 0.0, 0.0)));\n return clearSky(czm_inverseViewRotation * (cos(elevation) * level + sin(elevation) * up), czm_lightDirectionWC);\n}\n\n// the clear sky toward a view direction in eye coordinates, from the sky table: at its azimuth from\n// the sun's, SKY_ELEVATION above the horizon, toward overhead as it looks up\nvec3 skyFromTable(vec3 view) {\n vec3 level = view - dot(view, up) * up;\n vec3 sun = czm_lightDirectionEC - dot(czm_lightDirectionEC, up) * up;\n float cosAzimuth = dot(level, level) > 1e-6 && dot(sun, sun) > 1e-6 ? dot(normalize(level), normalize(sun)) : 1.0;\n float u = (acos(clamp(cosAzimuth, -1.0, 1.0)) / czm_pi * (SKY_TABLE - 1.0) + 0.5) / (SKY_TABLE + 1.0);\n vec3 horizon = texture(skyTable, vec2(u, 0.5)).rgb;\n vec3 zenith = texture(skyTable, vec2((SKY_TABLE + 0.5) / (SKY_TABLE + 1.0), 0.5)).rgb;\n return mix(horizon, zenith, max(dot(view, up), 0.0));\n}\n\n// the haze's light toward the eye along a view direction in eye coordinates, under an overcast as\n// thick as it rains at the camera or along the ray, 0 to 1: the sky's, from the clear sky to the\n// overcast gray, and the sun's through the clouds, as much of it as reaches the haze, 0 to 1.\n// Drops scatter nearly all the light they intercept: an albedo of 1\nvec3 rainHazeColor(vec3 view, float day, float snow, float overcast, float sunlit) {\n float elevation = max(dot(view, up), 0.0);\n vec3 sky = skyFromTable(view);\n vec3 overcastSky = mix(RAIN_HAZE, SNOW_HAZE, snow) * (1.0 + 2.0 * elevation) * (1.0 - THICK_CLOUDS * overcast);\n vec3 sun = czm_lightColor * sunlit * henyeyGreenstein(dot(view, czm_lightDirectionEC), mix(HAZE_ASYMMETRY.x, HAZE_ASYMMETRY.y, snow));\n vec3 color = min(mix(sky, overcastSky, overcast) + sun, vec3(1.0));\n return byDaylight(color, day);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Shockwave.glsl.
|
|
2
|
+
export default "// Shockwave: from each impact, a ring of refraction spreads over the picture, pushing it\n// outward, with a faint bright rim, and dies out. The ring is measured in meters at the\n// impact's depth, so a far impact makes a small ring.\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\nconst int MAX_IMPACTS = 8;\n// in eye coordinates; w is the age in seconds, negative for an unused slot\nuniform vec4 impacts[MAX_IMPACTS];\n// 0 to 1\nuniform float strength;\n// meters per second\nuniform float speed;\n// seconds\nuniform float duration;\nin vec2 v_textureCoordinates;\n\n// meters, at full strength\nconst float WIDTH = 0.4;\nconst float AMPLITUDE = 0.1;\nconst float GLOW = 0.06;\n// an impact this far behind the depth under it is hidden, meters\nconst float OCCLUSION = 0.5;\n\nvoid main() {\n vec2 uv = v_textureCoordinates;\n vec2 aspect = vec2(czm_viewport.z / czm_viewport.w, 1.0);\n // in half screen heights, from the middle of the screen\n vec2 pixel = (2.0 * uv - 1.0) * aspect;\n vec2 offset = vec2(0.0);\n float glow = 0.0;\n for (int i = 0; i < MAX_IMPACTS; i++) {\n vec4 impact = impacts[i];\n if (impact.w < 0.0 || impact.w > duration || impact.z >= 0.0) {\n continue;\n }\n vec4 clip = czm_projection * vec4(impact.xyz, 1.0);\n vec2 center = clip.xy / clip.w;\n vec4 under = eyeAt(depthTexture, 0.5 * center + 0.5);\n if (under.w != 0.0 && length(under.xyz) < length(impact.xyz) - OCCLUSION) {\n continue;\n }\n // meters at the impact's depth to half screen heights\n float scale = czm_projection[1][1] / -impact.z;\n vec2 toPixel = pixel - center * aspect;\n float distance = length(toPixel) / scale;\n float radius = speed * impact.w;\n float band = exp(-pow((distance - radius) / WIDTH, 2.0));\n float decay = strength * pow(1.0 - impact.w / duration, 2.0);\n vec2 direction = distance > 0.0 ? normalize(toPixel) : vec2(0.0);\n // pushed outward: the pixel shows what was nearer the center\n offset -= direction * AMPLITUDE * scale * band * decay;\n glow += GLOW * band * decay;\n }\n // half screen heights to texture coordinates\n vec4 color = texture(colorTexture, clamp(uv + 0.5 * offset / aspect, 0.0, 1.0));\n out_FragColor = vec4(color.rgb + glow, color.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from SkyTable.glsl.
|
|
2
|
+
export default "// The clear sky the haze scatters, drawn by precipitation.js into a small table at each frame,\n// rather than computed twice for each pixel: its first SKY_TABLE texels at SKY_ELEVATION above the\n// horizon, at azimuths from the sun's to its opposite, the sky being symmetric about the sun's\n// vertical plane, and its last overhead, seen from no higher than the cloud base. In world\n// coordinates, the camera's up the ellipsoid's normal under it, so that the camera turning between\n// frames does not skew it. Needs Height, Fog, Phase and RainHaze.\n// meters above the ellipsoid, the precipitation's\nuniform float cloudBase;\nin vec2 v_textureCoordinates;\n\nvoid main() {\n vec3 upWC = normalize(czm_viewerPositionWC * czm_ellipsoidInverseRadii * czm_ellipsoidInverseRadii);\n float i = floor(v_textureCoordinates.x * (SKY_TABLE + 1.0));\n if (i >= SKY_TABLE) {\n out_FragColor = vec4(clearSkyBelow(cloudBase, upWC, czm_lightDirectionWC), 1.0);\n return;\n }\n vec3 sun = czm_lightDirectionWC - dot(czm_lightDirectionWC, upWC) * upWC;\n sun = dot(sun, sun) > 1e-6 ? normalize(sun) : normalize(cross(upWC, vec3(1.0, 0.0, 0.0)));\n float azimuth = czm_pi * i / (SKY_TABLE - 1.0);\n vec3 level = cos(azimuth) * sun + sin(azimuth) * cross(upWC, sun);\n out_FragColor = vec4(clearSkyBelow(cloudBase, cos(SKY_ELEVATION) * level + sin(SKY_ELEVATION) * upWC, czm_lightDirectionWC), 1.0);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from SpeedLines.glsl.
|
|
2
|
+
export default "// Speed lines: the picture zooms toward the focus, more toward the edges of the screen, and thin\n// streaks radiate from it, a new pattern many times a second, as in comics.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// in eye coordinates; w is 0 for the middle of the screen\nuniform vec4 focus;\n// 0 to 1\nuniform float strength;\n// seconds\nuniform float time;\nin vec2 v_textureCoordinates;\n\nconst int BLUR_STEPS = 8;\n// length of the zoom blur at full strength, as a fraction of the distance to the focus\nconst float BLUR = 0.12;\n// streaks around the focus, and their changes per second\nconst float STREAK_COUNT = 360.0;\nconst float STREAK_RATE = 24.0;\nconst float STREAK_OPACITY = 0.2;\n\n// the streaks draw their randomness from hash: with a sin, they disappeared for half of each\n// cycle of frames\nvoid main() {\n vec2 uv = v_textureCoordinates;\n vec4 clip = czm_projection * vec4(focus.xyz, 1.0);\n // on the screen, in front of the camera\n vec2 center = focus.w > 0.0 && focus.z < 0.0 ? 0.5 * clip.xy / clip.w + 0.5 : vec2(0.5);\n vec2 toCenter = center - uv;\n // distance to the focus, 1 at the half height of the screen\n vec2 aspect = vec2(czm_viewport.z / czm_viewport.w, 1.0);\n float fromCenter = 2.0 * length(toCenter * aspect);\n float edge = smoothstep(0.2, 1.0, fromCenter);\n\n vec4 sceneColor = texture(colorTexture, uv);\n vec3 color = sceneColor.rgb;\n // zoom blur, from a start that varies with the pixel so that the steps blur into noise; none\n // where it would be shorter than a pixel, around the focus\n float blur = strength * BLUR * edge;\n if (blur * length(toCenter * czm_viewport.zw) > 0.5) {\n float jitter = pixelNoise(gl_FragCoord.xy);\n color = vec3(0.0);\n for (int i = 0; i < BLUR_STEPS; i++) {\n color += texture(colorTexture, uv + toCenter * blur * (float(i) + jitter) / float(BLUR_STEPS)).rgb;\n }\n color /= float(BLUR_STEPS);\n }\n\n // streaks: noise around the focus, constant along each ray from it, only toward the edges\n float streaks = STREAK_OPACITY * strength * smoothstep(0.5, 1.2, fromCenter);\n if (streaks > 0.0) {\n vec2 direction = toCenter * aspect;\n float frame = mod(floor(time * STREAK_RATE), 1000.0);\n // value noise over the angle, in whole cells so that it closes around the focus\n float around = (atan(direction.y, direction.x) / czm_twoPi + 0.5) * STREAK_COUNT;\n float cell = floor(around);\n float t = fract(around);\n float noise = mix(hash(vec2(cell, frame)), hash(vec2(mod(cell + 1.0, STREAK_COUNT), frame)), t * t * (3.0 - 2.0 * t));\n color = mix(color, vec3(1.0), streaks * smoothstep(0.72, 0.9, noise));\n }\n out_FragColor = vec4(color, sceneColor.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from SpotlightBeam.glsl.
|
|
2
|
+
export default "// The haze lit by the searchlight's beam, at a fraction of the resolution, as in Killzone Shadow\n// Fall's volumetrics (Valient, SIGGRAPH 2014): r is the light scattered along the view ray, g the\n// log2 of the scene distance for SpotlightBeamBlur.glsl and the upsampling: half floats hold it, and\n// distances compare as ratios. Needs EyeFromDepth, Hash, Noise, SpotlightCone and Beam.\n// brightness of the beam in the air, 0 to 1\nuniform float beam;\n// Henyey-Greenstein asymmetry of the haze, 0 to 1: it scatters mostly forward, so the beam is\n// brighter when looking toward the light\nuniform float beamAnisotropy;\nin vec2 v_textureCoordinates;\n\n// radius of the beam's bright core around the light, in heights: the inverse square is softened\n// within it, as the pool's is capped, so that the top of the beam does not burn out\nconst float BEAM_CORE = 0.25;\n// brightness of the lit haze, over the height so that it keeps with the radius, matched to the\n// searchlight's former analytic beam at the default anisotropy\nconst float BEAM_DENSITY = 19.0;\n// contrast and density of the dust streaks in the beam, across its width\nconst float STREAKS = 0.2;\nconst float STREAK_SCALE = 4.0;\n\nvoid main() {\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\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 Cone spot;\n if (beam <= 0.0 || !spotlightCone(spot)) {\n out_FragColor = vec4(0.0, log2(sceneDistance), 0.0, 1.0);\n return;\n }\n Beam cone;\n cone.apex = spot.light;\n cone.forward = -up;\n cone.edgeCos = spot.coneCos;\n cone.coreCos = spot.penumbraCos;\n cone.hotCore = 0.0;\n cone.light = spot.light;\n cone.reference = spot.height;\n cone.core = BEAM_CORE * spot.height;\n cone.reach = 1e30;\n cone.extinction = 0.0;\n cone.anisotropy = beamAnisotropy;\n cone.spacing = 1.0;\n cone.nearFade = vec2(0.0);\n cone.dust = STREAKS;\n cone.dustScale = STREAK_SCALE;\n float scattered = BEAM_DENSITY / spot.height * beamAlong(normalize(eye.xyz), sceneDistance, cone);\n out_FragColor = vec4(scattered, log2(sceneDistance), 0.0, 1.0);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from SpotlightBeamBlur.glsl.
|
|
2
|
+
export default "// The beam's haze, blurred over the neighboring texels at its own resolution, each weighted by how\n// close its scene distance is to the texel's: the dither of the samples along the rays smooths\n// out, but the haze in front of a ridge does not spill onto the ridge.\nprecision highp sampler2D;\nuniform sampler2D beamTexture;\nin vec2 v_textureCoordinates;\n\n// difference of the log2 of the distances at which a neighbor no longer counts, about 10 %\nconst float SOFT_DEPTH = 0.14;\n\nvoid main() {\n // czm_viewport is this pass's texture, the size of the beam's\n vec2 texel = 1.0 / czm_viewport.zw;\n float centerDepth = texture(beamTexture, v_textureCoordinates).g;\n float sum = 0.0;\n float total = 0.0;\n for (int y = -1; y <= 1; y++) {\n for (int x = -1; x <= 1; x++) {\n vec2 sample_ = texture(beamTexture, v_textureCoordinates + vec2(float(x), float(y)) * texel).rg;\n float gap = abs(sample_.g - centerDepth);\n float weight = (x == 0 ? 2.0 : 1.0) * (y == 0 ? 2.0 : 1.0) * clamp(1.0 - gap / SOFT_DEPTH, 0.0, 1.0);\n sum += weight * sample_.r;\n total += weight;\n }\n }\n out_FragColor = vec4(sum / total, centerDepth, 0.0, 1.0);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from SpotlightCone.glsl.
|
|
2
|
+
export default "// The searchlight's cone, for its beam and for its pool of light: the light hangs above the focus,\n// pointing down. Needs EyeFromDepth.\nprecision highp sampler2D;\nuniform sampler2D depthTexture;\n// in eye coordinates; w is 0 for what is in the middle of the screen\nuniform vec4 focus;\n// local up direction, in eye coordinates\nuniform vec3 up;\n// meters\nuniform float radius;\n// width of the penumbra, as a fraction of the radius\nuniform float softness;\n\n// height of the light above the focus, in radii: a cone of 53 degrees\nconst float HEIGHT = 2.0;\n\nstruct Cone {\n vec3 light;\n float height;\n // cosines of the angles, from the light's axis, at which the pool ends and its penumbra starts\n float coneCos;\n float penumbraCos;\n};\n\n// false without a focus in front of the camera (the sky in the middle, a focus behind)\nbool spotlightCone(out Cone cone) {\n vec4 target = focus.w > 0.0 ? focus : eyeAt(depthTexture, vec2(0.5));\n if (target.w == 0.0 || target.z >= 0.0) {\n return false;\n }\n cone.height = HEIGHT * radius;\n cone.light = target.xyz + cone.height * up;\n float inner = radius * (1.0 - softness);\n cone.coneCos = cone.height / sqrt(cone.height * cone.height + radius * radius);\n cone.penumbraCos = cone.height / sqrt(cone.height * cone.height + inner * inner);\n return true;\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Technicolor.glsl.
|
|
2
|
+
export default "// Three-strip Technicolor: the camera split the scene into red, green and blue records, each\n// printed with its complementary dye, the three dyes over each other. Each color gains its purity,\n// what it has over the other two, and loses some of theirs: grays stay, primaries deepen. After\n// prod80's ReShade Technicolor (MIT), https://github.com/prod80/prod80-ReShade-Repository\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\n// 0 to 1\nuniform float strength;\n// width over height of the visible frame, 0 for the whole canvas\nuniform float aspectRatio;\nin vec2 v_textureCoordinates;\n\n// how much of the other colors' purity each color loses\nconst float COLOR_STRENGTH = 0.2;\n\nvoid main() {\n vec2 inFrame = (v_textureCoordinates - 0.5) / frameSize(aspectRatio);\n if (max(abs(inFrame.x), abs(inFrame.y)) > 0.5) {\n out_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n return;\n }\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n vec3 color = sceneColor.rgb;\n // red: r (1 - g) (1 - b), and so on\n vec3 negative = 1.0 - color;\n vec3 purity = color * negative.grg * negative.bbr;\n vec3 taken = COLOR_STRENGTH * purity;\n vec3 printed = color + purity - taken.yxy - taken.zzx;\n out_FragColor = vec4(clamp(mix(color, printed, strength), 0.0, 1.0), sceneColor.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
package/shaders/Toy.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from Trails.glsl.
|
|
2
|
+
export default "// Trails: a glowing streak along the recent positions of each moving thing, fading with age.\n// The streak is measured in meters at its depth, with a floor of a few pixels so that a far one\n// still shows, and is hidden where the scene is in front of it.\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\nconst int MAX_POINTS = 64;\n// in eye coordinates, consecutive points of a trail; w is the age in seconds, negative for a\n// break between trails or an unused slot\nuniform vec4 trail[MAX_POINTS];\n// seconds\nuniform float fade;\n// half width of the streak, meters\nuniform float width;\nuniform vec3 color;\nin vec2 v_textureCoordinates;\n\n// pixels\nconst float MIN_WIDTH = 1.5;\n// brightness of a streak at its center, fresh\nconst float INTENSITY = 0.5;\n// a point this far behind the depth under it is hidden, meters\nconst float OCCLUSION = 0.5;\n\nvoid main() {\n vec2 uv = v_textureCoordinates;\n vec2 aspect = vec2(czm_viewport.z / czm_viewport.w, 1.0);\n vec2 pixel = (2.0 * uv - 1.0) * aspect;\n float minWidth = MIN_WIDTH * czm_pixelRatio * 2.0 / czm_viewport.w;\n vec4 under = eyeAt(depthTexture, uv);\n float underDistance = under.w != 0.0 ? length(under.xyz) : 1e30;\n\n float glow = 0.0;\n for (int i = 0; i < MAX_POINTS - 1; i++) {\n vec4 a = trail[i];\n vec4 b = trail[i + 1];\n if (a.w < 0.0 || b.w < 0.0 || a.z >= 0.0 || b.z >= 0.0 || a.w > fade) {\n continue;\n }\n vec4 clipA = czm_projection * vec4(a.xyz, 1.0);\n vec4 clipB = czm_projection * vec4(b.xyz, 1.0);\n vec2 pa = clipA.xy / clipA.w * aspect;\n vec2 pb = clipB.xy / clipB.w * aspect;\n vec2 ab = pb - pa;\n float t = clamp(dot(pixel - pa, ab) / max(dot(ab, ab), 1e-12), 0.0, 1.0);\n float distance = length(pixel - (pa + t * ab));\n float depth = mix(-a.z, -b.z, t);\n if (mix(length(a.xyz), length(b.xyz), t) > underDistance + OCCLUSION) {\n continue;\n }\n float halfWidth = max(width * czm_projection[1][1] / depth, minWidth);\n float age = mix(a.w, b.w, t);\n glow += exp(-pow(distance / halfWidth, 2.0)) * (1.0 - age / fade);\n }\n vec4 sceneColor = texture(colorTexture, uv);\n out_FragColor = vec4(sceneColor.rgb + color * INTENSITY * min(glow, 1.0), sceneColor.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from ValleyFog.glsl.
|
|
2
|
+
export default "// Fog filling the valleys below an altitude: the fog along the ray from the camera to the pixel\n// is the part of the ray below the top, the density thinning over a soft band under the top. Needs\n// EyeFromDepth, Height and Fog.\nprecision highp sampler2D;\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\n// meters above the ellipsoid\nuniform float top;\n// per meter\nuniform float density;\n// meters\nuniform float softness;\nuniform vec4 color;\nin vec2 v_textureCoordinates;\n\n// the integral of clamp(x, 0, 1)\nfloat ramp(float x) {\n return x <= 0.0 ? 0.0 : x < 1.0 ? 0.5 * x * x : x - 0.5;\n}\n\n// mean fraction of the density along a straight ray from height h0 to h1\nfloat fogFraction(float h0, float h1) {\n float band = max(softness, 1e-3);\n if (abs(h1 - h0) < 1e-2) {\n return clamp((top - h0) / band, 0.0, 1.0);\n }\n return band * (ramp((top - h0) / band) - ramp((top - h1) / band)) / (h1 - h0);\n}\n\nvoid main() {\n vec4 sceneColor = texture(colorTexture, v_textureCoordinates);\n vec4 eye = eyeAt(depthTexture, v_textureCoordinates);\n // the sky is a direction\n vec3 end = eye.w == 0.0 ? eye.xyz * SKY_DISTANCE : eye.xyz;\n float amount = 1.0 - exp(-density * length(end) * fogFraction(cameraHeight, heightAt(end)));\n // lit by the sun, as Cesium's fog: the fog color during the day, turning around sunset to a\n // moonlit blue as bright as the scene's fog.minimumBrightness, rather than a flat gray\n vec3 fogColor = byDaylight(color.rgb, daylight());\n out_FragColor = vec4(mix(sceneColor.rgb, fogColor, amount * color.a), sceneColor.a);\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is automatically rebuilt from WeatherMap.glsl.
|
|
2
|
+
export default "// Where it rains: the map of the local intensity, 0 to 1 in its red channel, over a rectangle of\n// longitudes and latitudes, sampled bilinearly, which fades the rain in over a cell at its edges.\n// Without a map, a pixel over the whole globe: rain everywhere for Precipitation, none for Clouds.\n// A next map over the same rectangle crossfades from it by the blend, as the radar from one hour to\n// the next; without one, the map again.\nprecision highp sampler2D;\nuniform sampler2D map;\nuniform sampler2D nextMap;\nuniform float mapBlend;\n// the rectangle in the ellipsoid's texture coordinates: its west and south, and one over its width\n// and height\nuniform vec4 mapBounds;\n\n// a point in world coordinates on the map, 0 to 1 over its rectangle: single precision rounds it to\n// about half a meter, nothing at the scale of a map\nvec2 mapUv(vec3 positionWC) {\n vec3 normal = normalize(positionWC * czm_ellipsoidInverseRadii * czm_ellipsoidInverseRadii);\n return (czm_ellipsoidTextureCoordinates(normal) - mapBounds.xy) * mapBounds.zw;\n}\n\n// the map's texel at a point in world coordinates, all its channels, 0 outside the map\nvec4 mapTexelAt(vec3 positionWC) {\n vec2 uv = mapUv(positionWC);\n if (any(lessThan(uv, vec2(0.0))) || any(greaterThan(uv, vec2(1.0)))) {\n return vec4(0.0);\n }\n return mix(texture(map, uv), texture(nextMap, uv), mapBlend);\n}\n\n// the local intensity at a point in world coordinates, 0 outside the map\nfloat mapAt(vec3 positionWC) {\n return mapTexelAt(positionWC).r;\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
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";
|
package/snow-line.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {Math as CesiumMath} from '@cesium/core';
|
|
2
|
+
import {PostProcessStage} from '@cesium/engine';
|
|
3
|
+
import {acquireTerrainDepth, releaseTerrainDepth} from './depth-test.js';
|
|
4
|
+
import Effect from './effect.js';
|
|
5
|
+
import {heightUniforms} from './height.js';
|
|
6
|
+
import EyeFromDepth from './shaders/EyeFromDepth.js';
|
|
7
|
+
import Height from './shaders/Height.js';
|
|
8
|
+
import Normal from './shaders/Normal.js';
|
|
9
|
+
import SnowLineShader from './shaders/SnowLine.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Snow above an altitude, on the slopes gentle enough to hold it, shaded by
|
|
13
|
+
* the sun.
|
|
14
|
+
*/
|
|
15
|
+
export default class SnowLine extends Effect {
|
|
16
|
+
/**
|
|
17
|
+
* @param {import('@cesium/engine').CesiumWidget} viewer
|
|
18
|
+
* @param {{altitude?: number, transition?: number, maxSlope?: number, coverage?: number}} [options]
|
|
19
|
+
*/
|
|
20
|
+
constructor(viewer, options = {}) {
|
|
21
|
+
super(viewer);
|
|
22
|
+
this.altitude_ = options.altitude ?? 2000;
|
|
23
|
+
this.transition_ = options.transition ?? 200;
|
|
24
|
+
this.maxSlope_ = options.maxSlope ?? 50;
|
|
25
|
+
this.coverage_ = options.coverage ?? 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @override
|
|
30
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
31
|
+
*/
|
|
32
|
+
createStage_(scene) {
|
|
33
|
+
return new PostProcessStage({
|
|
34
|
+
fragmentShader: EyeFromDepth + Height + Normal + SnowLineShader,
|
|
35
|
+
uniforms: {
|
|
36
|
+
...heightUniforms(scene),
|
|
37
|
+
altitude: () => this.altitude_,
|
|
38
|
+
transition: () => this.transition_,
|
|
39
|
+
maxSlope: () => CesiumMath.toRadians(this.maxSlope_),
|
|
40
|
+
coverage: () => this.coverage_,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @override
|
|
47
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
48
|
+
*/
|
|
49
|
+
activated_(scene) {
|
|
50
|
+
acquireTerrainDepth(scene);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @override
|
|
55
|
+
* @param {import('@cesium/engine').Scene} scene
|
|
56
|
+
*/
|
|
57
|
+
deactivating_(scene) {
|
|
58
|
+
releaseTerrainDepth(scene);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Altitude of the snow line, in meters above the ellipsoid.
|
|
63
|
+
*/
|
|
64
|
+
get altitude() {
|
|
65
|
+
return this.altitude_;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
set altitude(value) {
|
|
69
|
+
this.altitude_ = value;
|
|
70
|
+
this.viewer.scene.requestRender();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Height of the band around the altitude over which the snow thins out, in meters.
|
|
75
|
+
*/
|
|
76
|
+
get transition() {
|
|
77
|
+
return this.transition_;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
set transition(value) {
|
|
81
|
+
this.transition_ = value;
|
|
82
|
+
this.viewer.scene.requestRender();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Steepest slope that holds snow, in degrees.
|
|
87
|
+
*/
|
|
88
|
+
get maxSlope() {
|
|
89
|
+
return this.maxSlope_;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
set maxSlope(value) {
|
|
93
|
+
this.maxSlope_ = value;
|
|
94
|
+
this.viewer.scene.requestRender();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Opacity of the snow, 0 to 1.
|
|
99
|
+
*/
|
|
100
|
+
get coverage() {
|
|
101
|
+
return this.coverage_;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
set coverage(value) {
|
|
105
|
+
this.coverage_ = value;
|
|
106
|
+
this.viewer.scene.requestRender();
|
|
107
|
+
}
|
|
108
|
+
}
|