babylon.quarks 0.26.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 +769 -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 +1959 -588
  11. package/package.json +12 -6
  12. package/src/BatchedRenderer.ts +204 -101
  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 +909 -2061
  17. package/src/QuarksLoader.ts +421 -67
  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 +106 -246
  22. package/src/TrailBatch.ts +533 -342
  23. package/src/VFXBatch.ts +236 -136
  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 +32 -33
  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 +10 -0
  35. package/src/shaders/local_particle_physics_vert.glsl.ts +14 -28
  36. package/src/shaders/local_particle_physics_vert.wgsl.ts +16 -29
  37. package/src/shaders/particle_frag.glsl.ts +3 -22
  38. package/src/shaders/particle_frag.wgsl.ts +3 -23
  39. package/src/shaders/particle_physics_frag.glsl.ts +12 -25
  40. package/src/shaders/particle_physics_frag.wgsl.ts +12 -26
  41. package/src/shaders/particle_vert.glsl.ts +5 -27
  42. package/src/shaders/particle_vert.wgsl.ts +5 -26
  43. package/src/shaders/stretched_bb_particle_vert.glsl.ts +7 -33
  44. package/src/shaders/stretched_bb_particle_vert.wgsl.ts +7 -33
  45. package/src/shaders/trail_frag.glsl.ts +11 -1
  46. package/src/shaders/trail_frag.wgsl.ts +11 -0
  47. package/src/shaders/trail_vert.glsl.ts +45 -36
  48. package/src/shaders/trail_vert.wgsl.ts +46 -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
+ }
@@ -108,6 +108,7 @@ export function surfaceColorGLSL(tileBlend: boolean): string {
108
108
  baseColor.a = 1.0;
109
109
  float erosionMaskValue = 1.0;
110
110
  #endif
111
+ #if defined(USE_MAP) || defined(GRADIENT_MAP)
111
112
  #ifdef USE_MAP
112
113
  vec4 texColor = texture2D(map, mapUV);
113
114
  ${
@@ -130,6 +131,10 @@ export function surfaceColorGLSL(tileBlend: boolean): string {
130
131
  #endif`
131
132
  : ''
132
133
  }
134
+ #else
135
+ // No map: the ramp is looked up at white, its far end.
136
+ vec4 texColor = vec4(1.0);
137
+ #endif
133
138
  #ifdef GRADIENT_MAP
134
139
  float rampAt = clamp(dot(texColor, gradientMapChannel), 0.0, 1.0);
135
140
  vec4 ramp = texture2D(gradientMap, vec2(mix(gradientMapRange.x, gradientMapRange.y, rampAt), 0.5));
@@ -221,6 +226,7 @@ export function surfaceColorWGSL(tileBlend: boolean): string {
221
226
  baseColor.a = 1.0;
222
227
  var erosionMaskValue = 1.0;
223
228
  #endif
229
+ #if defined(USE_MAP) || defined(GRADIENT_MAP)
224
230
  #ifdef USE_MAP
225
231
  var texColor = textureSample(map, mapSampler, mapUV);
226
232
  ${
@@ -243,6 +249,10 @@ export function surfaceColorWGSL(tileBlend: boolean): string {
243
249
  #endif`
244
250
  : ''
245
251
  }
252
+ #else
253
+ // No map: the ramp is looked up at white, its far end.
254
+ var texColor = vec4f(1.0);
255
+ #endif
246
256
  #ifdef GRADIENT_MAP
247
257
  let rampAt = clamp(dot(texColor, uniforms.gradientMapChannel), 0.0, 1.0);
248
258
  let ramp = textureSample(gradientMap, gradientMapSampler, vec2f(mix(uniforms.gradientMapRange.x, uniforms.gradientMapRange.y, rampAt), 0.5));
@@ -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,11 +14,7 @@ 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;
@@ -32,11 +29,6 @@ varying vec2 vUV1;
32
29
  varying vec3 vNormal;
33
30
  varying vec3 vWorldPos;
34
31
 
35
- #ifdef TILE_BLEND
36
- varying vec2 vUV2;
37
- varying float vTileBlend;
38
- #endif
39
-
40
32
  #ifdef SOFT_PARTICLES
41
33
  varying vec4 projPosition;
42
34
  varying float linearDepth;
@@ -53,7 +45,15 @@ ${fogVertexDeclarationGLSL}
53
45
  void main() {
54
46
  vec3 scaledPosition = position * size;
55
47
  vec3 rotatedPosition = applyQuaternion(scaledPosition, rotation);
56
- 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);
57
57
  vec3 localPos = rotatedPosition + offset;
58
58
 
59
59
  vec4 worldPos = world * vec4(localPos, 1.0);
@@ -66,25 +66,11 @@ ${fogVertexGLSL('viewPos.xyz')}
66
66
 
67
67
  #ifdef SOFT_PARTICLES
68
68
  projPosition = gl_Position;
69
- 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);
70
71
  #endif
71
72
 
72
- #ifdef UV_TILE
73
- vec2 tc = vec2(tileCountX, tileCountY);
74
- float baseTile = floor(uvTile);
75
- float tileU = mod(baseTile, tc.x) / tc.x;
76
- float tileV = 1.0 - floor(baseTile / tc.x) / tc.y - 1.0 / tc.y;
77
- vUV = uv / tc + vec2(tileU, tileV);
78
- #ifdef TILE_BLEND
79
- float nextTile = ceil(uvTile);
80
- float nextU = mod(nextTile, tc.x) / tc.x;
81
- float nextV = 1.0 - floor(nextTile / tc.x) / tc.y - 1.0 / tc.y;
82
- vUV2 = uv / tc + vec2(nextU, nextV);
83
- vTileBlend = fract(uvTile);
84
- #endif
85
- #else
86
- vUV = uv;
87
- #endif
73
+ ${sheetVertexGLSL('uv')}
88
74
 
89
75
  #ifdef UV1
90
76
  #ifdef MESH_UV2
@@ -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,11 +14,7 @@ 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;
@@ -32,11 +29,6 @@ varying vUV1: vec2f;
32
29
  varying vNormal: vec3f;
33
30
  varying vWorldPos: vec3f;
34
31
 
35
- #ifdef TILE_BLEND
36
- varying vUV2: vec2f;
37
- varying vTileBlend: f32;
38
- #endif
39
-
40
32
  #ifdef SOFT_PARTICLES
41
33
  varying projPosition: vec4f;
42
34
  varying linearDepth: f32;
@@ -52,9 +44,18 @@ fn applyQuaternion(v: vec3f, q: vec4f) -> vec3f {
52
44
  ${fogVertexDeclarationWGSL}
53
45
  @vertex
54
46
  fn main(input: VertexInputs) -> FragmentInputs {
55
- let scaledPosition = vertexInputs.position * vertexInputs.size;
47
+ let size = vertexInputs.size;
48
+ let scaledPosition = vertexInputs.position * size;
56
49
  let rotatedPosition = applyQuaternion(scaledPosition, vertexInputs.rotation);
57
- 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);
58
59
  let localPos = rotatedPosition + vertexInputs.offset;
59
60
 
60
61
  let worldPos = uniforms.world * vec4f(localPos, 1.0);
@@ -68,25 +69,11 @@ ${fogVertexWGSL('viewPos.xyz')}
68
69
 
69
70
  #ifdef SOFT_PARTICLES
70
71
  vertexOutputs.projPosition = clipPosition;
71
- 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);
72
74
  #endif
73
75
 
74
- #ifdef UV_TILE
75
- let tc = vec2f(uniforms.tileCountX, uniforms.tileCountY);
76
- let baseTile = floor(vertexInputs.uvTile);
77
- let tileU = (baseTile % tc.x) / tc.x;
78
- let tileV = 1.0 - floor(baseTile / tc.x) / tc.y - 1.0 / tc.y;
79
- vertexOutputs.vUV = vertexInputs.uv / tc + vec2f(tileU, tileV);
80
- #ifdef TILE_BLEND
81
- let nextTile = ceil(vertexInputs.uvTile);
82
- let nextU = (nextTile % tc.x) / tc.x;
83
- let nextV = 1.0 - floor(nextTile / tc.x) / tc.y - 1.0 / tc.y;
84
- vertexOutputs.vUV2 = vertexInputs.uv / tc + vec2f(nextU, nextV);
85
- vertexOutputs.vTileBlend = fract(vertexInputs.uvTile);
86
- #endif
87
- #else
88
- vertexOutputs.vUV = vertexInputs.uv;
89
- #endif
76
+ ${sheetVertexWGSL('vertexInputs.uv')}
90
77
 
91
78
  #ifdef UV1
92
79
  #ifdef MESH_UV2
@@ -1,4 +1,5 @@
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 */ `
@@ -17,26 +18,16 @@ varying float vTileBlend;
17
18
  uniform sampler2D map;
18
19
  #endif
19
20
 
20
- #ifdef SOFT_PARTICLES
21
- uniform sampler2D depthTexture;
22
- uniform vec2 softParams;
23
- uniform vec4 projParams;
24
- varying vec4 projPosition;
25
- varying float linearDepth;
26
- #endif
27
21
 
28
22
  #ifdef USE_ALPHATEST
29
23
  uniform float alphaTest;
30
24
  #endif
31
25
 
32
26
  ${fogFragmentDeclarationGLSL}
27
+ ${softFragmentDeclarationGLSL}
33
28
  ${surfaceFragmentDeclarationGLSL}
34
29
  void main() {
35
30
  ${surfaceColorGLSL(true)}
36
- #ifdef USE_COLOR_AS_ALPHA
37
- baseColor.a *= (baseColor.r + baseColor.g + baseColor.b) / 3.0;
38
- #endif
39
-
40
31
  #ifdef USE_ALPHATEST
41
32
  if (baseColor.a < alphaTest) discard;
42
33
  #else
@@ -45,16 +36,6 @@ ${surfaceColorGLSL(true)}
45
36
 
46
37
  ${fogFragmentGLSL('baseColor.rgb')}
47
38
  gl_FragColor = baseColor;
48
-
49
- #ifdef SOFT_PARTICLES
50
- vec2 p2 = projPosition.xy / projPosition.w;
51
- p2 = 0.5 * p2 + 0.5;
52
- float readDepth = texture2D(depthTexture, p2.xy).r;
53
- float zNear = projParams.x;
54
- float zFar = projParams.y;
55
- float viewDepth = (zFar * zNear) / (zFar - readDepth * (zFar - zNear));
56
- float fade = clamp(softParams.y * ((viewDepth - softParams.x) - linearDepth), 0.0, 1.0);
57
- gl_FragColor *= fade;
58
- #endif
39
+ ${softFragmentGLSL('gl_FragColor')}
59
40
  }
60
41
  `;
@@ -1,4 +1,5 @@
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 */ `
@@ -18,46 +19,25 @@ var mapSampler: sampler;
18
19
  var map: texture_2d<f32>;
19
20
  #endif
20
21
 
21
- #ifdef SOFT_PARTICLES
22
- var depthTextureSampler: sampler;
23
- var depthTexture: texture_2d<f32>;
24
- uniform softParams: vec2f;
25
- uniform projParams: vec4f;
26
- varying projPosition: vec4f;
27
- varying linearDepth: f32;
28
- #endif
29
22
 
30
23
  #ifdef USE_ALPHATEST
31
24
  uniform alphaTest: f32;
32
25
  #endif
33
26
 
34
27
  ${fogFragmentDeclarationWGSL}
28
+ ${softFragmentDeclarationWGSL}
35
29
  ${surfaceFragmentDeclarationWGSL}
36
30
  @fragment
37
31
  fn main(input: FragmentInputs) -> FragmentOutputs {
38
32
  ${surfaceColorWGSL(true)}
39
- #ifdef USE_COLOR_AS_ALPHA
40
- baseColor.a *= (baseColor.r + baseColor.g + baseColor.b) / 3.0;
41
- #endif
42
-
43
33
  #ifdef USE_ALPHATEST
44
34
  if (baseColor.a < uniforms.alphaTest) { discard; }
45
35
  #else
46
36
  if (baseColor.a < 0.01) { discard; }
47
37
  #endif
48
38
 
49
- #ifdef SOFT_PARTICLES
50
- var p2 = fragmentInputs.projPosition.xy / fragmentInputs.projPosition.w;
51
- p2 = 0.5 * p2 + 0.5;
52
- let readDepth = textureSample(depthTexture, depthTextureSampler, p2).r;
53
- let zNear = uniforms.projParams.x;
54
- let zFar = uniforms.projParams.y;
55
- let viewDepth = (zFar * zNear) / (zFar - readDepth * (zFar - zNear));
56
- let fade = clamp(uniforms.softParams.y * ((viewDepth - uniforms.softParams.x) - fragmentInputs.linearDepth), 0.0, 1.0);
57
- baseColor *= fade;
58
- #endif
59
-
60
39
  ${fogFragmentWGSL('baseColor')}
40
+ ${softFragmentWGSL('baseColor')}
61
41
  fragmentOutputs.color = baseColor;
62
42
  }
63
43
  `;
@@ -1,4 +1,5 @@
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 */ `
@@ -79,29 +80,18 @@ vec3 sampleEnvAtlas(vec3 r) {
79
80
  }
80
81
  #endif
81
82
 
82
- #ifdef SOFT_PARTICLES
83
- uniform sampler2D depthTexture;
84
- uniform vec2 softParams;
85
- uniform vec4 projParams;
86
- varying vec4 projPosition;
87
- varying float linearDepth;
88
- #endif
89
83
 
90
84
  #ifdef USE_ALPHATEST
91
85
  uniform float alphaTest;
92
86
  #endif
93
87
 
94
88
  ${fogFragmentDeclarationGLSL}
89
+ ${softFragmentDeclarationGLSL}
95
90
  ${surfaceFragmentDeclarationGLSL}
96
91
  void main() {
97
92
  ${surfaceColorGLSL(true)}
98
- #ifdef USE_ALPHATEST
99
- if (baseColor.a < alphaTest) discard;
100
- #else
101
- if (baseColor.a < 0.01) discard;
102
- #endif
103
-
104
- vec3 N = normalize(vNormal);
93
+ // A back face lights, reflects and rims as the face the camera sees.
94
+ vec3 N = normalize(vNormal) * (gl_FrontFacing ? 1.0 : -1.0);
105
95
  #ifdef MESH_UNLIT
106
96
  vec3 color = baseColor.rgb;
107
97
  #else
@@ -132,18 +122,15 @@ ${surfaceColorGLSL(true)}
132
122
  #endif
133
123
  #endif
134
124
 
125
+ // After the rim, so a fade that empties a face can discard it.
126
+ #ifdef USE_ALPHATEST
127
+ if (baseColor.a < alphaTest) discard;
128
+ #else
129
+ if (baseColor.a < 0.01) discard;
130
+ #endif
131
+
135
132
  ${fogFragmentGLSL('color')}
136
133
  gl_FragColor = vec4(color, baseColor.a);
137
-
138
- #ifdef SOFT_PARTICLES
139
- vec2 p2 = projPosition.xy / projPosition.w;
140
- p2 = 0.5 * p2 + 0.5;
141
- float readDepth = texture2D(depthTexture, p2.xy).r;
142
- float zNear = projParams.x;
143
- float zFar = projParams.y;
144
- float viewDepth = (zFar * zNear) / (zFar - readDepth * (zFar - zNear));
145
- float fade = clamp(softParams.y * ((viewDepth - softParams.x) - linearDepth), 0.0, 1.0);
146
- gl_FragColor *= fade;
147
- #endif
134
+ ${softFragmentGLSL('gl_FragColor')}
148
135
  }
149
136
  `;
@@ -1,4 +1,5 @@
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 */ `
@@ -78,31 +79,19 @@ fn sampleEnvAtlas(r: vec3f) -> vec3f {
78
79
  }
79
80
  #endif
80
81
 
81
- #ifdef SOFT_PARTICLES
82
- var depthTextureSampler: sampler;
83
- var depthTexture: texture_2d<f32>;
84
- uniform softParams: vec2f;
85
- uniform projParams: vec4f;
86
- varying projPosition: vec4f;
87
- varying linearDepth: f32;
88
- #endif
89
82
 
90
83
  #ifdef USE_ALPHATEST
91
84
  uniform alphaTest: f32;
92
85
  #endif
93
86
 
94
87
  ${fogFragmentDeclarationWGSL}
88
+ ${softFragmentDeclarationWGSL}
95
89
  ${surfaceFragmentDeclarationWGSL}
96
90
  @fragment
97
91
  fn main(input: FragmentInputs) -> FragmentOutputs {
98
92
  ${surfaceColorWGSL(true)}
99
- #ifdef USE_ALPHATEST
100
- if (baseColor.a < uniforms.alphaTest) { discard; }
101
- #else
102
- if (baseColor.a < 0.01) { discard; }
103
- #endif
104
-
105
- let N = normalize(fragmentInputs.vNormal);
93
+ // A back face lights, reflects and rims as the face the camera sees.
94
+ let N = normalize(fragmentInputs.vNormal) * select(-1.0, 1.0, fragmentInputs.frontFacing);
106
95
  #ifdef MESH_UNLIT
107
96
  var color = baseColor.rgb;
108
97
  #else
@@ -133,20 +122,17 @@ ${surfaceColorWGSL(true)}
133
122
  #endif
134
123
  #endif
135
124
 
136
- var finalColor = vec4f(color, baseColor.a);
137
-
138
- #ifdef SOFT_PARTICLES
139
- var p2 = fragmentInputs.projPosition.xy / fragmentInputs.projPosition.w;
140
- p2 = 0.5 * p2 + 0.5;
141
- let readDepth = textureSample(depthTexture, depthTextureSampler, p2).r;
142
- let zNear = uniforms.projParams.x;
143
- let zFar = uniforms.projParams.y;
144
- let viewDepth = (zFar * zNear) / (zFar - readDepth * (zFar - zNear));
145
- let fade = clamp(uniforms.softParams.y * ((viewDepth - uniforms.softParams.x) - fragmentInputs.linearDepth), 0.0, 1.0);
146
- finalColor *= fade;
125
+ // After the rim, so a fade that empties a face can discard it.
126
+ #ifdef USE_ALPHATEST
127
+ if (baseColor.a < uniforms.alphaTest) { discard; }
128
+ #else
129
+ if (baseColor.a < 0.01) { discard; }
147
130
  #endif
148
131
 
132
+ var finalColor = vec4f(color, baseColor.a);
133
+
149
134
  ${fogFragmentWGSL('finalColor')}
135
+ ${softFragmentWGSL('finalColor')}
150
136
  fragmentOutputs.color = finalColor;
151
137
  }
152
138
  `;
@@ -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
  // Per-vertex attributes
@@ -16,12 +17,7 @@ attribute float uvTile;
16
17
  uniform mat4 world;
17
18
  uniform mat4 view;
18
19
  uniform mat4 projection;
19
- uniform mat4 worldViewProjection;
20
-
21
- #ifdef UV_TILE
22
- uniform float tileCountX;
23
- uniform float tileCountY;
24
- #endif
20
+ ${sheetVertexDeclarationGLSL}
25
21
 
26
22
  #ifdef PIVOT
27
23
  // Quad offset in particle-size units: xy in the quad's plane, z toward the camera.
@@ -35,10 +31,6 @@ varying vec4 vColor;
35
31
  // The particle's own UV, never moved by the sheet (UV channel 1).
36
32
  varying vec2 vUV1;
37
33
  #endif
38
- #ifdef TILE_BLEND
39
- varying vec2 vUV2;
40
- varying float vTileBlend;
41
- #endif
42
34
  #ifdef SOFT_PARTICLES
43
35
  varying vec4 projPosition;
44
36
  varying float linearDepth;
@@ -103,25 +95,11 @@ void main() {
103
95
  ${fogVertexGLSL('mvPosition.xyz')}
104
96
  #ifdef SOFT_PARTICLES
105
97
  projPosition = gl_Position;
106
- linearDepth = -mvPosition.z;
98
+ // View depth is +z ahead of a left-handed camera and -z ahead of a right-handed one.
99
+ linearDepth = abs(mvPosition.z);
107
100
  #endif
108
101
 
109
- #ifdef UV_TILE
110
- vec2 tc = vec2(tileCountX, tileCountY);
111
- float baseTile = floor(uvTile);
112
- float tileU = mod(baseTile, tc.x) / tc.x;
113
- float tileV = 1.0 - floor(baseTile / tc.x) / tc.y - 1.0 / tc.y;
114
- vUV = uv / tc + vec2(tileU, tileV);
115
- #ifdef TILE_BLEND
116
- float nextTile = ceil(uvTile);
117
- float nextU = mod(nextTile, tc.x) / tc.x;
118
- float nextV = 1.0 - floor(nextTile / tc.x) / tc.y - 1.0 / tc.y;
119
- vUV2 = uv / tc + vec2(nextU, nextV);
120
- vTileBlend = fract(uvTile);
121
- #endif
122
- #else
123
- vUV = uv;
124
- #endif
102
+ ${sheetVertexGLSL('uv')}
125
103
 
126
104
  #ifdef UV1
127
105
  vUV1 = uv;
@@ -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
  // Per-vertex attributes
@@ -16,11 +17,7 @@ attribute uvTile: f32;
16
17
  uniform world: mat4x4f;
17
18
  uniform view: mat4x4f;
18
19
  uniform projection: mat4x4f;
19
-
20
- #ifdef UV_TILE
21
- uniform tileCountX: f32;
22
- uniform tileCountY: f32;
23
- #endif
20
+ ${sheetVertexDeclarationWGSL}
24
21
 
25
22
  #ifdef PIVOT
26
23
  // Quad offset in particle-size units: xy in the quad's plane, z toward the camera.
@@ -34,10 +31,6 @@ varying vColor: vec4f;
34
31
  // The particle's own UV, never moved by the sheet (UV channel 1).
35
32
  varying vUV1: vec2f;
36
33
  #endif
37
- #ifdef TILE_BLEND
38
- varying vUV2: vec2f;
39
- varying vTileBlend: f32;
40
- #endif
41
34
  #ifdef SOFT_PARTICLES
42
35
  varying projPosition: vec4f;
43
36
  varying linearDepth: f32;
@@ -105,25 +98,11 @@ fn main(input: VertexInputs) -> FragmentInputs {
105
98
  ${fogVertexWGSL('mvPosition.xyz')}
106
99
  #ifdef SOFT_PARTICLES
107
100
  vertexOutputs.projPosition = clipPosition;
108
- vertexOutputs.linearDepth = -mvPosition.z;
101
+ // View depth is +z ahead of a left-handed camera and -z ahead of a right-handed one.
102
+ vertexOutputs.linearDepth = abs(mvPosition.z);
109
103
  #endif
110
104
 
111
- #ifdef UV_TILE
112
- let tc = vec2f(uniforms.tileCountX, uniforms.tileCountY);
113
- let baseTile = floor(vertexInputs.uvTile);
114
- let tileU = (baseTile % tc.x) / tc.x;
115
- let tileV = 1.0 - floor(baseTile / tc.x) / tc.y - 1.0 / tc.y;
116
- vertexOutputs.vUV = vertexInputs.uv / tc + vec2f(tileU, tileV);
117
- #ifdef TILE_BLEND
118
- let nextTile = ceil(vertexInputs.uvTile);
119
- let nextU = (nextTile % tc.x) / tc.x;
120
- let nextV = 1.0 - floor(nextTile / tc.x) / tc.y - 1.0 / tc.y;
121
- vertexOutputs.vUV2 = vertexInputs.uv / tc + vec2f(nextU, nextV);
122
- vertexOutputs.vTileBlend = fract(vertexInputs.uvTile);
123
- #endif
124
- #else
125
- vertexOutputs.vUV = vertexInputs.uv;
126
- #endif
105
+ ${sheetVertexWGSL('vertexInputs.uv')}
127
106
 
128
107
  #ifdef UV1
129
108
  vertexOutputs.vUV1 = vertexInputs.uv;