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,27 +1,51 @@
1
- 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=`
1
+ 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=`
2
+ @group(1) @binding(0) var frontTex: texture_2d<f32>;
3
+
2
4
  struct Oit {
3
- @location(0) accum: vec4f,
4
- @location(1) extra: vec4f,
5
+ @location(0) front: vec4f,
6
+ @location(1) accum: vec4f,
7
+ @location(2) extra: vec4f,
8
+ @location(3) behind: vec4f,
5
9
  };
6
10
 
7
- fn oit(c: vec4f, depth: f32) -> Oit {
11
+ fn oit(c: vec4f, depth: f32, xy: vec4f) -> Oit {
8
12
  var o: Oit;
13
+ o.front = vec4f(0.0);
14
+ o.accum = vec4f(0.0);
15
+ o.extra = vec4f(0.0);
16
+ o.behind = vec4f(0.0);
17
+ let z = clamp(depth, 0.0, 1.0);
18
+ let near = z <= textureLoad(frontTex, vec2i(xy.xy), 0).r + ${d(c)};
9
19
  if (u.light.w > 0.5) {
10
- o.accum = vec4f(0.0);
11
- o.extra = vec4f(c.rgb, 0.0);
20
+ if (near) { o.extra = vec4f(c.rgb, 0.0); } else { o.behind = vec4f(c.rgb, 0.0); }
21
+ return o;
22
+ }
23
+ if (near) {
24
+ o.front = c;
12
25
  return o;
13
26
  }
14
- let a = clamp(c.a, 0.0, ${c(r)});
15
- let w = max(${c(n)}, ${c(e)} * exp2(-${c(t)} * clamp(depth, 0.0, 1.0)));
27
+ let a = clamp(c.a, 0.0, ${d(r)});
28
+ let w = max(${d(n)}, ${d(e)} * exp2(-${d(t)} * z));
16
29
  o.accum = vec4f(c.rgb, a) * w;
17
30
  o.extra = vec4f(0.0, 0.0, 0.0, -log(1.0 - a));
18
31
  return o;
19
32
  }
20
- `,u=`${`
33
+
34
+ fn frontDepth(c: vec4f, depth: f32) -> vec4f {
35
+ if (c.a < ${d(l)}) { discard; }
36
+ return vec4f(clamp(depth, 0.0, 1.0));
37
+ }
38
+
39
+ fn screenLight(c: vec4f) -> vec4f {
40
+ if (u.light.w < 0.5) { return c; }
41
+ let v = vec3f(1.0) - exp(-${d(u)} * max(c.rgb, vec3f(0.0)));
42
+ return vec4f(v, max(max(v.r, v.g), v.b));
43
+ }
44
+ `,p=`${`
21
45
  fn shoulder(x: f32) -> f32 {
22
- if (x <= ${c(i)}) { return x; }
23
- let t = (x - ${c(i)}) / ${c(.4)};
24
- return ${c(i)} + ${c(.4)} * t / (1.0 + t);
46
+ if (x <= ${d(i)}) { return x; }
47
+ let t = (x - ${d(i)}) / ${d(.4)};
48
+ return ${d(i)} + ${d(.4)} * t / (1.0 + t);
25
49
  }
26
50
 
27
51
  fn toneMap(rgb: vec3f) -> vec3f {
@@ -29,11 +53,14 @@ fn toneMap(rgb: vec3f) -> vec3f {
29
53
  if (m <= 1e-6) { return rgb; }
30
54
  let peak = shoulder(m);
31
55
  let scaled = rgb * (peak / m);
32
- return mix(scaled, vec3f(peak), smoothstep(${c(a)}, ${c(o)}, m) * ${c(s)});
56
+ return mix(scaled, vec3f(peak), smoothstep(${d(a)}, ${d(o)}, m) * ${d(s)});
33
57
  }
34
58
  `}
35
- @group(0) @binding(0) var accumTex: texture_2d<f32>;
36
- @group(0) @binding(1) var extraTex: texture_2d<f32>;
59
+ @group(0) @binding(0) var frontTex: texture_2d<f32>;
60
+ @group(0) @binding(1) var accumTex: texture_2d<f32>;
61
+ @group(0) @binding(2) var extraTex: texture_2d<f32>;
62
+ @group(0) @binding(3) var behindTex: texture_2d<f32>;
63
+
37
64
 
38
65
  @vertex
39
66
  fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
@@ -44,66 +71,124 @@ fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
44
71
  @fragment
45
72
  fn fs(@builtin(position) position: vec4f) -> @location(0) vec4f {
46
73
  let xy = vec2i(position.xy);
74
+ let front = textureLoad(frontTex, xy, 0);
47
75
  let accum = textureLoad(accumTex, xy, 0);
48
76
  let extra = textureLoad(extraTex, xy, 0);
49
- let alpha = 1.0 - exp(-extra.a);
77
+ let behind = textureLoad(behindTex, xy, 0);
78
+ let tau = max(extra.a, 0.0);
79
+ let restAlpha = 1.0 - exp(-tau);
80
+ let rest = accum.rgb * (restAlpha / max(accum.a, 1e-6));
81
+ let sum = max(front.a, 0.0);
82
+ let scale = select(1.0, 1.0 / max(sum, 1e-6), sum > 1.0);
83
+ let fa = sum * scale;
84
+ let alpha = fa + restAlpha * (1.0 - fa);
85
+ let color = front.rgb * scale + rest * (1.0 - fa);
86
+ let meanT = select(1.0, restAlpha / max(tau, 1e-6), tau > 1e-4);
87
+ let t = (1.0 - fa) * meanT;
88
+ let back = toneMap(behind.rgb);
89
+ let backAlpha = clamp(max(max(back.r, back.g), back.b), 0.0, 1.0);
90
+ let under = color + back * t;
91
+ let underAlpha = min(1.0, alpha + backAlpha * t);
50
92
  let light = toneMap(extra.rgb);
51
93
  let lightAlpha = clamp(max(max(light.r, light.g), light.b), 0.0, 1.0);
52
- let rgb = accum.rgb * (alpha / max(accum.a, 1e-6)) + light * (1.0 - alpha);
53
- let a = alpha + lightAlpha * (1.0 - alpha);
94
+ let a = lightAlpha + underAlpha * (1.0 - lightAlpha);
95
+ let rgb = light + under * (1.0 - lightAlpha);
54
96
  if (a <= 0.0) { discard; }
55
97
  return vec4f(rgb, a);
56
98
  }
57
- `,d=`
58
- layout(location = 0) out vec4 o_accum;
59
- layout(location = 1) out vec4 o_extra;
99
+ `,m=`
100
+ uniform highp sampler2D u_front;
101
+ layout(location = 0) out vec4 o_front;
102
+ layout(location = 1) out vec4 o_accum;
103
+ layout(location = 2) out vec4 o_extra;
104
+ layout(location = 3) out vec4 o_behind;
60
105
  void emitOit(vec4 c, float depth) {
106
+ o_front = vec4(0.0);
107
+ o_accum = vec4(0.0);
108
+ o_extra = vec4(0.0);
109
+ o_behind = vec4(0.0);
110
+ float z = clamp(depth, 0.0, 1.0);
111
+ bool near = z <= texelFetch(u_front, ivec2(gl_FragCoord.xy), 0).r + ${d(c)};
61
112
  if (u_light.w > 0.5) {
62
- o_accum = vec4(0.0);
63
- o_extra = vec4(c.rgb, 0.0);
113
+ if (near) o_extra = vec4(c.rgb, 0.0); else o_behind = vec4(c.rgb, 0.0);
114
+ return;
115
+ }
116
+ if (near) {
117
+ o_front = c;
64
118
  return;
65
119
  }
66
- float a = clamp(c.a, 0.0, ${c(r)});
67
- float w = max(${c(n)}, ${c(e)} * exp2(-${c(t)} * clamp(depth, 0.0, 1.0)));
120
+ float a = clamp(c.a, 0.0, ${d(r)});
121
+ float w = max(${d(n)}, ${d(e)} * exp2(-${d(t)} * z));
68
122
  o_accum = vec4(c.rgb, a) * w;
69
123
  o_extra = vec4(0.0, 0.0, 0.0, -log(1.0 - a));
70
124
  }
71
125
  #define EMIT(c, d) emitOit(c, d)
72
- `,f={vertex:`#version 300 es
126
+ `,h=`
127
+ out vec4 fragColor;
128
+ void emitFront(vec4 c, float depth) {
129
+ if (c.a < ${d(l)}) discard;
130
+ fragColor = vec4(clamp(depth, 0.0, 1.0));
131
+ }
132
+ #define EMIT(c, d) emitFront(c, d)
133
+ `,g=e=>`
134
+ vec4 screenLight(vec4 c) {
135
+ if (u_light.w < 0.5) return c;
136
+ vec3 v = vec3(1.0) - exp(-${d(u)} * max(c.rgb, vec3(0.0)));
137
+ return vec4(v, max(max(v.r, v.g), v.b));
138
+ }
139
+ #define EMIT(c, d) ${e} = screenLight(c)
140
+ `,_={vertex:`#version 300 es
73
141
  void main() {
74
142
  vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2)) * 2.0 - 1.0;
75
143
  gl_Position = vec4(p, 0.0, 1.0);
76
144
  }
77
145
  `,fragment:`#version 300 es
78
146
  precision highp float;
147
+ uniform sampler2D u_frontColor;
79
148
  uniform sampler2D u_accum;
80
149
  uniform sampler2D u_extra;
150
+ uniform sampler2D u_behind;
81
151
  out vec4 fragColor;
82
152
  ${`
83
153
  float shoulder(float x) {
84
- if (x <= ${c(i)}) return x;
85
- float t = (x - ${c(i)}) / ${c(.4)};
86
- return ${c(i)} + ${c(.4)} * t / (1.0 + t);
154
+ if (x <= ${d(i)}) return x;
155
+ float t = (x - ${d(i)}) / ${d(.4)};
156
+ return ${d(i)} + ${d(.4)} * t / (1.0 + t);
87
157
  }
88
158
  vec3 toneMap(vec3 rgb) {
89
159
  float m = max(max(rgb.r, rgb.g), rgb.b);
90
160
  if (m <= 1e-6) return rgb;
91
161
  float peak = shoulder(m);
92
162
  vec3 scaled = rgb * (peak / m);
93
- return mix(scaled, vec3(peak), smoothstep(${c(a)}, ${c(o)}, m) * ${c(s)});
163
+ return mix(scaled, vec3(peak), smoothstep(${d(a)}, ${d(o)}, m) * ${d(s)});
94
164
  }
95
165
  `}
96
166
  void main() {
97
167
  ivec2 xy = ivec2(gl_FragCoord.xy);
168
+ vec4 front = texelFetch(u_frontColor, xy, 0);
98
169
  vec4 accum = texelFetch(u_accum, xy, 0);
99
170
  vec4 extra = texelFetch(u_extra, xy, 0);
100
- float alpha = 1.0 - exp(-extra.a);
171
+ vec4 behind = texelFetch(u_behind, xy, 0);
172
+ float tau = max(extra.a, 0.0);
173
+ float restAlpha = 1.0 - exp(-tau);
174
+ vec3 rest = accum.rgb * (restAlpha / max(accum.a, 1e-6));
175
+ float sum = max(front.a, 0.0);
176
+ float scale = sum > 1.0 ? 1.0 / sum : 1.0;
177
+ float fa = sum * scale;
178
+ float alpha = fa + restAlpha * (1.0 - fa);
179
+ vec3 color = front.rgb * scale + rest * (1.0 - fa);
180
+ float meanT = tau > 1e-4 ? restAlpha / tau : 1.0;
181
+ float t = (1.0 - fa) * meanT;
182
+ vec3 back = toneMap(behind.rgb);
183
+ float backAlpha = clamp(max(max(back.r, back.g), back.b), 0.0, 1.0);
184
+ vec3 under = color + back * t;
185
+ float underAlpha = min(1.0, alpha + backAlpha * t);
101
186
  vec3 light = toneMap(extra.rgb);
102
187
  float lightAlpha = clamp(max(max(light.r, light.g), light.b), 0.0, 1.0);
103
- vec3 rgb = accum.rgb * (alpha / max(accum.a, 1e-6)) + light * (1.0 - alpha);
104
- float a = alpha + lightAlpha * (1.0 - alpha);
188
+ float a = lightAlpha + underAlpha * (1.0 - lightAlpha);
189
+ vec3 rgb = light + under * (1.0 - lightAlpha);
105
190
  if (a <= 0.0) discard;
106
191
  fragColor = vec4(rgb, a);
107
192
  }
108
- `};exports.GLSL_COMPOSITE=f,exports.GLSL_OIT=d,exports.TONE_KNEE=i,exports.WGSL_COMPOSITE=u,exports.WGSL_OIT=l;
193
+ `};exports.FRONT_EPSILON=c,exports.FRONT_MIN_ALPHA=l,exports.GLSL_COMPOSITE=_,exports.GLSL_FRONT=h,exports.GLSL_OIT=m,exports.SCREEN_GAIN=u,exports.TONE_KNEE=i,exports.WGSL_COMPOSITE=p,exports.WGSL_OIT=f,exports.glslScreen=g;
109
194
  //# sourceMappingURL=oit.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"oit.cjs","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,EAAe,GACfC,EAAiB,GACjBC,EAAa,IAEbC,EAAY,KAELC,EAAY,GAEnBC,EAAa,EACbC,EAAW,EACXC,EAAe,IAgFfC,EAAK,GAAe,OAAO,UAAUC,CAAC,EAAIA,EAAE,QAAQ,CAAC,EAAI,OAAOA,CAAC,EAK1DC,EAAsB;;;;;;;;;;;;;4BAaPF,EAAEL,CAAS,EAAE;gBACzBK,EAAEN,CAAU,EAAE,IAAIM,EAAER,CAAY,EAAE,WAAWQ,EAAEP,CAAc,EAAE;;;;;EAwBlEU,EAA4B,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;;;;;;;;;;;;;;;;;;;;;;;EA4BzCK,EAAW;;;;;;;;;8BASMJ,EAAEL,CAAS,EAAE;kBACzBK,EAAEN,CAAU,EAAE,IAAIM,EAAER,CAAY,EAAE,WAAWQ,EAAEP,CAAc,EAAE;;;;;EAuBpEY,EAAiB,CAC5B,OAAQ;;;;;EAMR,SAAU;;;;;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;;;;;;;;;;;;;CAcZ"}
1
+ {"version":3,"file":"oit.cjs","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,EAAe,GACfC,EAAiB,GACjBC,EAAa,IAEbC,EAAY,KAELC,EAAY,GAEnBC,EAAa,EACbC,EAAW,EACXC,EAAe,IAKRC,EAAgB,KAIhBC,EAAkB,IAKlBC,EAAc,IA0KrBC,EAAK,GAAe,OAAO,UAAUC,CAAC,EAAIA,EAAE,QAAQ,CAAC,EAAI,OAAOA,CAAC,EAQ1DC,EAAsB;;;;;;;;;;;;;;;;;+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;;;EAsBhCI,EAA4B,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDzCQ,EAAW;;;;;;;;;;;;wEAYgDJ,EAAEH,CAAa,EAAE;;;;;;;;;8BAS3DG,EAAER,CAAS,EAAE;kBACzBQ,EAAET,CAAU,EAAE,IAAIS,EAAEX,CAAY,EAAE,WAAWW,EAAEV,CAAc,EAAE;;;;;EAQpEe,EAAa;;;cAGZL,EAAEF,CAAe,EAAE;;;;EAUpBQ,EAAc,GAAmB;;;8BAGhBN,EAAED,CAAW,EAAE;;;qBAGxBQ,EAAO;EAmBfC,EAAiB,CAC5B,OAAQ;;;;;EAMR,SAAU;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BZ"}
@@ -1,5 +1,19 @@
1
1
  /** Light values below the knee are kept */
2
2
  export declare const TONE_KNEE = 0.6;
3
+ /**
4
+ * Depth (fraction of the box sphere diameter) within which the fragments
5
+ * belong to the front layer: a few half-float steps
6
+ */
7
+ export declare const FRONT_EPSILON = 0.002;
8
+ /** Front depth of the pixels without transparent fragment */
9
+ export declare const FRONT_CLEAR = 4;
10
+ /** Fainter fragments don't define the front layer (soft edges, glows) */
11
+ export declare const FRONT_MIN_ALPHA = 0.05;
12
+ /**
13
+ * Gain of the exponential tone curve of the screen-blended light (WebGL 1,
14
+ * no float targets): 1 − e^(−1.4 x) stays within 0.06 of `toneMap`'s curve
15
+ */
16
+ export declare const SCREEN_GAIN = 1.4;
3
17
  /**
4
18
  * Weight of a blended fragment at a depth (0 nearest … 1 farthest point of
5
19
  * the box sphere): 64 × 2^(−12 depth), at least 0.01. Accumulated in half
@@ -12,21 +26,62 @@ export declare const oitWeight: (depth: number) => number;
12
26
  * gradient instead of a flat plateau
13
27
  */
14
28
  export declare const shoulder: (x: number) => number;
15
- /** Tone mapping of additive light (linear, premultiplied) */
16
- export declare const toneMap: (rgb: readonly [number, number, number]) => [number, number, number];
17
- /** What a blended fragment adds to the two targets (JS mirror of the shaders) */
18
- export declare const oitFragment: (premultiplied: readonly [number, number, number, number], depth: number, additive: boolean) => {
29
+ /**
30
+ * Tone mapping of additive light (linear, premultiplied), into `out` when
31
+ * given (no allocation: per pixel on the CPU)
32
+ */
33
+ export declare const toneMap: (rgb: readonly [number, number, number], out?: [number, number, number]) => [number, number, number];
34
+ /**
35
+ * Screen-blended light: what an additive fragment of light `x` (one channel)
36
+ * writes with the blending `(one, one − src color)`. The framebuffer then
37
+ * holds `1 − e^(−SCREEN_GAIN Σx)`: the light is tone mapped as it adds up.
38
+ */
39
+ export declare const screenTone: (x: number) => number;
40
+ /** What a blended fragment adds to the four targets */
41
+ export interface OitOutput {
42
+ front: number[];
19
43
  accum: number[];
20
44
  extra: number[];
21
- };
22
- /** Composite of the targets: a premultiplied color (JS mirror of the shaders) */
23
- export declare const oitComposite: (accum: readonly number[], extra: readonly number[]) => [number, number, number, number];
24
- /** Second fragment entry point of the points and meshes: `fsOit` */
45
+ behind: number[];
46
+ }
47
+ /**
48
+ * Depth of the front layer of a pixel from its blended fragments (JS
49
+ * mirror of the front pass): the nearest visible non-additive fragment
50
+ */
51
+ export declare const oitFrontDepth: (fragments: readonly {
52
+ color: readonly [number, number, number, number];
53
+ depth: number;
54
+ additive?: boolean;
55
+ }[]) => number;
56
+ /**
57
+ * What a blended fragment adds to the targets (JS mirror of the shaders),
58
+ * `front` the depth of the front layer of its pixel (`oitFrontDepth`)
59
+ */
60
+ export declare const oitFragment: (premultiplied: readonly [number, number, number, number], depth: number, additive: boolean, front?: number) => OitOutput;
61
+ /**
62
+ * Composite of the targets: a premultiplied color blended over the opaque
63
+ * scene (JS mirror of the shaders)
64
+ */
65
+ export declare const oitComposite: (accum: readonly number[], extra: readonly number[], front?: readonly number[], behind?: readonly number[]) => [number, number, number, number];
66
+ /**
67
+ * Transparency outputs of the points and meshes (`fsOit`, `fsFront`): the
68
+ * front depth texture is bound in group 1 by the main pass only
69
+ */
25
70
  export declare const WGSL_OIT: string;
26
- /** Full-screen composite of the two targets over the opaque scene */
71
+ /** Full-screen composite of the four targets over the opaque scene */
27
72
  export declare const WGSL_COMPOSITE: string;
28
- /** Outputs of the OIT variants (GLSL ES 3.0): `EMIT(color, depth)` */
73
+ /**
74
+ * Outputs of the OIT variants (GLSL ES 3.0): `EMIT(color, depth)`; the
75
+ * front depth texture is `u_front`
76
+ */
29
77
  export declare const GLSL_OIT: string;
78
+ /** Output of the front pass (GLSL ES 3.0): the depth of visible fragments */
79
+ export declare const GLSL_FRONT: string;
80
+ /**
81
+ * Plain output (`output` the output variable): additive light screen
82
+ * blended, tone mapped as it adds up (`screenTone`)
83
+ */
84
+ export declare const glslScreen: (output: string) => string;
30
85
  /** Composite program of WebGL2 (full-screen triangle from `gl_VertexID`) */
31
86
  export declare const GLSL_COMPOSITE: {
32
87
  vertex: string;
@@ -1,5 +1,19 @@
1
1
  /** Light values below the knee are kept */
2
2
  export declare const TONE_KNEE = 0.6;
3
+ /**
4
+ * Depth (fraction of the box sphere diameter) within which the fragments
5
+ * belong to the front layer: a few half-float steps
6
+ */
7
+ export declare const FRONT_EPSILON = 0.002;
8
+ /** Front depth of the pixels without transparent fragment */
9
+ export declare const FRONT_CLEAR = 4;
10
+ /** Fainter fragments don't define the front layer (soft edges, glows) */
11
+ export declare const FRONT_MIN_ALPHA = 0.05;
12
+ /**
13
+ * Gain of the exponential tone curve of the screen-blended light (WebGL 1,
14
+ * no float targets): 1 − e^(−1.4 x) stays within 0.06 of `toneMap`'s curve
15
+ */
16
+ export declare const SCREEN_GAIN = 1.4;
3
17
  /**
4
18
  * Weight of a blended fragment at a depth (0 nearest … 1 farthest point of
5
19
  * the box sphere): 64 × 2^(−12 depth), at least 0.01. Accumulated in half
@@ -12,21 +26,62 @@ export declare const oitWeight: (depth: number) => number;
12
26
  * gradient instead of a flat plateau
13
27
  */
14
28
  export declare const shoulder: (x: number) => number;
15
- /** Tone mapping of additive light (linear, premultiplied) */
16
- export declare const toneMap: (rgb: readonly [number, number, number]) => [number, number, number];
17
- /** What a blended fragment adds to the two targets (JS mirror of the shaders) */
18
- export declare const oitFragment: (premultiplied: readonly [number, number, number, number], depth: number, additive: boolean) => {
29
+ /**
30
+ * Tone mapping of additive light (linear, premultiplied), into `out` when
31
+ * given (no allocation: per pixel on the CPU)
32
+ */
33
+ export declare const toneMap: (rgb: readonly [number, number, number], out?: [number, number, number]) => [number, number, number];
34
+ /**
35
+ * Screen-blended light: what an additive fragment of light `x` (one channel)
36
+ * writes with the blending `(one, one − src color)`. The framebuffer then
37
+ * holds `1 − e^(−SCREEN_GAIN Σx)`: the light is tone mapped as it adds up.
38
+ */
39
+ export declare const screenTone: (x: number) => number;
40
+ /** What a blended fragment adds to the four targets */
41
+ export interface OitOutput {
42
+ front: number[];
19
43
  accum: number[];
20
44
  extra: number[];
21
- };
22
- /** Composite of the targets: a premultiplied color (JS mirror of the shaders) */
23
- export declare const oitComposite: (accum: readonly number[], extra: readonly number[]) => [number, number, number, number];
24
- /** Second fragment entry point of the points and meshes: `fsOit` */
45
+ behind: number[];
46
+ }
47
+ /**
48
+ * Depth of the front layer of a pixel from its blended fragments (JS
49
+ * mirror of the front pass): the nearest visible non-additive fragment
50
+ */
51
+ export declare const oitFrontDepth: (fragments: readonly {
52
+ color: readonly [number, number, number, number];
53
+ depth: number;
54
+ additive?: boolean;
55
+ }[]) => number;
56
+ /**
57
+ * What a blended fragment adds to the targets (JS mirror of the shaders),
58
+ * `front` the depth of the front layer of its pixel (`oitFrontDepth`)
59
+ */
60
+ export declare const oitFragment: (premultiplied: readonly [number, number, number, number], depth: number, additive: boolean, front?: number) => OitOutput;
61
+ /**
62
+ * Composite of the targets: a premultiplied color blended over the opaque
63
+ * scene (JS mirror of the shaders)
64
+ */
65
+ export declare const oitComposite: (accum: readonly number[], extra: readonly number[], front?: readonly number[], behind?: readonly number[]) => [number, number, number, number];
66
+ /**
67
+ * Transparency outputs of the points and meshes (`fsOit`, `fsFront`): the
68
+ * front depth texture is bound in group 1 by the main pass only
69
+ */
25
70
  export declare const WGSL_OIT: string;
26
- /** Full-screen composite of the two targets over the opaque scene */
71
+ /** Full-screen composite of the four targets over the opaque scene */
27
72
  export declare const WGSL_COMPOSITE: string;
28
- /** Outputs of the OIT variants (GLSL ES 3.0): `EMIT(color, depth)` */
73
+ /**
74
+ * Outputs of the OIT variants (GLSL ES 3.0): `EMIT(color, depth)`; the
75
+ * front depth texture is `u_front`
76
+ */
29
77
  export declare const GLSL_OIT: string;
78
+ /** Output of the front pass (GLSL ES 3.0): the depth of visible fragments */
79
+ export declare const GLSL_FRONT: string;
80
+ /**
81
+ * Plain output (`output` the output variable): additive light screen
82
+ * blended, tone mapped as it adds up (`screenTone`)
83
+ */
84
+ export declare const glslScreen: (output: string) => string;
30
85
  /** Composite program of WebGL2 (full-screen triangle from `gl_VertexID`) */
31
86
  export declare const GLSL_COMPOSITE: {
32
87
  vertex: string;