@tableslayer/ui 0.1.3 → 0.1.4

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.
Files changed (205) hide show
  1. package/package.json +2 -13
  2. package/src/lib/components/Avatar/Avatar.svelte +82 -0
  3. package/src/lib/components/Avatar/AvatarFileInput.svelte +85 -0
  4. package/src/lib/components/Avatar/AvatarPopover.svelte +34 -0
  5. package/src/lib/components/Avatar/index.ts +4 -0
  6. package/src/lib/components/Avatar/types.ts +24 -0
  7. package/src/lib/components/BrushSizeSlider/BrushSizeSlider.svelte +174 -0
  8. package/src/lib/components/BrushSizeSlider/index.ts +1 -0
  9. package/src/lib/components/Button/Button.svelte +182 -0
  10. package/src/lib/components/Button/ConfirmActionButton.svelte +98 -0
  11. package/src/lib/components/Button/IconButton.svelte +121 -0
  12. package/src/lib/components/Button/RadioButton.svelte +93 -0
  13. package/src/lib/components/Button/index.ts +5 -0
  14. package/src/lib/components/Button/types.ts +54 -0
  15. package/src/lib/components/CardFan/CardFan.svelte +165 -0
  16. package/src/lib/components/CardFan/index.ts +2 -0
  17. package/src/lib/components/CardFan/types.ts +6 -0
  18. package/src/lib/components/CodeBlock/Code.svelte +7 -0
  19. package/src/lib/components/CodeBlock/CodeBlock.svelte +102 -0
  20. package/src/lib/components/CodeBlock/index.ts +3 -0
  21. package/src/lib/components/CodeBlock/types.ts +10 -0
  22. package/src/lib/components/ColorMode/ColorMode.svelte +8 -0
  23. package/src/lib/components/ColorMode/index.ts +2 -0
  24. package/src/lib/components/ColorMode/types.ts +12 -0
  25. package/src/lib/components/ColorPicker/ColorPicker.svelte +838 -0
  26. package/src/lib/components/ColorPicker/ColorPickerSwatch.svelte +32 -0
  27. package/src/lib/components/ColorPicker/index.ts +3 -0
  28. package/src/lib/components/ColorPicker/types.ts +51 -0
  29. package/src/lib/components/ContextMenu/ContextMenu.svelte +86 -0
  30. package/src/lib/components/ContextMenu/index.ts +2 -0
  31. package/src/lib/components/ContextMenu/types.ts +15 -0
  32. package/src/lib/components/DrawingSliders/DrawingSliders.svelte +379 -0
  33. package/src/lib/components/DrawingSliders/index.ts +1 -0
  34. package/src/lib/components/Editor/Editor.svelte +825 -0
  35. package/src/lib/components/Editor/index.ts +1 -0
  36. package/src/lib/components/FogSliders/FogSliders.svelte +33 -0
  37. package/src/lib/components/FogSliders/index.ts +1 -0
  38. package/src/lib/components/Hr/Hr.svelte +15 -0
  39. package/src/lib/components/Hr/index.ts +1 -0
  40. package/src/lib/components/Icon/Icon.svelte +6 -0
  41. package/src/lib/components/Icon/index.ts +2 -0
  42. package/src/lib/components/Icon/types.ts +20 -0
  43. package/src/lib/components/Input/DualInputSlider.svelte +126 -0
  44. package/src/lib/components/Input/FileInput.svelte +176 -0
  45. package/src/lib/components/Input/FormControl.svelte +150 -0
  46. package/src/lib/components/Input/FormError.svelte +37 -0
  47. package/src/lib/components/Input/Input.svelte +56 -0
  48. package/src/lib/components/Input/InputCheckbox.svelte +99 -0
  49. package/src/lib/components/Input/InputSlider.svelte +86 -0
  50. package/src/lib/components/Input/Label.svelte +19 -0
  51. package/src/lib/components/Input/index.ts +9 -0
  52. package/src/lib/components/Input/types.ts +39 -0
  53. package/src/lib/components/Link/Link.svelte +41 -0
  54. package/src/lib/components/Link/LinkBox.svelte +20 -0
  55. package/src/lib/components/Link/LinkOverlay.svelte +23 -0
  56. package/src/lib/components/Link/index.ts +4 -0
  57. package/src/lib/components/Link/types.ts +17 -0
  58. package/src/lib/components/Loading/Loader.svelte +60 -0
  59. package/src/lib/components/Loading/Skeleton.svelte +9 -0
  60. package/src/lib/components/Loading/index.ts +2 -0
  61. package/src/lib/components/Logo/Logo.svelte +16 -0
  62. package/src/lib/components/Logo/index.ts +1 -0
  63. package/src/lib/components/MarkerTooltip/MarkerTooltip.svelte +435 -0
  64. package/src/lib/components/MarkerTooltip/index.ts +1 -0
  65. package/src/lib/components/Menu/SelectorMenu.svelte +280 -0
  66. package/src/lib/components/Menu/index.ts +2 -0
  67. package/src/lib/components/Menu/types.ts +17 -0
  68. package/src/lib/components/MyCounterButton.svelte +11 -0
  69. package/src/lib/components/Panel/index.ts +2 -0
  70. package/src/lib/components/Panel/panel.svelte +18 -0
  71. package/src/lib/components/Panel/types.ts +8 -0
  72. package/src/lib/components/PersistButton/PersistButton.svelte +100 -0
  73. package/src/lib/components/PersistButton/index.ts +1 -0
  74. package/src/lib/components/Popover/Popover.svelte +81 -0
  75. package/src/lib/components/Popover/index.ts +2 -0
  76. package/src/lib/components/Popover/types.ts +19 -0
  77. package/src/lib/components/PropsTable/PropsTable.svelte +107 -0
  78. package/src/lib/components/RadialMenu/EffectPreview.svelte +36 -0
  79. package/src/lib/components/RadialMenu/EffectPreviewScene.svelte +194 -0
  80. package/src/lib/components/RadialMenu/RadialMenu.svelte +503 -0
  81. package/src/lib/components/RadialMenu/RadialMenuItem.svelte +176 -0
  82. package/src/lib/components/RadialMenu/index.ts +2 -0
  83. package/src/lib/components/RadialMenu/types.ts +35 -0
  84. package/src/lib/components/Select/Select.svelte +342 -0
  85. package/src/lib/components/Select/index.ts +2 -0
  86. package/src/lib/components/Select/types.ts +22 -0
  87. package/src/lib/components/Spacer/Spacer.svelte +14 -0
  88. package/src/lib/components/Spacer/index.ts +2 -0
  89. package/src/lib/components/Spacer/types.ts +5 -0
  90. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationLayer.svelte +445 -0
  91. package/src/lib/components/Stage/components/AnnotationLayer/AnnotationMaterial.svelte +167 -0
  92. package/src/lib/components/Stage/components/AnnotationLayer/types.ts +196 -0
  93. package/src/lib/components/Stage/components/CursorLayer/CursorLayer.svelte +148 -0
  94. package/src/lib/components/Stage/components/CursorLayer/cursor.svg +26 -0
  95. package/src/lib/components/Stage/components/CursorLayer/index.ts +2 -0
  96. package/src/lib/components/Stage/components/CursorLayer/types.ts +23 -0
  97. package/src/lib/components/Stage/components/DrawingLayer/DrawingMaterial.svelte +364 -0
  98. package/src/lib/components/Stage/components/DrawingLayer/types.ts +65 -0
  99. package/src/lib/components/Stage/components/EdgeOverlayLayer/EdgeOverlayLayer.svelte +72 -0
  100. package/src/lib/components/Stage/components/EdgeOverlayLayer/types.ts +34 -0
  101. package/src/lib/components/Stage/components/FogLayer/FogLayer.svelte +75 -0
  102. package/src/lib/components/Stage/components/FogLayer/types.ts +51 -0
  103. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarLayer.svelte +249 -0
  104. package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarMaterial.svelte +200 -0
  105. package/src/lib/components/Stage/components/FogOfWarLayer/types.ts +116 -0
  106. package/src/lib/components/Stage/components/GridLayer/GridLayer.svelte +20 -0
  107. package/src/lib/components/Stage/components/GridLayer/GridMaterial.svelte +69 -0
  108. package/src/lib/components/Stage/components/GridLayer/types.ts +79 -0
  109. package/src/lib/components/Stage/components/LayerInput/LayerInput.svelte +300 -0
  110. package/src/lib/components/Stage/components/MapLayer/MapLayer.svelte +196 -0
  111. package/src/lib/components/Stage/components/MapLayer/dataSources/GifDataSource.ts +265 -0
  112. package/src/lib/components/Stage/components/MapLayer/dataSources/IMapDataSource.ts +55 -0
  113. package/src/lib/components/Stage/components/MapLayer/dataSources/ImageDataSource.ts +87 -0
  114. package/src/lib/components/Stage/components/MapLayer/dataSources/VideoDataSource.ts +150 -0
  115. package/src/lib/components/Stage/components/MapLayer/dataSources/dataSourceFactory.ts +48 -0
  116. package/src/lib/components/Stage/components/MapLayer/dataSources/index.ts +16 -0
  117. package/src/lib/components/Stage/components/MapLayer/types.ts +58 -0
  118. package/src/lib/components/Stage/components/MarkerLayer/MarkerLayer.svelte +398 -0
  119. package/src/lib/components/Stage/components/MarkerLayer/MarkerToken.svelte +262 -0
  120. package/src/lib/components/Stage/components/MarkerLayer/types.ts +126 -0
  121. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementLayer.svelte +364 -0
  122. package/src/lib/components/Stage/components/MeasurementLayer/MeasurementManager.svelte +473 -0
  123. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BaseMeasurement.ts +427 -0
  124. package/src/lib/components/Stage/components/MeasurementLayer/measurements/BeamMeasurement.ts +105 -0
  125. package/src/lib/components/Stage/components/MeasurementLayer/measurements/CircleMeasurement.ts +98 -0
  126. package/src/lib/components/Stage/components/MeasurementLayer/measurements/ConeMeasurement.ts +163 -0
  127. package/src/lib/components/Stage/components/MeasurementLayer/measurements/LineMeasurement.ts +102 -0
  128. package/src/lib/components/Stage/components/MeasurementLayer/measurements/RectangleMeasurement.ts +120 -0
  129. package/src/lib/components/Stage/components/MeasurementLayer/measurements/index.ts +7 -0
  130. package/src/lib/components/Stage/components/MeasurementLayer/types.ts +94 -0
  131. package/src/lib/components/Stage/components/MeasurementLayer/utils/canvasDrawing.ts +357 -0
  132. package/src/lib/components/Stage/components/MeasurementLayer/utils/distanceCalculations.ts +170 -0
  133. package/src/lib/components/Stage/components/ParticleSystem/ParticleSystem.svelte +220 -0
  134. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/ash.png +0 -0
  135. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/leaves.png +0 -0
  136. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/rain.png +0 -0
  137. package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/snow.png +0 -0
  138. package/src/lib/components/Stage/components/ParticleSystem/rng.js +20 -0
  139. package/src/lib/components/Stage/components/ParticleSystem/types.ts +95 -0
  140. package/src/lib/components/Stage/components/PerformanceDebugger/PerformanceDebugger.svelte +144 -0
  141. package/src/lib/components/Stage/components/PerformanceDebugger/index.ts +1 -0
  142. package/src/lib/components/Stage/components/PerformanceOverlay/PerformanceOverlay.svelte +208 -0
  143. package/src/lib/components/Stage/components/PerformanceOverlay/index.ts +1 -0
  144. package/src/lib/components/Stage/components/PointerInputManager/PointerInputManager.svelte +201 -0
  145. package/src/lib/components/Stage/components/Scene/Scene.svelte +651 -0
  146. package/src/lib/components/Stage/components/Scene/luts.ts +24 -0
  147. package/src/lib/components/Stage/components/Scene/types.ts +225 -0
  148. package/src/lib/components/Stage/components/Stage/Stage.svelte +332 -0
  149. package/src/lib/components/Stage/components/Stage/types.ts +136 -0
  150. package/src/lib/components/Stage/components/WeatherLayer/WeatherLayer.svelte +135 -0
  151. package/src/lib/components/Stage/components/WeatherLayer/presets/AshPreset.ts +71 -0
  152. package/src/lib/components/Stage/components/WeatherLayer/presets/LeavesPreset.ts +70 -0
  153. package/src/lib/components/Stage/components/WeatherLayer/presets/RainPreset.ts +68 -0
  154. package/src/lib/components/Stage/components/WeatherLayer/presets/SnowPreset.ts +70 -0
  155. package/src/lib/components/Stage/components/WeatherLayer/presets/index.ts +6 -0
  156. package/src/lib/components/Stage/components/WeatherLayer/types.ts +35 -0
  157. package/src/lib/components/Stage/helpers/clippingPlaneStore.svelte.ts +28 -0
  158. package/src/lib/components/Stage/helpers/debugState.svelte.ts +18 -0
  159. package/src/lib/components/Stage/helpers/grid.ts +548 -0
  160. package/src/lib/components/Stage/helpers/lazyBrush.ts +171 -0
  161. package/src/lib/components/Stage/helpers/performanceMetrics.svelte.ts +220 -0
  162. package/src/lib/components/Stage/helpers/utils.ts +21 -0
  163. package/src/lib/components/Stage/index.ts +49 -0
  164. package/src/lib/components/Stage/shaders/AnnotationEffects.frag +1070 -0
  165. package/src/lib/components/Stage/shaders/Annotations.frag +29 -0
  166. package/src/lib/components/Stage/shaders/Drawing.frag +83 -0
  167. package/src/lib/components/Stage/shaders/Drawing.vert +5 -0
  168. package/src/lib/components/Stage/shaders/Fog.frag +147 -0
  169. package/src/lib/components/Stage/shaders/FractalNoise.frag +96 -0
  170. package/src/lib/components/Stage/shaders/GridShader.frag +174 -0
  171. package/src/lib/components/Stage/shaders/Overlay.frag +23 -0
  172. package/src/lib/components/Stage/shaders/Overlay.vert +0 -0
  173. package/src/lib/components/Stage/shaders/Particles.frag +27 -0
  174. package/src/lib/components/Stage/shaders/Particles.vert +51 -0
  175. package/src/lib/components/Stage/shaders/ToolOutline.frag +59 -0
  176. package/src/lib/components/Stage/shaders/default.vert +8 -0
  177. package/src/lib/components/Stage/types.ts +4 -0
  178. package/src/lib/components/Table/Table.svelte +16 -0
  179. package/src/lib/components/Table/Td.svelte +17 -0
  180. package/src/lib/components/Table/Th.svelte +18 -0
  181. package/src/lib/components/Table/index.ts +4 -0
  182. package/src/lib/components/Table/types.ts +14 -0
  183. package/src/lib/components/Text/Text.svelte +23 -0
  184. package/src/lib/components/Text/index.ts +2 -0
  185. package/src/lib/components/Text/types.ts +12 -0
  186. package/src/lib/components/Title/Title.svelte +54 -0
  187. package/src/lib/components/Title/index.ts +2 -0
  188. package/src/lib/components/Title/types.ts +9 -0
  189. package/src/lib/components/Toast/Toast.svelte +155 -0
  190. package/src/lib/components/Toast/index.ts +5 -0
  191. package/src/lib/components/Toast/toastCookie.ts +24 -0
  192. package/src/lib/components/Toast/types.ts +6 -0
  193. package/src/lib/components/ToolTip/ToolTip.svelte +70 -0
  194. package/src/lib/components/ToolTip/index.ts +2 -0
  195. package/src/lib/components/ToolTip/types.ts +14 -0
  196. package/src/lib/components/index.ts +32 -0
  197. package/src/lib/components/types.ts +0 -0
  198. package/src/lib/index.ts +2 -0
  199. package/src/lib/styles/globals.css +108 -0
  200. package/src/lib/styles/normalize.css +9 -0
  201. package/src/lib/styles/reset.css +133 -0
  202. package/src/lib/styles/utilities.css +179 -0
  203. package/src/lib/styles/vars.css +1103 -0
  204. package/src/lib/types/awareness.ts +17 -0
  205. package/src/lib/utils/rle.ts +217 -0
@@ -0,0 +1,1070 @@
1
+ precision highp float;
2
+
3
+ uniform sampler2D uMaskTexture;
4
+ uniform float uTime;
5
+ uniform int uEffectType;
6
+ uniform vec3 uBaseColor;
7
+ uniform float uOpacity;
8
+ uniform float uSpeed;
9
+ uniform float uIntensity;
10
+ uniform float uSoftness;
11
+ uniform float uBorder;
12
+ uniform float uRoughness;
13
+
14
+ uniform int uEdgeMinMipMapLevel;
15
+ uniform int uEdgeMaxMipMapLevel;
16
+
17
+ uniform vec4 uClippingPlanes[NUM_CLIPPING_PLANES];
18
+
19
+ varying vec2 vUv;
20
+ varying vec3 vWorldPosition;
21
+
22
+ // Simplex noise from ashima/webgl-noise
23
+ vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
24
+ vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
25
+ vec3 permute(vec3 x) { return mod289(((x * 34.0) + 1.0) * x); }
26
+
27
+ float snoise(vec2 v) {
28
+ const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
29
+ vec2 i = floor(v + dot(v, C.yy));
30
+ vec2 x0 = v - i + dot(i, C.xx);
31
+ vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
32
+ vec4 x12 = x0.xyxy + C.xxzz;
33
+ x12.xy -= i1;
34
+ i = mod289(i);
35
+ vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
36
+ vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
37
+ m = m * m * m * m;
38
+ vec3 x = 2.0 * fract(p * C.www) - 1.0;
39
+ vec3 h = abs(x) - 0.5;
40
+ vec3 ox = floor(x + 0.5);
41
+ vec3 a0 = x - ox;
42
+ m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
43
+ vec3 g;
44
+ g.x = a0.x * x0.x + h.x * x0.y;
45
+ g.yz = a0.yz * x12.xz + h.yz * x12.yw;
46
+ return 130.0 * dot(m, g);
47
+ }
48
+
49
+ // Fractal Brownian Motion
50
+ float fbm(vec2 p, int octaves, float persistence, float lacunarity) {
51
+ float sum = 0.0;
52
+ float amp = 1.0;
53
+ float freq = 1.0;
54
+ float maxSum = 0.0;
55
+ for(int i = 0; i < 8; i++) {
56
+ if(i >= octaves) break;
57
+ sum += amp * snoise(p * freq);
58
+ maxSum += amp;
59
+ amp *= persistence;
60
+ freq *= lacunarity;
61
+ }
62
+ return sum / maxSum;
63
+ }
64
+
65
+ // Simplified fbm for performance
66
+ float fbm3(vec2 p) {
67
+ return fbm(p, 3, 0.5, 2.0);
68
+ }
69
+
70
+ float fbm5(vec2 p) {
71
+ return fbm(p, 5, 0.5, 2.0);
72
+ }
73
+
74
+ // Domain warping - creates organic, natural patterns (Inigo Quilez technique)
75
+ float domainWarp(vec2 p, float time) {
76
+ vec2 q = vec2(fbm3(p + vec2(0.0, 0.0) + time * 0.1),
77
+ fbm3(p + vec2(5.2, 1.3) + time * 0.15));
78
+ vec2 r = vec2(fbm3(p + 4.0 * q + vec2(1.7, 9.2) + time * 0.2),
79
+ fbm3(p + 4.0 * q + vec2(8.3, 2.8) + time * 0.1));
80
+ return fbm3(p + 4.0 * r);
81
+ }
82
+
83
+ // Get feathered mask with animated rolling edges (like Fog.frag)
84
+ float getVolumeMask(vec2 uv, vec2 texSize, float time, float edgeFreq, float edgeAmp, float edgeSpeed) {
85
+ // === ROUGHNESS: Add jagged edges via UV displacement ===
86
+ // Multiple noise frequencies for organic, random-looking edges
87
+ vec2 roughnessUV = uv * texSize * 0.008;
88
+
89
+ // Layer 1: Medium-scale jagged edges
90
+ float roughNoise1 = snoise(roughnessUV * 3.0 + time * 0.02);
91
+ float roughNoise2 = snoise(roughnessUV * 3.0 + vec2(50.0, 50.0) + time * 0.015);
92
+
93
+ // Layer 2: Fine detail roughness
94
+ float roughNoise3 = snoise(roughnessUV * 8.0 + time * 0.01);
95
+ float roughNoise4 = snoise(roughnessUV * 8.0 + vec2(100.0, 100.0) + time * 0.008);
96
+
97
+ // Layer 3: Large-scale variation
98
+ float roughNoise5 = snoise(roughnessUV * 1.0 + time * 0.005);
99
+
100
+ // Combine noise layers
101
+ vec2 roughnessOffset = vec2(
102
+ roughNoise1 * 0.5 + roughNoise3 * 0.3 + roughNoise5 * 0.2,
103
+ roughNoise2 * 0.5 + roughNoise4 * 0.3 + roughNoise5 * 0.2
104
+ );
105
+
106
+ // Scale offset by roughness - max displacement of ~3% of texture size
107
+ float roughnessStrength = uRoughness * 0.03;
108
+ vec2 displacedUV = uv + roughnessOffset * roughnessStrength;
109
+
110
+ // Remap softness to a much higher range
111
+ // 0 -> 0.8, 1 -> 2.0 (very strong effect across the board)
112
+ float softnessFactor = uSoftness * 1.2 + 0.8;
113
+
114
+ // Sample mask at multiple mipmap levels for soft edges
115
+ // Always sample at least 7 mip levels, up to 12 at max softness
116
+ float featheredMask = 0.0;
117
+ float totalWeight = 0.0;
118
+ int maxMip = int(7.0 + uSoftness * 5.0); // 7-12 mip levels
119
+
120
+ for(int i = 0; i <= 12; i++) {
121
+ if(i > maxMip) break;
122
+ // Higher softness = much more weight on higher mip levels
123
+ float weight = 1.0 + float(i) * softnessFactor;
124
+ // Use displaced UV for roughness effect
125
+ featheredMask += textureLod(uMaskTexture, displacedUV, float(i)).a * weight;
126
+ totalWeight += weight;
127
+ }
128
+ featheredMask /= totalWeight;
129
+ featheredMask = clamp(featheredMask, 0.0, 1.0);
130
+
131
+ // Edge width scales with softness - even wider range
132
+ float edgeWidth = 0.45 + uSoftness * 0.45; // 0.45-0.9 based on softness
133
+
134
+ // Create edge mask - only non-zero near the transition zone
135
+ float edgeMask = smoothstep(0.0, edgeWidth, featheredMask) *
136
+ (1.0 - smoothstep(1.0 - edgeWidth, 1.0, featheredMask));
137
+
138
+ // Add animated noise to the edges for rolling effect
139
+ // Scale noise UV based on softness (larger softness = larger scale noise)
140
+ float noiseScale = 0.005 + uSoftness * 0.008;
141
+ vec2 noiseUV = uv * texSize * noiseScale;
142
+ float edgeNoise = snoise(noiseUV * edgeFreq + time * edgeSpeed);
143
+
144
+ // Additional lower frequency noise for broader variance
145
+ float broadNoise = snoise(noiseUV * edgeFreq * 0.2 + time * edgeSpeed * 0.3);
146
+
147
+ // Blend noise at edges - combine frequencies for natural look
148
+ // More softness = more noise influence
149
+ float noiseInfluence = edgeAmp * (1.5 + uSoftness * 1.0);
150
+ float combinedNoise = edgeNoise * 0.5 + broadNoise * 0.5;
151
+ float finalMask = featheredMask + combinedNoise * noiseInfluence * edgeMask;
152
+
153
+ finalMask = clamp(finalMask, 0.0, 1.0);
154
+
155
+ // Outer falloff curve - softness controls how gradual the fade is
156
+ // Even at 0, we want a decent fade. At 1, very gradual fade.
157
+ float fadeStart = 0.2 - uSoftness * 0.18; // 0.2 to 0.02
158
+ float fadeEnd = 0.35 - uSoftness * 0.25; // 0.35 to 0.1
159
+
160
+ // Create smooth falloff - values below fadeEnd fade gradually to 0
161
+ float outerFalloff = smoothstep(0.0, fadeStart + 0.1, finalMask);
162
+ float innerRegion = smoothstep(fadeEnd, fadeEnd + 0.25, finalMask);
163
+
164
+ // Mix creates gradual fade at edges, solid in center
165
+ // Higher softness = more gradual outer fade (lower max for outerFalloff)
166
+ float outerMax = 0.65 - uSoftness * 0.35; // 0.65 to 0.3
167
+ return mix(outerFalloff * outerMax, 1.0, innerRegion);
168
+ }
169
+
170
+ // Calculate edge intensity for distortion amplification
171
+ // Returns higher values near the mask boundaries (both inside and outside)
172
+ float getEdgeDistortion(vec2 uv, vec2 texSize) {
173
+ // Sample at multiple mip levels to get soft edge detection
174
+ float mask0 = textureLod(uMaskTexture, uv, 0.0).a;
175
+ float mask2 = textureLod(uMaskTexture, uv, 2.0).a;
176
+ float mask4 = textureLod(uMaskTexture, uv, 4.0).a;
177
+ float mask6 = textureLod(uMaskTexture, uv, 6.0).a;
178
+
179
+ // Edge is where there's difference between sharp and blurred versions
180
+ float edge = abs(mask0 - mask4) + abs(mask2 - mask6) * 0.5;
181
+
182
+ // Also detect transition zones in the blurred mask
183
+ float transitionZone = smoothstep(0.1, 0.4, mask4) * (1.0 - smoothstep(0.6, 0.9, mask4));
184
+
185
+ // Combine edge detection methods
186
+ float edgeIntensity = edge * 2.0 + transitionZone;
187
+
188
+ // Scale by softness - more softness = wider edge distortion zone
189
+ edgeIntensity *= (1.0 + uSoftness * 1.5);
190
+
191
+ return clamp(edgeIntensity, 0.0, 1.0);
192
+ }
193
+
194
+ // Helper: get fire color from intensity
195
+ vec3 getFireColor(float t) {
196
+ vec3 black = vec3(0.1, 0.0, 0.0);
197
+ vec3 darkRed = vec3(0.5, 0.05, 0.0);
198
+ vec3 red = vec3(0.9, 0.15, 0.0);
199
+ vec3 orange = vec3(1.0, 0.4, 0.05);
200
+ vec3 yellow = vec3(1.1, 0.85, 0.2);
201
+ vec3 white = vec3(1.4, 1.2, 0.9);
202
+
203
+ if(t < 0.15) return mix(black, darkRed, t / 0.15);
204
+ if(t < 0.3) return mix(darkRed, red, (t - 0.15) / 0.15);
205
+ if(t < 0.5) return mix(red, orange, (t - 0.3) / 0.2);
206
+ if(t < 0.75) return mix(orange, yellow, (t - 0.5) / 0.25);
207
+ return mix(yellow, white, (t - 0.75) / 0.25);
208
+ }
209
+
210
+ // Fire effect - multi-layered flames (top-down view)
211
+ vec4 fireEffect(vec2 uv, vec2 texSize, float time) {
212
+ float mask = getVolumeMask(uv, texSize, time, 3.0, 0.25, 1.5);
213
+ if(mask < 0.001) return vec4(0.0);
214
+
215
+ float edgeDist = getEdgeDistortion(uv, texSize);
216
+ vec2 noiseUV = uv * texSize * 0.001;
217
+ float warpScale = 1.0 + edgeDist * 2.0 * uIntensity;
218
+
219
+ // === LAYER 1: Deep/base fire (large scale, slow) ===
220
+ float base1 = domainWarp(noiseUV * 0.8, time * 0.15);
221
+ float base2 = domainWarp(noiseUV * 1.0 + 50.0, time * 0.2 + 10.0);
222
+ float baseLayer = (base1 + base2) * 0.5;
223
+ baseLayer = baseLayer * 0.5 + 0.5;
224
+ float baseIntensity = baseLayer * mask * uIntensity * 0.7;
225
+
226
+ // === LAYER 2: Mid fire (medium scale, medium speed) ===
227
+ float mid1 = domainWarp(noiseUV * 1.5 + 100.0, time * 0.3) * warpScale;
228
+ float mid2 = domainWarp(noiseUV * 2.0 + 150.0, time * 0.35 + 5.0) * warpScale;
229
+ float midLayer = (mid1 + mid2) * 0.5;
230
+ midLayer = midLayer * 0.5 + 0.5;
231
+ // Offset the mid layer mask slightly for depth
232
+ float midMask = getVolumeMask(uv, texSize, time * 1.1, 2.5, 0.3, 1.2);
233
+ float midIntensity = midLayer * midMask * uIntensity * 0.85;
234
+
235
+ // === LAYER 3: Hot spots/flickers (small scale, fast) ===
236
+ float hot1 = domainWarp(noiseUV * 3.0 + 200.0, time * 0.5);
237
+ float hot2 = fbm3(noiseUV * 4.0 + time * 0.6 + 250.0);
238
+ float hotLayer = (hot1 + hot2) * 0.5;
239
+ hotLayer = hotLayer * 0.5 + 0.5;
240
+ hotLayer = pow(hotLayer, 0.7); // Boost hot spots
241
+ // Tighter mask for hot spots
242
+ float hotMask = getVolumeMask(uv, texSize, time * 1.3, 2.0, 0.2, 1.8);
243
+ float hotIntensity = hotLayer * hotMask * uIntensity;
244
+
245
+ // === LAYER 4: Edge turbulence ===
246
+ float edgeTurb = fbm5(noiseUV * 5.0 + time * 0.4) * edgeDist;
247
+
248
+ // Composite layers with screen-like blending
249
+ // Each layer contributes color independently
250
+ vec3 baseColor = getFireColor(baseIntensity) * baseIntensity;
251
+ vec3 midColor = getFireColor(midIntensity) * midIntensity;
252
+ vec3 hotColor = getFireColor(hotIntensity * 1.2) * hotIntensity; // Hotter = brighter colors
253
+
254
+ // Screen blend: result = 1 - (1-a)(1-b)(1-c)
255
+ vec3 color = baseColor + midColor + hotColor;
256
+ color += vec3(1.0, 0.5, 0.2) * edgeTurb * uBorder * uIntensity;
257
+
258
+ // Composite alpha from all layers
259
+ float alpha = max(max(baseIntensity, midIntensity), hotIntensity);
260
+ alpha = smoothstep(0.0, 0.2, alpha);
261
+ alpha *= mask * uOpacity;
262
+
263
+ return vec4(color, alpha);
264
+ }
265
+
266
+ // Space tear - dark void with stars (top-down view)
267
+ // Hash function for consistent random values per grid cell
268
+ float hash(vec2 p) {
269
+ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
270
+ }
271
+
272
+ // Hash that returns vec2
273
+ vec2 hash2(vec2 p) {
274
+ return vec2(
275
+ fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453),
276
+ fract(sin(dot(p, vec2(269.5, 183.3))) * 43758.5453)
277
+ );
278
+ }
279
+
280
+ // Generate a single star layer with grid-based placement
281
+ // Returns: x = star intensity, yzw = star color
282
+ vec4 starLayer(vec2 uv, float time, float gridSize, float speed, float brightness, float twinkleSpeed) {
283
+ // Grid cell
284
+ vec2 gridUV = uv * gridSize;
285
+ vec2 cellId = floor(gridUV);
286
+ vec2 cellUV = fract(gridUV);
287
+
288
+ float totalStar = 0.0;
289
+ vec3 totalColor = vec3(0.0);
290
+
291
+ // Check this cell and neighbors for stars
292
+ for(int x = -1; x <= 1; x++) {
293
+ for(int y = -1; y <= 1; y++) {
294
+ vec2 neighborId = cellId + vec2(float(x), float(y));
295
+
296
+ // Random position within cell
297
+ vec2 starPos = hash2(neighborId);
298
+
299
+ // Add slow drift movement
300
+ starPos += vec2(
301
+ sin(time * speed * 0.3 + hash(neighborId) * 6.28) * 0.1,
302
+ cos(time * speed * 0.2 + hash(neighborId + 100.0) * 6.28) * 0.1
303
+ );
304
+
305
+ // Distance from current pixel to star center
306
+ vec2 toStar = (cellUV - vec2(float(x), float(y))) - starPos;
307
+ float dist = length(toStar);
308
+
309
+ // Star visibility (only some cells have stars)
310
+ float hasStar = step(0.7, hash(neighborId + 50.0));
311
+
312
+ // Star size varies
313
+ float starSize = 0.02 + hash(neighborId + 80.0) * 0.03;
314
+
315
+ // Core star (sharp point)
316
+ float star = smoothstep(starSize, 0.0, dist) * hasStar;
317
+
318
+ // Soft glow around star
319
+ float glow = smoothstep(starSize * 4.0, 0.0, dist) * 0.3 * hasStar;
320
+
321
+ // Independent twinkle per star
322
+ float twinklePhase = hash(neighborId + 200.0) * 6.28;
323
+ float twinkleFreq = 0.5 + hash(neighborId + 300.0) * 1.5;
324
+ float twinkle = sin(time * twinkleSpeed * twinkleFreq + twinklePhase);
325
+ twinkle = twinkle * 0.4 + 0.6;
326
+
327
+ // Some stars blink more dramatically
328
+ float blinkStrength = hash(neighborId + 400.0);
329
+ if(blinkStrength > 0.8) {
330
+ float blink = sin(time * twinkleSpeed * 0.3 + hash(neighborId + 500.0) * 6.28);
331
+ twinkle *= smoothstep(-0.8, 0.2, blink);
332
+ }
333
+
334
+ // Star color based on temperature
335
+ float temp = hash(neighborId + 600.0);
336
+ vec3 starColor;
337
+ if(temp < 0.3) {
338
+ starColor = vec3(0.7, 0.85, 1.0); // Blue-white hot
339
+ } else if(temp < 0.7) {
340
+ starColor = vec3(1.0, 1.0, 1.0); // Pure white
341
+ } else if(temp < 0.9) {
342
+ starColor = vec3(1.0, 0.95, 0.8); // Yellow-white
343
+ } else {
344
+ starColor = vec3(1.0, 0.7, 0.5); // Orange-red cool
345
+ }
346
+
347
+ float finalStar = (star + glow) * twinkle * brightness;
348
+ totalStar += finalStar;
349
+ totalColor += starColor * finalStar;
350
+ }
351
+ }
352
+
353
+ if(totalStar > 0.001) {
354
+ totalColor /= totalStar;
355
+ }
356
+
357
+ return vec4(totalStar, totalColor);
358
+ }
359
+
360
+ vec4 spaceTearEffect(vec2 uv, vec2 texSize, float time) {
361
+ float mask = getVolumeMask(uv, texSize, time, 1.5, 0.2, 0.5);
362
+ if(mask < 0.001) return vec4(0.0);
363
+
364
+ float edgeDist = getEdgeDistortion(uv, texSize);
365
+ vec2 baseUV = uv * texSize * 0.001;
366
+
367
+ // === BASE: Deep black void with subtle variation ===
368
+ float voidNoise = fbm3(baseUV * 0.5 + time * 0.02);
369
+ vec3 voidColor = vec3(0.01, 0.01, 0.02) + vec3(0.01, 0.005, 0.02) * voidNoise;
370
+
371
+ // === STAR LAYERS with parallax movement ===
372
+ vec3 starColor = vec3(0.0);
373
+
374
+ // Layer 1: Distant tiny stars (many, dim, slow)
375
+ vec2 layer1UV = baseUV + vec2(time * 0.002, time * 0.001);
376
+ vec4 stars1 = starLayer(layer1UV, time, 25.0, 0.3, 0.5, 1.5);
377
+ starColor += stars1.yzw * stars1.x;
378
+
379
+ // Layer 2: Medium distance stars
380
+ vec2 layer2UV = baseUV + vec2(time * 0.004, -time * 0.002);
381
+ vec4 stars2 = starLayer(layer2UV, time, 15.0, 0.5, 0.8, 1.2);
382
+ starColor += stars2.yzw * stars2.x;
383
+
384
+ // Layer 3: Closer bright stars (fewer, brighter, faster drift)
385
+ vec2 layer3UV = baseUV + vec2(-time * 0.006, time * 0.003);
386
+ vec4 stars3 = starLayer(layer3UV, time, 8.0, 0.8, 1.2, 0.8);
387
+ starColor += stars3.yzw * stars3.x;
388
+
389
+ // Layer 4: Rare very bright stars with strong glow
390
+ vec2 layer4UV = baseUV + vec2(time * 0.003, time * 0.005);
391
+ vec4 stars4 = starLayer(layer4UV, time, 4.0, 0.4, 1.8, 0.5);
392
+ starColor += stars4.yzw * stars4.x;
393
+
394
+ // === NEBULA WISPS at edges ===
395
+ float nebula = domainWarp(baseUV * 1.5 + 300.0, time * 0.05);
396
+ nebula = nebula * 0.5 + 0.5;
397
+ nebula *= edgeDist * uBorder;
398
+ vec3 nebulaColor = vec3(0.12, 0.04, 0.2) * nebula;
399
+
400
+ // Occasional colored nebula patches
401
+ float nebulaHue = snoise(baseUV * 2.0 + 500.0);
402
+ if(nebulaHue > 0.3) {
403
+ nebulaColor = mix(nebulaColor, vec3(0.05, 0.1, 0.2), (nebulaHue - 0.3) * nebula);
404
+ }
405
+
406
+ // === COMBINE ===
407
+ vec3 color = voidColor + starColor * uIntensity + nebulaColor * uIntensity;
408
+
409
+ // Edge glow - subtle purple rim
410
+ color += vec3(0.08, 0.02, 0.12) * edgeDist * uBorder * uIntensity;
411
+
412
+ float alpha = mask * uOpacity;
413
+
414
+ return vec4(color, alpha);
415
+ }
416
+
417
+ // Water effect - proper ripples using sum of sine waves with lighting
418
+ vec4 waterEffect(vec2 uv, vec2 texSize, float time) {
419
+ float mask = getVolumeMask(uv, texSize, time, 2.0, 0.1, 1.0);
420
+ if(mask < 0.001) return vec4(0.0);
421
+
422
+ float edgeDist = getEdgeDistortion(uv, texSize);
423
+ vec2 basePos = uv * texSize * 0.004; // Higher scale = smaller waves (viewing from ~100ft)
424
+
425
+ // === DOMAIN WARPING - bend waves organically to break up repetition ===
426
+ float warpStrength = 0.15;
427
+ vec2 warp = vec2(
428
+ snoise(basePos * 0.3 + time * 0.05),
429
+ snoise(basePos * 0.3 + vec2(50.0, 50.0) + time * 0.04)
430
+ );
431
+ vec2 pos = basePos + warp * warpStrength;
432
+
433
+ // === SUM OF DIRECTIONAL SINE WAVES ===
434
+ // Each wave has: direction, frequency, amplitude, speed
435
+ // Directions slowly rotate over time for organic movement
436
+ float height = 0.0;
437
+ vec2 gradient = vec2(0.0); // For normal calculation
438
+
439
+ // Local noise for phase variation - breaks up sync
440
+ float phaseNoise1 = snoise(basePos * 0.5) * 2.0;
441
+ float phaseNoise2 = snoise(basePos * 0.7 + 100.0) * 2.0;
442
+
443
+ // Wave 1: Primary wave - direction slowly rotates
444
+ float angle1 = time * 0.02;
445
+ vec2 dir1 = normalize(vec2(cos(angle1) + 0.3, sin(angle1) * 0.5 + 0.7));
446
+ float freq1 = 25.0;
447
+ float amp1 = 0.25 * (0.8 + snoise(basePos * 0.2) * 0.2); // Vary amplitude locally
448
+ float phase1 = dot(dir1, pos) * freq1 - time * 2.0 + phaseNoise1;
449
+ height += amp1 * sin(phase1);
450
+ gradient += dir1 * amp1 * freq1 * cos(phase1);
451
+
452
+ // Wave 2: Cross wave - counter-rotating
453
+ float angle2 = -time * 0.015 + 1.5;
454
+ vec2 dir2 = normalize(vec2(cos(angle2) - 0.5, sin(angle2) + 0.3));
455
+ float freq2 = 35.0;
456
+ float amp2 = 0.18 * (0.85 + snoise(basePos * 0.25 + 30.0) * 0.15);
457
+ float phase2 = dot(dir2, pos) * freq2 - time * 2.5 + phaseNoise2;
458
+ height += amp2 * sin(phase2);
459
+ gradient += dir2 * amp2 * freq2 * cos(phase2);
460
+
461
+ // Wave 3: Diagonal - slight wobble
462
+ float angle3 = sin(time * 0.03) * 0.2 + 2.35;
463
+ vec2 dir3 = normalize(vec2(cos(angle3), sin(angle3)));
464
+ float freq3 = 20.0;
465
+ float amp3 = 0.2;
466
+ float phase3 = dot(dir3, pos) * freq3 - time * 1.5 + phaseNoise1 * 0.5;
467
+ height += amp3 * sin(phase3);
468
+ gradient += dir3 * amp3 * freq3 * cos(phase3);
469
+
470
+ // Wave 4: Fine detail
471
+ float angle4 = time * 0.025 + 4.0;
472
+ vec2 dir4 = normalize(vec2(cos(angle4) - 0.3, sin(angle4) - 0.9));
473
+ float freq4 = 50.0;
474
+ float amp4 = 0.1;
475
+ float phase4 = dot(dir4, pos) * freq4 - time * 3.0 + phaseNoise2 * 0.7;
476
+ height += amp4 * sin(phase4);
477
+ gradient += dir4 * amp4 * freq4 * cos(phase4);
478
+
479
+ // Wave 5: Extra fine detail - faster rotation
480
+ float angle5 = -time * 0.03 + 1.0;
481
+ vec2 dir5 = normalize(vec2(cos(angle5) + 0.9, sin(angle5) + 0.5));
482
+ float freq5 = 65.0;
483
+ float amp5 = 0.08;
484
+ float phase5 = dot(dir5, pos) * freq5 - time * 3.5;
485
+ height += amp5 * sin(phase5);
486
+ gradient += dir5 * amp5 * freq5 * cos(phase5);
487
+
488
+ // === CONCENTRIC RIPPLES - centers drift slowly ===
489
+ for(int i = 0; i < 8; i++) {
490
+ // Ripple centers drift over time
491
+ float drift = time * 0.02;
492
+ vec2 center = vec2(
493
+ fract(sin(float(i) * 127.1) * 43758.5453 + drift * 0.1) * 0.8 + 0.1,
494
+ fract(sin(float(i) * 269.5) * 43758.5453 + drift * 0.07) * 0.8 + 0.1
495
+ );
496
+ // Add noise-based wobble to centers
497
+ center += vec2(
498
+ snoise(vec2(float(i) * 3.0, time * 0.1)) * 0.05,
499
+ snoise(vec2(time * 0.1, float(i) * 5.0)) * 0.05
500
+ );
501
+
502
+ vec2 toCenter = pos - center * texSize.x * 0.004;
503
+ float dist = length(toCenter);
504
+ float rippleFreq = 40.0 + float(i) * 5.0;
505
+ float rippleAmp = 0.12 / (1.0 + dist * 3.0);
506
+ // Vary ripple speed slightly per source
507
+ float rippleSpeed = 4.0 + snoise(vec2(float(i) * 10.0, 0.0)) * 0.5;
508
+ float ripplePhase = dist * rippleFreq - time * rippleSpeed - float(i) * 1.0;
509
+
510
+ height += rippleAmp * sin(ripplePhase);
511
+ if(dist > 0.001) {
512
+ vec2 rippleDir = toCenter / dist;
513
+ gradient += rippleDir * rippleAmp * rippleFreq * cos(ripplePhase);
514
+ }
515
+ }
516
+
517
+ // === CALCULATE NORMAL FROM GRADIENT ===
518
+ vec3 normal = normalize(vec3(-gradient.x, -gradient.y, 1.0));
519
+
520
+ // === LIGHTING ===
521
+ vec3 lightDir = normalize(vec3(0.3, 0.3, 1.0)); // Light from above-front
522
+ float diffuse = max(dot(normal, lightDir), 0.0);
523
+
524
+ // Specular highlight (Blinn-Phong) - high exponent for small sharp highlights
525
+ vec3 viewDir = vec3(0.0, 0.0, 1.0); // Top-down view
526
+ vec3 halfDir = normalize(lightDir + viewDir);
527
+ float specular = pow(max(dot(normal, halfDir), 0.0), 128.0);
528
+
529
+ // === DEPTH/SHALLOWNESS - border controls how much shallow water ===
530
+ // Sample mask at multiple mipmap levels to find distance from edge
531
+ float maskHigh = textureLod(uMaskTexture, uv, 0.0).a;
532
+ float maskMid = textureLod(uMaskTexture, uv, 3.0).a;
533
+ float maskLow = textureLod(uMaskTexture, uv, 5.0).a;
534
+ float maskVeryLow = textureLod(uMaskTexture, uv, 7.0).a;
535
+
536
+ // Edge distance: higher at edges, lower in center
537
+ float edgeProximity = 1.0 - min(maskHigh, min(maskMid, maskLow));
538
+
539
+ // Border controls how far shallow water extends into the center
540
+ // At border=0, only very edge is shallow. At border=1, most of water is shallow
541
+ float shallowSpread = uBorder * 0.8 + 0.1; // 0.1 to 0.9
542
+ float shallowZone = smoothstep(0.0, shallowSpread, edgeProximity);
543
+
544
+ // Also use blurred mask difference for smoother depth transition
545
+ float depthGradient = abs(maskHigh - maskVeryLow);
546
+ shallowZone = max(shallowZone, depthGradient * uBorder * 2.0);
547
+ shallowZone = clamp(shallowZone, 0.0, 1.0);
548
+
549
+ // Add some noise to break up the depth bands
550
+ float depthNoise = snoise(pos * 2.0 + time * 0.05) * 0.15;
551
+ shallowZone = clamp(shallowZone + depthNoise * uBorder, 0.0, 1.0);
552
+
553
+ // === COLORS ===
554
+ vec3 deepColor = vec3(0.05, 0.15, 0.35); // Dark blue for deep water
555
+ vec3 midColor = vec3(0.1, 0.3, 0.5); // Medium blue
556
+ vec3 shallowColor = vec3(0.2, 0.5, 0.6); // Lighter cyan for shallow
557
+ vec3 veryShallowColor = vec3(0.35, 0.6, 0.55); // Turquoise/teal for very shallow
558
+ vec3 beachColor = vec3(0.55, 0.65, 0.5); // Sandy greenish for beach edge
559
+ vec3 highlightColor = vec3(0.9, 0.95, 1.0); // Slight blue tint to highlights
560
+
561
+ // Height affects color (deeper in troughs)
562
+ float heightNorm = height * 0.5 + 0.5;
563
+
564
+ // Multi-step depth gradient based on shallow zone
565
+ vec3 baseWaterColor;
566
+ if(shallowZone < 0.25) {
567
+ // Deep water
568
+ baseWaterColor = mix(deepColor, midColor, shallowZone * 4.0);
569
+ } else if(shallowZone < 0.5) {
570
+ // Mid depth
571
+ baseWaterColor = mix(midColor, shallowColor, (shallowZone - 0.25) * 4.0);
572
+ } else if(shallowZone < 0.75) {
573
+ // Shallow
574
+ baseWaterColor = mix(shallowColor, veryShallowColor, (shallowZone - 0.5) * 4.0);
575
+ } else {
576
+ // Very shallow / beach
577
+ baseWaterColor = mix(veryShallowColor, beachColor, (shallowZone - 0.75) * 4.0);
578
+ }
579
+
580
+ // Wave height creates visible color variation - more visible in shallow water
581
+ float heightInfluence = 0.3 + shallowZone * 0.3; // Shallow water shows more height variation
582
+ vec3 waterColor = mix(baseWaterColor * 0.85, baseWaterColor * 1.2, heightNorm * heightInfluence + diffuse * 0.3);
583
+
584
+ // Add specular highlights - stronger in shallow water where you can see the surface better
585
+ float specularStrength = 1.0 + shallowZone * 0.5;
586
+ waterColor += highlightColor * specular * specularStrength * uIntensity;
587
+
588
+ // Subtle foam on wave peaks in very shallow areas only
589
+ float foam = smoothstep(0.6, 0.9, heightNorm) * smoothstep(0.5, 0.8, shallowZone) * 0.3;
590
+ foam += edgeDist * smoothstep(0.7, 1.0, shallowZone) * 0.2; // Foam right at the edge
591
+ float foamNoise = snoise(pos * 8.0 + time * 0.5) * 0.5 + 0.5;
592
+ foam *= foamNoise;
593
+ waterColor += vec3(0.9, 0.85, 0.75) * foam;
594
+
595
+ // Final color with intensity
596
+ vec3 finalColor = waterColor * uIntensity;
597
+
598
+ float alpha = mask * uOpacity;
599
+
600
+ return vec4(finalColor, alpha);
601
+ }
602
+
603
+ // Helper: get magic color from intensity (uses base color)
604
+ vec3 getMagicColor(float t) {
605
+ vec3 darkColor = uBaseColor * 0.2;
606
+ vec3 midColor = uBaseColor * 0.6;
607
+ vec3 brightColor = uBaseColor * 1.0;
608
+ vec3 glow = uBaseColor * 1.4 + vec3(0.2);
609
+ vec3 white = vec3(1.3, 1.3, 1.4);
610
+
611
+ if(t < 0.2) return mix(darkColor, midColor, t * 5.0);
612
+ if(t < 0.45) return mix(midColor, brightColor, (t - 0.2) * 4.0);
613
+ if(t < 0.7) return mix(brightColor, glow, (t - 0.45) * 4.0);
614
+ return mix(glow, white, (t - 0.7) / 0.3);
615
+ }
616
+
617
+ // Magic effect - multi-layered arcane energy (top-down view)
618
+ vec4 magicEffect(vec2 uv, vec2 texSize, float time) {
619
+ float mask = getVolumeMask(uv, texSize, time, 2.5, 0.2, 1.2);
620
+ if(mask < 0.001) return vec4(0.0);
621
+
622
+ float edgeDist = getEdgeDistortion(uv, texSize);
623
+ vec2 noiseUV = uv * texSize * 0.001;
624
+ float warpScale = 1.0 + edgeDist * 2.2 * uIntensity;
625
+
626
+ // Soft pulsating - affects all layers
627
+ float pulseNoise = fbm3(noiseUV * 0.2 + time * 0.25);
628
+ float pulse = pulseNoise * 0.12 + 0.88;
629
+
630
+ // === LAYER 1: Deep arcane base (large scale, slow) ===
631
+ float deep1 = domainWarp(noiseUV * 0.7, time * 0.12);
632
+ float deep2 = domainWarp(noiseUV * 0.9 + 25.0, time * 0.15 + 7.0);
633
+ float deepLayer = (deep1 + deep2) * 0.5;
634
+ deepLayer = deepLayer * 0.5 + 0.5;
635
+ float deepIntensity = deepLayer * mask * uIntensity * pulse * 0.6;
636
+
637
+ // === LAYER 2: Mid energy swirls (medium scale) ===
638
+ float mid1 = domainWarp(noiseUV * 1.4 + 50.0, time * 0.25) * warpScale;
639
+ float mid2 = domainWarp(noiseUV * 1.8 + 75.0, time * 0.3 + 4.0) * warpScale;
640
+ float midLayer = (mid1 + mid2) * 0.5;
641
+ midLayer = midLayer * 0.5 + 0.5;
642
+ float midMask = getVolumeMask(uv, texSize, time * 1.1, 2.2, 0.25, 1.0);
643
+ float midIntensity = midLayer * midMask * uIntensity * pulse * 0.8;
644
+
645
+ // === LAYER 3: Surface energy crackle (small scale, faster) ===
646
+ float surf1 = domainWarp(noiseUV * 2.5 + 100.0, time * 0.4);
647
+ float surf2 = fbm3(noiseUV * 3.0 + time * 0.35 + 130.0);
648
+ float surfLayer = (surf1 + surf2) * 0.5;
649
+ surfLayer = surfLayer * 0.5 + 0.5;
650
+ surfLayer = pow(surfLayer, 0.85); // Slight boost
651
+ float surfMask = getVolumeMask(uv, texSize, time * 1.25, 2.0, 0.2, 1.4);
652
+ float surfIntensity = surfLayer * surfMask * uIntensity * pulse;
653
+
654
+ // === LAYER 4: Edge turbulence and sparkles ===
655
+ float edgeTurb = fbm5(noiseUV * 4.5 + time * 0.5) * edgeDist;
656
+
657
+ // Soft sparkles - concentrated at edges
658
+ float sparkleBase = fbm5(noiseUV * 5.0 + time * 0.18);
659
+ float sparkle = smoothstep(0.45, 0.75, sparkleBase) * smoothstep(1.0, 0.65, sparkleBase);
660
+ sparkle *= (1.0 + edgeDist * 0.8);
661
+
662
+ // Composite layers with colors
663
+ vec3 deepColor = getMagicColor(deepIntensity * 0.9) * deepIntensity;
664
+ vec3 midColor = getMagicColor(midIntensity) * midIntensity;
665
+ vec3 surfColor = getMagicColor(surfIntensity * 1.2) * surfIntensity;
666
+
667
+ vec3 color = deepColor + midColor + surfColor;
668
+
669
+ // Add edge turbulence glow - controlled by border
670
+ color += uBaseColor * edgeTurb * uBorder * uIntensity;
671
+
672
+ // Soft white sparkles
673
+ color += vec3(1.2, 1.2, 1.3) * sparkle * 0.35 * uIntensity;
674
+
675
+ // Enhanced edge glow - controlled by border
676
+ float edgeGlow = edgeDist * uBorder * uIntensity;
677
+ color += uBaseColor * edgeGlow;
678
+
679
+ // Composite alpha from all layers
680
+ float alpha = max(max(deepIntensity, midIntensity), surfIntensity);
681
+ alpha = smoothstep(0.0, 0.12, alpha);
682
+ alpha *= mask * uOpacity;
683
+
684
+ return vec4(color, alpha);
685
+ }
686
+
687
+ // Grease effect - murky brown liquid with depth (same technique as water)
688
+ vec4 greaseEffect(vec2 uv, vec2 texSize, float time) {
689
+ float mask = getVolumeMask(uv, texSize, time, 2.0, 0.1, 0.8);
690
+ if(mask < 0.001) return vec4(0.0);
691
+
692
+ float edgeDist = getEdgeDistortion(uv, texSize);
693
+ vec2 basePos = uv * texSize * 0.004;
694
+
695
+ // === DOMAIN WARPING - slower, more viscous movement ===
696
+ float warpStrength = 0.12;
697
+ vec2 warp = vec2(
698
+ snoise(basePos * 0.25 + time * 0.03),
699
+ snoise(basePos * 0.25 + vec2(50.0, 50.0) + time * 0.025)
700
+ );
701
+ vec2 pos = basePos + warp * warpStrength;
702
+
703
+ // === SLOWER SINE WAVES for viscous liquid ===
704
+ float height = 0.0;
705
+ vec2 gradient = vec2(0.0);
706
+
707
+ float phaseNoise1 = snoise(basePos * 0.5) * 2.0;
708
+ float phaseNoise2 = snoise(basePos * 0.7 + 100.0) * 2.0;
709
+
710
+ // Wave 1: Primary - slower
711
+ float angle1 = time * 0.015;
712
+ vec2 dir1 = normalize(vec2(cos(angle1) + 0.3, sin(angle1) * 0.5 + 0.7));
713
+ float freq1 = 20.0;
714
+ float amp1 = 0.2 * (0.8 + snoise(basePos * 0.2) * 0.2);
715
+ float phase1 = dot(dir1, pos) * freq1 - time * 1.2 + phaseNoise1;
716
+ height += amp1 * sin(phase1);
717
+ gradient += dir1 * amp1 * freq1 * cos(phase1);
718
+
719
+ // Wave 2: Cross wave
720
+ float angle2 = -time * 0.01 + 1.5;
721
+ vec2 dir2 = normalize(vec2(cos(angle2) - 0.5, sin(angle2) + 0.3));
722
+ float freq2 = 28.0;
723
+ float amp2 = 0.15 * (0.85 + snoise(basePos * 0.25 + 30.0) * 0.15);
724
+ float phase2 = dot(dir2, pos) * freq2 - time * 1.5 + phaseNoise2;
725
+ height += amp2 * sin(phase2);
726
+ gradient += dir2 * amp2 * freq2 * cos(phase2);
727
+
728
+ // Wave 3: Diagonal
729
+ float angle3 = sin(time * 0.02) * 0.2 + 2.35;
730
+ vec2 dir3 = normalize(vec2(cos(angle3), sin(angle3)));
731
+ float freq3 = 15.0;
732
+ float amp3 = 0.18;
733
+ float phase3 = dot(dir3, pos) * freq3 - time * 1.0 + phaseNoise1 * 0.5;
734
+ height += amp3 * sin(phase3);
735
+ gradient += dir3 * amp3 * freq3 * cos(phase3);
736
+
737
+ // Wave 4: Fine detail (less prominent for viscous look)
738
+ float angle4 = time * 0.018 + 4.0;
739
+ vec2 dir4 = normalize(vec2(cos(angle4) - 0.3, sin(angle4) - 0.9));
740
+ float freq4 = 40.0;
741
+ float amp4 = 0.08;
742
+ float phase4 = dot(dir4, pos) * freq4 - time * 2.0 + phaseNoise2 * 0.7;
743
+ height += amp4 * sin(phase4);
744
+ gradient += dir4 * amp4 * freq4 * cos(phase4);
745
+
746
+ // === CALCULATE NORMAL FROM GRADIENT ===
747
+ vec3 normal = normalize(vec3(-gradient.x, -gradient.y, 1.0));
748
+
749
+ // === LIGHTING ===
750
+ vec3 lightDir = normalize(vec3(0.3, 0.3, 1.0));
751
+ float diffuse = max(dot(normal, lightDir), 0.0);
752
+
753
+ vec3 viewDir = vec3(0.0, 0.0, 1.0);
754
+ vec3 halfDir = normalize(lightDir + viewDir);
755
+ float specular = pow(max(dot(normal, halfDir), 0.0), 64.0); // Lower exponent for broader highlights
756
+
757
+ // === DEPTH/THICKNESS - border controls how dark the edges are ===
758
+ float maskHigh = textureLod(uMaskTexture, uv, 0.0).a;
759
+ float maskMid = textureLod(uMaskTexture, uv, 3.0).a;
760
+ float maskLow = textureLod(uMaskTexture, uv, 5.0).a;
761
+ float maskVeryLow = textureLod(uMaskTexture, uv, 7.0).a;
762
+
763
+ // How far from center (1 = edge, 0 = center)
764
+ float edgeProximity = 1.0 - min(maskHigh, min(maskMid, maskLow));
765
+
766
+ // Border controls how much darkness spreads from edges
767
+ float darkSpread = uBorder * 0.8 + 0.1;
768
+ float darkZone = smoothstep(0.0, darkSpread, edgeProximity);
769
+
770
+ float depthGradient = abs(maskHigh - maskVeryLow);
771
+ darkZone = max(darkZone, depthGradient * uBorder * 2.0);
772
+ darkZone = clamp(darkZone, 0.0, 1.0);
773
+
774
+ float depthNoise = snoise(pos * 2.0 + time * 0.03) * 0.15;
775
+ darkZone = clamp(darkZone + depthNoise * uBorder, 0.0, 1.0);
776
+
777
+ // === BROWN COLORS - inverted: edges get darker with border (softened) ===
778
+ vec3 centerColor = vec3(0.4, 0.3, 0.18); // Lighter brown in center
779
+ vec3 midColor = vec3(0.32, 0.24, 0.14); // Medium brown
780
+ vec3 darkColor = vec3(0.24, 0.18, 0.1); // Dark brown
781
+ vec3 veryDarkColor = vec3(0.16, 0.12, 0.06); // Very dark brown
782
+ vec3 edgeColor = vec3(0.1, 0.07, 0.03); // Dark but not black at edges
783
+ vec3 highlightColor = vec3(0.8, 0.7, 0.5); // Warm highlights
784
+
785
+ float heightNorm = height * 0.5 + 0.5;
786
+
787
+ // Multi-step depth gradient - darker towards edges
788
+ vec3 baseGreaseColor;
789
+ if(darkZone < 0.25) {
790
+ baseGreaseColor = mix(centerColor, midColor, darkZone * 4.0);
791
+ } else if(darkZone < 0.5) {
792
+ baseGreaseColor = mix(midColor, darkColor, (darkZone - 0.25) * 4.0);
793
+ } else if(darkZone < 0.75) {
794
+ baseGreaseColor = mix(darkColor, veryDarkColor, (darkZone - 0.5) * 4.0);
795
+ } else {
796
+ baseGreaseColor = mix(veryDarkColor, edgeColor, (darkZone - 0.75) * 4.0);
797
+ }
798
+
799
+ float heightInfluence = 0.25 + (1.0 - darkZone) * 0.25; // More height variation in lighter areas
800
+ vec3 greaseColor = mix(baseGreaseColor * 0.85, baseGreaseColor * 1.15, heightNorm * heightInfluence + diffuse * 0.25);
801
+
802
+ // Specular highlights - oily sheen, stronger in lighter center areas
803
+ float specularStrength = 0.8 + (1.0 - darkZone) * 0.4;
804
+ greaseColor += highlightColor * specular * specularStrength * uIntensity * 0.6;
805
+
806
+ vec3 finalColor = greaseColor * uIntensity;
807
+
808
+ float alpha = mask * uOpacity;
809
+
810
+ return vec4(finalColor, alpha);
811
+ }
812
+
813
+ // Ice effect - frosted glass look that lets map show through
814
+ vec4 iceEffect(vec2 uv, vec2 texSize, float time) {
815
+ float mask = getVolumeMask(uv, texSize, time, 1.2, 0.05, 0.3);
816
+ if(mask < 0.001) return vec4(0.0);
817
+
818
+ float edgeDist = getEdgeDistortion(uv, texSize);
819
+ vec2 basePos = uv * texSize * 0.002;
820
+
821
+ // === FROST PATTERN - subtle, organic ice crystals with cold air movement ===
822
+ // Drifting direction for cold air effect
823
+ vec2 drift = vec2(time * 0.15, time * 0.08);
824
+
825
+ // Multiple layers of noise at different scales for natural frost look
826
+ float frost1 = fbm3(basePos * 4.0 + drift * 0.5) * 0.5 + 0.5;
827
+ float frost2 = fbm3(basePos * 8.0 + vec2(50.0, 50.0) + drift * 0.7) * 0.5 + 0.5;
828
+ float frost3 = snoise(basePos * 16.0 + drift) * 0.5 + 0.5;
829
+
830
+ // Combine for layered frost texture
831
+ float frostPattern = frost1 * 0.5 + frost2 * 0.3 + frost3 * 0.2;
832
+
833
+ // Cold air wisps - flowing patterns
834
+ float wisp1 = snoise(basePos * 3.0 + vec2(time * 0.2, time * 0.1)) * 0.5 + 0.5;
835
+ float wisp2 = snoise(basePos * 5.0 + vec2(-time * 0.15, time * 0.12) + 80.0) * 0.5 + 0.5;
836
+ float coldAir = wisp1 * 0.6 + wisp2 * 0.4;
837
+ coldAir = smoothstep(0.3, 0.7, coldAir); // Soften the wisps
838
+
839
+ // === GEOMETRIC CRACKS - voronoi cell edges for angular look ===
840
+ float crackScale = 20.0;
841
+ vec2 crackPos = basePos * crackScale;
842
+
843
+ // Voronoi for geometric angular cracks
844
+ vec2 cellId = floor(crackPos);
845
+ vec2 cellUV = fract(crackPos);
846
+
847
+ float minDist = 1.0;
848
+ float secondDist = 1.0;
849
+
850
+ // Find two closest cell centers
851
+ for(int x = -1; x <= 1; x++) {
852
+ for(int y = -1; y <= 1; y++) {
853
+ vec2 neighbor = vec2(float(x), float(y));
854
+ // Jittered cell centers for irregular but angular pattern
855
+ vec2 point = neighbor + hash2(cellId + neighbor) * 0.8 + 0.1;
856
+ float dist = length(cellUV - point);
857
+
858
+ if(dist < minDist) {
859
+ secondDist = minDist;
860
+ minDist = dist;
861
+ } else if(dist < secondDist) {
862
+ secondDist = dist;
863
+ }
864
+ }
865
+ }
866
+
867
+ // Crack lines are at cell boundaries (where distances are similar)
868
+ float cellEdgeDist = secondDist - minDist;
869
+ float cracks = 1.0 - smoothstep(0.0, 0.08, cellEdgeDist);
870
+
871
+ // Second layer at different scale for more detail
872
+ vec2 crackPos2 = basePos * crackScale * 2.5;
873
+ vec2 cellId2 = floor(crackPos2);
874
+ vec2 cellUV2 = fract(crackPos2);
875
+
876
+ float minDist2 = 1.0;
877
+ float secondDist2 = 1.0;
878
+
879
+ for(int x = -1; x <= 1; x++) {
880
+ for(int y = -1; y <= 1; y++) {
881
+ vec2 neighbor = vec2(float(x), float(y));
882
+ vec2 point = neighbor + hash2(cellId2 + neighbor + 200.0) * 0.7 + 0.15;
883
+ float dist = length(cellUV2 - point);
884
+
885
+ if(dist < minDist2) {
886
+ secondDist2 = minDist2;
887
+ minDist2 = dist;
888
+ } else if(dist < secondDist2) {
889
+ secondDist2 = dist;
890
+ }
891
+ }
892
+ }
893
+
894
+ float cellEdgeDist2 = secondDist2 - minDist2;
895
+ float cracks2 = 1.0 - smoothstep(0.0, 0.06, cellEdgeDist2);
896
+
897
+ // Combine both crack layers
898
+ cracks = max(cracks, cracks2 * 0.7);
899
+
900
+ // Always visible, roughness makes them stronger
901
+ cracks = cracks * (0.5 + uRoughness * 0.5);
902
+
903
+ // === DEPTH - border controls how much shallow/white ice at edges ===
904
+ float maskHigh = textureLod(uMaskTexture, uv, 0.0).a;
905
+ float maskMid = textureLod(uMaskTexture, uv, 3.0).a;
906
+ float maskLow = textureLod(uMaskTexture, uv, 5.0).a;
907
+ float maskVeryLow = textureLod(uMaskTexture, uv, 7.0).a;
908
+
909
+ // Edge proximity: higher at edges, lower in center
910
+ float edgeProximity = 1.0 - min(maskHigh, min(maskMid, maskLow));
911
+
912
+ // Border controls how far the light/white ice extends into center
913
+ float shallowSpread = uBorder * 0.8 + 0.1;
914
+ float shallowZone = smoothstep(0.0, shallowSpread, edgeProximity);
915
+
916
+ // Also use blurred mask difference for smoother depth transition
917
+ float depthGradient = abs(maskHigh - maskVeryLow);
918
+ shallowZone = max(shallowZone, depthGradient * uBorder * 2.0);
919
+ shallowZone = clamp(shallowZone, 0.0, 1.0);
920
+
921
+ // Add noise to break up depth bands
922
+ float depthNoise = snoise(basePos * 2.0 + drift * 0.3) * 0.1;
923
+ shallowZone = clamp(shallowZone + depthNoise * uBorder, 0.0, 1.0);
924
+
925
+ // === SUBTLE SHIMMER - slow sparkle ===
926
+ float shimmer = snoise(basePos * 6.0 + drift * 1.2) * 0.5 + 0.5;
927
+ shimmer = pow(shimmer, 3.0); // Make sparkles more sparse
928
+
929
+ // === COLORS - subtle blue center, white edges ===
930
+ vec3 deepBlue = vec3(0.45, 0.6, 0.8); // Lighter blue for center
931
+ vec3 midBlue = vec3(0.55, 0.7, 0.85); // Medium blue
932
+ vec3 lightBlue = vec3(0.7, 0.82, 0.92); // Light blue
933
+ vec3 paleBlue = vec3(0.82, 0.9, 0.97); // Pale blue
934
+ vec3 frostWhite = vec3(0.94, 0.97, 1.0); // White frost at edges
935
+ vec3 crackColor = vec3(0.08, 0.15, 0.3); // Darker crack lines for contrast
936
+ vec3 coldAirColor = vec3(0.85, 0.92, 1.0); // Pale blue for cold air
937
+
938
+ // Multi-step depth gradient - darker in center, lighter at edges
939
+ vec3 iceColor;
940
+ if(shallowZone < 0.25) {
941
+ iceColor = mix(deepBlue, midBlue, shallowZone * 4.0);
942
+ } else if(shallowZone < 0.5) {
943
+ iceColor = mix(midBlue, lightBlue, (shallowZone - 0.25) * 4.0);
944
+ } else if(shallowZone < 0.75) {
945
+ iceColor = mix(lightBlue, paleBlue, (shallowZone - 0.5) * 4.0);
946
+ } else {
947
+ iceColor = mix(paleBlue, frostWhite, (shallowZone - 0.75) * 4.0);
948
+ }
949
+
950
+ // Add frost pattern variation
951
+ iceColor = mix(iceColor, iceColor * 1.1, frostPattern * 0.2);
952
+
953
+ // Add cold air wisps - subtle pale blue flowing over the ice
954
+ iceColor = mix(iceColor, coldAirColor, coldAir * 0.2);
955
+
956
+ // Add crack lines - dark blue cracks stand out
957
+ iceColor = mix(iceColor, crackColor, cracks * 0.8);
958
+
959
+ // Add sparse shimmer highlights - subtle
960
+ iceColor += frostWhite * shimmer * 0.2;
961
+
962
+ // Edge frost - lighter at edges
963
+ iceColor = mix(iceColor, lightBlue, edgeDist * 0.25);
964
+
965
+ // Apply intensity without washing out blue
966
+ vec3 finalColor = iceColor * (0.7 + uIntensity * 0.5);
967
+
968
+ // === ALPHA - semi-transparent to show map through ===
969
+ // Thicker/more opaque in center, thinner at edges
970
+ float iceThickness = 1.0 - shallowZone; // Inverse of shallow zone
971
+ float baseAlpha = 0.4 + iceThickness * 0.35; // 0.4 to 0.75 base
972
+ baseAlpha += frostPattern * 0.1; // Variation from frost pattern
973
+ baseAlpha += cracks * 0.15; // Cracks are more opaque
974
+ baseAlpha += coldAir * 0.08; // Cold air adds slight opacity variation
975
+ baseAlpha = clamp(baseAlpha, 0.0, 0.85); // Never fully opaque
976
+
977
+ float alpha = mask * uOpacity * baseAlpha;
978
+
979
+ return vec4(finalColor, alpha);
980
+ }
981
+
982
+ void main() {
983
+ // Clipping planes
984
+ vec4 plane;
985
+ #pragma unroll_loop
986
+ for(int i = 0; i < NUM_CLIPPING_PLANES; i++) {
987
+ plane = uClippingPlanes[i];
988
+ if(dot(-vWorldPosition, plane.xyz) > plane.w) {
989
+ discard;
990
+ }
991
+ }
992
+
993
+ float time = uTime * uSpeed;
994
+ vec2 texSize = vec2(textureSize(uMaskTexture, 0));
995
+
996
+ // === OUTER SHADOW - makes effects feel inset (color burn style) ===
997
+ // Sample mask at increasing mip levels to get expanded/blurred versions
998
+ // Higher mip = more blur = wider spread
999
+ float maskSharp = textureLod(uMaskTexture, vUv, 0.0).a;
1000
+ float maskBlur1 = textureLod(uMaskTexture, vUv, 4.0 + uBorder * 3.0).a;
1001
+ float maskBlur2 = textureLod(uMaskTexture, vUv, 6.0 + uBorder * 4.0).a;
1002
+ float maskBlur3 = textureLod(uMaskTexture, vUv, 8.0 + uBorder * 5.0).a;
1003
+ float maskBlur4 = textureLod(uMaskTexture, vUv, 10.0 + uBorder * 6.0).a;
1004
+
1005
+ // Noise for organic shadow edges
1006
+ vec2 shadowNoiseUV = vUv * texSize * 0.003;
1007
+ float shadowNoise1 = snoise(shadowNoiseUV * 2.0 + time * 0.03) * 0.5 + 0.5;
1008
+ float shadowNoise2 = snoise(shadowNoiseUV * 4.0 + time * 0.02 + 50.0) * 0.5 + 0.5;
1009
+ float combinedShadowNoise = shadowNoise1 * 0.6 + shadowNoise2 * 0.4;
1010
+
1011
+ // Inner shadow zone: close to the edge, darkest
1012
+ float innerZone = smoothstep(0.0, 0.7, maskBlur1) * (1.0 - smoothstep(0.0, 0.1, maskSharp));
1013
+ // Mid-inner shadow zone
1014
+ float midInnerZone = smoothstep(0.0, 0.6, maskBlur2) * (1.0 - smoothstep(0.0, 0.3, maskBlur1));
1015
+ // Mid-outer shadow zone
1016
+ float midOuterZone = smoothstep(0.0, 0.5, maskBlur3) * (1.0 - smoothstep(0.0, 0.25, maskBlur2));
1017
+ // Outer shadow zone: farthest, lightest
1018
+ float outerZone = smoothstep(0.0, 0.4, maskBlur4) * (1.0 - smoothstep(0.0, 0.2, maskBlur3));
1019
+
1020
+ // Apply noise to break up the shadow - more noise on outer zones
1021
+ innerZone *= (0.85 + combinedShadowNoise * 0.15);
1022
+ midInnerZone *= (0.7 + combinedShadowNoise * 0.3);
1023
+ midOuterZone *= (0.5 + combinedShadowNoise * 0.5);
1024
+ outerZone *= (0.3 + combinedShadowNoise * 0.7);
1025
+
1026
+ // Combine with heavier weight near the edge (color burn effect)
1027
+ float shadowIntensity = innerZone * 1.0 + midInnerZone * 0.7 + midOuterZone * 0.4 + outerZone * 0.2;
1028
+ shadowIntensity *= uBorder * uOpacity;
1029
+
1030
+ vec4 result;
1031
+
1032
+ if(uEffectType == 1) {
1033
+ result = fireEffect(vUv, texSize, time);
1034
+ } else if(uEffectType == 2) {
1035
+ result = spaceTearEffect(vUv, texSize, time);
1036
+ } else if(uEffectType == 3) {
1037
+ result = waterEffect(vUv, texSize, time);
1038
+ } else if(uEffectType == 4) {
1039
+ result = magicEffect(vUv, texSize, time);
1040
+ } else if(uEffectType == 5) {
1041
+ result = greaseEffect(vUv, texSize, time);
1042
+ } else if(uEffectType == 6) {
1043
+ result = iceEffect(vUv, texSize, time);
1044
+ } else {
1045
+ // No effect - solid color
1046
+ float mask = texture2D(uMaskTexture, vUv).a;
1047
+ if(mask < 0.001 && shadowIntensity < 0.001) discard;
1048
+ result = vec4(uBaseColor, mask * uOpacity);
1049
+ }
1050
+
1051
+ // Blend outer shadow under the effect (skip for no effect, water, grease, ice - they handle their own depth or don't need it)
1052
+ // Shadow is dark, high opacity near edge for color burn effect
1053
+ vec3 shadowColor = vec3(0.0, 0.0, 0.0);
1054
+ float shadowAlpha = (uEffectType == 0 || uEffectType == 3 || uEffectType == 5 || uEffectType == 6) ? 0.0 : shadowIntensity * 0.85; // No shadow for plain color/water/grease/ice
1055
+
1056
+ // If we have shadow but no effect, show just the shadow
1057
+ if(result.a < 0.001 && shadowAlpha > 0.001) {
1058
+ gl_FragColor = vec4(shadowColor, shadowAlpha);
1059
+ return;
1060
+ }
1061
+
1062
+ // Composite: shadow underneath, effect on top
1063
+ // Use standard alpha blending: result over shadow
1064
+ float finalAlpha = result.a + shadowAlpha * (1.0 - result.a);
1065
+ vec3 finalColor = (result.rgb * result.a + shadowColor * shadowAlpha * (1.0 - result.a)) / max(finalAlpha, 0.001);
1066
+
1067
+ if(finalAlpha < 0.001) discard;
1068
+
1069
+ gl_FragColor = vec4(finalColor, finalAlpha);
1070
+ }