minerva-design 1.6.0 → 1.7.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.
Files changed (120) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/charts/engine/data.cjs +1 -1
  3. package/dist/charts/engine/data.cjs.map +1 -1
  4. package/dist/charts/engine/data.d.cts +2 -0
  5. package/dist/charts/engine/data.d.ts +2 -0
  6. package/dist/charts/engine/data.js +6 -4
  7. package/dist/charts/engine/data.js.map +1 -1
  8. package/dist/charts/engine/defaults.cjs +1 -1
  9. package/dist/charts/engine/defaults.cjs.map +1 -1
  10. package/dist/charts/engine/defaults.js +22 -18
  11. package/dist/charts/engine/defaults.js.map +1 -1
  12. package/dist/charts/engine/graph/chart.cjs +1 -1
  13. package/dist/charts/engine/graph/chart.cjs.map +1 -1
  14. package/dist/charts/engine/graph/chart.js +532 -493
  15. package/dist/charts/engine/graph/chart.js.map +1 -1
  16. package/dist/charts/engine/graph/geometry.cjs +1 -1
  17. package/dist/charts/engine/graph/geometry.cjs.map +1 -1
  18. package/dist/charts/engine/graph/geometry.d.cts +12 -2
  19. package/dist/charts/engine/graph/geometry.d.ts +12 -2
  20. package/dist/charts/engine/graph/geometry.js +35 -31
  21. package/dist/charts/engine/graph/geometry.js.map +1 -1
  22. package/dist/charts/engine/model.cjs +1 -1
  23. package/dist/charts/engine/model.cjs.map +1 -1
  24. package/dist/charts/engine/model.js +95 -74
  25. package/dist/charts/engine/model.js.map +1 -1
  26. package/dist/charts/engine/orderbook/field.cjs +1 -1
  27. package/dist/charts/engine/orderbook/field.cjs.map +1 -1
  28. package/dist/charts/engine/orderbook/field.d.cts +15 -2
  29. package/dist/charts/engine/orderbook/field.d.ts +15 -2
  30. package/dist/charts/engine/orderbook/field.js +21 -9
  31. package/dist/charts/engine/orderbook/field.js.map +1 -1
  32. package/dist/charts/engine/orderbook/heatmap.cjs +1 -1
  33. package/dist/charts/engine/orderbook/heatmap.cjs.map +1 -1
  34. package/dist/charts/engine/orderbook/heatmap.d.cts +29 -0
  35. package/dist/charts/engine/orderbook/heatmap.d.ts +29 -0
  36. package/dist/charts/engine/orderbook/heatmap.js +576 -453
  37. package/dist/charts/engine/orderbook/heatmap.js.map +1 -1
  38. package/dist/charts/engine/orderbook/types.d.cts +36 -11
  39. package/dist/charts/engine/orderbook/types.d.ts +36 -11
  40. package/dist/charts/engine/presets.cjs +1 -1
  41. package/dist/charts/engine/presets.cjs.map +1 -1
  42. package/dist/charts/engine/presets.d.cts +16 -3
  43. package/dist/charts/engine/presets.d.ts +16 -3
  44. package/dist/charts/engine/presets.js +42 -32
  45. package/dist/charts/engine/presets.js.map +1 -1
  46. package/dist/charts/engine/scene.cjs +1 -1
  47. package/dist/charts/engine/scene.cjs.map +1 -1
  48. package/dist/charts/engine/scene.d.cts +4 -2
  49. package/dist/charts/engine/scene.d.ts +4 -2
  50. package/dist/charts/engine/scene.js +185 -107
  51. package/dist/charts/engine/scene.js.map +1 -1
  52. package/dist/charts/engine/three-d/oit.cjs +119 -34
  53. package/dist/charts/engine/three-d/oit.cjs.map +1 -1
  54. package/dist/charts/engine/three-d/oit.d.cts +65 -10
  55. package/dist/charts/engine/three-d/oit.d.ts +65 -10
  56. package/dist/charts/engine/three-d/oit.js +119 -34
  57. package/dist/charts/engine/three-d/oit.js.map +1 -1
  58. package/dist/charts/engine/three-d/render-types.cjs.map +1 -1
  59. package/dist/charts/engine/three-d/render-types.d.cts +19 -0
  60. package/dist/charts/engine/three-d/render-types.d.ts +19 -0
  61. package/dist/charts/engine/three-d/render-types.js.map +1 -1
  62. package/dist/charts/engine/three-d/shaders.cjs +14 -6
  63. package/dist/charts/engine/three-d/shaders.cjs.map +1 -1
  64. package/dist/charts/engine/three-d/shaders.d.cts +6 -1
  65. package/dist/charts/engine/three-d/shaders.d.ts +6 -1
  66. package/dist/charts/engine/three-d/shaders.js +40 -26
  67. package/dist/charts/engine/three-d/shaders.js.map +1 -1
  68. package/dist/charts/engine/three-d/webgl.cjs +1 -1
  69. package/dist/charts/engine/three-d/webgl.cjs.map +1 -1
  70. package/dist/charts/engine/three-d/webgl.js +167 -125
  71. package/dist/charts/engine/three-d/webgl.js.map +1 -1
  72. package/dist/charts/engine/three-d/webgpu.cjs +1 -1
  73. package/dist/charts/engine/three-d/webgpu.cjs.map +1 -1
  74. package/dist/charts/engine/three-d/webgpu.js +169 -140
  75. package/dist/charts/engine/three-d/webgpu.js.map +1 -1
  76. package/dist/charts/engine/trading/chart.cjs +1 -1
  77. package/dist/charts/engine/trading/chart.cjs.map +1 -1
  78. package/dist/charts/engine/trading/chart.js +1149 -1023
  79. package/dist/charts/engine/trading/chart.js.map +1 -1
  80. package/dist/charts/engine/trading/labels.cjs +1 -1
  81. package/dist/charts/engine/trading/labels.cjs.map +1 -1
  82. package/dist/charts/engine/trading/labels.js +12 -0
  83. package/dist/charts/engine/trading/labels.js.map +1 -1
  84. package/dist/charts/engine/trading/layout.cjs +2 -0
  85. package/dist/charts/engine/trading/layout.cjs.map +1 -0
  86. package/dist/charts/engine/trading/layout.d.cts +37 -0
  87. package/dist/charts/engine/trading/layout.d.ts +37 -0
  88. package/dist/charts/engine/trading/layout.js +63 -0
  89. package/dist/charts/engine/trading/layout.js.map +1 -0
  90. package/dist/charts/engine/trading/types.d.cts +24 -7
  91. package/dist/charts/engine/trading/types.d.ts +24 -7
  92. package/dist/charts/engine/types.d.cts +21 -8
  93. package/dist/charts/engine/types.d.ts +21 -8
  94. package/dist/core/contracts.cjs +1 -1
  95. package/dist/core/contracts.cjs.map +1 -1
  96. package/dist/core/contracts.js +1 -1
  97. package/dist/core/contracts.js.map +1 -1
  98. package/dist/native/charts/src/engine/data.cjs +1 -1
  99. package/dist/native/charts/src/engine/data.cjs.map +1 -1
  100. package/dist/native/charts/src/engine/data.js +6 -4
  101. package/dist/native/charts/src/engine/data.js.map +1 -1
  102. package/dist/native/charts/src/engine/model.cjs +1 -1
  103. package/dist/native/charts/src/engine/model.cjs.map +1 -1
  104. package/dist/native/charts/src/engine/model.js +104 -83
  105. package/dist/native/charts/src/engine/model.js.map +1 -1
  106. package/dist/native/charts/src/engine/presets.cjs +1 -1
  107. package/dist/native/charts/src/engine/presets.cjs.map +1 -1
  108. package/dist/native/charts/src/engine/presets.js +42 -32
  109. package/dist/native/charts/src/engine/presets.js.map +1 -1
  110. package/dist/native/charts/src/engine/scene.cjs +1 -1
  111. package/dist/native/charts/src/engine/scene.cjs.map +1 -1
  112. package/dist/native/charts/src/engine/scene.js +185 -107
  113. package/dist/native/charts/src/engine/scene.js.map +1 -1
  114. package/dist/taro/index.js +1263 -1152
  115. package/dist/vue/components/Chart/OrderBookHeatmap.vue.d.ts +11 -0
  116. package/dist/vue/components/Chart/orderbook.d.ts +7 -0
  117. package/dist/vue/components/Chart/orderbook.js +2 -0
  118. package/dist/vue/components/Chart/orderbook.js.map +1 -1
  119. package/dist/weapp/charts/canvas.js +234 -58
  120. package/package.json +1 -1
@@ -1,28 +1,52 @@
1
1
  //#region src/engine/three-d/oit.ts
2
- var e = 64, t = 12, n = .01, r = .999, i = .6, a = 1, o = 6, s = .35, c = (e) => Number.isInteger(e) ? e.toFixed(1) : String(e), l = `
2
+ var e = 64, t = 12, n = .01, r = .999, i = .6, a = 1, o = 6, s = .35, c = .002, l = .05, u = 1.4, d = (e) => Number.isInteger(e) ? e.toFixed(1) : String(e), f = `
3
+ @group(1) @binding(0) var frontTex: texture_2d<f32>;
4
+
3
5
  struct Oit {
4
- @location(0) accum: vec4f,
5
- @location(1) extra: vec4f,
6
+ @location(0) front: vec4f,
7
+ @location(1) accum: vec4f,
8
+ @location(2) extra: vec4f,
9
+ @location(3) behind: vec4f,
6
10
  };
7
11
 
8
- fn oit(c: vec4f, depth: f32) -> Oit {
12
+ fn oit(c: vec4f, depth: f32, xy: vec4f) -> Oit {
9
13
  var o: Oit;
14
+ o.front = vec4f(0.0);
15
+ o.accum = vec4f(0.0);
16
+ o.extra = vec4f(0.0);
17
+ o.behind = vec4f(0.0);
18
+ let z = clamp(depth, 0.0, 1.0);
19
+ let near = z <= textureLoad(frontTex, vec2i(xy.xy), 0).r + ${d(c)};
10
20
  if (u.light.w > 0.5) {
11
- o.accum = vec4f(0.0);
12
- o.extra = vec4f(c.rgb, 0.0);
21
+ if (near) { o.extra = vec4f(c.rgb, 0.0); } else { o.behind = vec4f(c.rgb, 0.0); }
22
+ return o;
23
+ }
24
+ if (near) {
25
+ o.front = c;
13
26
  return o;
14
27
  }
15
- let a = clamp(c.a, 0.0, ${c(r)});
16
- let w = max(${c(n)}, ${c(e)} * exp2(-${c(t)} * clamp(depth, 0.0, 1.0)));
28
+ let a = clamp(c.a, 0.0, ${d(r)});
29
+ let w = max(${d(n)}, ${d(e)} * exp2(-${d(t)} * z));
17
30
  o.accum = vec4f(c.rgb, a) * w;
18
31
  o.extra = vec4f(0.0, 0.0, 0.0, -log(1.0 - a));
19
32
  return o;
20
33
  }
21
- `, u = `${`
34
+
35
+ fn frontDepth(c: vec4f, depth: f32) -> vec4f {
36
+ if (c.a < ${d(l)}) { discard; }
37
+ return vec4f(clamp(depth, 0.0, 1.0));
38
+ }
39
+
40
+ fn screenLight(c: vec4f) -> vec4f {
41
+ if (u.light.w < 0.5) { return c; }
42
+ let v = vec3f(1.0) - exp(-${d(u)} * max(c.rgb, vec3f(0.0)));
43
+ return vec4f(v, max(max(v.r, v.g), v.b));
44
+ }
45
+ `, p = `${`
22
46
  fn shoulder(x: f32) -> f32 {
23
- if (x <= ${c(i)}) { return x; }
24
- let t = (x - ${c(i)}) / ${c(.4)};
25
- return ${c(i)} + ${c(.4)} * t / (1.0 + t);
47
+ if (x <= ${d(i)}) { return x; }
48
+ let t = (x - ${d(i)}) / ${d(.4)};
49
+ return ${d(i)} + ${d(.4)} * t / (1.0 + t);
26
50
  }
27
51
 
28
52
  fn toneMap(rgb: vec3f) -> vec3f {
@@ -30,11 +54,14 @@ fn toneMap(rgb: vec3f) -> vec3f {
30
54
  if (m <= 1e-6) { return rgb; }
31
55
  let peak = shoulder(m);
32
56
  let scaled = rgb * (peak / m);
33
- return mix(scaled, vec3f(peak), smoothstep(${c(a)}, ${c(o)}, m) * ${c(s)});
57
+ return mix(scaled, vec3f(peak), smoothstep(${d(a)}, ${d(o)}, m) * ${d(s)});
34
58
  }
35
59
  `}
36
- @group(0) @binding(0) var accumTex: texture_2d<f32>;
37
- @group(0) @binding(1) var extraTex: texture_2d<f32>;
60
+ @group(0) @binding(0) var frontTex: texture_2d<f32>;
61
+ @group(0) @binding(1) var accumTex: texture_2d<f32>;
62
+ @group(0) @binding(2) var extraTex: texture_2d<f32>;
63
+ @group(0) @binding(3) var behindTex: texture_2d<f32>;
64
+
38
65
 
39
66
  @vertex
40
67
  fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
@@ -45,67 +72,125 @@ fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
45
72
  @fragment
46
73
  fn fs(@builtin(position) position: vec4f) -> @location(0) vec4f {
47
74
  let xy = vec2i(position.xy);
75
+ let front = textureLoad(frontTex, xy, 0);
48
76
  let accum = textureLoad(accumTex, xy, 0);
49
77
  let extra = textureLoad(extraTex, xy, 0);
50
- let alpha = 1.0 - exp(-extra.a);
78
+ let behind = textureLoad(behindTex, xy, 0);
79
+ let tau = max(extra.a, 0.0);
80
+ let restAlpha = 1.0 - exp(-tau);
81
+ let rest = accum.rgb * (restAlpha / max(accum.a, 1e-6));
82
+ let sum = max(front.a, 0.0);
83
+ let scale = select(1.0, 1.0 / max(sum, 1e-6), sum > 1.0);
84
+ let fa = sum * scale;
85
+ let alpha = fa + restAlpha * (1.0 - fa);
86
+ let color = front.rgb * scale + rest * (1.0 - fa);
87
+ let meanT = select(1.0, restAlpha / max(tau, 1e-6), tau > 1e-4);
88
+ let t = (1.0 - fa) * meanT;
89
+ let back = toneMap(behind.rgb);
90
+ let backAlpha = clamp(max(max(back.r, back.g), back.b), 0.0, 1.0);
91
+ let under = color + back * t;
92
+ let underAlpha = min(1.0, alpha + backAlpha * t);
51
93
  let light = toneMap(extra.rgb);
52
94
  let lightAlpha = clamp(max(max(light.r, light.g), light.b), 0.0, 1.0);
53
- let rgb = accum.rgb * (alpha / max(accum.a, 1e-6)) + light * (1.0 - alpha);
54
- let a = alpha + lightAlpha * (1.0 - alpha);
95
+ let a = lightAlpha + underAlpha * (1.0 - lightAlpha);
96
+ let rgb = light + under * (1.0 - lightAlpha);
55
97
  if (a <= 0.0) { discard; }
56
98
  return vec4f(rgb, a);
57
99
  }
58
- `, d = `
59
- layout(location = 0) out vec4 o_accum;
60
- layout(location = 1) out vec4 o_extra;
100
+ `, m = `
101
+ uniform highp sampler2D u_front;
102
+ layout(location = 0) out vec4 o_front;
103
+ layout(location = 1) out vec4 o_accum;
104
+ layout(location = 2) out vec4 o_extra;
105
+ layout(location = 3) out vec4 o_behind;
61
106
  void emitOit(vec4 c, float depth) {
107
+ o_front = vec4(0.0);
108
+ o_accum = vec4(0.0);
109
+ o_extra = vec4(0.0);
110
+ o_behind = vec4(0.0);
111
+ float z = clamp(depth, 0.0, 1.0);
112
+ bool near = z <= texelFetch(u_front, ivec2(gl_FragCoord.xy), 0).r + ${d(c)};
62
113
  if (u_light.w > 0.5) {
63
- o_accum = vec4(0.0);
64
- o_extra = vec4(c.rgb, 0.0);
114
+ if (near) o_extra = vec4(c.rgb, 0.0); else o_behind = vec4(c.rgb, 0.0);
115
+ return;
116
+ }
117
+ if (near) {
118
+ o_front = c;
65
119
  return;
66
120
  }
67
- float a = clamp(c.a, 0.0, ${c(r)});
68
- float w = max(${c(n)}, ${c(e)} * exp2(-${c(t)} * clamp(depth, 0.0, 1.0)));
121
+ float a = clamp(c.a, 0.0, ${d(r)});
122
+ float w = max(${d(n)}, ${d(e)} * exp2(-${d(t)} * z));
69
123
  o_accum = vec4(c.rgb, a) * w;
70
124
  o_extra = vec4(0.0, 0.0, 0.0, -log(1.0 - a));
71
125
  }
72
126
  #define EMIT(c, d) emitOit(c, d)
73
- `, f = {
127
+ `, h = `
128
+ out vec4 fragColor;
129
+ void emitFront(vec4 c, float depth) {
130
+ if (c.a < ${d(l)}) discard;
131
+ fragColor = vec4(clamp(depth, 0.0, 1.0));
132
+ }
133
+ #define EMIT(c, d) emitFront(c, d)
134
+ `, g = (e) => `
135
+ vec4 screenLight(vec4 c) {
136
+ if (u_light.w < 0.5) return c;
137
+ vec3 v = vec3(1.0) - exp(-${d(u)} * max(c.rgb, vec3(0.0)));
138
+ return vec4(v, max(max(v.r, v.g), v.b));
139
+ }
140
+ #define EMIT(c, d) ${e} = screenLight(c)
141
+ `, _ = {
74
142
  vertex: "#version 300 es\nvoid main() {\n vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)) * 2.0 - 1.0;\n gl_Position = vec4(p, 0.0, 1.0);\n}\n",
75
143
  fragment: `#version 300 es
76
144
  precision highp float;
145
+ uniform sampler2D u_frontColor;
77
146
  uniform sampler2D u_accum;
78
147
  uniform sampler2D u_extra;
148
+ uniform sampler2D u_behind;
79
149
  out vec4 fragColor;
80
150
  ${`
81
151
  float shoulder(float x) {
82
- if (x <= ${c(i)}) return x;
83
- float t = (x - ${c(i)}) / ${c(.4)};
84
- return ${c(i)} + ${c(.4)} * t / (1.0 + t);
152
+ if (x <= ${d(i)}) return x;
153
+ float t = (x - ${d(i)}) / ${d(.4)};
154
+ return ${d(i)} + ${d(.4)} * t / (1.0 + t);
85
155
  }
86
156
  vec3 toneMap(vec3 rgb) {
87
157
  float m = max(max(rgb.r, rgb.g), rgb.b);
88
158
  if (m <= 1e-6) return rgb;
89
159
  float peak = shoulder(m);
90
160
  vec3 scaled = rgb * (peak / m);
91
- return mix(scaled, vec3(peak), smoothstep(${c(a)}, ${c(o)}, m) * ${c(s)});
161
+ return mix(scaled, vec3(peak), smoothstep(${d(a)}, ${d(o)}, m) * ${d(s)});
92
162
  }
93
163
  `}
94
164
  void main() {
95
165
  ivec2 xy = ivec2(gl_FragCoord.xy);
166
+ vec4 front = texelFetch(u_frontColor, xy, 0);
96
167
  vec4 accum = texelFetch(u_accum, xy, 0);
97
168
  vec4 extra = texelFetch(u_extra, xy, 0);
98
- float alpha = 1.0 - exp(-extra.a);
169
+ vec4 behind = texelFetch(u_behind, xy, 0);
170
+ float tau = max(extra.a, 0.0);
171
+ float restAlpha = 1.0 - exp(-tau);
172
+ vec3 rest = accum.rgb * (restAlpha / max(accum.a, 1e-6));
173
+ float sum = max(front.a, 0.0);
174
+ float scale = sum > 1.0 ? 1.0 / sum : 1.0;
175
+ float fa = sum * scale;
176
+ float alpha = fa + restAlpha * (1.0 - fa);
177
+ vec3 color = front.rgb * scale + rest * (1.0 - fa);
178
+ float meanT = tau > 1e-4 ? restAlpha / tau : 1.0;
179
+ float t = (1.0 - fa) * meanT;
180
+ vec3 back = toneMap(behind.rgb);
181
+ float backAlpha = clamp(max(max(back.r, back.g), back.b), 0.0, 1.0);
182
+ vec3 under = color + back * t;
183
+ float underAlpha = min(1.0, alpha + backAlpha * t);
99
184
  vec3 light = toneMap(extra.rgb);
100
185
  float lightAlpha = clamp(max(max(light.r, light.g), light.b), 0.0, 1.0);
101
- vec3 rgb = accum.rgb * (alpha / max(accum.a, 1e-6)) + light * (1.0 - alpha);
102
- float a = alpha + lightAlpha * (1.0 - alpha);
186
+ float a = lightAlpha + underAlpha * (1.0 - lightAlpha);
187
+ vec3 rgb = light + under * (1.0 - lightAlpha);
103
188
  if (a <= 0.0) discard;
104
189
  fragColor = vec4(rgb, a);
105
190
  }
106
191
  `
107
192
  };
108
193
  //#endregion
109
- export { f as GLSL_COMPOSITE, d as GLSL_OIT, i as TONE_KNEE, u as WGSL_COMPOSITE, l as WGSL_OIT };
194
+ export { c as FRONT_EPSILON, l as FRONT_MIN_ALPHA, _ as GLSL_COMPOSITE, h as GLSL_FRONT, m as GLSL_OIT, u as SCREEN_GAIN, i as TONE_KNEE, p as WGSL_COMPOSITE, f as WGSL_OIT, g as glslScreen };
110
195
 
111
196
  //# sourceMappingURL=oit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"oit.js","names":["WEIGHT_SCALE","WEIGHT_FALLOFF","WEIGHT_MIN","MAX_ALPHA","TONE_KNEE","WHITE_FROM","WHITE_TO","WHITE_AMOUNT","f","v","WGSL_OIT","WGSL_COMPOSITE","GLSL_OIT","GLSL_COMPOSITE"],"sources":["../../../../../charts/src/engine/three-d/oit.ts"],"sourcesContent":["// Weighted blended order-independent transparency (McGuire & Bavoil 2013)\n// of the WebGPU and WebGL2 backends, and the tone mapping of the additive\n// light. The blended items (transparent meshes, blended and additive points)\n// are drawn in any order, without writing depth, into two float targets\n// cleared to 0 and blended by plain addition (one, one):\n//\n// accum rgb: Σ premultiplied color × w a: Σ alpha × w\n// extra rgb: Σ additive light a: Σ −ln(1 − alpha)\n//\n// w favours the fragments near the camera (`oitWeight`). The revealage\n// Π(1 − alpha) is stored as the sum of its logarithms so that every target\n// uses the same additive blending (WebGL2 has no per-target blend\n// functions). The composite pass blends over the opaque scene:\n//\n// transparent = (accum.rgb / accum.a) × (1 − e^−extra.a), alpha 1 − e^−extra.a\n// light = toneMap(extra.rgb), alpha its largest channel\n//\n// `toneMap` keeps the colors below a knee and rolls the brighter ones off\n// towards 1 (Reinhard) by their largest channel (hue preserving), whitening\n// only the very bright light a little: dense glowing clouds saturate\n// gracefully instead of clipping to white.\n\n/** Weight of the closest fragments; halves every 1/12 of the depth range */\nconst WEIGHT_SCALE = 64;\nconst WEIGHT_FALLOFF = 12;\nconst WEIGHT_MIN = 0.01;\n/** Largest alpha of a blended fragment (keeps −ln(1 − alpha) finite) */\nconst MAX_ALPHA = 0.999;\n/** Light values below the knee are kept */\nexport const TONE_KNEE = 0.6;\n/** Light values where the whitening starts / is full, and its amount */\nconst WHITE_FROM = 1;\nconst WHITE_TO = 6;\nconst WHITE_AMOUNT = 0.35;\n\n/**\n * Weight of a blended fragment at a depth (0 nearest … 1 farthest point of\n * the box sphere): 64 × 2^(−12 depth), at least 0.01. Accumulated in half\n * floats: about a thousand layers of the nearest fragments fit.\n */\nexport const oitWeight = (depth: number): number =>\n Math.max(\n WEIGHT_MIN,\n WEIGHT_SCALE *\n Math.pow(2, -WEIGHT_FALLOFF * Math.min(1, Math.max(0, depth))),\n );\n\n/**\n * Soft shoulder: identity below `TONE_KNEE`, then a Reinhard roll-off\n * towards 1 (never reached) with a continuous slope: bright regions keep a\n * gradient instead of a flat plateau\n */\nexport const shoulder = (x: number): number => {\n if (x <= TONE_KNEE) return x;\n const t = (x - TONE_KNEE) / (1 - TONE_KNEE);\n return TONE_KNEE + (1 - TONE_KNEE) * (t / (1 + t));\n};\n\nconst smoothstep = (a: number, b: number, x: number) => {\n const t = Math.min(1, Math.max(0, (x - a) / (b - a)));\n return t * t * (3 - 2 * t);\n};\n\n/** Tone mapping of additive light (linear, premultiplied) */\nexport const toneMap = (\n rgb: readonly [number, number, number],\n): [number, number, number] => {\n const m = Math.max(rgb[0], rgb[1], rgb[2]);\n if (m <= 1e-6) return [rgb[0], rgb[1], rgb[2]];\n const peak = shoulder(m);\n const s = peak / m;\n const white = smoothstep(WHITE_FROM, WHITE_TO, m) * WHITE_AMOUNT;\n return [\n rgb[0] * s + (peak - rgb[0] * s) * white,\n rgb[1] * s + (peak - rgb[1] * s) * white,\n rgb[2] * s + (peak - rgb[2] * s) * white,\n ];\n};\n\n/** What a blended fragment adds to the two targets (JS mirror of the shaders) */\nexport const oitFragment = (\n premultiplied: readonly [number, number, number, number],\n depth: number,\n additive: boolean,\n): { accum: number[]; extra: number[] } => {\n const [r, g, b, alpha] = premultiplied;\n if (additive) return { accum: [0, 0, 0, 0], extra: [r, g, b, 0] };\n const a = Math.min(MAX_ALPHA, Math.max(0, alpha));\n const w = oitWeight(depth);\n return {\n accum: [r * w, g * w, b * w, a * w],\n extra: [0, 0, 0, -Math.log(1 - a)],\n };\n};\n\n/** Composite of the targets: a premultiplied color (JS mirror of the shaders) */\nexport const oitComposite = (\n accum: readonly number[],\n extra: readonly number[],\n): [number, number, number, number] => {\n const alpha = 1 - Math.exp(-(extra[3] ?? 0));\n const k = alpha / Math.max(accum[3] ?? 0, 1e-6);\n const light = toneMap([extra[0] ?? 0, extra[1] ?? 0, extra[2] ?? 0]);\n const lightAlpha = Math.min(1, Math.max(light[0], light[1], light[2]));\n const behind = 1 - alpha;\n return [\n (accum[0] ?? 0) * k + light[0] * behind,\n (accum[1] ?? 0) * k + light[1] * behind,\n (accum[2] ?? 0) * k + light[2] * behind,\n alpha + lightAlpha * behind,\n ];\n};\n\nconst f = (v: number) => (Number.isInteger(v) ? v.toFixed(1) : String(v));\n\n// ------------------------------------------------------------------- WGSL\n\n/** Second fragment entry point of the points and meshes: `fsOit` */\nexport const WGSL_OIT = /* wgsl */ `\nstruct Oit {\n @location(0) accum: vec4f,\n @location(1) extra: vec4f,\n};\n\nfn oit(c: vec4f, depth: f32) -> Oit {\n var o: Oit;\n if (u.light.w > 0.5) {\n o.accum = vec4f(0.0);\n o.extra = vec4f(c.rgb, 0.0);\n return o;\n }\n let a = clamp(c.a, 0.0, ${f(MAX_ALPHA)});\n let w = max(${f(WEIGHT_MIN)}, ${f(WEIGHT_SCALE)} * exp2(-${f(WEIGHT_FALLOFF)} * clamp(depth, 0.0, 1.0)));\n o.accum = vec4f(c.rgb, a) * w;\n o.extra = vec4f(0.0, 0.0, 0.0, -log(1.0 - a));\n return o;\n}\n`;\n\nconst WGSL_TONE = /* wgsl */ `\nfn shoulder(x: f32) -> f32 {\n if (x <= ${f(TONE_KNEE)}) { return x; }\n let t = (x - ${f(TONE_KNEE)}) / ${f(1 - TONE_KNEE)};\n return ${f(TONE_KNEE)} + ${f(1 - TONE_KNEE)} * t / (1.0 + t);\n}\n\nfn toneMap(rgb: vec3f) -> vec3f {\n let m = max(max(rgb.r, rgb.g), rgb.b);\n if (m <= 1e-6) { return rgb; }\n let peak = shoulder(m);\n let scaled = rgb * (peak / m);\n return mix(scaled, vec3f(peak), smoothstep(${f(WHITE_FROM)}, ${f(WHITE_TO)}, m) * ${f(WHITE_AMOUNT)});\n}\n`;\n\n/** Full-screen composite of the two targets over the opaque scene */\nexport const WGSL_COMPOSITE = /* wgsl */ `${WGSL_TONE}\n@group(0) @binding(0) var accumTex: texture_2d<f32>;\n@group(0) @binding(1) var extraTex: texture_2d<f32>;\n\n@vertex\nfn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {\n let p = vec2f(f32((vi << 1u) & 2u), f32(vi & 2u)) * 2.0 - 1.0;\n return vec4f(p, 0.0, 1.0);\n}\n\n@fragment\nfn fs(@builtin(position) position: vec4f) -> @location(0) vec4f {\n let xy = vec2i(position.xy);\n let accum = textureLoad(accumTex, xy, 0);\n let extra = textureLoad(extraTex, xy, 0);\n let alpha = 1.0 - exp(-extra.a);\n let light = toneMap(extra.rgb);\n let lightAlpha = clamp(max(max(light.r, light.g), light.b), 0.0, 1.0);\n let rgb = accum.rgb * (alpha / max(accum.a, 1e-6)) + light * (1.0 - alpha);\n let a = alpha + lightAlpha * (1.0 - alpha);\n if (a <= 0.0) { discard; }\n return vec4f(rgb, a);\n}\n`;\n\n// ------------------------------------------------------------------- GLSL\n\n/** Outputs of the OIT variants (GLSL ES 3.0): `EMIT(color, depth)` */\nexport const GLSL_OIT = `\nlayout(location = 0) out vec4 o_accum;\nlayout(location = 1) out vec4 o_extra;\nvoid emitOit(vec4 c, float depth) {\n if (u_light.w > 0.5) {\n o_accum = vec4(0.0);\n o_extra = vec4(c.rgb, 0.0);\n return;\n }\n float a = clamp(c.a, 0.0, ${f(MAX_ALPHA)});\n float w = max(${f(WEIGHT_MIN)}, ${f(WEIGHT_SCALE)} * exp2(-${f(WEIGHT_FALLOFF)} * clamp(depth, 0.0, 1.0)));\n o_accum = vec4(c.rgb, a) * w;\n o_extra = vec4(0.0, 0.0, 0.0, -log(1.0 - a));\n}\n#define EMIT(c, d) emitOit(c, d)\n`;\n\nconst GLSL_TONE = `\nfloat shoulder(float x) {\n if (x <= ${f(TONE_KNEE)}) return x;\n float t = (x - ${f(TONE_KNEE)}) / ${f(1 - TONE_KNEE)};\n return ${f(TONE_KNEE)} + ${f(1 - TONE_KNEE)} * t / (1.0 + t);\n}\nvec3 toneMap(vec3 rgb) {\n float m = max(max(rgb.r, rgb.g), rgb.b);\n if (m <= 1e-6) return rgb;\n float peak = shoulder(m);\n vec3 scaled = rgb * (peak / m);\n return mix(scaled, vec3(peak), smoothstep(${f(WHITE_FROM)}, ${f(WHITE_TO)}, m) * ${f(WHITE_AMOUNT)});\n}\n`;\n\n/** Composite program of WebGL2 (full-screen triangle from `gl_VertexID`) */\nexport const GLSL_COMPOSITE = {\n vertex: `#version 300 es\nvoid main() {\n vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)) * 2.0 - 1.0;\n gl_Position = vec4(p, 0.0, 1.0);\n}\n`,\n fragment: `#version 300 es\nprecision highp float;\nuniform sampler2D u_accum;\nuniform sampler2D u_extra;\nout vec4 fragColor;\n${GLSL_TONE}\nvoid main() {\n ivec2 xy = ivec2(gl_FragCoord.xy);\n vec4 accum = texelFetch(u_accum, xy, 0);\n vec4 extra = texelFetch(u_extra, xy, 0);\n float alpha = 1.0 - exp(-extra.a);\n vec3 light = toneMap(extra.rgb);\n float lightAlpha = clamp(max(max(light.r, light.g), light.b), 0.0, 1.0);\n vec3 rgb = accum.rgb * (alpha / max(accum.a, 1e-6)) + light * (1.0 - alpha);\n float a = alpha + lightAlpha * (1.0 - alpha);\n if (a <= 0.0) discard;\n fragColor = vec4(rgb, a);\n}\n`,\n};\n"],"mappings":";AAuBA,IAAMA,IAAe,IACfC,IAAiB,IACjBC,IAAa,KAEbC,IAAY,MAELC,IAAY,IAEnBC,IAAa,GACbC,IAAW,GACXC,IAAe,KAgFfC,KAAK,MAAe,OAAO,UAAUC,CAAC,IAAIA,EAAE,QAAQ,CAAC,IAAI,OAAOA,CAAC,GAK1DC,IAAsB;;;;;;;;;;;;;4BAaPF,EAAEL,CAAS,EAAE;gBACzBK,EAAEN,CAAU,EAAE,IAAIM,EAAER,CAAY,EAAE,WAAWQ,EAAEP,CAAc,EAAE;;;;;GAwBlEU,IAA4B,GAAG;;aAf/BH,EAAEJ,CAAS,EAAE;iBACTI,EAAEJ,CAAS,EAAE,MAAMI,EAAE,EAAa,EAAE;WAC1CA,EAAEJ,CAAS,EAAE,KAAKI,EAAE,EAAa,EAAE;;;;;;;;+CAQCA,EAAEH,CAAU,EAAE,IAAIG,EAAEF,CAAQ,EAAE,SAASE,EAAED,CAAY,EAAE;;EAKhD;;;;;;;;;;;;;;;;;;;;;;;GA4BzCK,IAAW;;;;;;;;;8BASMJ,EAAEL,CAAS,EAAE;kBACzBK,EAAEN,CAAU,EAAE,IAAIM,EAAER,CAAY,EAAE,WAAWQ,EAAEP,CAAc,EAAE;;;;;GAuBpEY,IAAiB;CAC5B,QAAQ;CAMR,UAAU;;;;;EAKV;;aA1BWL,EAAEJ,CAAS,EAAE;mBACPI,EAAEJ,CAAS,EAAE,MAAMI,EAAE,EAAa,EAAE;WAC5CA,EAAEJ,CAAS,EAAE,KAAKI,EAAE,EAAa,EAAE;;;;;;;8CAOAA,EAAEH,CAAU,EAAE,IAAIG,EAAEF,CAAQ,EAAE,SAASE,EAAED,CAAY,EAAE;;EAiBzF;;;;;;;;;;;;;;AAcZ"}
1
+ {"version":3,"file":"oit.js","names":["WEIGHT_SCALE","WEIGHT_FALLOFF","WEIGHT_MIN","MAX_ALPHA","TONE_KNEE","WHITE_FROM","WHITE_TO","WHITE_AMOUNT","FRONT_EPSILON","FRONT_MIN_ALPHA","SCREEN_GAIN","f","v","WGSL_OIT","WGSL_COMPOSITE","GLSL_OIT","GLSL_FRONT","glslScreen","output","GLSL_COMPOSITE"],"sources":["../../../../../charts/src/engine/three-d/oit.ts"],"sourcesContent":["// Order-independent transparency of the WebGPU and WebGL2 backends: a hybrid\n// of an exact front layer and weighted blended OIT (McGuire & Bavoil 2013)\n// behind it, and the tone mapping of the additive light.\n//\n// 1. Front pass: the blended (non-additive) items write the depth of their\n// visible fragments (alpha ≥ `FRONT_MIN_ALPHA`, depth 0 nearest … 1\n// farthest point of the box sphere) into a single-channel float target\n// cleared to `FRONT_CLEAR`, blended by `min`: the depth of the front-most\n// transparent layer of every pixel. No depth test: a transparent fragment\n// in front of the opaque scene is always nearer than a hidden one.\n// 2. Main pass: every blended item, in any order, without writing depth,\n// into four float targets cleared to 0 and blended by plain addition:\n//\n// front rgba: Σ premultiplied color of the fragments of the front layer\n// (at most `FRONT_EPSILON` behind its depth): exact\n// accum rgb: Σ premultiplied color × w a: Σ alpha × w (the rest)\n// extra rgb: Σ additive light in front of the front layer\n// a: Σ −ln(1 − alpha) of the rest (its revealage)\n// behind rgb: Σ additive light behind the front layer\n//\n// w favours the fragments near the camera (`oitWeight`). The revealage\n// Π(1 − alpha) is stored as the sum of its logarithms so that every\n// target uses the same additive blending (WebGL2 has no per-target blend\n// functions).\n// 3. Composite over the opaque scene (`oitComposite`): the front layer over\n// the weighted average of the rest; the light behind the front layer,\n// attenuated by it and by the mean transmittance of the rest, under the\n// layers; the light in front of them over everything. Far-apart layers\n// stay exact for the nearest one (plain WBOIT lets the farther layers\n// bleed through), and glowing points behind a surface are dimmed by it.\n//\n// `toneMap` keeps the light below a knee and rolls the brighter light off\n// towards 1 (Reinhard) by its largest channel (hue preserving), whitening\n// only the very bright light a little: dense glowing clouds saturate\n// gracefully instead of clipping to white. Without float targets (WebGL 1,\n// Canvas2D), the light accumulates by screen blending, 1 − Π(1 − light):\n// the exponential `1 − e^(−SCREEN_GAIN × Σ light)` of its sum, close to the\n// same curve (`screenTone`); Canvas2D inverts it per pixel and applies\n// `toneMap` exactly.\n\n/** Weight of the closest fragments; halves every 1/12 of the depth range */\nconst WEIGHT_SCALE = 64;\nconst WEIGHT_FALLOFF = 12;\nconst WEIGHT_MIN = 0.01;\n/** Largest alpha of a blended fragment (keeps −ln(1 − alpha) finite) */\nconst MAX_ALPHA = 0.999;\n/** Light values below the knee are kept */\nexport const TONE_KNEE = 0.6;\n/** Light values where the whitening starts / is full, and its amount */\nconst WHITE_FROM = 1;\nconst WHITE_TO = 6;\nconst WHITE_AMOUNT = 0.35;\n/**\n * Depth (fraction of the box sphere diameter) within which the fragments\n * belong to the front layer: a few half-float steps\n */\nexport const FRONT_EPSILON = 2e-3;\n/** Front depth of the pixels without transparent fragment */\nexport const FRONT_CLEAR = 4;\n/** Fainter fragments don't define the front layer (soft edges, glows) */\nexport const FRONT_MIN_ALPHA = 0.05;\n/**\n * Gain of the exponential tone curve of the screen-blended light (WebGL 1,\n * no float targets): 1 − e^(−1.4 x) stays within 0.06 of `toneMap`'s curve\n */\nexport const SCREEN_GAIN = 1.4;\n\nconst clamp01 = (v: number) => Math.min(1, Math.max(0, v));\n\n/**\n * Weight of a blended fragment at a depth (0 nearest … 1 farthest point of\n * the box sphere): 64 × 2^(−12 depth), at least 0.01. Accumulated in half\n * floats: about a thousand layers of the nearest fragments fit.\n */\nexport const oitWeight = (depth: number): number =>\n Math.max(\n WEIGHT_MIN,\n WEIGHT_SCALE * Math.pow(2, -WEIGHT_FALLOFF * clamp01(depth)),\n );\n\n/**\n * Soft shoulder: identity below `TONE_KNEE`, then a Reinhard roll-off\n * towards 1 (never reached) with a continuous slope: bright regions keep a\n * gradient instead of a flat plateau\n */\nexport const shoulder = (x: number): number => {\n if (x <= TONE_KNEE) return x;\n const t = (x - TONE_KNEE) / (1 - TONE_KNEE);\n return TONE_KNEE + (1 - TONE_KNEE) * (t / (1 + t));\n};\n\nconst smoothstep = (a: number, b: number, x: number) => {\n const t = clamp01((x - a) / (b - a));\n return t * t * (3 - 2 * t);\n};\n\n/**\n * Tone mapping of additive light (linear, premultiplied), into `out` when\n * given (no allocation: per pixel on the CPU)\n */\nexport const toneMap = (\n rgb: readonly [number, number, number],\n out: [number, number, number] = [0, 0, 0],\n): [number, number, number] => {\n const m = Math.max(rgb[0], rgb[1], rgb[2]);\n if (m <= 1e-6) {\n out[0] = rgb[0];\n out[1] = rgb[1];\n out[2] = rgb[2];\n return out;\n }\n const peak = shoulder(m);\n const s = peak / m;\n const white = smoothstep(WHITE_FROM, WHITE_TO, m) * WHITE_AMOUNT;\n const r = rgb[0] * s;\n const g = rgb[1] * s;\n const b = rgb[2] * s;\n out[0] = r + (peak - r) * white;\n out[1] = g + (peak - g) * white;\n out[2] = b + (peak - b) * white;\n return out;\n};\n\n/**\n * Screen-blended light: what an additive fragment of light `x` (one channel)\n * writes with the blending `(one, one − src color)`. The framebuffer then\n * holds `1 − e^(−SCREEN_GAIN Σx)`: the light is tone mapped as it adds up.\n */\nexport const screenTone = (x: number): number =>\n 1 - Math.exp(-SCREEN_GAIN * Math.max(0, x));\n\n/** What a blended fragment adds to the four targets */\nexport interface OitOutput {\n front: number[];\n accum: number[];\n extra: number[];\n behind: number[];\n}\n\nconst ZERO = [0, 0, 0, 0] as const;\n\n/**\n * Depth of the front layer of a pixel from its blended fragments (JS\n * mirror of the front pass): the nearest visible non-additive fragment\n */\nexport const oitFrontDepth = (\n fragments: readonly {\n color: readonly [number, number, number, number];\n depth: number;\n additive?: boolean;\n }[],\n): number => {\n let front = FRONT_CLEAR;\n for (const f of fragments) {\n if (f.additive || f.color[3] < FRONT_MIN_ALPHA) continue;\n front = Math.min(front, clamp01(f.depth));\n }\n return front;\n};\n\n/**\n * What a blended fragment adds to the targets (JS mirror of the shaders),\n * `front` the depth of the front layer of its pixel (`oitFrontDepth`)\n */\nexport const oitFragment = (\n premultiplied: readonly [number, number, number, number],\n depth: number,\n additive: boolean,\n front: number = FRONT_CLEAR,\n): OitOutput => {\n const [r, g, b, alpha] = premultiplied;\n const near = clamp01(depth) <= front + FRONT_EPSILON;\n const out: OitOutput = {\n front: [...ZERO],\n accum: [...ZERO],\n extra: [...ZERO],\n behind: [...ZERO],\n };\n if (additive) {\n if (near) out.extra = [r, g, b, 0];\n else out.behind = [r, g, b, 0];\n return out;\n }\n if (near) {\n out.front = [r, g, b, alpha];\n return out;\n }\n const a = Math.min(MAX_ALPHA, Math.max(0, alpha));\n const w = oitWeight(depth);\n out.accum = [r * w, g * w, b * w, a * w];\n out.extra = [0, 0, 0, -Math.log(1 - a)];\n return out;\n};\n\n/**\n * Composite of the targets: a premultiplied color blended over the opaque\n * scene (JS mirror of the shaders)\n */\nexport const oitComposite = (\n accum: readonly number[],\n extra: readonly number[],\n front: readonly number[] = ZERO,\n behind: readonly number[] = ZERO,\n): [number, number, number, number] => {\n // the rest: weighted average color, alpha from the revealage\n const tau = Math.max(0, extra[3] ?? 0);\n const restAlpha = 1 - Math.exp(-tau);\n const k = restAlpha / Math.max(accum[3] ?? 0, 1e-6);\n // the front layer (several fragments at its depth: normalized)\n const sum = Math.max(0, front[3] ?? 0);\n const scale = sum > 1 ? 1 / sum : 1;\n const fa = sum * scale;\n const alpha = fa + restAlpha * (1 - fa);\n const color = [0, 1, 2].map(\n (i) => (front[i] ?? 0) * scale + (accum[i] ?? 0) * k * (1 - fa),\n );\n // light behind the front layer, mixed through the rest: its mean\n // transmittance (1 − e^−τ) / τ\n const meanT = tau > 1e-4 ? restAlpha / tau : 1;\n const t = (1 - fa) * meanT;\n const back = toneMap([behind[0] ?? 0, behind[1] ?? 0, behind[2] ?? 0]);\n const backAlpha = clamp01(Math.max(back[0], back[1], back[2]));\n const under = color.map((c, i) => c + back[i]! * t);\n const underAlpha = Math.min(1, alpha + backAlpha * t);\n // light in front of every layer\n const light = toneMap([extra[0] ?? 0, extra[1] ?? 0, extra[2] ?? 0]);\n const lightAlpha = clamp01(Math.max(light[0], light[1], light[2]));\n return [\n light[0] + under[0]! * (1 - lightAlpha),\n light[1] + under[1]! * (1 - lightAlpha),\n light[2] + under[2]! * (1 - lightAlpha),\n lightAlpha + underAlpha * (1 - lightAlpha),\n ];\n};\n\nconst f = (v: number) => (Number.isInteger(v) ? v.toFixed(1) : String(v));\n\n// ------------------------------------------------------------------- WGSL\n\n/**\n * Transparency outputs of the points and meshes (`fsOit`, `fsFront`): the\n * front depth texture is bound in group 1 by the main pass only\n */\nexport const WGSL_OIT = /* wgsl */ `\n@group(1) @binding(0) var frontTex: texture_2d<f32>;\n\nstruct Oit {\n @location(0) front: vec4f,\n @location(1) accum: vec4f,\n @location(2) extra: vec4f,\n @location(3) behind: vec4f,\n};\n\nfn oit(c: vec4f, depth: f32, xy: vec4f) -> Oit {\n var o: Oit;\n o.front = vec4f(0.0);\n o.accum = vec4f(0.0);\n o.extra = vec4f(0.0);\n o.behind = vec4f(0.0);\n let z = clamp(depth, 0.0, 1.0);\n let near = z <= textureLoad(frontTex, vec2i(xy.xy), 0).r + ${f(FRONT_EPSILON)};\n if (u.light.w > 0.5) {\n if (near) { o.extra = vec4f(c.rgb, 0.0); } else { o.behind = vec4f(c.rgb, 0.0); }\n return o;\n }\n if (near) {\n o.front = c;\n return o;\n }\n let a = clamp(c.a, 0.0, ${f(MAX_ALPHA)});\n let w = max(${f(WEIGHT_MIN)}, ${f(WEIGHT_SCALE)} * exp2(-${f(WEIGHT_FALLOFF)} * z));\n o.accum = vec4f(c.rgb, a) * w;\n o.extra = vec4f(0.0, 0.0, 0.0, -log(1.0 - a));\n return o;\n}\n\nfn frontDepth(c: vec4f, depth: f32) -> vec4f {\n if (c.a < ${f(FRONT_MIN_ALPHA)}) { discard; }\n return vec4f(clamp(depth, 0.0, 1.0));\n}\n\nfn screenLight(c: vec4f) -> vec4f {\n if (u.light.w < 0.5) { return c; }\n let v = vec3f(1.0) - exp(-${f(SCREEN_GAIN)} * max(c.rgb, vec3f(0.0)));\n return vec4f(v, max(max(v.r, v.g), v.b));\n}\n`;\n\nconst WGSL_TONE = /* wgsl */ `\nfn shoulder(x: f32) -> f32 {\n if (x <= ${f(TONE_KNEE)}) { return x; }\n let t = (x - ${f(TONE_KNEE)}) / ${f(1 - TONE_KNEE)};\n return ${f(TONE_KNEE)} + ${f(1 - TONE_KNEE)} * t / (1.0 + t);\n}\n\nfn toneMap(rgb: vec3f) -> vec3f {\n let m = max(max(rgb.r, rgb.g), rgb.b);\n if (m <= 1e-6) { return rgb; }\n let peak = shoulder(m);\n let scaled = rgb * (peak / m);\n return mix(scaled, vec3f(peak), smoothstep(${f(WHITE_FROM)}, ${f(WHITE_TO)}, m) * ${f(WHITE_AMOUNT)});\n}\n`;\n\n/** Full-screen composite of the four targets over the opaque scene */\nexport const WGSL_COMPOSITE = /* wgsl */ `${WGSL_TONE}\n@group(0) @binding(0) var frontTex: texture_2d<f32>;\n@group(0) @binding(1) var accumTex: texture_2d<f32>;\n@group(0) @binding(2) var extraTex: texture_2d<f32>;\n@group(0) @binding(3) var behindTex: texture_2d<f32>;\n\n\n@vertex\nfn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {\n let p = vec2f(f32((vi << 1u) & 2u), f32(vi & 2u)) * 2.0 - 1.0;\n return vec4f(p, 0.0, 1.0);\n}\n\n@fragment\nfn fs(@builtin(position) position: vec4f) -> @location(0) vec4f {\n let xy = vec2i(position.xy);\n let front = textureLoad(frontTex, xy, 0);\n let accum = textureLoad(accumTex, xy, 0);\n let extra = textureLoad(extraTex, xy, 0);\n let behind = textureLoad(behindTex, xy, 0);\n let tau = max(extra.a, 0.0);\n let restAlpha = 1.0 - exp(-tau);\n let rest = accum.rgb * (restAlpha / max(accum.a, 1e-6));\n let sum = max(front.a, 0.0);\n let scale = select(1.0, 1.0 / max(sum, 1e-6), sum > 1.0);\n let fa = sum * scale;\n let alpha = fa + restAlpha * (1.0 - fa);\n let color = front.rgb * scale + rest * (1.0 - fa);\n let meanT = select(1.0, restAlpha / max(tau, 1e-6), tau > 1e-4);\n let t = (1.0 - fa) * meanT;\n let back = toneMap(behind.rgb);\n let backAlpha = clamp(max(max(back.r, back.g), back.b), 0.0, 1.0);\n let under = color + back * t;\n let underAlpha = min(1.0, alpha + backAlpha * t);\n let light = toneMap(extra.rgb);\n let lightAlpha = clamp(max(max(light.r, light.g), light.b), 0.0, 1.0);\n let a = lightAlpha + underAlpha * (1.0 - lightAlpha);\n let rgb = light + under * (1.0 - lightAlpha);\n if (a <= 0.0) { discard; }\n return vec4f(rgb, a);\n}\n`;\n\n// ------------------------------------------------------------------- GLSL\n\n/**\n * Outputs of the OIT variants (GLSL ES 3.0): `EMIT(color, depth)`; the\n * front depth texture is `u_front`\n */\nexport const GLSL_OIT = `\nuniform highp sampler2D u_front;\nlayout(location = 0) out vec4 o_front;\nlayout(location = 1) out vec4 o_accum;\nlayout(location = 2) out vec4 o_extra;\nlayout(location = 3) out vec4 o_behind;\nvoid emitOit(vec4 c, float depth) {\n o_front = vec4(0.0);\n o_accum = vec4(0.0);\n o_extra = vec4(0.0);\n o_behind = vec4(0.0);\n float z = clamp(depth, 0.0, 1.0);\n bool near = z <= texelFetch(u_front, ivec2(gl_FragCoord.xy), 0).r + ${f(FRONT_EPSILON)};\n if (u_light.w > 0.5) {\n if (near) o_extra = vec4(c.rgb, 0.0); else o_behind = vec4(c.rgb, 0.0);\n return;\n }\n if (near) {\n o_front = c;\n return;\n }\n float a = clamp(c.a, 0.0, ${f(MAX_ALPHA)});\n float w = max(${f(WEIGHT_MIN)}, ${f(WEIGHT_SCALE)} * exp2(-${f(WEIGHT_FALLOFF)} * z));\n o_accum = vec4(c.rgb, a) * w;\n o_extra = vec4(0.0, 0.0, 0.0, -log(1.0 - a));\n}\n#define EMIT(c, d) emitOit(c, d)\n`;\n\n/** Output of the front pass (GLSL ES 3.0): the depth of visible fragments */\nexport const GLSL_FRONT = `\nout vec4 fragColor;\nvoid emitFront(vec4 c, float depth) {\n if (c.a < ${f(FRONT_MIN_ALPHA)}) discard;\n fragColor = vec4(clamp(depth, 0.0, 1.0));\n}\n#define EMIT(c, d) emitFront(c, d)\n`;\n\n/**\n * Plain output (`output` the output variable): additive light screen\n * blended, tone mapped as it adds up (`screenTone`)\n */\nexport const glslScreen = (output: string) => `\nvec4 screenLight(vec4 c) {\n if (u_light.w < 0.5) return c;\n vec3 v = vec3(1.0) - exp(-${f(SCREEN_GAIN)} * max(c.rgb, vec3(0.0)));\n return vec4(v, max(max(v.r, v.g), v.b));\n}\n#define EMIT(c, d) ${output} = screenLight(c)\n`;\n\nconst GLSL_TONE = `\nfloat shoulder(float x) {\n if (x <= ${f(TONE_KNEE)}) return x;\n float t = (x - ${f(TONE_KNEE)}) / ${f(1 - TONE_KNEE)};\n return ${f(TONE_KNEE)} + ${f(1 - TONE_KNEE)} * t / (1.0 + t);\n}\nvec3 toneMap(vec3 rgb) {\n float m = max(max(rgb.r, rgb.g), rgb.b);\n if (m <= 1e-6) return rgb;\n float peak = shoulder(m);\n vec3 scaled = rgb * (peak / m);\n return mix(scaled, vec3(peak), smoothstep(${f(WHITE_FROM)}, ${f(WHITE_TO)}, m) * ${f(WHITE_AMOUNT)});\n}\n`;\n\n/** Composite program of WebGL2 (full-screen triangle from `gl_VertexID`) */\nexport const GLSL_COMPOSITE = {\n vertex: `#version 300 es\nvoid main() {\n vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)) * 2.0 - 1.0;\n gl_Position = vec4(p, 0.0, 1.0);\n}\n`,\n fragment: `#version 300 es\nprecision highp float;\nuniform sampler2D u_frontColor;\nuniform sampler2D u_accum;\nuniform sampler2D u_extra;\nuniform sampler2D u_behind;\nout vec4 fragColor;\n${GLSL_TONE}\nvoid main() {\n ivec2 xy = ivec2(gl_FragCoord.xy);\n vec4 front = texelFetch(u_frontColor, xy, 0);\n vec4 accum = texelFetch(u_accum, xy, 0);\n vec4 extra = texelFetch(u_extra, xy, 0);\n vec4 behind = texelFetch(u_behind, xy, 0);\n float tau = max(extra.a, 0.0);\n float restAlpha = 1.0 - exp(-tau);\n vec3 rest = accum.rgb * (restAlpha / max(accum.a, 1e-6));\n float sum = max(front.a, 0.0);\n float scale = sum > 1.0 ? 1.0 / sum : 1.0;\n float fa = sum * scale;\n float alpha = fa + restAlpha * (1.0 - fa);\n vec3 color = front.rgb * scale + rest * (1.0 - fa);\n float meanT = tau > 1e-4 ? restAlpha / tau : 1.0;\n float t = (1.0 - fa) * meanT;\n vec3 back = toneMap(behind.rgb);\n float backAlpha = clamp(max(max(back.r, back.g), back.b), 0.0, 1.0);\n vec3 under = color + back * t;\n float underAlpha = min(1.0, alpha + backAlpha * t);\n vec3 light = toneMap(extra.rgb);\n float lightAlpha = clamp(max(max(light.r, light.g), light.b), 0.0, 1.0);\n float a = lightAlpha + underAlpha * (1.0 - lightAlpha);\n vec3 rgb = light + under * (1.0 - lightAlpha);\n if (a <= 0.0) discard;\n fragColor = vec4(rgb, a);\n}\n`,\n};\n"],"mappings":";AAyCA,IAAMA,IAAe,IACfC,IAAiB,IACjBC,IAAa,KAEbC,IAAY,MAELC,IAAY,IAEnBC,IAAa,GACbC,IAAW,GACXC,IAAe,KAKRC,IAAgB,MAIhBC,IAAkB,KAKlBC,IAAc,KA0KrBC,KAAK,MAAe,OAAO,UAAUC,CAAC,IAAIA,EAAE,QAAQ,CAAC,IAAI,OAAOA,CAAC,GAQ1DC,IAAsB;;;;;;;;;;;;;;;;;+DAiB4BF,EAAEH,CAAa,EAAE;;;;;;;;;4BASpDG,EAAER,CAAS,EAAE;gBACzBQ,EAAET,CAAU,EAAE,IAAIS,EAAEX,CAAY,EAAE,WAAWW,EAAEV,CAAc,EAAE;;;;;;;cAOjEU,EAAEF,CAAe,EAAE;;;;;;8BAMHE,EAAED,CAAW,EAAE;;;GAsBhCI,IAA4B,GAAG;;aAf/BH,EAAEP,CAAS,EAAE;iBACTO,EAAEP,CAAS,EAAE,MAAMO,EAAE,EAAa,EAAE;WAC1CA,EAAEP,CAAS,EAAE,KAAKO,EAAE,EAAa,EAAE;;;;;;;;+CAQCA,EAAEN,CAAU,EAAE,IAAIM,EAAEL,CAAQ,EAAE,SAASK,EAAEJ,CAAY,EAAE;;EAKhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDzCQ,IAAW;;;;;;;;;;;;wEAYgDJ,EAAEH,CAAa,EAAE;;;;;;;;;8BAS3DG,EAAER,CAAS,EAAE;kBACzBQ,EAAET,CAAU,EAAE,IAAIS,EAAEX,CAAY,EAAE,WAAWW,EAAEV,CAAc,EAAE;;;;;GAQpEe,IAAa;;;cAGZL,EAAEF,CAAe,EAAE;;;;GAUpBQ,KAAc,MAAmB;;;8BAGhBN,EAAED,CAAW,EAAE;;;qBAGxBQ,EAAO;GAmBfC,IAAiB;CAC5B,QAAQ;CAMR,UAAU;;;;;;;EAOV;;aA5BWR,EAAEP,CAAS,EAAE;mBACPO,EAAEP,CAAS,EAAE,MAAMO,EAAE,EAAa,EAAE;WAC5CA,EAAEP,CAAS,EAAE,KAAKO,EAAE,EAAa,EAAE;;;;;;;8CAOAA,EAAEN,CAAU,EAAE,IAAIM,EAAEL,CAAQ,EAAE,SAASK,EAAEJ,CAAY,EAAE;;EAmBzF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"render-types.cjs","names":["writesDepth","item","drawRank","drawOrder","items","a","b"],"sources":["../../../../../charts/src/engine/three-d/render-types.ts"],"sourcesContent":["// Contract between the 3D chart and its data-layer backends.\n\nimport type { Rgba } from \"../color\";\nimport type { ChartRendererKind } from \"../types\";\nimport type { Vec3 } from \"./math\";\n\n/** Bytes per point instance: x, y, z f32, diameter (CSS px) f32, color unorm8x4 */\nexport const POINT_STRIDE = 20;\n/** Bytes per mesh vertex: position f32x3, normal f32x3, color unorm8x4 */\nexport const VERTEX_STRIDE = 28;\n/** Bytes per line segment instance: a f32x3, b f32x3, color unorm8x4 */\nexport const SEGMENT_STRIDE = 28;\n\ninterface ItemBase {\n /** Identifies the GPU resources of the item */\n key: string;\n /** Changes when the buffers must be uploaded again */\n version: number;\n /** 0-1; below 1 the item is drawn without writing depth */\n opacity: number;\n}\n\n/** Shapes of the points (`PointsItem.style`) */\nexport const POINT_SPHERE = 0;\nexport const POINT_DISC = 1;\nexport const POINT_GLOW = 2;\n\n/** Instanced billboards: shaded spheres, flat discs or glowing discs */\nexport interface PointsItem extends ItemBase {\n kind: \"points\";\n data: ArrayBuffer;\n count: number;\n /** Scales the dots with the distance (perspective) */\n perspective: boolean;\n /** `POINT_SPHERE`, `POINT_DISC` or `POINT_GLOW` (sphere when omitted) */\n style?: number;\n /** Adds the light of the dots (no depth writes, order independent) */\n additive?: boolean;\n /**\n * The data was thinned to 1 point out of `thinned` (CPU backend): the\n * drawn dots get denser to keep the look of the cloud\n */\n thinned?: number;\n}\n\n/** Lit triangles (surfaces, bars) */\nexport interface MeshItem extends ItemBase {\n kind: \"mesh\";\n vertices: ArrayBuffer;\n vertexCount: number;\n indices: Uint32Array;\n /**\n * Changes when only the order of the triangles changed (transparent meshes\n * sorted back to front): the index buffer is rewritten, same length\n */\n order?: number;\n /**\n * Blended without writing depth whatever its `opacity` (merged transparent\n * meshes: their opacity is baked into the vertex colors)\n */\n transparent?: boolean;\n}\n\n/** Screen-space wide segments (wireframes, contours, polylines) */\nexport interface LinesItem extends ItemBase {\n kind: \"lines\";\n data: ArrayBuffer;\n count: number;\n /** CSS pixels */\n width: number;\n /** Pulls the lines towards the camera (wireframes over their surface) */\n bias: number;\n}\n\nexport type Item3D = PointsItem | MeshItem | LinesItem;\n\nexport interface Frame3D {\n /** World -> clip (OpenGL convention) */\n viewProj: Float32Array;\n eye: Vec3;\n /** Unit vector towards the light, in world space */\n light: Vec3;\n orthographic: boolean;\n /** Distance of the camera to the box center (reference of the point sizes) */\n distance: number;\n /** Radius of the sphere around the box (depth cue range) */\n radius: number;\n /** Drawing buffer size in device pixels */\n width: number;\n height: number;\n dpr: number;\n /** ambient, diffuse, specular, shininess */\n lighting: readonly [number, number, number, number];\n /** Background color: far points fade towards it */\n fog: Rgba;\n /** Scale of the light of the additive points (tone mapped when possible) */\n exposure: number;\n}\n\nexport interface Renderer3D {\n readonly kind: ChartRendererKind;\n readonly canvas: HTMLCanvasElement;\n /**\n * Blends the transparent items in any order (weighted blended\n * order-independent transparency): they need no depth sorting\n */\n readonly orderIndependent?: boolean;\n /** Drawing buffer size in device pixels */\n resize(width: number, height: number): void;\n render(items: readonly Item3D[], frame: Frame3D): void;\n destroy(): void;\n}\n\n/** Whether an item writes the depth buffer (opaque items) */\nexport const writesDepth = (item: Item3D): boolean => {\n if (item.opacity < 1) return false;\n if (item.kind === \"mesh\") return !item.transparent;\n if (item.kind === \"points\")\n return !item.additive && item.style !== POINT_GLOW;\n return true;\n};\n\n/**\n * Draw passes of the items: 0 opaque meshes, 1 opaque points, 2 blended\n * items (transparent meshes and points: order independent, or sorted back to\n * front together), 3 lines (wireframes and contours stay visible over\n * transparent surfaces)\n */\nexport const drawRank = (item: Item3D): number =>\n item.kind === \"lines\"\n ? 3\n : !writesDepth(item)\n ? 2\n : item.kind === \"mesh\"\n ? 0\n : 1;\n\n/**\n * Order of the draws (`drawRank`). Stable: items of a rank keep their order\n * (the back-to-front order of the depth sorter for the blended items).\n */\nexport const drawOrder = (items: readonly Item3D[]): Item3D[] =>\n items\n .map((item, i) => ({ item, i, r: drawRank(item) }))\n .sort((a, b) => a.r - b.r || a.i - b.i)\n .map((e) => e.item);\n"],"mappings":"AAkHA,IAAaA,EAAe,GACtBC,EAAK,QAAU,EAAU,GACzBA,EAAK,OAAS,OAAe,CAACA,EAAK,YACnCA,EAAK,OAAS,UACT,CAACA,EAAK,UAAYA,EAAK,QAAA,EAUrBC,EAAY,GACvBD,EAAK,OAAS,QACV,EACCD,EAAYC,CAAI,EAEfA,EAAK,OAAS,OACZ,EACA,EAHF,EASKE,EAAa,GACxBC,EACG,KAAK,EAAM,KAAO,CAAE,OAAM,IAAG,EAAGF,EAASD,CAAI,CAAE,EAAE,CAAC,CAClD,MAAM,EAAG,IAAMI,EAAE,EAAIC,EAAE,GAAKD,EAAE,EAAIC,EAAE,CAAC,CAAC,CACtC,IAAK,GAAM,EAAE,IAAI"}
1
+ {"version":3,"file":"render-types.cjs","names":["writesDepth","item","drawRank","drawOrder","items","a","b"],"sources":["../../../../../charts/src/engine/three-d/render-types.ts"],"sourcesContent":["// Contract between the 3D chart and its data-layer backends.\n\nimport type { Rgba } from \"../color\";\nimport type { ChartRendererKind } from \"../types\";\nimport type { Vec3 } from \"./math\";\n\n/** Bytes per point instance: x, y, z f32, diameter (CSS px) f32, color unorm8x4 */\nexport const POINT_STRIDE = 20;\n/** Bytes per mesh vertex: position f32x3, normal f32x3, color unorm8x4 */\nexport const VERTEX_STRIDE = 28;\n/** Bytes per line segment instance: a f32x3, b f32x3, color unorm8x4 */\nexport const SEGMENT_STRIDE = 28;\n\ninterface ItemBase {\n /** Identifies the GPU resources of the item */\n key: string;\n /** Changes when the buffers must be uploaded again */\n version: number;\n /** 0-1; below 1 the item is drawn without writing depth */\n opacity: number;\n /**\n * Draws only a part of the item (GPU backends): `first` and `count` in\n * indices for meshes, in instances for points. Several parts of an item\n * interleave with other items (depth sorting without OIT).\n */\n range?: { first: number; count: number };\n}\n\n/** Shapes of the points (`PointsItem.style`) */\nexport const POINT_SPHERE = 0;\nexport const POINT_DISC = 1;\nexport const POINT_GLOW = 2;\n\n/** Instanced billboards: shaded spheres, flat discs or glowing discs */\nexport interface PointsItem extends ItemBase {\n kind: \"points\";\n data: ArrayBuffer;\n count: number;\n /** Scales the dots with the distance (perspective) */\n perspective: boolean;\n /** `POINT_SPHERE`, `POINT_DISC` or `POINT_GLOW` (sphere when omitted) */\n style?: number;\n /** Adds the light of the dots (no depth writes, order independent) */\n additive?: boolean;\n /**\n * The data was thinned to 1 point out of `thinned` (CPU backend): the\n * drawn dots get denser to keep the look of the cloud\n */\n thinned?: number;\n /**\n * Changes when only the order of the points changed (sorted back to\n * front): the vertex buffer is rewritten, same length\n */\n order?: number;\n}\n\n/** Lit triangles (surfaces, bars) */\nexport interface MeshItem extends ItemBase {\n kind: \"mesh\";\n vertices: ArrayBuffer;\n vertexCount: number;\n indices: Uint32Array;\n /**\n * Changes when only the order of the triangles changed (transparent meshes\n * sorted back to front): the index buffer is rewritten, same length\n */\n order?: number;\n /**\n * Blended without writing depth whatever its `opacity` (merged transparent\n * meshes: their opacity is baked into the vertex colors)\n */\n transparent?: boolean;\n}\n\n/** Screen-space wide segments (wireframes, contours, polylines) */\nexport interface LinesItem extends ItemBase {\n kind: \"lines\";\n data: ArrayBuffer;\n count: number;\n /** CSS pixels */\n width: number;\n /** Pulls the lines towards the camera (wireframes over their surface) */\n bias: number;\n}\n\nexport type Item3D = PointsItem | MeshItem | LinesItem;\n\nexport interface Frame3D {\n /** World -> clip (OpenGL convention) */\n viewProj: Float32Array;\n eye: Vec3;\n /** Unit vector towards the light, in world space */\n light: Vec3;\n orthographic: boolean;\n /** Distance of the camera to the box center (reference of the point sizes) */\n distance: number;\n /** Radius of the sphere around the box (depth cue range) */\n radius: number;\n /** Drawing buffer size in device pixels */\n width: number;\n height: number;\n dpr: number;\n /** ambient, diffuse, specular, shininess */\n lighting: readonly [number, number, number, number];\n /** Background color: far points fade towards it */\n fog: Rgba;\n /** Scale of the light of the additive points (tone mapped when possible) */\n exposure: number;\n /**\n * The camera or the data is moving (drag, wheel, animation, auto-rotation,\n * live data): the CPU backend may trade quality for speed\n */\n interacting?: boolean;\n}\n\nexport interface Renderer3D {\n readonly kind: ChartRendererKind;\n readonly canvas: HTMLCanvasElement;\n /**\n * Blends the transparent items in any order (weighted blended\n * order-independent transparency): they need no depth sorting\n */\n readonly orderIndependent?: boolean;\n /** Drawing buffer size in device pixels */\n resize(width: number, height: number): void;\n render(items: readonly Item3D[], frame: Frame3D): void;\n destroy(): void;\n}\n\n/** Whether an item writes the depth buffer (opaque items) */\nexport const writesDepth = (item: Item3D): boolean => {\n if (item.opacity < 1) return false;\n if (item.kind === \"mesh\") return !item.transparent;\n if (item.kind === \"points\")\n return !item.additive && item.style !== POINT_GLOW;\n return true;\n};\n\n/**\n * Draw passes of the items: 0 opaque meshes, 1 opaque points, 2 blended\n * items (transparent meshes and points: order independent, or sorted back to\n * front together), 3 lines (wireframes and contours stay visible over\n * transparent surfaces)\n */\nexport const drawRank = (item: Item3D): number =>\n item.kind === \"lines\"\n ? 3\n : !writesDepth(item)\n ? 2\n : item.kind === \"mesh\"\n ? 0\n : 1;\n\n/**\n * Order of the draws (`drawRank`). Stable: items of a rank keep their order\n * (the back-to-front order of the depth sorter for the blended items).\n */\nexport const drawOrder = (items: readonly Item3D[]): Item3D[] =>\n items\n .map((item, i) => ({ item, i, r: drawRank(item) }))\n .sort((a, b) => a.r - b.r || a.i - b.i)\n .map((e) => e.item);\n"],"mappings":"AAkIA,IAAaA,EAAe,GACtBC,EAAK,QAAU,EAAU,GACzBA,EAAK,OAAS,OAAe,CAACA,EAAK,YACnCA,EAAK,OAAS,UACT,CAACA,EAAK,UAAYA,EAAK,QAAA,EAUrBC,EAAY,GACvBD,EAAK,OAAS,QACV,EACCD,EAAYC,CAAI,EAEfA,EAAK,OAAS,OACZ,EACA,EAHF,EASKE,EAAa,GACxBC,EACG,KAAK,EAAM,KAAO,CAAE,OAAM,IAAG,EAAGF,EAASD,CAAI,CAAE,EAAE,CAAC,CAClD,MAAM,EAAG,IAAMI,EAAE,EAAIC,EAAE,GAAKD,EAAE,EAAIC,EAAE,CAAC,CAAC,CACtC,IAAK,GAAM,EAAE,IAAI"}
@@ -14,6 +14,15 @@ interface ItemBase {
14
14
  version: number;
15
15
  /** 0-1; below 1 the item is drawn without writing depth */
16
16
  opacity: number;
17
+ /**
18
+ * Draws only a part of the item (GPU backends): `first` and `count` in
19
+ * indices for meshes, in instances for points. Several parts of an item
20
+ * interleave with other items (depth sorting without OIT).
21
+ */
22
+ range?: {
23
+ first: number;
24
+ count: number;
25
+ };
17
26
  }
18
27
  /** Shapes of the points (`PointsItem.style`) */
19
28
  export declare const POINT_SPHERE = 0;
@@ -35,6 +44,11 @@ export interface PointsItem extends ItemBase {
35
44
  * drawn dots get denser to keep the look of the cloud
36
45
  */
37
46
  thinned?: number;
47
+ /**
48
+ * Changes when only the order of the points changed (sorted back to
49
+ * front): the vertex buffer is rewritten, same length
50
+ */
51
+ order?: number;
38
52
  }
39
53
  /** Lit triangles (surfaces, bars) */
40
54
  export interface MeshItem extends ItemBase {
@@ -85,6 +99,11 @@ export interface Frame3D {
85
99
  fog: Rgba;
86
100
  /** Scale of the light of the additive points (tone mapped when possible) */
87
101
  exposure: number;
102
+ /**
103
+ * The camera or the data is moving (drag, wheel, animation, auto-rotation,
104
+ * live data): the CPU backend may trade quality for speed
105
+ */
106
+ interacting?: boolean;
88
107
  }
89
108
  export interface Renderer3D {
90
109
  readonly kind: ChartRendererKind;
@@ -14,6 +14,15 @@ interface ItemBase {
14
14
  version: number;
15
15
  /** 0-1; below 1 the item is drawn without writing depth */
16
16
  opacity: number;
17
+ /**
18
+ * Draws only a part of the item (GPU backends): `first` and `count` in
19
+ * indices for meshes, in instances for points. Several parts of an item
20
+ * interleave with other items (depth sorting without OIT).
21
+ */
22
+ range?: {
23
+ first: number;
24
+ count: number;
25
+ };
17
26
  }
18
27
  /** Shapes of the points (`PointsItem.style`) */
19
28
  export declare const POINT_SPHERE = 0;
@@ -35,6 +44,11 @@ export interface PointsItem extends ItemBase {
35
44
  * drawn dots get denser to keep the look of the cloud
36
45
  */
37
46
  thinned?: number;
47
+ /**
48
+ * Changes when only the order of the points changed (sorted back to
49
+ * front): the vertex buffer is rewritten, same length
50
+ */
51
+ order?: number;
38
52
  }
39
53
  /** Lit triangles (surfaces, bars) */
40
54
  export interface MeshItem extends ItemBase {
@@ -85,6 +99,11 @@ export interface Frame3D {
85
99
  fog: Rgba;
86
100
  /** Scale of the light of the additive points (tone mapped when possible) */
87
101
  exposure: number;
102
+ /**
103
+ * The camera or the data is moving (drag, wheel, animation, auto-rotation,
104
+ * live data): the CPU backend may trade quality for speed
105
+ */
106
+ interacting?: boolean;
88
107
  }
89
108
  export interface Renderer3D {
90
109
  readonly kind: ChartRendererKind;
@@ -1 +1 @@
1
- {"version":3,"file":"render-types.js","names":["writesDepth","item","drawRank","drawOrder","items","a","b"],"sources":["../../../../../charts/src/engine/three-d/render-types.ts"],"sourcesContent":["// Contract between the 3D chart and its data-layer backends.\n\nimport type { Rgba } from \"../color\";\nimport type { ChartRendererKind } from \"../types\";\nimport type { Vec3 } from \"./math\";\n\n/** Bytes per point instance: x, y, z f32, diameter (CSS px) f32, color unorm8x4 */\nexport const POINT_STRIDE = 20;\n/** Bytes per mesh vertex: position f32x3, normal f32x3, color unorm8x4 */\nexport const VERTEX_STRIDE = 28;\n/** Bytes per line segment instance: a f32x3, b f32x3, color unorm8x4 */\nexport const SEGMENT_STRIDE = 28;\n\ninterface ItemBase {\n /** Identifies the GPU resources of the item */\n key: string;\n /** Changes when the buffers must be uploaded again */\n version: number;\n /** 0-1; below 1 the item is drawn without writing depth */\n opacity: number;\n}\n\n/** Shapes of the points (`PointsItem.style`) */\nexport const POINT_SPHERE = 0;\nexport const POINT_DISC = 1;\nexport const POINT_GLOW = 2;\n\n/** Instanced billboards: shaded spheres, flat discs or glowing discs */\nexport interface PointsItem extends ItemBase {\n kind: \"points\";\n data: ArrayBuffer;\n count: number;\n /** Scales the dots with the distance (perspective) */\n perspective: boolean;\n /** `POINT_SPHERE`, `POINT_DISC` or `POINT_GLOW` (sphere when omitted) */\n style?: number;\n /** Adds the light of the dots (no depth writes, order independent) */\n additive?: boolean;\n /**\n * The data was thinned to 1 point out of `thinned` (CPU backend): the\n * drawn dots get denser to keep the look of the cloud\n */\n thinned?: number;\n}\n\n/** Lit triangles (surfaces, bars) */\nexport interface MeshItem extends ItemBase {\n kind: \"mesh\";\n vertices: ArrayBuffer;\n vertexCount: number;\n indices: Uint32Array;\n /**\n * Changes when only the order of the triangles changed (transparent meshes\n * sorted back to front): the index buffer is rewritten, same length\n */\n order?: number;\n /**\n * Blended without writing depth whatever its `opacity` (merged transparent\n * meshes: their opacity is baked into the vertex colors)\n */\n transparent?: boolean;\n}\n\n/** Screen-space wide segments (wireframes, contours, polylines) */\nexport interface LinesItem extends ItemBase {\n kind: \"lines\";\n data: ArrayBuffer;\n count: number;\n /** CSS pixels */\n width: number;\n /** Pulls the lines towards the camera (wireframes over their surface) */\n bias: number;\n}\n\nexport type Item3D = PointsItem | MeshItem | LinesItem;\n\nexport interface Frame3D {\n /** World -> clip (OpenGL convention) */\n viewProj: Float32Array;\n eye: Vec3;\n /** Unit vector towards the light, in world space */\n light: Vec3;\n orthographic: boolean;\n /** Distance of the camera to the box center (reference of the point sizes) */\n distance: number;\n /** Radius of the sphere around the box (depth cue range) */\n radius: number;\n /** Drawing buffer size in device pixels */\n width: number;\n height: number;\n dpr: number;\n /** ambient, diffuse, specular, shininess */\n lighting: readonly [number, number, number, number];\n /** Background color: far points fade towards it */\n fog: Rgba;\n /** Scale of the light of the additive points (tone mapped when possible) */\n exposure: number;\n}\n\nexport interface Renderer3D {\n readonly kind: ChartRendererKind;\n readonly canvas: HTMLCanvasElement;\n /**\n * Blends the transparent items in any order (weighted blended\n * order-independent transparency): they need no depth sorting\n */\n readonly orderIndependent?: boolean;\n /** Drawing buffer size in device pixels */\n resize(width: number, height: number): void;\n render(items: readonly Item3D[], frame: Frame3D): void;\n destroy(): void;\n}\n\n/** Whether an item writes the depth buffer (opaque items) */\nexport const writesDepth = (item: Item3D): boolean => {\n if (item.opacity < 1) return false;\n if (item.kind === \"mesh\") return !item.transparent;\n if (item.kind === \"points\")\n return !item.additive && item.style !== POINT_GLOW;\n return true;\n};\n\n/**\n * Draw passes of the items: 0 opaque meshes, 1 opaque points, 2 blended\n * items (transparent meshes and points: order independent, or sorted back to\n * front together), 3 lines (wireframes and contours stay visible over\n * transparent surfaces)\n */\nexport const drawRank = (item: Item3D): number =>\n item.kind === \"lines\"\n ? 3\n : !writesDepth(item)\n ? 2\n : item.kind === \"mesh\"\n ? 0\n : 1;\n\n/**\n * Order of the draws (`drawRank`). Stable: items of a rank keep their order\n * (the back-to-front order of the depth sorter for the blended items).\n */\nexport const drawOrder = (items: readonly Item3D[]): Item3D[] =>\n items\n .map((item, i) => ({ item, i, r: drawRank(item) }))\n .sort((a, b) => a.r - b.r || a.i - b.i)\n .map((e) => e.item);\n"],"mappings":"AAkHA,IAAaA,KAAe,MACtBC,EAAK,UAAU,IAAU,KACzBA,EAAK,SAAS,SAAe,CAACA,EAAK,cACnCA,EAAK,SAAS,YACT,CAACA,EAAK,YAAYA,EAAK,UAAA,GAUrBC,KAAY,MACvBD,EAAK,SAAS,UACV,IACCD,EAAYC,CAAI,IAEfA,EAAK,SAAS,SACZ,IACA,IAHF,GASKE,KAAa,MACxBC,EACG,KAAK,GAAM,OAAO;CAAE;CAAM;CAAG,GAAGF,EAASD,CAAI;AAAE,EAAE,CAAC,CAClD,MAAM,GAAG,MAAMI,EAAE,IAAIC,EAAE,KAAKD,EAAE,IAAIC,EAAE,CAAC,CAAC,CACtC,KAAK,MAAM,EAAE,IAAI"}
1
+ {"version":3,"file":"render-types.js","names":["writesDepth","item","drawRank","drawOrder","items","a","b"],"sources":["../../../../../charts/src/engine/three-d/render-types.ts"],"sourcesContent":["// Contract between the 3D chart and its data-layer backends.\n\nimport type { Rgba } from \"../color\";\nimport type { ChartRendererKind } from \"../types\";\nimport type { Vec3 } from \"./math\";\n\n/** Bytes per point instance: x, y, z f32, diameter (CSS px) f32, color unorm8x4 */\nexport const POINT_STRIDE = 20;\n/** Bytes per mesh vertex: position f32x3, normal f32x3, color unorm8x4 */\nexport const VERTEX_STRIDE = 28;\n/** Bytes per line segment instance: a f32x3, b f32x3, color unorm8x4 */\nexport const SEGMENT_STRIDE = 28;\n\ninterface ItemBase {\n /** Identifies the GPU resources of the item */\n key: string;\n /** Changes when the buffers must be uploaded again */\n version: number;\n /** 0-1; below 1 the item is drawn without writing depth */\n opacity: number;\n /**\n * Draws only a part of the item (GPU backends): `first` and `count` in\n * indices for meshes, in instances for points. Several parts of an item\n * interleave with other items (depth sorting without OIT).\n */\n range?: { first: number; count: number };\n}\n\n/** Shapes of the points (`PointsItem.style`) */\nexport const POINT_SPHERE = 0;\nexport const POINT_DISC = 1;\nexport const POINT_GLOW = 2;\n\n/** Instanced billboards: shaded spheres, flat discs or glowing discs */\nexport interface PointsItem extends ItemBase {\n kind: \"points\";\n data: ArrayBuffer;\n count: number;\n /** Scales the dots with the distance (perspective) */\n perspective: boolean;\n /** `POINT_SPHERE`, `POINT_DISC` or `POINT_GLOW` (sphere when omitted) */\n style?: number;\n /** Adds the light of the dots (no depth writes, order independent) */\n additive?: boolean;\n /**\n * The data was thinned to 1 point out of `thinned` (CPU backend): the\n * drawn dots get denser to keep the look of the cloud\n */\n thinned?: number;\n /**\n * Changes when only the order of the points changed (sorted back to\n * front): the vertex buffer is rewritten, same length\n */\n order?: number;\n}\n\n/** Lit triangles (surfaces, bars) */\nexport interface MeshItem extends ItemBase {\n kind: \"mesh\";\n vertices: ArrayBuffer;\n vertexCount: number;\n indices: Uint32Array;\n /**\n * Changes when only the order of the triangles changed (transparent meshes\n * sorted back to front): the index buffer is rewritten, same length\n */\n order?: number;\n /**\n * Blended without writing depth whatever its `opacity` (merged transparent\n * meshes: their opacity is baked into the vertex colors)\n */\n transparent?: boolean;\n}\n\n/** Screen-space wide segments (wireframes, contours, polylines) */\nexport interface LinesItem extends ItemBase {\n kind: \"lines\";\n data: ArrayBuffer;\n count: number;\n /** CSS pixels */\n width: number;\n /** Pulls the lines towards the camera (wireframes over their surface) */\n bias: number;\n}\n\nexport type Item3D = PointsItem | MeshItem | LinesItem;\n\nexport interface Frame3D {\n /** World -> clip (OpenGL convention) */\n viewProj: Float32Array;\n eye: Vec3;\n /** Unit vector towards the light, in world space */\n light: Vec3;\n orthographic: boolean;\n /** Distance of the camera to the box center (reference of the point sizes) */\n distance: number;\n /** Radius of the sphere around the box (depth cue range) */\n radius: number;\n /** Drawing buffer size in device pixels */\n width: number;\n height: number;\n dpr: number;\n /** ambient, diffuse, specular, shininess */\n lighting: readonly [number, number, number, number];\n /** Background color: far points fade towards it */\n fog: Rgba;\n /** Scale of the light of the additive points (tone mapped when possible) */\n exposure: number;\n /**\n * The camera or the data is moving (drag, wheel, animation, auto-rotation,\n * live data): the CPU backend may trade quality for speed\n */\n interacting?: boolean;\n}\n\nexport interface Renderer3D {\n readonly kind: ChartRendererKind;\n readonly canvas: HTMLCanvasElement;\n /**\n * Blends the transparent items in any order (weighted blended\n * order-independent transparency): they need no depth sorting\n */\n readonly orderIndependent?: boolean;\n /** Drawing buffer size in device pixels */\n resize(width: number, height: number): void;\n render(items: readonly Item3D[], frame: Frame3D): void;\n destroy(): void;\n}\n\n/** Whether an item writes the depth buffer (opaque items) */\nexport const writesDepth = (item: Item3D): boolean => {\n if (item.opacity < 1) return false;\n if (item.kind === \"mesh\") return !item.transparent;\n if (item.kind === \"points\")\n return !item.additive && item.style !== POINT_GLOW;\n return true;\n};\n\n/**\n * Draw passes of the items: 0 opaque meshes, 1 opaque points, 2 blended\n * items (transparent meshes and points: order independent, or sorted back to\n * front together), 3 lines (wireframes and contours stay visible over\n * transparent surfaces)\n */\nexport const drawRank = (item: Item3D): number =>\n item.kind === \"lines\"\n ? 3\n : !writesDepth(item)\n ? 2\n : item.kind === \"mesh\"\n ? 0\n : 1;\n\n/**\n * Order of the draws (`drawRank`). Stable: items of a rank keep their order\n * (the back-to-front order of the depth sorter for the blended items).\n */\nexport const drawOrder = (items: readonly Item3D[]): Item3D[] =>\n items\n .map((item, i) => ({ item, i, r: drawRank(item) }))\n .sort((a, b) => a.r - b.r || a.i - b.i)\n .map((e) => e.item);\n"],"mappings":"AAkIA,IAAaA,KAAe,MACtBC,EAAK,UAAU,IAAU,KACzBA,EAAK,SAAS,SAAe,CAACA,EAAK,cACnCA,EAAK,SAAS,YACT,CAACA,EAAK,YAAYA,EAAK,UAAA,GAUrBC,KAAY,MACvBD,EAAK,SAAS,UACV,IACCD,EAAYC,CAAI,IAEfA,EAAK,SAAS,SACZ,IACA,IAHF,GASKE,KAAa,MACxBC,EACG,KAAK,GAAM,OAAO;CAAE;CAAM;CAAG,GAAGF,EAASD,CAAI;AAAE,EAAE,CAAC,CAClD,MAAM,GAAG,MAAMI,EAAE,IAAIC,EAAE,KAAKD,EAAE,IAAIC,EAAE,CAAC,CAAC,CACtC,KAAK,MAAM,EAAE,IAAI"}
@@ -90,12 +90,17 @@ fn shade(in: Out) -> vec4f {
90
90
 
91
91
  @fragment
92
92
  fn fs(in: Out) -> @location(0) vec4f {
93
- return shade(in);
93
+ return screenLight(shade(in));
94
94
  }
95
95
 
96
96
  @fragment
97
97
  fn fsOit(in: Out) -> Oit {
98
- return oit(shade(in), in.fog);
98
+ return oit(shade(in), in.fog, in.position);
99
+ }
100
+
101
+ @fragment
102
+ fn fsFront(in: Out) -> @location(0) vec4f {
103
+ return frontDepth(shade(in), in.fog);
99
104
  }
100
105
  `,o=`${i}
101
106
  struct Out {
@@ -139,7 +144,12 @@ fn fs(in: Out) -> @location(0) vec4f {
139
144
 
140
145
  @fragment
141
146
  fn fsOit(in: Out) -> Oit {
142
- return oit(shade(in), depthCue(in.world));
147
+ return oit(shade(in), depthCue(in.world), in.position);
148
+ }
149
+
150
+ @fragment
151
+ fn fsFront(in: Out) -> @location(0) vec4f {
152
+ return frontDepth(shade(in), depthCue(in.world));
143
153
  }
144
154
  `,s=`${i}
145
155
  struct Out {
@@ -331,7 +341,5 @@ precision highp float;
331
341
  #else
332
342
  precision mediump float;
333
343
  #endif
334
- `,g=(e,n,r,i=!1)=>r===2?{vertex:`#version 300 es\n#define attribute in\n#define varying out\n${c}${e}`,fragment:`#version 300 es\nprecision highp float;\n#define varying in\n${c}${i?t.GLSL_OIT:`out vec4 fragColor;
335
- #define EMIT(c, d) fragColor = (c)
336
- `}${n}`}:{vertex:`${c}${e}`,fragment:`${h}#define EMIT(c, d) gl_FragColor = (c)\n${c}${n}`},_=e=>({points:g(l,u,e),mesh:g(d,f,e),lines:g(p,m,e)}),v=()=>({points:g(l,u,2,!0),mesh:g(d,f,2,!0)}),y={points:[`a_corner`,`a_pos`,`a_size`,`a_color`],mesh:[`a_pos`,`a_normal`,`a_color`],lines:[`a_corner`,`a_a`,`a_b`,`a_color`]};exports.GLSL_ATTRIBUTES=y,exports.WGSL_LINES=s,exports.WGSL_MESH=o,exports.WGSL_POINTS=a,exports.glslOitPrograms=v,exports.glslPrograms=_,exports.itemUniforms=r,exports.writesDepth=e.writesDepth;
344
+ `,g=(e,n,r,i=`plain`)=>r===2?{vertex:`#version 300 es\n#define attribute in\n#define varying out\n${c}${e}`,fragment:`#version 300 es\nprecision highp float;\n#define varying in\n${c}${i===`oit`?t.GLSL_OIT:i===`front`?t.GLSL_FRONT:`out vec4 fragColor;\n${t.glslScreen(`fragColor`)}`}${n}`}:{vertex:`${c}${e}`,fragment:`${h}${c}${t.glslScreen(`gl_FragColor`)}${n}`},_=e=>({points:g(l,u,e),mesh:g(d,f,e),lines:g(p,m,e)}),v=()=>({points:g(l,u,2,`oit`),mesh:g(d,f,2,`oit`),front:{points:g(l,u,2,`front`),mesh:g(d,f,2,`front`)}}),y={points:[`a_corner`,`a_pos`,`a_size`,`a_color`],mesh:[`a_pos`,`a_normal`,`a_color`],lines:[`a_corner`,`a_a`,`a_b`,`a_color`]};exports.GLSL_ATTRIBUTES=y,exports.WGSL_LINES=s,exports.WGSL_MESH=o,exports.WGSL_POINTS=a,exports.glslOitPrograms=v,exports.glslPrograms=_,exports.itemUniforms=r,exports.writesDepth=e.writesDepth;
337
345
  //# sourceMappingURL=shaders.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"shaders.cjs","names":["WGSL_OIT","GLSL_OIT","require_render_types","require_oit","writeUniforms","out","viewProj","viewport","eye","light","material","params","fog","itemUniforms","second","item","frame","WGSL_UNIFORMS","WGSL_POINTS","WGSL_MESH","WGSL_LINES","GLSL_UNIFORMS","GLSL_POINTS_VS","GLSL_POINTS_FS","GLSL_MESH_VS","GLSL_MESH_FS","GLSL_LINES_VS","GLSL_LINES_FS","PRECISION","glsl","version","vs","oit","fs","glslPrograms","glslOitPrograms","GLSL_ATTRIBUTES"],"sources":["../../../../../charts/src/engine/three-d/shaders.ts"],"sourcesContent":["// Shaders of the 3D data layer: instanced point billboards (shaded spheres,\n// flat discs or glowing discs), lit triangles (two-sided Lambert +\n// Blinn-Phong) and screen-space wide line segments. WGSL for WebGPU, GLSL ES\n// 1.0 for WebGL (turned into GLSL ES 3.0 for WebGL2 by a macro prefix).\n// Outputs are premultiplied.\n//\n// Uniforms (WGSL struct, GLSL vec4 uniforms of the same names):\n// viewProj world -> clip (OpenGL depth convention)\n// viewport drawing buffer width, height (device px), dpr, orthographic\n// eye camera position, depth bias of the lines (NDC) | point style\n// light unit vector towards the light, additive points (0/1)\n// material ambient, diffuse, specular, shininess\n// params opacity, perspective sizes (0/1) | line width (CSS px), distance, radius\n// fog background color (far points fade towards it), exposure of the\n// additive light\n//\n// Points and meshes have a second fragment entry point writing the targets\n// of the order-independent transparency (`oit.ts`): `fsOit` in WGSL, the\n// `oit` variant of the GLSL programs (WebGL2).\n//\n// Point sizes are CSS pixels times the device pixel ratio (billboards: no\n// point size limit). Dots thinner than the smallest drawn radius keep their\n// energy: their alpha scales with the area they would cover.\n\nimport { GLSL_OIT, WGSL_OIT } from \"./oit\";\nimport { POINT_SPHERE, type Frame3D, type Item3D } from \"./render-types\";\n\nexport { writesDepth } from \"./render-types\";\n\n/** Bytes of the WGSL uniform struct */\nexport const UNIFORM_BYTES = 160;\n\n/** Writes the uniforms of an item in the WGSL struct layout */\nconst writeUniforms = (\n out: Float32Array,\n viewProj: Float32Array,\n viewport: readonly [number, number, number, number],\n eye: readonly [number, number, number, number],\n light: readonly [number, number, number, number],\n material: readonly [number, number, number, number],\n params: readonly [number, number, number, number],\n fog: readonly [number, number, number, number],\n): Float32Array => {\n out.set(viewProj, 0);\n out.set(viewport, 16);\n out.set(eye, 20);\n out.set(light, 24);\n out.set(material, 28);\n out.set(params, 32);\n out.set(fog, 36);\n return out;\n};\n\n/** Uniform values of an item (40 floats, the WGSL struct layout) */\nexport const itemUniforms = (\n item: Item3D,\n frame: Frame3D,\n out: Float32Array,\n): Float32Array => {\n const second =\n item.kind === \"points\"\n ? item.perspective\n ? 1\n : 0\n : item.kind === \"lines\"\n ? item.width\n : 0;\n return writeUniforms(\n out,\n frame.viewProj,\n [frame.width, frame.height, frame.dpr, frame.orthographic ? 1 : 0],\n [\n frame.eye[0],\n frame.eye[1],\n frame.eye[2],\n item.kind === \"lines\"\n ? item.bias\n : item.kind === \"points\"\n ? (item.style ?? POINT_SPHERE)\n : 0,\n ],\n [\n frame.light[0],\n frame.light[1],\n frame.light[2],\n item.kind === \"points\" && item.additive ? 1 : 0,\n ],\n frame.lighting,\n [item.opacity, second, frame.distance, frame.radius],\n [frame.fog[0], frame.fog[1], frame.fog[2], frame.exposure],\n );\n};\n\n// ------------------------------------------------------------------- WGSL\n\nconst WGSL_UNIFORMS = /* wgsl */ `\nstruct U {\n viewProj: mat4x4f,\n viewport: vec4f,\n eye: vec4f,\n light: vec4f,\n material: vec4f,\n params: vec4f,\n fog: vec4f,\n};\n@group(0) @binding(0) var<uniform> u: U;\n\nfn toWebGpuDepth(p: vec4f) -> vec4f {\n return vec4f(p.x, p.y, (p.z + p.w) * 0.5, p.w);\n}\n\nfn depthCue(world: vec3f) -> f32 {\n let near = u.params.z - u.params.w;\n return clamp((length(world - u.eye.xyz) - near) / (2.0 * u.params.w), 0.0, 1.0);\n}\n${WGSL_OIT}`;\n\nexport const WGSL_POINTS = /* wgsl */ `${WGSL_UNIFORMS}\nstruct Out {\n @builtin(position) position: vec4f,\n @location(0) offset: vec2f,\n @location(1) radius: f32,\n @location(2) color: vec4f,\n @location(3) fog: f32,\n @location(4) coverage: f32,\n};\n\n@vertex\nfn vs(\n @builtin(vertex_index) vi: u32,\n @location(0) pos: vec3f,\n @location(1) size: f32,\n @location(2) color: vec4f,\n) -> Out {\n var out: Out;\n let corner = vec2f(f32(vi & 1u), f32(vi >> 1u)) * 2.0 - 1.0;\n let clip = u.viewProj * vec4f(pos, 1.0);\n var factor = 1.0;\n if (u.params.y > 0.5 && u.viewport.w < 0.5) {\n factor = clamp(u.params.z / max(clip.w, 1e-6), 0.5, 2.0);\n }\n let glow = u.eye.w > 1.5;\n let raw = max(size, 0.0) * u.viewport.z * factor * 0.5;\n let radius = max(raw, select(0.5, 0.75, glow));\n let reach = select(radius + 1.0, radius * 2.2 + 1.0, glow);\n let shift = corner * reach * 2.0 / u.viewport.xy * clip.w;\n out.position = toWebGpuDepth(vec4f(clip.xy + shift, clip.z, clip.w));\n out.offset = corner * reach;\n out.radius = radius;\n out.color = color;\n out.fog = depthCue(pos);\n out.coverage = min(1.0, raw * raw / (radius * radius));\n return out;\n}\n\nfn shade(in: Out) -> vec4f {\n let d = length(in.offset);\n let r = in.radius;\n var alpha = clamp(r + 0.5 - d, 0.0, 1.0);\n var rgb = in.color.rgb;\n if (u.eye.w > 1.5) {\n // glow: an anti-aliased core and a gaussian halo\n let core = clamp(r * 0.6 + 0.5 - d, 0.0, 1.0);\n let q = d / r;\n alpha = core + (1.0 - core) * exp(-1.2 * q * q);\n rgb = rgb + (vec3f(1.0) - rgb) * (0.2 * core);\n } else if (u.eye.w < 0.5) {\n let q = min(d / r, 1.0);\n let n = vec3f(in.offset.x / r, in.offset.y / r, sqrt(max(0.0, 1.0 - q * q)));\n let l = normalize(vec3f(-0.45, 0.55, 0.7));\n let lambert = max(dot(n, l), 0.0);\n let shaded = rgb * (0.62 + 0.45 * lambert) + vec3f(pow(lambert, 18.0) * 0.28);\n rgb = mix(rgb, shaded, smoothstep(1.5, 3.5, r));\n }\n if (alpha <= 0.004) { discard; }\n let fog = in.fog * 0.45;\n if (u.light.w > 0.5) {\n // additive light fades with the distance\n alpha = alpha * (1.0 - fog);\n rgb = rgb * u.fog.w;\n } else {\n rgb = mix(rgb, u.fog.rgb, fog);\n }\n alpha = alpha * in.coverage * in.color.a * u.params.x;\n return vec4f(rgb * alpha, alpha);\n}\n\n@fragment\nfn fs(in: Out) -> @location(0) vec4f {\n return shade(in);\n}\n\n@fragment\nfn fsOit(in: Out) -> Oit {\n return oit(shade(in), in.fog);\n}\n`;\n\nexport const WGSL_MESH = /* wgsl */ `${WGSL_UNIFORMS}\nstruct Out {\n @builtin(position) position: vec4f,\n @location(0) world: vec3f,\n @location(1) normal: vec3f,\n @location(2) color: vec4f,\n};\n\n@vertex\nfn vs(\n @location(0) pos: vec3f,\n @location(1) normal: vec3f,\n @location(2) color: vec4f,\n) -> Out {\n var out: Out;\n out.position = toWebGpuDepth(u.viewProj * vec4f(pos, 1.0));\n out.world = pos;\n out.normal = normal;\n out.color = color;\n return out;\n}\n\nfn shade(in: Out) -> vec4f {\n var n = normalize(in.normal);\n let v = normalize(u.eye.xyz - in.world);\n if (dot(n, v) < 0.0) { n = -n; }\n let l = u.light.xyz;\n let diffuse = max(dot(n, l), 0.0);\n let h = normalize(l + v);\n let specular = pow(max(dot(n, h), 0.0), u.material.w) * u.material.z;\n let rgb = in.color.rgb * (u.material.x + u.material.y * diffuse) + vec3f(specular);\n let alpha = in.color.a * u.params.x;\n return vec4f(rgb * alpha, alpha);\n}\n\n@fragment\nfn fs(in: Out) -> @location(0) vec4f {\n return shade(in);\n}\n\n@fragment\nfn fsOit(in: Out) -> Oit {\n return oit(shade(in), depthCue(in.world));\n}\n`;\n\nexport const WGSL_LINES = /* wgsl */ `${WGSL_UNIFORMS}\nstruct Out {\n @builtin(position) position: vec4f,\n @location(0) across: f32,\n @location(1) color: vec4f,\n};\n\n@vertex\nfn vs(\n @builtin(vertex_index) vi: u32,\n @location(0) a: vec3f,\n @location(1) b: vec3f,\n @location(2) color: vec4f,\n) -> Out {\n var out: Out;\n let corner = vec2f(f32(vi & 1u), f32(vi >> 1u));\n let ca = u.viewProj * vec4f(a, 1.0);\n let cb = u.viewProj * vec4f(b, 1.0);\n let halfSize = u.viewport.xy * 0.5;\n let sa = ca.xy / ca.w * halfSize;\n let sb = cb.xy / cb.w * halfSize;\n var dir = sb - sa;\n let len = length(dir);\n dir = select(vec2f(1.0, 0.0), dir / len, len > 1e-6);\n let normal = vec2f(-dir.y, dir.x);\n let w = max(0.5, u.params.y * u.viewport.z * 0.5) + 0.5;\n let clip = select(ca, cb, corner.x > 0.5);\n let side = corner.y * 2.0 - 1.0;\n let pixel = select(sa, sb, corner.x > 0.5) + dir * (corner.x * 2.0 - 1.0) * w + normal * side * w;\n let z = clip.z - u.eye.w * clip.w;\n out.position = toWebGpuDepth(vec4f(pixel / halfSize * clip.w, z, clip.w));\n out.across = side * w;\n out.color = color;\n return out;\n}\n\n@fragment\nfn fs(in: Out) -> @location(0) vec4f {\n let core = u.params.y * u.viewport.z * 0.5;\n let coverage = clamp(max(core, 0.5) + 0.5 - abs(in.across), 0.0, 1.0) * min(1.0, core * 2.0);\n let alpha = coverage * in.color.a * u.params.x;\n if (alpha <= 0.0) { discard; }\n return vec4f(in.color.rgb * alpha, alpha);\n}\n`;\n\n// ------------------------------------------------------------------- GLSL\n\nconst GLSL_UNIFORMS = `\nuniform mat4 u_viewProj;\nuniform vec4 u_viewport;\nuniform vec4 u_eye;\nuniform vec4 u_light;\nuniform vec4 u_material;\nuniform vec4 u_params;\nuniform vec4 u_fog;\n`;\n\nconst GLSL_POINTS_VS = `\nattribute vec2 a_corner;\nattribute vec3 a_pos;\nattribute float a_size;\nattribute vec4 a_color;\nvarying vec2 v_offset;\nvarying float v_radius;\nvarying vec4 v_color;\nvarying float v_fog;\nvarying float v_coverage;\nvoid main() {\n vec2 corner = a_corner * 2.0 - 1.0;\n vec4 clip = u_viewProj * vec4(a_pos, 1.0);\n float factor = 1.0;\n if (u_params.y > 0.5 && u_viewport.w < 0.5) {\n factor = clamp(u_params.z / max(clip.w, 1e-6), 0.5, 2.0);\n }\n bool glow = u_eye.w > 1.5;\n float raw = max(a_size, 0.0) * u_viewport.z * factor * 0.5;\n float radius = max(raw, glow ? 0.75 : 0.5);\n float reach = glow ? radius * 2.2 + 1.0 : radius + 1.0;\n vec2 shift = corner * reach * 2.0 / u_viewport.xy * clip.w;\n gl_Position = vec4(clip.xy + shift, clip.z, clip.w);\n v_offset = corner * reach;\n v_radius = radius;\n v_color = a_color;\n float near = u_params.z - u_params.w;\n v_fog = clamp((length(a_pos - u_eye.xyz) - near) / (2.0 * u_params.w), 0.0, 1.0);\n v_coverage = min(1.0, raw * raw / (radius * radius));\n}\n`;\n\nconst GLSL_POINTS_FS = `\nvarying vec2 v_offset;\nvarying float v_radius;\nvarying vec4 v_color;\nvarying float v_fog;\nvarying float v_coverage;\nvoid main() {\n float d = length(v_offset);\n float r = v_radius;\n float alpha = clamp(r + 0.5 - d, 0.0, 1.0);\n vec3 rgb = v_color.rgb;\n if (u_eye.w > 1.5) {\n float core = clamp(r * 0.6 + 0.5 - d, 0.0, 1.0);\n float q = d / r;\n alpha = core + (1.0 - core) * exp(-1.2 * q * q);\n rgb = rgb + (vec3(1.0) - rgb) * (0.2 * core);\n } else if (u_eye.w < 0.5) {\n float q = min(d / r, 1.0);\n vec3 n = vec3(v_offset / r, sqrt(max(0.0, 1.0 - q * q)));\n vec3 l = normalize(vec3(-0.45, 0.55, 0.7));\n float lambert = max(dot(n, l), 0.0);\n vec3 shaded = rgb * (0.62 + 0.45 * lambert) + vec3(pow(lambert, 18.0) * 0.28);\n rgb = mix(rgb, shaded, smoothstep(1.5, 3.5, r));\n }\n if (alpha <= 0.004) discard;\n float fog = v_fog * 0.45;\n if (u_light.w > 0.5) {\n alpha = alpha * (1.0 - fog);\n rgb = rgb * u_fog.w;\n } else {\n rgb = mix(rgb, u_fog.rgb, fog);\n }\n alpha = alpha * v_coverage * v_color.a * u_params.x;\n EMIT(vec4(rgb * alpha, alpha), v_fog);\n}\n`;\n\nconst GLSL_MESH_VS = `\nattribute vec3 a_pos;\nattribute vec3 a_normal;\nattribute vec4 a_color;\nvarying vec3 v_world;\nvarying vec3 v_normal;\nvarying vec4 v_color;\nvoid main() {\n gl_Position = u_viewProj * vec4(a_pos, 1.0);\n v_world = a_pos;\n v_normal = a_normal;\n v_color = a_color;\n}\n`;\n\nconst GLSL_MESH_FS = `\nvarying vec3 v_world;\nvarying vec3 v_normal;\nvarying vec4 v_color;\nvoid main() {\n vec3 n = normalize(v_normal);\n vec3 v = normalize(u_eye.xyz - v_world);\n if (dot(n, v) < 0.0) n = -n;\n vec3 l = u_light.xyz;\n float diffuse = max(dot(n, l), 0.0);\n vec3 h = normalize(l + v);\n float specular = pow(max(dot(n, h), 0.0), u_material.w) * u_material.z;\n vec3 rgb = v_color.rgb * (u_material.x + u_material.y * diffuse) + vec3(specular);\n float alpha = v_color.a * u_params.x;\n float near = u_params.z - u_params.w;\n float depth = (length(v_world - u_eye.xyz) - near) / (2.0 * u_params.w);\n EMIT(vec4(rgb * alpha, alpha), depth);\n}\n`;\n\nconst GLSL_LINES_VS = `\nattribute vec2 a_corner;\nattribute vec3 a_a;\nattribute vec3 a_b;\nattribute vec4 a_color;\nvarying float v_across;\nvarying vec4 v_color;\nvoid main() {\n vec4 ca = u_viewProj * vec4(a_a, 1.0);\n vec4 cb = u_viewProj * vec4(a_b, 1.0);\n vec2 halfSize = u_viewport.xy * 0.5;\n vec2 sa = ca.xy / ca.w * halfSize;\n vec2 sb = cb.xy / cb.w * halfSize;\n vec2 dir = sb - sa;\n float len = length(dir);\n dir = len > 1e-6 ? dir / len : vec2(1.0, 0.0);\n vec2 normal = vec2(-dir.y, dir.x);\n float w = max(0.5, u_params.y * u_viewport.z * 0.5) + 0.5;\n bool atEnd = a_corner.x > 0.5;\n vec4 clip = atEnd ? cb : ca;\n float side = a_corner.y * 2.0 - 1.0;\n vec2 pixel = (atEnd ? sb : sa) + dir * (a_corner.x * 2.0 - 1.0) * w + normal * side * w;\n gl_Position = vec4(pixel / halfSize * clip.w, clip.z - u_eye.w * clip.w, clip.w);\n v_across = side * w;\n v_color = a_color;\n}\n`;\n\nconst GLSL_LINES_FS = `\nvarying float v_across;\nvarying vec4 v_color;\nvoid main() {\n float core = u_params.y * u_viewport.z * 0.5;\n float coverage = clamp(max(core, 0.5) + 0.5 - abs(v_across), 0.0, 1.0) * min(1.0, core * 2.0);\n float alpha = coverage * v_color.a * u_params.x;\n if (alpha <= 0.0) discard;\n EMIT(vec4(v_color.rgb * alpha, alpha), 0.0);\n}\n`;\n\nconst PRECISION = `\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n`;\n\nexport interface GlslSource {\n vertex: string;\n fragment: string;\n}\n\nconst glsl = (\n vs: string,\n fs: string,\n version: 1 | 2,\n oit = false,\n): GlslSource =>\n version === 2\n ? {\n vertex: `#version 300 es\\n#define attribute in\\n#define varying out\\n${GLSL_UNIFORMS}${vs}`,\n fragment: `#version 300 es\\nprecision highp float;\\n#define varying in\\n${GLSL_UNIFORMS}${\n oit\n ? GLSL_OIT\n : \"out vec4 fragColor;\\n#define EMIT(c, d) fragColor = (c)\\n\"\n }${fs}`,\n }\n : {\n vertex: `${GLSL_UNIFORMS}${vs}`,\n fragment: `${PRECISION}#define EMIT(c, d) gl_FragColor = (c)\\n${GLSL_UNIFORMS}${fs}`,\n };\n\n/** GLSL programs of the three primitives for WebGL (1) or WebGL2 */\nexport const glslPrograms = (version: 1 | 2) => ({\n points: glsl(GLSL_POINTS_VS, GLSL_POINTS_FS, version),\n mesh: glsl(GLSL_MESH_VS, GLSL_MESH_FS, version),\n lines: glsl(GLSL_LINES_VS, GLSL_LINES_FS, version),\n});\n\n/**\n * Variants of the points and meshes writing the targets of the\n * order-independent transparency (WebGL2)\n */\nexport const glslOitPrograms = () => ({\n points: glsl(GLSL_POINTS_VS, GLSL_POINTS_FS, 2, true),\n mesh: glsl(GLSL_MESH_VS, GLSL_MESH_FS, 2, true),\n});\n\n/** Attribute names of every program, bound to locations 0, 1, ... */\nexport const GLSL_ATTRIBUTES = {\n points: [\"a_corner\", \"a_pos\", \"a_size\", \"a_color\"],\n mesh: [\"a_pos\", \"a_normal\", \"a_color\"],\n lines: [\"a_corner\", \"a_a\", \"a_b\", \"a_color\"],\n} as const;\n"],"mappings":"6DAiCA,IAAMI,GACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,KAEAC,EAAI,IAAIC,EAAU,CAAC,EACnBD,EAAI,IAAIE,EAAU,EAAE,EACpBF,EAAI,IAAIG,EAAK,EAAE,EACfH,EAAI,IAAII,EAAO,EAAE,EACjBJ,EAAI,IAAIK,EAAU,EAAE,EACpBL,EAAI,IAAIM,EAAQ,EAAE,EAClBN,EAAI,IAAIO,EAAK,EAAE,EACRP,GAIIQ,GACX,EACA,EACA,IACiB,CACjB,IAAMC,EACJC,EAAK,OAAS,SACV,KAAK,YAGLA,EAAK,OAAS,QACZA,EAAK,MACL,EACR,OAAOX,EACLC,EACAW,EAAM,SACN,CAACA,EAAM,MAAOA,EAAM,OAAQA,EAAM,IAAK,KAAM,YAAoB,EACjE,CACEA,EAAM,IAAI,GACVA,EAAM,IAAI,GACVA,EAAM,IAAI,GACVD,EAAK,OAAS,QACVA,EAAK,KACLA,EAAK,OAAS,SACXA,EAAK,OAAA,EACN,CACR,EACA,CACEC,EAAM,MAAM,GACZA,EAAM,MAAM,GACZA,EAAM,MAAM,GACZD,EAAK,OAAS,UAAYA,EAAK,SAAW,EAAI,CAChD,EACAC,EAAM,SACN,CAACD,EAAK,QAASD,EAAQE,EAAM,SAAUA,EAAM,MAAM,EACnD,CAACA,EAAM,IAAI,GAAIA,EAAM,IAAI,GAAIA,EAAM,IAAI,GAAIA,EAAM,QAAQ,CAC3D,CACF,EAIMC,EAA2B;;;;;;;;;;;;;;;;;;;;EAoB/BjB,EAAAA,WAEWkB,EAAyB,GAAGD,EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiF1CE,EAAuB,GAAGF,EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CxCG,EAAwB,GAAGH,EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgDhDI,EAAgB;;;;;;;;EAUhBC,EAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCjBC,EAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCjBC,EAAe;;;;;;;;;;;;;EAefC,EAAe;;;;;;;;;;;;;;;;;;EAoBfC,EAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BhBC,EAAgB;;;;;;;;;;EAYhBC,EAAY;;;;;;EAaZC,GACJ,EACA,EACA,EACA,EAAM,KAENC,IAAY,EACR,CACE,OAAQ,+DAA+DT,IAAgBU,IACvF,SAAU,gEAAgEV,IACxEW,EACI/B,EAAAA,SACA;;IACHgC,GACL,EACA,CACE,OAAQ,GAAGZ,IAAgBU,IAC3B,SAAU,GAAGH,EAAU,yCAAyCP,IAAgBY,GAClF,EAGOC,EAAgB,IAAoB,CAC/C,OAAQL,EAAKP,EAAgBC,EAAgBO,CAAO,EACpD,KAAMD,EAAKL,EAAcC,EAAcK,CAAO,EAC9C,MAAOD,EAAKH,EAAeC,EAAeG,CAAO,CACnD,GAMaK,OAAyB,CACpC,OAAQN,EAAKP,EAAgBC,EAAgB,EAAG,EAAI,EACpD,KAAMM,EAAKL,EAAcC,EAAc,EAAG,EAAI,CAChD,GAGaW,EAAkB,CAC7B,OAAQ,CAAC,WAAY,QAAS,SAAU,SAAS,EACjD,KAAM,CAAC,QAAS,WAAY,SAAS,EACrC,MAAO,CAAC,WAAY,MAAO,MAAO,SAAS,CAC7C"}
1
+ {"version":3,"file":"shaders.cjs","names":["WGSL_OIT","GLSL_OIT","GLSL_FRONT","glslScreen","require_render_types","require_oit","writeUniforms","out","viewProj","viewport","eye","light","material","params","fog","itemUniforms","second","item","frame","WGSL_UNIFORMS","WGSL_POINTS","WGSL_MESH","WGSL_LINES","GLSL_UNIFORMS","GLSL_POINTS_VS","GLSL_POINTS_FS","GLSL_MESH_VS","GLSL_MESH_FS","GLSL_LINES_VS","GLSL_LINES_FS","PRECISION","glsl","version","vs","output","fs","glslPrograms","glslOitPrograms","GLSL_ATTRIBUTES"],"sources":["../../../../../charts/src/engine/three-d/shaders.ts"],"sourcesContent":["// Shaders of the 3D data layer: instanced point billboards (shaded spheres,\n// flat discs or glowing discs), lit triangles (two-sided Lambert +\n// Blinn-Phong) and screen-space wide line segments. WGSL for WebGPU, GLSL ES\n// 1.0 for WebGL (turned into GLSL ES 3.0 for WebGL2 by a macro prefix).\n// Outputs are premultiplied.\n//\n// Uniforms (WGSL struct, GLSL vec4 uniforms of the same names):\n// viewProj world -> clip (OpenGL depth convention)\n// viewport drawing buffer width, height (device px), dpr, orthographic\n// eye camera position, depth bias of the lines (NDC) | point style\n// light unit vector towards the light, additive points (0/1)\n// material ambient, diffuse, specular, shininess\n// params opacity, perspective sizes (0/1) | line width (CSS px), distance, radius\n// fog background color (far points fade towards it), exposure of the\n// additive light\n//\n// Points and meshes have two more fragment entry points for the hybrid\n// order-independent transparency (`oit.ts`): `fsOit` (the four targets) and\n// `fsFront` (the depth of the front transparent layer) in WGSL, the `oit`\n// and `front` variants of the GLSL programs (WebGL2). Elsewhere, additive\n// points are screen blended (`screenTone`: their light is tone mapped as it\n// adds up, no clipping on 8-bit framebuffers).\n//\n// Point sizes are CSS pixels times the device pixel ratio (billboards: no\n// point size limit). Dots thinner than the smallest drawn radius keep their\n// energy: their alpha scales with the area they would cover.\n\nimport { GLSL_FRONT, GLSL_OIT, WGSL_OIT, glslScreen } from \"./oit\";\nimport { POINT_SPHERE, type Frame3D, type Item3D } from \"./render-types\";\n\nexport { writesDepth } from \"./render-types\";\n\n/** Bytes of the WGSL uniform struct */\nexport const UNIFORM_BYTES = 160;\n\n/** Writes the uniforms of an item in the WGSL struct layout */\nconst writeUniforms = (\n out: Float32Array,\n viewProj: Float32Array,\n viewport: readonly [number, number, number, number],\n eye: readonly [number, number, number, number],\n light: readonly [number, number, number, number],\n material: readonly [number, number, number, number],\n params: readonly [number, number, number, number],\n fog: readonly [number, number, number, number],\n): Float32Array => {\n out.set(viewProj, 0);\n out.set(viewport, 16);\n out.set(eye, 20);\n out.set(light, 24);\n out.set(material, 28);\n out.set(params, 32);\n out.set(fog, 36);\n return out;\n};\n\n/** Uniform values of an item (40 floats, the WGSL struct layout) */\nexport const itemUniforms = (\n item: Item3D,\n frame: Frame3D,\n out: Float32Array,\n): Float32Array => {\n const second =\n item.kind === \"points\"\n ? item.perspective\n ? 1\n : 0\n : item.kind === \"lines\"\n ? item.width\n : 0;\n return writeUniforms(\n out,\n frame.viewProj,\n [frame.width, frame.height, frame.dpr, frame.orthographic ? 1 : 0],\n [\n frame.eye[0],\n frame.eye[1],\n frame.eye[2],\n item.kind === \"lines\"\n ? item.bias\n : item.kind === \"points\"\n ? (item.style ?? POINT_SPHERE)\n : 0,\n ],\n [\n frame.light[0],\n frame.light[1],\n frame.light[2],\n item.kind === \"points\" && item.additive ? 1 : 0,\n ],\n frame.lighting,\n [item.opacity, second, frame.distance, frame.radius],\n [frame.fog[0], frame.fog[1], frame.fog[2], frame.exposure],\n );\n};\n\n// ------------------------------------------------------------------- WGSL\n\nconst WGSL_UNIFORMS = /* wgsl */ `\nstruct U {\n viewProj: mat4x4f,\n viewport: vec4f,\n eye: vec4f,\n light: vec4f,\n material: vec4f,\n params: vec4f,\n fog: vec4f,\n};\n@group(0) @binding(0) var<uniform> u: U;\n\nfn toWebGpuDepth(p: vec4f) -> vec4f {\n return vec4f(p.x, p.y, (p.z + p.w) * 0.5, p.w);\n}\n\nfn depthCue(world: vec3f) -> f32 {\n let near = u.params.z - u.params.w;\n return clamp((length(world - u.eye.xyz) - near) / (2.0 * u.params.w), 0.0, 1.0);\n}\n${WGSL_OIT}`;\n\nexport const WGSL_POINTS = /* wgsl */ `${WGSL_UNIFORMS}\nstruct Out {\n @builtin(position) position: vec4f,\n @location(0) offset: vec2f,\n @location(1) radius: f32,\n @location(2) color: vec4f,\n @location(3) fog: f32,\n @location(4) coverage: f32,\n};\n\n@vertex\nfn vs(\n @builtin(vertex_index) vi: u32,\n @location(0) pos: vec3f,\n @location(1) size: f32,\n @location(2) color: vec4f,\n) -> Out {\n var out: Out;\n let corner = vec2f(f32(vi & 1u), f32(vi >> 1u)) * 2.0 - 1.0;\n let clip = u.viewProj * vec4f(pos, 1.0);\n var factor = 1.0;\n if (u.params.y > 0.5 && u.viewport.w < 0.5) {\n factor = clamp(u.params.z / max(clip.w, 1e-6), 0.5, 2.0);\n }\n let glow = u.eye.w > 1.5;\n let raw = max(size, 0.0) * u.viewport.z * factor * 0.5;\n let radius = max(raw, select(0.5, 0.75, glow));\n let reach = select(radius + 1.0, radius * 2.2 + 1.0, glow);\n let shift = corner * reach * 2.0 / u.viewport.xy * clip.w;\n out.position = toWebGpuDepth(vec4f(clip.xy + shift, clip.z, clip.w));\n out.offset = corner * reach;\n out.radius = radius;\n out.color = color;\n out.fog = depthCue(pos);\n out.coverage = min(1.0, raw * raw / (radius * radius));\n return out;\n}\n\nfn shade(in: Out) -> vec4f {\n let d = length(in.offset);\n let r = in.radius;\n var alpha = clamp(r + 0.5 - d, 0.0, 1.0);\n var rgb = in.color.rgb;\n if (u.eye.w > 1.5) {\n // glow: an anti-aliased core and a gaussian halo\n let core = clamp(r * 0.6 + 0.5 - d, 0.0, 1.0);\n let q = d / r;\n alpha = core + (1.0 - core) * exp(-1.2 * q * q);\n rgb = rgb + (vec3f(1.0) - rgb) * (0.2 * core);\n } else if (u.eye.w < 0.5) {\n let q = min(d / r, 1.0);\n let n = vec3f(in.offset.x / r, in.offset.y / r, sqrt(max(0.0, 1.0 - q * q)));\n let l = normalize(vec3f(-0.45, 0.55, 0.7));\n let lambert = max(dot(n, l), 0.0);\n let shaded = rgb * (0.62 + 0.45 * lambert) + vec3f(pow(lambert, 18.0) * 0.28);\n rgb = mix(rgb, shaded, smoothstep(1.5, 3.5, r));\n }\n if (alpha <= 0.004) { discard; }\n let fog = in.fog * 0.45;\n if (u.light.w > 0.5) {\n // additive light fades with the distance\n alpha = alpha * (1.0 - fog);\n rgb = rgb * u.fog.w;\n } else {\n rgb = mix(rgb, u.fog.rgb, fog);\n }\n alpha = alpha * in.coverage * in.color.a * u.params.x;\n return vec4f(rgb * alpha, alpha);\n}\n\n@fragment\nfn fs(in: Out) -> @location(0) vec4f {\n return screenLight(shade(in));\n}\n\n@fragment\nfn fsOit(in: Out) -> Oit {\n return oit(shade(in), in.fog, in.position);\n}\n\n@fragment\nfn fsFront(in: Out) -> @location(0) vec4f {\n return frontDepth(shade(in), in.fog);\n}\n`;\n\nexport const WGSL_MESH = /* wgsl */ `${WGSL_UNIFORMS}\nstruct Out {\n @builtin(position) position: vec4f,\n @location(0) world: vec3f,\n @location(1) normal: vec3f,\n @location(2) color: vec4f,\n};\n\n@vertex\nfn vs(\n @location(0) pos: vec3f,\n @location(1) normal: vec3f,\n @location(2) color: vec4f,\n) -> Out {\n var out: Out;\n out.position = toWebGpuDepth(u.viewProj * vec4f(pos, 1.0));\n out.world = pos;\n out.normal = normal;\n out.color = color;\n return out;\n}\n\nfn shade(in: Out) -> vec4f {\n var n = normalize(in.normal);\n let v = normalize(u.eye.xyz - in.world);\n if (dot(n, v) < 0.0) { n = -n; }\n let l = u.light.xyz;\n let diffuse = max(dot(n, l), 0.0);\n let h = normalize(l + v);\n let specular = pow(max(dot(n, h), 0.0), u.material.w) * u.material.z;\n let rgb = in.color.rgb * (u.material.x + u.material.y * diffuse) + vec3f(specular);\n let alpha = in.color.a * u.params.x;\n return vec4f(rgb * alpha, alpha);\n}\n\n@fragment\nfn fs(in: Out) -> @location(0) vec4f {\n return shade(in);\n}\n\n@fragment\nfn fsOit(in: Out) -> Oit {\n return oit(shade(in), depthCue(in.world), in.position);\n}\n\n@fragment\nfn fsFront(in: Out) -> @location(0) vec4f {\n return frontDepth(shade(in), depthCue(in.world));\n}\n`;\n\nexport const WGSL_LINES = /* wgsl */ `${WGSL_UNIFORMS}\nstruct Out {\n @builtin(position) position: vec4f,\n @location(0) across: f32,\n @location(1) color: vec4f,\n};\n\n@vertex\nfn vs(\n @builtin(vertex_index) vi: u32,\n @location(0) a: vec3f,\n @location(1) b: vec3f,\n @location(2) color: vec4f,\n) -> Out {\n var out: Out;\n let corner = vec2f(f32(vi & 1u), f32(vi >> 1u));\n let ca = u.viewProj * vec4f(a, 1.0);\n let cb = u.viewProj * vec4f(b, 1.0);\n let halfSize = u.viewport.xy * 0.5;\n let sa = ca.xy / ca.w * halfSize;\n let sb = cb.xy / cb.w * halfSize;\n var dir = sb - sa;\n let len = length(dir);\n dir = select(vec2f(1.0, 0.0), dir / len, len > 1e-6);\n let normal = vec2f(-dir.y, dir.x);\n let w = max(0.5, u.params.y * u.viewport.z * 0.5) + 0.5;\n let clip = select(ca, cb, corner.x > 0.5);\n let side = corner.y * 2.0 - 1.0;\n let pixel = select(sa, sb, corner.x > 0.5) + dir * (corner.x * 2.0 - 1.0) * w + normal * side * w;\n let z = clip.z - u.eye.w * clip.w;\n out.position = toWebGpuDepth(vec4f(pixel / halfSize * clip.w, z, clip.w));\n out.across = side * w;\n out.color = color;\n return out;\n}\n\n@fragment\nfn fs(in: Out) -> @location(0) vec4f {\n let core = u.params.y * u.viewport.z * 0.5;\n let coverage = clamp(max(core, 0.5) + 0.5 - abs(in.across), 0.0, 1.0) * min(1.0, core * 2.0);\n let alpha = coverage * in.color.a * u.params.x;\n if (alpha <= 0.0) { discard; }\n return vec4f(in.color.rgb * alpha, alpha);\n}\n`;\n\n// ------------------------------------------------------------------- GLSL\n\nconst GLSL_UNIFORMS = `\nuniform mat4 u_viewProj;\nuniform vec4 u_viewport;\nuniform vec4 u_eye;\nuniform vec4 u_light;\nuniform vec4 u_material;\nuniform vec4 u_params;\nuniform vec4 u_fog;\n`;\n\nconst GLSL_POINTS_VS = `\nattribute vec2 a_corner;\nattribute vec3 a_pos;\nattribute float a_size;\nattribute vec4 a_color;\nvarying vec2 v_offset;\nvarying float v_radius;\nvarying vec4 v_color;\nvarying float v_fog;\nvarying float v_coverage;\nvoid main() {\n vec2 corner = a_corner * 2.0 - 1.0;\n vec4 clip = u_viewProj * vec4(a_pos, 1.0);\n float factor = 1.0;\n if (u_params.y > 0.5 && u_viewport.w < 0.5) {\n factor = clamp(u_params.z / max(clip.w, 1e-6), 0.5, 2.0);\n }\n bool glow = u_eye.w > 1.5;\n float raw = max(a_size, 0.0) * u_viewport.z * factor * 0.5;\n float radius = max(raw, glow ? 0.75 : 0.5);\n float reach = glow ? radius * 2.2 + 1.0 : radius + 1.0;\n vec2 shift = corner * reach * 2.0 / u_viewport.xy * clip.w;\n gl_Position = vec4(clip.xy + shift, clip.z, clip.w);\n v_offset = corner * reach;\n v_radius = radius;\n v_color = a_color;\n float near = u_params.z - u_params.w;\n v_fog = clamp((length(a_pos - u_eye.xyz) - near) / (2.0 * u_params.w), 0.0, 1.0);\n v_coverage = min(1.0, raw * raw / (radius * radius));\n}\n`;\n\nconst GLSL_POINTS_FS = `\nvarying vec2 v_offset;\nvarying float v_radius;\nvarying vec4 v_color;\nvarying float v_fog;\nvarying float v_coverage;\nvoid main() {\n float d = length(v_offset);\n float r = v_radius;\n float alpha = clamp(r + 0.5 - d, 0.0, 1.0);\n vec3 rgb = v_color.rgb;\n if (u_eye.w > 1.5) {\n float core = clamp(r * 0.6 + 0.5 - d, 0.0, 1.0);\n float q = d / r;\n alpha = core + (1.0 - core) * exp(-1.2 * q * q);\n rgb = rgb + (vec3(1.0) - rgb) * (0.2 * core);\n } else if (u_eye.w < 0.5) {\n float q = min(d / r, 1.0);\n vec3 n = vec3(v_offset / r, sqrt(max(0.0, 1.0 - q * q)));\n vec3 l = normalize(vec3(-0.45, 0.55, 0.7));\n float lambert = max(dot(n, l), 0.0);\n vec3 shaded = rgb * (0.62 + 0.45 * lambert) + vec3(pow(lambert, 18.0) * 0.28);\n rgb = mix(rgb, shaded, smoothstep(1.5, 3.5, r));\n }\n if (alpha <= 0.004) discard;\n float fog = v_fog * 0.45;\n if (u_light.w > 0.5) {\n alpha = alpha * (1.0 - fog);\n rgb = rgb * u_fog.w;\n } else {\n rgb = mix(rgb, u_fog.rgb, fog);\n }\n alpha = alpha * v_coverage * v_color.a * u_params.x;\n EMIT(vec4(rgb * alpha, alpha), v_fog);\n}\n`;\n\nconst GLSL_MESH_VS = `\nattribute vec3 a_pos;\nattribute vec3 a_normal;\nattribute vec4 a_color;\nvarying vec3 v_world;\nvarying vec3 v_normal;\nvarying vec4 v_color;\nvoid main() {\n gl_Position = u_viewProj * vec4(a_pos, 1.0);\n v_world = a_pos;\n v_normal = a_normal;\n v_color = a_color;\n}\n`;\n\nconst GLSL_MESH_FS = `\nvarying vec3 v_world;\nvarying vec3 v_normal;\nvarying vec4 v_color;\nvoid main() {\n vec3 n = normalize(v_normal);\n vec3 v = normalize(u_eye.xyz - v_world);\n if (dot(n, v) < 0.0) n = -n;\n vec3 l = u_light.xyz;\n float diffuse = max(dot(n, l), 0.0);\n vec3 h = normalize(l + v);\n float specular = pow(max(dot(n, h), 0.0), u_material.w) * u_material.z;\n vec3 rgb = v_color.rgb * (u_material.x + u_material.y * diffuse) + vec3(specular);\n float alpha = v_color.a * u_params.x;\n float near = u_params.z - u_params.w;\n float depth = (length(v_world - u_eye.xyz) - near) / (2.0 * u_params.w);\n EMIT(vec4(rgb * alpha, alpha), depth);\n}\n`;\n\nconst GLSL_LINES_VS = `\nattribute vec2 a_corner;\nattribute vec3 a_a;\nattribute vec3 a_b;\nattribute vec4 a_color;\nvarying float v_across;\nvarying vec4 v_color;\nvoid main() {\n vec4 ca = u_viewProj * vec4(a_a, 1.0);\n vec4 cb = u_viewProj * vec4(a_b, 1.0);\n vec2 halfSize = u_viewport.xy * 0.5;\n vec2 sa = ca.xy / ca.w * halfSize;\n vec2 sb = cb.xy / cb.w * halfSize;\n vec2 dir = sb - sa;\n float len = length(dir);\n dir = len > 1e-6 ? dir / len : vec2(1.0, 0.0);\n vec2 normal = vec2(-dir.y, dir.x);\n float w = max(0.5, u_params.y * u_viewport.z * 0.5) + 0.5;\n bool atEnd = a_corner.x > 0.5;\n vec4 clip = atEnd ? cb : ca;\n float side = a_corner.y * 2.0 - 1.0;\n vec2 pixel = (atEnd ? sb : sa) + dir * (a_corner.x * 2.0 - 1.0) * w + normal * side * w;\n gl_Position = vec4(pixel / halfSize * clip.w, clip.z - u_eye.w * clip.w, clip.w);\n v_across = side * w;\n v_color = a_color;\n}\n`;\n\nconst GLSL_LINES_FS = `\nvarying float v_across;\nvarying vec4 v_color;\nvoid main() {\n float core = u_params.y * u_viewport.z * 0.5;\n float coverage = clamp(max(core, 0.5) + 0.5 - abs(v_across), 0.0, 1.0) * min(1.0, core * 2.0);\n float alpha = coverage * v_color.a * u_params.x;\n if (alpha <= 0.0) discard;\n EMIT(vec4(v_color.rgb * alpha, alpha), 0.0);\n}\n`;\n\nconst PRECISION = `\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n`;\n\nexport interface GlslSource {\n vertex: string;\n fragment: string;\n}\n\n/**\n * Output of a GLSL program: the color (`plain`), the transparency targets\n * (`oit`, WebGL2) or the depth of the front transparent layer (`front`, WebGL2)\n */\ntype GlslOutput = \"plain\" | \"oit\" | \"front\";\n\nconst glsl = (\n vs: string,\n fs: string,\n version: 1 | 2,\n output: GlslOutput = \"plain\",\n): GlslSource =>\n version === 2\n ? {\n vertex: `#version 300 es\\n#define attribute in\\n#define varying out\\n${GLSL_UNIFORMS}${vs}`,\n fragment: `#version 300 es\\nprecision highp float;\\n#define varying in\\n${GLSL_UNIFORMS}${\n output === \"oit\"\n ? GLSL_OIT\n : output === \"front\"\n ? GLSL_FRONT\n : `out vec4 fragColor;\\n${glslScreen(\"fragColor\")}`\n }${fs}`,\n }\n : {\n vertex: `${GLSL_UNIFORMS}${vs}`,\n fragment: `${PRECISION}${GLSL_UNIFORMS}${glslScreen(\"gl_FragColor\")}${fs}`,\n };\n\n/** GLSL programs of the three primitives for WebGL (1) or WebGL2 */\nexport const glslPrograms = (version: 1 | 2) => ({\n points: glsl(GLSL_POINTS_VS, GLSL_POINTS_FS, version),\n mesh: glsl(GLSL_MESH_VS, GLSL_MESH_FS, version),\n lines: glsl(GLSL_LINES_VS, GLSL_LINES_FS, version),\n});\n\n/**\n * Variants of the points and meshes writing the targets of the\n * order-independent transparency (WebGL2), and the front depth of the\n * transparent layers (`front`)\n */\nexport const glslOitPrograms = () => ({\n points: glsl(GLSL_POINTS_VS, GLSL_POINTS_FS, 2, \"oit\"),\n mesh: glsl(GLSL_MESH_VS, GLSL_MESH_FS, 2, \"oit\"),\n front: {\n points: glsl(GLSL_POINTS_VS, GLSL_POINTS_FS, 2, \"front\"),\n mesh: glsl(GLSL_MESH_VS, GLSL_MESH_FS, 2, \"front\"),\n },\n});\n\n/** Attribute names of every program, bound to locations 0, 1, ... */\nexport const GLSL_ATTRIBUTES = {\n points: [\"a_corner\", \"a_pos\", \"a_size\", \"a_color\"],\n mesh: [\"a_pos\", \"a_normal\", \"a_color\"],\n lines: [\"a_corner\", \"a_a\", \"a_b\", \"a_color\"],\n} as const;\n"],"mappings":"6DAoCA,IAAMM,GACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,KAEAC,EAAI,IAAIC,EAAU,CAAC,EACnBD,EAAI,IAAIE,EAAU,EAAE,EACpBF,EAAI,IAAIG,EAAK,EAAE,EACfH,EAAI,IAAII,EAAO,EAAE,EACjBJ,EAAI,IAAIK,EAAU,EAAE,EACpBL,EAAI,IAAIM,EAAQ,EAAE,EAClBN,EAAI,IAAIO,EAAK,EAAE,EACRP,GAIIQ,GACX,EACA,EACA,IACiB,CACjB,IAAMC,EACJC,EAAK,OAAS,SACV,KAAK,YAGLA,EAAK,OAAS,QACZA,EAAK,MACL,EACR,OAAOX,EACLC,EACAW,EAAM,SACN,CAACA,EAAM,MAAOA,EAAM,OAAQA,EAAM,IAAK,KAAM,YAAoB,EACjE,CACEA,EAAM,IAAI,GACVA,EAAM,IAAI,GACVA,EAAM,IAAI,GACVD,EAAK,OAAS,QACVA,EAAK,KACLA,EAAK,OAAS,SACXA,EAAK,OAAA,EACN,CACR,EACA,CACEC,EAAM,MAAM,GACZA,EAAM,MAAM,GACZA,EAAM,MAAM,GACZD,EAAK,OAAS,UAAYA,EAAK,SAAW,EAAI,CAChD,EACAC,EAAM,SACN,CAACD,EAAK,QAASD,EAAQE,EAAM,SAAUA,EAAM,MAAM,EACnD,CAACA,EAAM,IAAI,GAAIA,EAAM,IAAI,GAAIA,EAAM,IAAI,GAAIA,EAAM,QAAQ,CAC3D,CACF,EAIMC,EAA2B;;;;;;;;;;;;;;;;;;;;EAoB/BnB,EAAAA,WAEWoB,EAAyB,GAAGD,EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsF1CE,EAAuB,GAAGF,EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmDxCG,EAAwB,GAAGH,EAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgDhDI,EAAgB;;;;;;;;EAUhBC,EAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCjBC,EAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCjBC,EAAe;;;;;;;;;;;;;EAefC,EAAe;;;;;;;;;;;;;;;;;;EAoBfC,EAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BhBC,EAAgB;;;;;;;;;;EAYhBC,EAAY;;;;;;EAmBZC,GACJ,EACA,EACA,EACA,EAAqB,UAErBC,IAAY,EACR,CACE,OAAQ,+DAA+DT,IAAgBU,IACvF,SAAU,gEAAgEV,IACxEW,IAAW,MACPjC,EAAAA,SACAiC,IAAW,QACThC,EAAAA,WACA,wBAAwBC,EAAAA,WAAW,WAAW,MACnDgC,GACL,EACA,CACE,OAAQ,GAAGZ,IAAgBU,IAC3B,SAAU,GAAGH,IAAYP,IAAgBpB,EAAAA,WAAW,cAAc,IAAIgC,GACxE,EAGOC,EAAgB,IAAoB,CAC/C,OAAQL,EAAKP,EAAgBC,EAAgBO,CAAO,EACpD,KAAMD,EAAKL,EAAcC,EAAcK,CAAO,EAC9C,MAAOD,EAAKH,EAAeC,EAAeG,CAAO,CACnD,GAOaK,OAAyB,CACpC,OAAQN,EAAKP,EAAgBC,EAAgB,EAAG,KAAK,EACrD,KAAMM,EAAKL,EAAcC,EAAc,EAAG,KAAK,EAC/C,MAAO,CACL,OAAQI,EAAKP,EAAgBC,EAAgB,EAAG,OAAO,EACvD,KAAMM,EAAKL,EAAcC,EAAc,EAAG,OAAO,CACnD,CACF,GAGaW,EAAkB,CAC7B,OAAQ,CAAC,WAAY,QAAS,SAAU,SAAS,EACjD,KAAM,CAAC,QAAS,WAAY,SAAS,EACrC,MAAO,CAAC,WAAY,MAAO,MAAO,SAAS,CAC7C"}
@@ -19,11 +19,16 @@ export declare const glslPrograms: (version: 1 | 2) => {
19
19
  };
20
20
  /**
21
21
  * Variants of the points and meshes writing the targets of the
22
- * order-independent transparency (WebGL2)
22
+ * order-independent transparency (WebGL2), and the front depth of the
23
+ * transparent layers (`front`)
23
24
  */
24
25
  export declare const glslOitPrograms: () => {
25
26
  points: GlslSource;
26
27
  mesh: GlslSource;
28
+ front: {
29
+ points: GlslSource;
30
+ mesh: GlslSource;
31
+ };
27
32
  };
28
33
  /** Attribute names of every program, bound to locations 0, 1, ... */
29
34
  export declare const GLSL_ATTRIBUTES: {
@@ -19,11 +19,16 @@ export declare const glslPrograms: (version: 1 | 2) => {
19
19
  };
20
20
  /**
21
21
  * Variants of the points and meshes writing the targets of the
22
- * order-independent transparency (WebGL2)
22
+ * order-independent transparency (WebGL2), and the front depth of the
23
+ * transparent layers (`front`)
23
24
  */
24
25
  export declare const glslOitPrograms: () => {
25
26
  points: GlslSource;
26
27
  mesh: GlslSource;
28
+ front: {
29
+ points: GlslSource;
30
+ mesh: GlslSource;
31
+ };
27
32
  };
28
33
  /** Attribute names of every program, bound to locations 0, 1, ... */
29
34
  export declare const GLSL_ATTRIBUTES: {