babylon.quarks 0.25.0 → 0.27.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 (79) hide show
  1. package/CHANGELOG.md +854 -8
  2. package/README.md +10 -8
  3. package/dist/babylon.quarks.cjs +1 -1
  4. package/dist/babylon.quarks.cjs.map +1 -1
  5. package/dist/babylon.quarks.esm.js +1 -1
  6. package/dist/babylon.quarks.esm.js.map +1 -1
  7. package/dist/babylon.quarks.umd.min.js +1 -1
  8. package/dist/babylon.quarks.umd.min.js.map +1 -1
  9. package/dist/types/index.d.cts +6683 -0
  10. package/dist/types/index.d.ts +2210 -613
  11. package/package.json +12 -6
  12. package/src/BatchedRenderer.ts +215 -106
  13. package/src/EffectPool.ts +4 -1
  14. package/src/MeshSurfaceEmitter.ts +29 -4
  15. package/src/ParticleEmitter.ts +45 -15
  16. package/src/ParticleSystem.ts +1012 -1962
  17. package/src/QuarksLoader.ts +463 -75
  18. package/src/QuarksPrefab.ts +91 -4
  19. package/src/QuarksSerializer.ts +484 -0
  20. package/src/QuarksUtil.ts +15 -0
  21. package/src/SpriteBatch.ts +149 -238
  22. package/src/TrailBatch.ts +586 -286
  23. package/src/VFXBatch.ts +517 -153
  24. package/src/batchKey.ts +237 -0
  25. package/src/effectResources.ts +79 -0
  26. package/src/envAtlas.ts +119 -17
  27. package/src/index.ts +51 -35
  28. package/src/nodeId.ts +13 -0
  29. package/src/shaderProgram.ts +325 -0
  30. package/src/shaderVariants.ts +225 -0
  31. package/src/shaders/chunks/fog.ts +4 -2
  32. package/src/shaders/chunks/sheet.ts +111 -0
  33. package/src/shaders/chunks/soft.ts +84 -0
  34. package/src/shaders/chunks/surface.ts +132 -42
  35. package/src/shaders/local_particle_physics_vert.glsl.ts +28 -27
  36. package/src/shaders/local_particle_physics_vert.wgsl.ts +30 -28
  37. package/src/shaders/particle_frag.glsl.ts +6 -22
  38. package/src/shaders/particle_frag.wgsl.ts +6 -23
  39. package/src/shaders/particle_physics_frag.glsl.ts +22 -29
  40. package/src/shaders/particle_physics_frag.wgsl.ts +22 -30
  41. package/src/shaders/particle_vert.glsl.ts +11 -26
  42. package/src/shaders/particle_vert.wgsl.ts +11 -25
  43. package/src/shaders/stretched_bb_particle_vert.glsl.ts +21 -39
  44. package/src/shaders/stretched_bb_particle_vert.wgsl.ts +22 -40
  45. package/src/shaders/trail_frag.glsl.ts +14 -1
  46. package/src/shaders/trail_frag.wgsl.ts +14 -0
  47. package/src/shaders/trail_vert.glsl.ts +53 -36
  48. package/src/shaders/trail_vert.wgsl.ts +54 -40
  49. package/src/softDepth.ts +136 -0
  50. package/src/system/BehaviorPlan.ts +85 -0
  51. package/src/system/BoundsTracker.ts +181 -0
  52. package/src/system/EmissionController.ts +360 -0
  53. package/src/system/EventDispatcher.ts +44 -0
  54. package/src/system/ParticleSpawner.ts +274 -0
  55. package/src/system/SimulationClock.ts +130 -0
  56. package/src/system/geometry.ts +111 -0
  57. package/src/system/integrate.ts +428 -0
  58. package/src/system/materialResolve.ts +116 -0
  59. package/src/system/normalize.ts +95 -0
  60. package/src/system/serialization.ts +401 -0
  61. package/src/system/settingsSchema.ts +317 -0
  62. package/src/system/types.ts +410 -0
  63. package/src/textureSource.ts +30 -0
  64. package/src/trailSettings.ts +51 -0
  65. package/src/BatchedParticleRenderer.ts +0 -3
  66. package/src/materials/ParticleMaterials.ts +0 -23
  67. package/src/materials/index.ts +0 -1
  68. package/src/shaders/chunks/index.ts +0 -1
  69. package/src/shaders/chunks/register-shader-chunks.ts +0 -32
  70. package/src/shaders/chunks/soft_fragment.glsl.ts +0 -13
  71. package/src/shaders/chunks/soft_pars_fragment.glsl.ts +0 -20
  72. package/src/shaders/chunks/soft_pars_vertex.glsl.ts +0 -6
  73. package/src/shaders/chunks/soft_vertex.glsl.ts +0 -6
  74. package/src/shaders/chunks/tile_fragment.glsl.ts +0 -9
  75. package/src/shaders/chunks/tile_pars_fragment.glsl.ts +0 -9
  76. package/src/shaders/chunks/tile_pars_vertex.glsl.ts +0 -28
  77. package/src/shaders/chunks/tile_vertex.glsl.ts +0 -18
  78. package/src/shaders/index.ts +0 -9
  79. package/src/shaders/local_particle_vert.glsl.ts +0 -50
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Soft particles for the particle shaders, in both shader languages.
3
+ *
4
+ * The fragment reads the scene depth behind it from `depthTexture`, turns it into view depth with
5
+ * the coefficients in `projParams` (see `softDepthParams`), and fades out over the distance in
6
+ * `softParams` as it nears the scene, as Unity's soft particles and Unreal's Depth Fade do. The
7
+ * fade runs last, after fog, so both languages produce the same colour.
8
+ *
9
+ * `SOFT_DEPTH_PACKED` reads a depth packed into four 8-bit channels, as a Babylon
10
+ * `DepthRenderer` of type `UNSIGNED_BYTE` writes it.
11
+ */
12
+
13
+ /** Fragment-side declaration and depth decode, GLSL. */
14
+ export const softFragmentDeclarationGLSL = /* glsl */ `
15
+ #ifdef SOFT_PARTICLES
16
+ uniform sampler2D depthTexture;
17
+ uniform vec2 softParams;
18
+ uniform vec4 projParams;
19
+ varying vec4 projPosition;
20
+ varying float linearDepth;
21
+
22
+ float quarksSceneDepth(vec4 texel) {
23
+ #ifdef SOFT_DEPTH_PACKED
24
+ float d = dot(texel, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0));
25
+ #else
26
+ float d = texel.r;
27
+ #endif
28
+ float denominator = projParams.z * d + projParams.w;
29
+ float depth = abs(denominator) > 1e-8 ? abs((projParams.x * d + projParams.y) / denominator) : 0.0;
30
+ // Nothing drawn there reads as infinitely far.
31
+ return depth > 0.0 ? depth : 1e30;
32
+ }
33
+ #endif
34
+ `;
35
+
36
+ /** Fades `target` as the fragment nears the scene behind it, GLSL. */
37
+ export function softFragmentGLSL(target: string): string {
38
+ return /* glsl */ `
39
+ #ifdef SOFT_PARTICLES
40
+ {
41
+ vec2 softUV = 0.5 * (projPosition.xy / projPosition.w) + 0.5;
42
+ float sceneDepth = quarksSceneDepth(texture2D(depthTexture, softUV));
43
+ ${target} *= clamp(softParams.y * ((sceneDepth - softParams.x) - linearDepth), 0.0, 1.0);
44
+ }
45
+ #endif
46
+ `;
47
+ }
48
+
49
+ /** Fragment-side declaration and depth decode, WGSL. */
50
+ export const softFragmentDeclarationWGSL = /* wgsl */ `
51
+ #ifdef SOFT_PARTICLES
52
+ var depthTextureSampler: sampler;
53
+ var depthTexture: texture_2d<f32>;
54
+ uniform softParams: vec2f;
55
+ uniform projParams: vec4f;
56
+ varying projPosition: vec4f;
57
+ varying linearDepth: f32;
58
+
59
+ fn quarksSceneDepth(texel: vec4f) -> f32 {
60
+ #ifdef SOFT_DEPTH_PACKED
61
+ let d = dot(texel, vec4f(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0));
62
+ #else
63
+ let d = texel.r;
64
+ #endif
65
+ let denominator = uniforms.projParams.z * d + uniforms.projParams.w;
66
+ let depth = select(0.0, abs((uniforms.projParams.x * d + uniforms.projParams.y) / denominator), abs(denominator) > 1e-8);
67
+ // Nothing drawn there reads as infinitely far.
68
+ return select(1e30, depth, depth > 0.0);
69
+ }
70
+ #endif
71
+ `;
72
+
73
+ /** Fades `target` as the fragment nears the scene behind it, WGSL. */
74
+ export function softFragmentWGSL(target: string): string {
75
+ return /* wgsl */ `
76
+ #ifdef SOFT_PARTICLES
77
+ {
78
+ let softUV = 0.5 * (fragmentInputs.projPosition.xy / fragmentInputs.projPosition.w) + 0.5;
79
+ let sceneDepth = quarksSceneDepth(textureSample(depthTexture, depthTextureSampler, softUV));
80
+ ${target} *= clamp(uniforms.softParams.y * ((sceneDepth - uniforms.softParams.x) - fragmentInputs.linearDepth), 0.0, 1.0);
81
+ }
82
+ #endif
83
+ `;
84
+ }
@@ -1,43 +1,65 @@
1
1
  /**
2
2
  * Texture effects shared by the sprite, mesh and trail fragment shaders, in
3
- * both shader languages: UV scrolling, a gradient map and alpha erosion.
3
+ * both shader languages: UV scrolling, UV distortion, a gradient map and
4
+ * alpha erosion.
4
5
  *
5
- * - `UV_SCROLL`: `uvScrollOffset` (already wrapped to 0…1 on the CPU, so it
6
- * never loses precision) is added to the map's UV. The texture should wrap
7
- * (repeat); scrolling a tile of a sheet runs into its neighbours.
8
- * - `GRADIENT_MAP`: the map's red channel picks a colour along U of
9
- * `gradientMap`, whose alpha multiplies the map's. A greyscale texture is
10
- * then recoloured by a ramp, and the particle colour still tints it.
11
- * `gradientMapRange` keeps the lookup between the first and last texel
12
- * centres. Make the ramp without mipmaps: the lookup's UV jumps with the
13
- * map's detail, and the hardware would pick a blurred mip for it.
6
+ * - `UV1`: the vertex shader passes `vUV1`, the particle's own UV that the
7
+ * sprite sheet never moves (see `UVChannel`). The distortion noise and the
8
+ * erosion mask read it under `UV_DISTORTION_UV1` / `EROSION_MASK_UV1`, the
9
+ * map's UV (`vUV`) otherwise.
10
+ * - Each texture can be laid by a transform, `(tiling u, tiling v, offset u,
11
+ * offset v)`: sampled at `uv * transform.xy + transform.zw`, the offset
12
+ * holding its scroll, wrapped on the CPU so it never loses precision (see
13
+ * `TextureUV`).
14
+ * - `MAP_TRANSFORM`: the map is laid by `mapTransform`, on `vUV1` under
15
+ * `MAP_UV1` (where the sheet's second tile is not blended, the sheet not
16
+ * applying), on the sheet's UV otherwise, where a scroll runs into the
17
+ * neighbouring tiles.
18
+ * - `UV_DISTORTION`: the map's UV is pushed about by a noise texture
19
+ * (`distortionMap`, red and green around 0.5, laid by `distortionTransform`)
20
+ * by up to `distortionStrength` UV units: the heat shimmer, licking and
21
+ * breathing of fire and auras. On a sprite sheet the pushed UV can reach
22
+ * the neighbouring tile.
23
+ * - `GRADIENT_MAP`: the map's texel dotted with `gradientMapChannel` (one
24
+ * channel, or luminance weights) picks a colour along U of `gradientMap`.
25
+ * The ramp's alpha multiplies the map's, or replaces it when the lookup is
26
+ * by alpha (`gradientMapChannel.a` 1). The particle colour still tints the
27
+ * result. `gradientMapRange` keeps the lookup between the first and last
28
+ * texel centres. Make the ramp without mipmaps: the lookup's UV jumps with
29
+ * the map's detail, and the hardware would pick a blurred mip for it.
14
30
  * - `EROSION`: the particle's alpha stops being a transparency and becomes how
15
31
  * much of the shape is left. A fragment shows where the mask (the red
16
- * channel of `erosionMask` under `EROSION_MASK`, the map's alpha otherwise)
17
- * is above the cut that alpha sets: alpha 1 shows everything the map shows,
18
- * alpha 0 nothing, and on the way down the shape eats away from its lowest
19
- * mask values first — a noise mask dissolves it, a gradient mask wipes it.
20
- * `erosionParams` is (softness, edge width, mask offset u, mask offset v),
21
- * the edge band just above the cut glows in `erosionEdgeColor`. The mask is
22
- * sampled with the particle's UV as drawn, which on a sprite sheet is the
23
- * current tile's. Erosion only changes alpha, so blend modes that ignore
24
- * alpha (one-one, premultiplied) show a hard cut.
32
+ * channel of `erosionMask` under `EROSION_MASK`, laid by
33
+ * `erosionMaskTransform`; the map's alpha otherwise) is above the cut that
34
+ * alpha sets: alpha 1 shows everything the map shows, alpha 0 nothing, and
35
+ * on the way down the shape eats away from its lowest mask values first — a
36
+ * noise mask dissolves it, a gradient mask wipes it. `erosionParams` is
37
+ * (softness, edge width); the edge band just above the cut glows in
38
+ * `erosionEdgeColor`. Erosion only changes alpha, so blend modes that
39
+ * ignore alpha (one-one, premultiplied) show a hard cut.
25
40
  */
26
41
 
27
42
  /** Fragment-side declarations, GLSL. */
28
43
  export const surfaceFragmentDeclarationGLSL = /* glsl */ `
29
- #ifdef UV_SCROLL
30
- uniform vec2 uvScrollOffset;
44
+ #ifdef MAP_TRANSFORM
45
+ uniform vec4 mapTransform;
46
+ #endif
47
+ #ifdef UV_DISTORTION
48
+ uniform sampler2D distortionMap;
49
+ uniform vec4 distortionTransform;
50
+ uniform float distortionStrength;
31
51
  #endif
32
52
  #ifdef GRADIENT_MAP
33
53
  uniform sampler2D gradientMap;
34
54
  uniform vec2 gradientMapRange;
55
+ uniform vec4 gradientMapChannel;
35
56
  #endif
36
57
  #ifdef EROSION
37
- uniform vec4 erosionParams;
58
+ uniform vec2 erosionParams;
38
59
  uniform vec3 erosionEdgeColor;
39
60
  #ifdef EROSION_MASK
40
61
  uniform sampler2D erosionMask;
62
+ uniform vec4 erosionMaskTransform;
41
63
  #endif
42
64
 
43
65
  vec4 quarksErode(vec4 color, float level, float mask) {
@@ -65,33 +87,58 @@ vec4 quarksErode(vec4 color, float level, float mask) {
65
87
  export function surfaceColorGLSL(tileBlend: boolean): string {
66
88
  return /* glsl */ `
67
89
  vec4 baseColor = vColor;
68
- #ifdef UV_SCROLL
69
- vec2 mapUV = vUV + uvScrollOffset;
90
+ #if defined(MAP_TRANSFORM) && defined(MAP_UV1)
91
+ vec2 mapUV = vUV1 * mapTransform.xy + mapTransform.zw;
92
+ #elif defined(MAP_TRANSFORM)
93
+ vec2 mapUV = vUV * mapTransform.xy + mapTransform.zw;
70
94
  #else
71
95
  vec2 mapUV = vUV;
72
96
  #endif
97
+ #ifdef UV_DISTORTION
98
+ #ifdef UV_DISTORTION_UV1
99
+ vec2 distortionUV = vUV1;
100
+ #else
101
+ vec2 distortionUV = vUV;
102
+ #endif
103
+ vec2 distortion = (texture2D(distortionMap, distortionUV * distortionTransform.xy + distortionTransform.zw).rg - 0.5) * 2.0 * distortionStrength;
104
+ mapUV += distortion;
105
+ #endif
73
106
  #ifdef EROSION
74
107
  float erosionLevel = baseColor.a;
75
108
  baseColor.a = 1.0;
76
109
  float erosionMaskValue = 1.0;
77
110
  #endif
111
+ #if defined(USE_MAP) || defined(GRADIENT_MAP)
78
112
  #ifdef USE_MAP
79
113
  vec4 texColor = texture2D(map, mapUV);
80
114
  ${
81
115
  tileBlend
82
116
  ? `#ifdef TILE_BLEND
83
- #ifdef UV_SCROLL
84
- vec4 texColor2 = texture2D(map, vUV2 + uvScrollOffset);
117
+ #if defined(MAP_TRANSFORM) && defined(MAP_UV1)
118
+ vec2 mapUV2 = mapUV;
85
119
  #else
86
- vec4 texColor2 = texture2D(map, vUV2);
120
+ #ifdef MAP_TRANSFORM
121
+ vec2 mapUV2 = vUV2 * mapTransform.xy + mapTransform.zw;
122
+ #else
123
+ vec2 mapUV2 = vUV2;
124
+ #endif
125
+ #ifdef UV_DISTORTION
126
+ mapUV2 += distortion;
127
+ #endif
87
128
  #endif
129
+ vec4 texColor2 = texture2D(map, mapUV2);
88
130
  texColor = mix(texColor, texColor2, vTileBlend);
89
131
  #endif`
90
132
  : ''
91
133
  }
134
+ #else
135
+ // No map: the ramp is looked up at white, its far end.
136
+ vec4 texColor = vec4(1.0);
137
+ #endif
92
138
  #ifdef GRADIENT_MAP
93
- vec4 ramp = texture2D(gradientMap, vec2(mix(gradientMapRange.x, gradientMapRange.y, clamp(texColor.r, 0.0, 1.0)), 0.5));
94
- texColor = vec4(ramp.rgb, ramp.a * texColor.a);
139
+ float rampAt = clamp(dot(texColor, gradientMapChannel), 0.0, 1.0);
140
+ vec4 ramp = texture2D(gradientMap, vec2(mix(gradientMapRange.x, gradientMapRange.y, rampAt), 0.5));
141
+ texColor = vec4(ramp.rgb, ramp.a * mix(texColor.a, 1.0, gradientMapChannel.a));
95
142
  #endif
96
143
  #ifdef EROSION
97
144
  erosionMaskValue = texColor.a;
@@ -100,7 +147,12 @@ export function surfaceColorGLSL(tileBlend: boolean): string {
100
147
  #endif
101
148
  #ifdef EROSION
102
149
  #ifdef EROSION_MASK
103
- erosionMaskValue = texture2D(erosionMask, vUV + erosionParams.zw).r;
150
+ #ifdef EROSION_MASK_UV1
151
+ vec2 erosionUV = vUV1;
152
+ #else
153
+ vec2 erosionUV = vUV;
154
+ #endif
155
+ erosionMaskValue = texture2D(erosionMask, erosionUV * erosionMaskTransform.xy + erosionMaskTransform.zw).r;
104
156
  #endif
105
157
  baseColor = quarksErode(baseColor, erosionLevel, erosionMaskValue);
106
158
  #endif
@@ -109,20 +161,28 @@ export function surfaceColorGLSL(tileBlend: boolean): string {
109
161
 
110
162
  /** Fragment-side declarations, WGSL. */
111
163
  export const surfaceFragmentDeclarationWGSL = /* wgsl */ `
112
- #ifdef UV_SCROLL
113
- uniform uvScrollOffset: vec2f;
164
+ #ifdef MAP_TRANSFORM
165
+ uniform mapTransform: vec4f;
166
+ #endif
167
+ #ifdef UV_DISTORTION
168
+ var distortionMapSampler: sampler;
169
+ var distortionMap: texture_2d<f32>;
170
+ uniform distortionTransform: vec4f;
171
+ uniform distortionStrength: f32;
114
172
  #endif
115
173
  #ifdef GRADIENT_MAP
116
174
  var gradientMapSampler: sampler;
117
175
  var gradientMap: texture_2d<f32>;
118
176
  uniform gradientMapRange: vec2f;
177
+ uniform gradientMapChannel: vec4f;
119
178
  #endif
120
179
  #ifdef EROSION
121
- uniform erosionParams: vec4f;
180
+ uniform erosionParams: vec2f;
122
181
  uniform erosionEdgeColor: vec3f;
123
182
  #ifdef EROSION_MASK
124
183
  var erosionMaskSampler: sampler;
125
184
  var erosionMask: texture_2d<f32>;
185
+ uniform erosionMaskTransform: vec4f;
126
186
  #endif
127
187
 
128
188
  fn quarksErode(colorIn: vec4f, level: f32, mask: f32) -> vec4f {
@@ -145,33 +205,58 @@ fn quarksErode(colorIn: vec4f, level: f32, mask: f32) -> vec4f {
145
205
  export function surfaceColorWGSL(tileBlend: boolean): string {
146
206
  return /* wgsl */ `
147
207
  var baseColor = fragmentInputs.vColor;
148
- #ifdef UV_SCROLL
149
- let mapUV = fragmentInputs.vUV + uniforms.uvScrollOffset;
208
+ #if defined(MAP_TRANSFORM) && defined(MAP_UV1)
209
+ var mapUV = fragmentInputs.vUV1 * uniforms.mapTransform.xy + uniforms.mapTransform.zw;
210
+ #elif defined(MAP_TRANSFORM)
211
+ var mapUV = fragmentInputs.vUV * uniforms.mapTransform.xy + uniforms.mapTransform.zw;
150
212
  #else
151
- let mapUV = fragmentInputs.vUV;
213
+ var mapUV = fragmentInputs.vUV;
214
+ #endif
215
+ #ifdef UV_DISTORTION
216
+ #ifdef UV_DISTORTION_UV1
217
+ let distortionUV = fragmentInputs.vUV1;
218
+ #else
219
+ let distortionUV = fragmentInputs.vUV;
220
+ #endif
221
+ let distortion = (textureSample(distortionMap, distortionMapSampler, distortionUV * uniforms.distortionTransform.xy + uniforms.distortionTransform.zw).rg - 0.5) * 2.0 * uniforms.distortionStrength;
222
+ mapUV += distortion;
152
223
  #endif
153
224
  #ifdef EROSION
154
225
  let erosionLevel = baseColor.a;
155
226
  baseColor.a = 1.0;
156
227
  var erosionMaskValue = 1.0;
157
228
  #endif
229
+ #if defined(USE_MAP) || defined(GRADIENT_MAP)
158
230
  #ifdef USE_MAP
159
231
  var texColor = textureSample(map, mapSampler, mapUV);
160
232
  ${
161
233
  tileBlend
162
234
  ? `#ifdef TILE_BLEND
163
- #ifdef UV_SCROLL
164
- let texColor2 = textureSample(map, mapSampler, fragmentInputs.vUV2 + uniforms.uvScrollOffset);
235
+ #if defined(MAP_TRANSFORM) && defined(MAP_UV1)
236
+ let mapUV2 = mapUV;
165
237
  #else
166
- let texColor2 = textureSample(map, mapSampler, fragmentInputs.vUV2);
238
+ #ifdef MAP_TRANSFORM
239
+ var mapUV2 = fragmentInputs.vUV2 * uniforms.mapTransform.xy + uniforms.mapTransform.zw;
240
+ #else
241
+ var mapUV2 = fragmentInputs.vUV2;
242
+ #endif
243
+ #ifdef UV_DISTORTION
244
+ mapUV2 += distortion;
245
+ #endif
167
246
  #endif
247
+ let texColor2 = textureSample(map, mapSampler, mapUV2);
168
248
  texColor = mix(texColor, texColor2, fragmentInputs.vTileBlend);
169
249
  #endif`
170
250
  : ''
171
251
  }
252
+ #else
253
+ // No map: the ramp is looked up at white, its far end.
254
+ var texColor = vec4f(1.0);
255
+ #endif
172
256
  #ifdef GRADIENT_MAP
173
- let ramp = textureSample(gradientMap, gradientMapSampler, vec2f(mix(uniforms.gradientMapRange.x, uniforms.gradientMapRange.y, clamp(texColor.r, 0.0, 1.0)), 0.5));
174
- texColor = vec4f(ramp.rgb, ramp.a * texColor.a);
257
+ let rampAt = clamp(dot(texColor, uniforms.gradientMapChannel), 0.0, 1.0);
258
+ let ramp = textureSample(gradientMap, gradientMapSampler, vec2f(mix(uniforms.gradientMapRange.x, uniforms.gradientMapRange.y, rampAt), 0.5));
259
+ texColor = vec4f(ramp.rgb, ramp.a * mix(texColor.a, 1.0, uniforms.gradientMapChannel.a));
175
260
  #endif
176
261
  #ifdef EROSION
177
262
  erosionMaskValue = texColor.a;
@@ -180,7 +265,12 @@ export function surfaceColorWGSL(tileBlend: boolean): string {
180
265
  #endif
181
266
  #ifdef EROSION
182
267
  #ifdef EROSION_MASK
183
- erosionMaskValue = textureSample(erosionMask, erosionMaskSampler, fragmentInputs.vUV + uniforms.erosionParams.zw).r;
268
+ #ifdef EROSION_MASK_UV1
269
+ let erosionUV = fragmentInputs.vUV1;
270
+ #else
271
+ let erosionUV = fragmentInputs.vUV;
272
+ #endif
273
+ erosionMaskValue = textureSample(erosionMask, erosionMaskSampler, erosionUV * uniforms.erosionMaskTransform.xy + uniforms.erosionMaskTransform.zw).r;
184
274
  #endif
185
275
  baseColor = quarksErode(baseColor, erosionLevel, erosionMaskValue);
186
276
  #endif
@@ -1,4 +1,5 @@
1
1
  import {fogVertexDeclarationGLSL, fogVertexGLSL} from './chunks/fog';
2
+ import {sheetVertexDeclarationGLSL, sheetVertexGLSL} from './chunks/sheet';
2
3
 
3
4
  export default /* glsl */ `
4
5
  attribute vec3 position;
@@ -13,22 +14,21 @@ attribute float uvTile;
13
14
  uniform mat4 world;
14
15
  uniform mat4 view;
15
16
  uniform mat4 projection;
16
-
17
- #ifdef UV_TILE
18
- uniform float tileCountX;
19
- uniform float tileCountY;
20
- #endif
17
+ ${sheetVertexDeclarationGLSL}
21
18
 
22
19
  varying vec2 vUV;
23
20
  varying vec4 vColor;
21
+ #ifdef UV1
22
+ #ifdef MESH_UV2
23
+ // The mesh's second UV set.
24
+ attribute vec2 uv2;
25
+ #endif
26
+ // UV channel 1: the mesh's second UV set, or its first when it has none.
27
+ varying vec2 vUV1;
28
+ #endif
24
29
  varying vec3 vNormal;
25
30
  varying vec3 vWorldPos;
26
31
 
27
- #ifdef TILE_BLEND
28
- varying vec2 vUV2;
29
- varying float vTileBlend;
30
- #endif
31
-
32
32
  #ifdef SOFT_PARTICLES
33
33
  varying vec4 projPosition;
34
34
  varying float linearDepth;
@@ -45,7 +45,15 @@ ${fogVertexDeclarationGLSL}
45
45
  void main() {
46
46
  vec3 scaledPosition = position * size;
47
47
  vec3 rotatedPosition = applyQuaternion(scaledPosition, rotation);
48
- vec3 rotatedNormal = normalize(applyQuaternion(normal, rotation));
48
+ // A normal scales by the inverse of the size, so a stretched mesh keeps its normals
49
+ // perpendicular; a flat size counts as a sliver rather than dividing by zero.
50
+ vec3 safeSize = vec3(
51
+ abs(size.x) > 1e-6 ? size.x : 1e-6,
52
+ abs(size.y) > 1e-6 ? size.y : 1e-6,
53
+ abs(size.z) > 1e-6 ? size.z : 1e-6);
54
+ // A mirrored size (an odd number of negative components) turns the winding inside out, so the
55
+ // normal is turned with it and the fragment's front-face test agrees with the geometry.
56
+ vec3 rotatedNormal = normalize(applyQuaternion(normal / safeSize, rotation)) * sign(safeSize.x * safeSize.y * safeSize.z);
49
57
  vec3 localPos = rotatedPosition + offset;
50
58
 
51
59
  vec4 worldPos = world * vec4(localPos, 1.0);
@@ -58,26 +66,19 @@ ${fogVertexGLSL('viewPos.xyz')}
58
66
 
59
67
  #ifdef SOFT_PARTICLES
60
68
  projPosition = gl_Position;
61
- linearDepth = -viewPos.z;
69
+ // View depth is +z ahead of a left-handed camera and -z ahead of a right-handed one.
70
+ linearDepth = abs(viewPos.z);
62
71
  #endif
63
72
 
64
- #ifdef UV_TILE
65
- vec2 tc = vec2(tileCountX, tileCountY);
66
- float baseTile = floor(uvTile);
67
- float tileU = mod(baseTile, tc.x) / tc.x;
68
- float tileV = 1.0 - floor(baseTile / tc.x) / tc.y - 1.0 / tc.y;
69
- vUV = uv / tc + vec2(tileU, tileV);
70
- #ifdef TILE_BLEND
71
- float nextTile = ceil(uvTile);
72
- float nextU = mod(nextTile, tc.x) / tc.x;
73
- float nextV = 1.0 - floor(nextTile / tc.x) / tc.y - 1.0 / tc.y;
74
- vUV2 = uv / tc + vec2(nextU, nextV);
75
- vTileBlend = fract(uvTile);
76
- #endif
73
+ ${sheetVertexGLSL('uv')}
74
+
75
+ #ifdef UV1
76
+ #ifdef MESH_UV2
77
+ vUV1 = uv2;
77
78
  #else
78
- vUV = uv;
79
+ vUV1 = uv;
80
+ #endif
79
81
  #endif
80
-
81
82
  vColor = color;
82
83
  }
83
84
  `;
@@ -1,4 +1,5 @@
1
1
  import {fogVertexDeclarationWGSL, fogVertexWGSL} from './chunks/fog';
2
+ import {sheetVertexDeclarationWGSL, sheetVertexWGSL} from './chunks/sheet';
2
3
 
3
4
  export default /* wgsl */ `
4
5
  attribute position: vec3f;
@@ -13,22 +14,21 @@ attribute uvTile: f32;
13
14
  uniform world: mat4x4f;
14
15
  uniform view: mat4x4f;
15
16
  uniform projection: mat4x4f;
16
-
17
- #ifdef UV_TILE
18
- uniform tileCountX: f32;
19
- uniform tileCountY: f32;
20
- #endif
17
+ ${sheetVertexDeclarationWGSL}
21
18
 
22
19
  varying vUV: vec2f;
23
20
  varying vColor: vec4f;
21
+ #ifdef UV1
22
+ #ifdef MESH_UV2
23
+ // The mesh's second UV set.
24
+ attribute uv2: vec2f;
25
+ #endif
26
+ // UV channel 1: the mesh's second UV set, or its first when it has none.
27
+ varying vUV1: vec2f;
28
+ #endif
24
29
  varying vNormal: vec3f;
25
30
  varying vWorldPos: vec3f;
26
31
 
27
- #ifdef TILE_BLEND
28
- varying vUV2: vec2f;
29
- varying vTileBlend: f32;
30
- #endif
31
-
32
32
  #ifdef SOFT_PARTICLES
33
33
  varying projPosition: vec4f;
34
34
  varying linearDepth: f32;
@@ -44,9 +44,18 @@ fn applyQuaternion(v: vec3f, q: vec4f) -> vec3f {
44
44
  ${fogVertexDeclarationWGSL}
45
45
  @vertex
46
46
  fn main(input: VertexInputs) -> FragmentInputs {
47
- let scaledPosition = vertexInputs.position * vertexInputs.size;
47
+ let size = vertexInputs.size;
48
+ let scaledPosition = vertexInputs.position * size;
48
49
  let rotatedPosition = applyQuaternion(scaledPosition, vertexInputs.rotation);
49
- let rotatedNormal = normalize(applyQuaternion(vertexInputs.normal, vertexInputs.rotation));
50
+ // A normal scales by the inverse of the size, so a stretched mesh keeps its normals
51
+ // perpendicular; a flat size counts as a sliver rather than dividing by zero.
52
+ let safeSize = vec3f(
53
+ select(1e-6, size.x, abs(size.x) > 1e-6),
54
+ select(1e-6, size.y, abs(size.y) > 1e-6),
55
+ select(1e-6, size.z, abs(size.z) > 1e-6));
56
+ // A mirrored size (an odd number of negative components) turns the winding inside out, so the
57
+ // normal is turned with it and the fragment's front-face test agrees with the geometry.
58
+ let rotatedNormal = normalize(applyQuaternion(vertexInputs.normal / safeSize, vertexInputs.rotation)) * sign(safeSize.x * safeSize.y * safeSize.z);
50
59
  let localPos = rotatedPosition + vertexInputs.offset;
51
60
 
52
61
  let worldPos = uniforms.world * vec4f(localPos, 1.0);
@@ -60,26 +69,19 @@ ${fogVertexWGSL('viewPos.xyz')}
60
69
 
61
70
  #ifdef SOFT_PARTICLES
62
71
  vertexOutputs.projPosition = clipPosition;
63
- vertexOutputs.linearDepth = -viewPos.z;
72
+ // View depth is +z ahead of a left-handed camera and -z ahead of a right-handed one.
73
+ vertexOutputs.linearDepth = abs(viewPos.z);
64
74
  #endif
65
75
 
66
- #ifdef UV_TILE
67
- let tc = vec2f(uniforms.tileCountX, uniforms.tileCountY);
68
- let baseTile = floor(vertexInputs.uvTile);
69
- let tileU = (baseTile % tc.x) / tc.x;
70
- let tileV = 1.0 - floor(baseTile / tc.x) / tc.y - 1.0 / tc.y;
71
- vertexOutputs.vUV = vertexInputs.uv / tc + vec2f(tileU, tileV);
72
- #ifdef TILE_BLEND
73
- let nextTile = ceil(vertexInputs.uvTile);
74
- let nextU = (nextTile % tc.x) / tc.x;
75
- let nextV = 1.0 - floor(nextTile / tc.x) / tc.y - 1.0 / tc.y;
76
- vertexOutputs.vUV2 = vertexInputs.uv / tc + vec2f(nextU, nextV);
77
- vertexOutputs.vTileBlend = fract(vertexInputs.uvTile);
78
- #endif
76
+ ${sheetVertexWGSL('vertexInputs.uv')}
77
+
78
+ #ifdef UV1
79
+ #ifdef MESH_UV2
80
+ vertexOutputs.vUV1 = vertexInputs.uv2;
79
81
  #else
80
- vertexOutputs.vUV = vertexInputs.uv;
82
+ vertexOutputs.vUV1 = vertexInputs.uv;
83
+ #endif
81
84
  #endif
82
-
83
85
  vertexOutputs.vColor = vertexInputs.color;
84
86
  }
85
87
  `;
@@ -1,9 +1,13 @@
1
1
  import {fogFragmentDeclarationGLSL, fogFragmentGLSL} from './chunks/fog';
2
+ import {softFragmentDeclarationGLSL, softFragmentGLSL} from './chunks/soft';
2
3
  import {surfaceColorGLSL, surfaceFragmentDeclarationGLSL} from './chunks/surface';
3
4
 
4
5
  export default /* glsl */ `
5
6
  varying vec2 vUV;
6
7
  varying vec4 vColor;
8
+ #ifdef UV1
9
+ varying vec2 vUV1;
10
+ #endif
7
11
 
8
12
  #ifdef TILE_BLEND
9
13
  varying vec2 vUV2;
@@ -14,26 +18,16 @@ varying float vTileBlend;
14
18
  uniform sampler2D map;
15
19
  #endif
16
20
 
17
- #ifdef SOFT_PARTICLES
18
- uniform sampler2D depthTexture;
19
- uniform vec2 softParams;
20
- uniform vec4 projParams;
21
- varying vec4 projPosition;
22
- varying float linearDepth;
23
- #endif
24
21
 
25
22
  #ifdef USE_ALPHATEST
26
23
  uniform float alphaTest;
27
24
  #endif
28
25
 
29
26
  ${fogFragmentDeclarationGLSL}
27
+ ${softFragmentDeclarationGLSL}
30
28
  ${surfaceFragmentDeclarationGLSL}
31
29
  void main() {
32
30
  ${surfaceColorGLSL(true)}
33
- #ifdef USE_COLOR_AS_ALPHA
34
- baseColor.a *= (baseColor.r + baseColor.g + baseColor.b) / 3.0;
35
- #endif
36
-
37
31
  #ifdef USE_ALPHATEST
38
32
  if (baseColor.a < alphaTest) discard;
39
33
  #else
@@ -42,16 +36,6 @@ ${surfaceColorGLSL(true)}
42
36
 
43
37
  ${fogFragmentGLSL('baseColor.rgb')}
44
38
  gl_FragColor = baseColor;
45
-
46
- #ifdef SOFT_PARTICLES
47
- vec2 p2 = projPosition.xy / projPosition.w;
48
- p2 = 0.5 * p2 + 0.5;
49
- float readDepth = texture2D(depthTexture, p2.xy).r;
50
- float zNear = projParams.x;
51
- float zFar = projParams.y;
52
- float viewDepth = (zFar * zNear) / (zFar - readDepth * (zFar - zNear));
53
- float fade = clamp(softParams.y * ((viewDepth - softParams.x) - linearDepth), 0.0, 1.0);
54
- gl_FragColor *= fade;
55
- #endif
39
+ ${softFragmentGLSL('gl_FragColor')}
56
40
  }
57
41
  `;
@@ -1,9 +1,13 @@
1
1
  import {fogFragmentDeclarationWGSL, fogFragmentWGSL} from './chunks/fog';
2
+ import {softFragmentDeclarationWGSL, softFragmentWGSL} from './chunks/soft';
2
3
  import {surfaceColorWGSL, surfaceFragmentDeclarationWGSL} from './chunks/surface';
3
4
 
4
5
  export default /* wgsl */ `
5
6
  varying vUV: vec2f;
6
7
  varying vColor: vec4f;
8
+ #ifdef UV1
9
+ varying vUV1: vec2f;
10
+ #endif
7
11
 
8
12
  #ifdef TILE_BLEND
9
13
  varying vUV2: vec2f;
@@ -15,46 +19,25 @@ var mapSampler: sampler;
15
19
  var map: texture_2d<f32>;
16
20
  #endif
17
21
 
18
- #ifdef SOFT_PARTICLES
19
- var depthTextureSampler: sampler;
20
- var depthTexture: texture_2d<f32>;
21
- uniform softParams: vec2f;
22
- uniform projParams: vec4f;
23
- varying projPosition: vec4f;
24
- varying linearDepth: f32;
25
- #endif
26
22
 
27
23
  #ifdef USE_ALPHATEST
28
24
  uniform alphaTest: f32;
29
25
  #endif
30
26
 
31
27
  ${fogFragmentDeclarationWGSL}
28
+ ${softFragmentDeclarationWGSL}
32
29
  ${surfaceFragmentDeclarationWGSL}
33
30
  @fragment
34
31
  fn main(input: FragmentInputs) -> FragmentOutputs {
35
32
  ${surfaceColorWGSL(true)}
36
- #ifdef USE_COLOR_AS_ALPHA
37
- baseColor.a *= (baseColor.r + baseColor.g + baseColor.b) / 3.0;
38
- #endif
39
-
40
33
  #ifdef USE_ALPHATEST
41
34
  if (baseColor.a < uniforms.alphaTest) { discard; }
42
35
  #else
43
36
  if (baseColor.a < 0.01) { discard; }
44
37
  #endif
45
38
 
46
- #ifdef SOFT_PARTICLES
47
- var p2 = fragmentInputs.projPosition.xy / fragmentInputs.projPosition.w;
48
- p2 = 0.5 * p2 + 0.5;
49
- let readDepth = textureSample(depthTexture, depthTextureSampler, p2).r;
50
- let zNear = uniforms.projParams.x;
51
- let zFar = uniforms.projParams.y;
52
- let viewDepth = (zFar * zNear) / (zFar - readDepth * (zFar - zNear));
53
- let fade = clamp(uniforms.softParams.y * ((viewDepth - uniforms.softParams.x) - fragmentInputs.linearDepth), 0.0, 1.0);
54
- baseColor *= fade;
55
- #endif
56
-
57
39
  ${fogFragmentWGSL('baseColor')}
40
+ ${softFragmentWGSL('baseColor')}
58
41
  fragmentOutputs.color = baseColor;
59
42
  }
60
43
  `;