babylon.quarks 0.25.0 → 0.26.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.
@@ -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,11 +87,22 @@ 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;
@@ -80,18 +113,27 @@ export function surfaceColorGLSL(tileBlend: boolean): string {
80
113
  ${
81
114
  tileBlend
82
115
  ? `#ifdef TILE_BLEND
83
- #ifdef UV_SCROLL
84
- vec4 texColor2 = texture2D(map, vUV2 + uvScrollOffset);
116
+ #if defined(MAP_TRANSFORM) && defined(MAP_UV1)
117
+ vec2 mapUV2 = mapUV;
85
118
  #else
86
- vec4 texColor2 = texture2D(map, vUV2);
119
+ #ifdef MAP_TRANSFORM
120
+ vec2 mapUV2 = vUV2 * mapTransform.xy + mapTransform.zw;
121
+ #else
122
+ vec2 mapUV2 = vUV2;
123
+ #endif
124
+ #ifdef UV_DISTORTION
125
+ mapUV2 += distortion;
126
+ #endif
87
127
  #endif
128
+ vec4 texColor2 = texture2D(map, mapUV2);
88
129
  texColor = mix(texColor, texColor2, vTileBlend);
89
130
  #endif`
90
131
  : ''
91
132
  }
92
133
  #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);
134
+ float rampAt = clamp(dot(texColor, gradientMapChannel), 0.0, 1.0);
135
+ vec4 ramp = texture2D(gradientMap, vec2(mix(gradientMapRange.x, gradientMapRange.y, rampAt), 0.5));
136
+ texColor = vec4(ramp.rgb, ramp.a * mix(texColor.a, 1.0, gradientMapChannel.a));
95
137
  #endif
96
138
  #ifdef EROSION
97
139
  erosionMaskValue = texColor.a;
@@ -100,7 +142,12 @@ export function surfaceColorGLSL(tileBlend: boolean): string {
100
142
  #endif
101
143
  #ifdef EROSION
102
144
  #ifdef EROSION_MASK
103
- erosionMaskValue = texture2D(erosionMask, vUV + erosionParams.zw).r;
145
+ #ifdef EROSION_MASK_UV1
146
+ vec2 erosionUV = vUV1;
147
+ #else
148
+ vec2 erosionUV = vUV;
149
+ #endif
150
+ erosionMaskValue = texture2D(erosionMask, erosionUV * erosionMaskTransform.xy + erosionMaskTransform.zw).r;
104
151
  #endif
105
152
  baseColor = quarksErode(baseColor, erosionLevel, erosionMaskValue);
106
153
  #endif
@@ -109,20 +156,28 @@ export function surfaceColorGLSL(tileBlend: boolean): string {
109
156
 
110
157
  /** Fragment-side declarations, WGSL. */
111
158
  export const surfaceFragmentDeclarationWGSL = /* wgsl */ `
112
- #ifdef UV_SCROLL
113
- uniform uvScrollOffset: vec2f;
159
+ #ifdef MAP_TRANSFORM
160
+ uniform mapTransform: vec4f;
161
+ #endif
162
+ #ifdef UV_DISTORTION
163
+ var distortionMapSampler: sampler;
164
+ var distortionMap: texture_2d<f32>;
165
+ uniform distortionTransform: vec4f;
166
+ uniform distortionStrength: f32;
114
167
  #endif
115
168
  #ifdef GRADIENT_MAP
116
169
  var gradientMapSampler: sampler;
117
170
  var gradientMap: texture_2d<f32>;
118
171
  uniform gradientMapRange: vec2f;
172
+ uniform gradientMapChannel: vec4f;
119
173
  #endif
120
174
  #ifdef EROSION
121
- uniform erosionParams: vec4f;
175
+ uniform erosionParams: vec2f;
122
176
  uniform erosionEdgeColor: vec3f;
123
177
  #ifdef EROSION_MASK
124
178
  var erosionMaskSampler: sampler;
125
179
  var erosionMask: texture_2d<f32>;
180
+ uniform erosionMaskTransform: vec4f;
126
181
  #endif
127
182
 
128
183
  fn quarksErode(colorIn: vec4f, level: f32, mask: f32) -> vec4f {
@@ -145,10 +200,21 @@ fn quarksErode(colorIn: vec4f, level: f32, mask: f32) -> vec4f {
145
200
  export function surfaceColorWGSL(tileBlend: boolean): string {
146
201
  return /* wgsl */ `
147
202
  var baseColor = fragmentInputs.vColor;
148
- #ifdef UV_SCROLL
149
- let mapUV = fragmentInputs.vUV + uniforms.uvScrollOffset;
203
+ #if defined(MAP_TRANSFORM) && defined(MAP_UV1)
204
+ var mapUV = fragmentInputs.vUV1 * uniforms.mapTransform.xy + uniforms.mapTransform.zw;
205
+ #elif defined(MAP_TRANSFORM)
206
+ var mapUV = fragmentInputs.vUV * uniforms.mapTransform.xy + uniforms.mapTransform.zw;
150
207
  #else
151
- let mapUV = fragmentInputs.vUV;
208
+ var mapUV = fragmentInputs.vUV;
209
+ #endif
210
+ #ifdef UV_DISTORTION
211
+ #ifdef UV_DISTORTION_UV1
212
+ let distortionUV = fragmentInputs.vUV1;
213
+ #else
214
+ let distortionUV = fragmentInputs.vUV;
215
+ #endif
216
+ let distortion = (textureSample(distortionMap, distortionMapSampler, distortionUV * uniforms.distortionTransform.xy + uniforms.distortionTransform.zw).rg - 0.5) * 2.0 * uniforms.distortionStrength;
217
+ mapUV += distortion;
152
218
  #endif
153
219
  #ifdef EROSION
154
220
  let erosionLevel = baseColor.a;
@@ -160,18 +226,27 @@ export function surfaceColorWGSL(tileBlend: boolean): string {
160
226
  ${
161
227
  tileBlend
162
228
  ? `#ifdef TILE_BLEND
163
- #ifdef UV_SCROLL
164
- let texColor2 = textureSample(map, mapSampler, fragmentInputs.vUV2 + uniforms.uvScrollOffset);
229
+ #if defined(MAP_TRANSFORM) && defined(MAP_UV1)
230
+ let mapUV2 = mapUV;
165
231
  #else
166
- let texColor2 = textureSample(map, mapSampler, fragmentInputs.vUV2);
232
+ #ifdef MAP_TRANSFORM
233
+ var mapUV2 = fragmentInputs.vUV2 * uniforms.mapTransform.xy + uniforms.mapTransform.zw;
234
+ #else
235
+ var mapUV2 = fragmentInputs.vUV2;
236
+ #endif
237
+ #ifdef UV_DISTORTION
238
+ mapUV2 += distortion;
239
+ #endif
167
240
  #endif
241
+ let texColor2 = textureSample(map, mapSampler, mapUV2);
168
242
  texColor = mix(texColor, texColor2, fragmentInputs.vTileBlend);
169
243
  #endif`
170
244
  : ''
171
245
  }
172
246
  #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);
247
+ let rampAt = clamp(dot(texColor, uniforms.gradientMapChannel), 0.0, 1.0);
248
+ let ramp = textureSample(gradientMap, gradientMapSampler, vec2f(mix(uniforms.gradientMapRange.x, uniforms.gradientMapRange.y, rampAt), 0.5));
249
+ texColor = vec4f(ramp.rgb, ramp.a * mix(texColor.a, 1.0, uniforms.gradientMapChannel.a));
175
250
  #endif
176
251
  #ifdef EROSION
177
252
  erosionMaskValue = texColor.a;
@@ -180,7 +255,12 @@ export function surfaceColorWGSL(tileBlend: boolean): string {
180
255
  #endif
181
256
  #ifdef EROSION
182
257
  #ifdef EROSION_MASK
183
- erosionMaskValue = textureSample(erosionMask, erosionMaskSampler, fragmentInputs.vUV + uniforms.erosionParams.zw).r;
258
+ #ifdef EROSION_MASK_UV1
259
+ let erosionUV = fragmentInputs.vUV1;
260
+ #else
261
+ let erosionUV = fragmentInputs.vUV;
262
+ #endif
263
+ erosionMaskValue = textureSample(erosionMask, erosionMaskSampler, erosionUV * uniforms.erosionMaskTransform.xy + uniforms.erosionMaskTransform.zw).r;
184
264
  #endif
185
265
  baseColor = quarksErode(baseColor, erosionLevel, erosionMaskValue);
186
266
  #endif
@@ -21,6 +21,14 @@ uniform float tileCountY;
21
21
 
22
22
  varying vec2 vUV;
23
23
  varying vec4 vColor;
24
+ #ifdef UV1
25
+ #ifdef MESH_UV2
26
+ // The mesh's second UV set.
27
+ attribute vec2 uv2;
28
+ #endif
29
+ // UV channel 1: the mesh's second UV set, or its first when it has none.
30
+ varying vec2 vUV1;
31
+ #endif
24
32
  varying vec3 vNormal;
25
33
  varying vec3 vWorldPos;
26
34
 
@@ -78,6 +86,13 @@ ${fogVertexGLSL('viewPos.xyz')}
78
86
  vUV = uv;
79
87
  #endif
80
88
 
89
+ #ifdef UV1
90
+ #ifdef MESH_UV2
91
+ vUV1 = uv2;
92
+ #else
93
+ vUV1 = uv;
94
+ #endif
95
+ #endif
81
96
  vColor = color;
82
97
  }
83
98
  `;
@@ -21,6 +21,14 @@ uniform tileCountY: f32;
21
21
 
22
22
  varying vUV: vec2f;
23
23
  varying vColor: vec4f;
24
+ #ifdef UV1
25
+ #ifdef MESH_UV2
26
+ // The mesh's second UV set.
27
+ attribute uv2: vec2f;
28
+ #endif
29
+ // UV channel 1: the mesh's second UV set, or its first when it has none.
30
+ varying vUV1: vec2f;
31
+ #endif
24
32
  varying vNormal: vec3f;
25
33
  varying vWorldPos: vec3f;
26
34
 
@@ -80,6 +88,13 @@ ${fogVertexWGSL('viewPos.xyz')}
80
88
  vertexOutputs.vUV = vertexInputs.uv;
81
89
  #endif
82
90
 
91
+ #ifdef UV1
92
+ #ifdef MESH_UV2
93
+ vertexOutputs.vUV1 = vertexInputs.uv2;
94
+ #else
95
+ vertexOutputs.vUV1 = vertexInputs.uv;
96
+ #endif
97
+ #endif
83
98
  vertexOutputs.vColor = vertexInputs.color;
84
99
  }
85
100
  `;
@@ -4,6 +4,9 @@ import {surfaceColorGLSL, surfaceFragmentDeclarationGLSL} from './chunks/surface
4
4
  export default /* glsl */ `
5
5
  varying vec2 vUV;
6
6
  varying vec4 vColor;
7
+ #ifdef UV1
8
+ varying vec2 vUV1;
9
+ #endif
7
10
 
8
11
  #ifdef TILE_BLEND
9
12
  varying vec2 vUV2;
@@ -4,6 +4,9 @@ import {surfaceColorWGSL, surfaceFragmentDeclarationWGSL} from './chunks/surface
4
4
  export default /* wgsl */ `
5
5
  varying vUV: vec2f;
6
6
  varying vColor: vec4f;
7
+ #ifdef UV1
8
+ varying vUV1: vec2f;
9
+ #endif
7
10
 
8
11
  #ifdef TILE_BLEND
9
12
  varying vUV2: vec2f;
@@ -4,6 +4,9 @@ import {surfaceColorGLSL, surfaceFragmentDeclarationGLSL} from './chunks/surface
4
4
  export default /* glsl */ `
5
5
  varying vec2 vUV;
6
6
  varying vec4 vColor;
7
+ #ifdef UV1
8
+ varying vec2 vUV1;
9
+ #endif
7
10
  varying vec3 vNormal;
8
11
  varying vec3 vWorldPos;
9
12
 
@@ -118,11 +121,14 @@ ${surfaceColorGLSL(true)}
118
121
  #ifdef MESH_RIM
119
122
  // Fresnel: strongest where the surface turns away from the camera.
120
123
  vec3 V = normalize(eyePosition - vWorldPos);
121
- float rim = pow(1.0 - abs(dot(N, V)), rimParams.x) * rimParams.y;
122
- #ifdef MESH_RIM_MIX
123
- color = mix(color, rimColor, clamp(rim, 0.0, 1.0));
124
+ float edgeTerm = pow(1.0 - abs(dot(N, V)), rimParams.x);
125
+ #if defined(MESH_RIM_FADE)
126
+ // Faces turned to the camera fade by the strength; edges stay.
127
+ baseColor.a *= clamp(1.0 - rimParams.y * (1.0 - edgeTerm), 0.0, 1.0);
128
+ #elif defined(MESH_RIM_MIX)
129
+ color = mix(color, rimColor, clamp(edgeTerm * rimParams.y, 0.0, 1.0));
124
130
  #else
125
- color += rimColor * rim;
131
+ color += rimColor * edgeTerm * rimParams.y;
126
132
  #endif
127
133
  #endif
128
134
 
@@ -4,6 +4,9 @@ import {surfaceColorWGSL, surfaceFragmentDeclarationWGSL} from './chunks/surface
4
4
  export default /* wgsl */ `
5
5
  varying vUV: vec2f;
6
6
  varying vColor: vec4f;
7
+ #ifdef UV1
8
+ varying vUV1: vec2f;
9
+ #endif
7
10
  varying vNormal: vec3f;
8
11
  varying vWorldPos: vec3f;
9
12
 
@@ -119,11 +122,14 @@ ${surfaceColorWGSL(true)}
119
122
  #ifdef MESH_RIM
120
123
  // Fresnel: strongest where the surface turns away from the camera.
121
124
  let V = normalize(uniforms.eyePosition - fragmentInputs.vWorldPos);
122
- let rimTerm = pow(1.0 - abs(dot(N, V)), uniforms.rimParams.x) * uniforms.rimParams.y;
123
- #ifdef MESH_RIM_MIX
124
- color = mix(color, uniforms.rimColor, clamp(rimTerm, 0.0, 1.0));
125
+ let edgeTerm = pow(1.0 - abs(dot(N, V)), uniforms.rimParams.x);
126
+ #if defined(MESH_RIM_FADE)
127
+ // Faces turned to the camera fade by the strength; edges stay.
128
+ baseColor.a *= clamp(1.0 - uniforms.rimParams.y * (1.0 - edgeTerm), 0.0, 1.0);
129
+ #elif defined(MESH_RIM_MIX)
130
+ color = mix(color, uniforms.rimColor, clamp(edgeTerm * uniforms.rimParams.y, 0.0, 1.0));
125
131
  #else
126
- color += uniforms.rimColor * rimTerm;
132
+ color += uniforms.rimColor * edgeTerm * uniforms.rimParams.y;
127
133
  #endif
128
134
  #endif
129
135
 
@@ -31,6 +31,10 @@ uniform vec3 pivot;
31
31
  // Varyings
32
32
  varying vec2 vUV;
33
33
  varying vec4 vColor;
34
+ #ifdef UV1
35
+ // The particle's own UV, never moved by the sheet (UV channel 1).
36
+ varying vec2 vUV1;
37
+ #endif
34
38
  #ifdef TILE_BLEND
35
39
  varying vec2 vUV2;
36
40
  varying float vTileBlend;
@@ -119,6 +123,9 @@ ${fogVertexGLSL('mvPosition.xyz')}
119
123
  vUV = uv;
120
124
  #endif
121
125
 
126
+ #ifdef UV1
127
+ vUV1 = uv;
128
+ #endif
122
129
  vColor = color;
123
130
  }
124
131
  `;
@@ -30,6 +30,10 @@ uniform pivot: vec3f;
30
30
  // Varyings
31
31
  varying vUV: vec2f;
32
32
  varying vColor: vec4f;
33
+ #ifdef UV1
34
+ // The particle's own UV, never moved by the sheet (UV channel 1).
35
+ varying vUV1: vec2f;
36
+ #endif
33
37
  #ifdef TILE_BLEND
34
38
  varying vUV2: vec2f;
35
39
  varying vTileBlend: f32;
@@ -121,6 +125,9 @@ ${fogVertexWGSL('mvPosition.xyz')}
121
125
  vertexOutputs.vUV = vertexInputs.uv;
122
126
  #endif
123
127
 
128
+ #ifdef UV1
129
+ vertexOutputs.vUV1 = vertexInputs.uv;
130
+ #endif
124
131
  vertexOutputs.vColor = vertexInputs.color;
125
132
  }
126
133
  `;
@@ -32,6 +32,10 @@ uniform vec3 pivot;
32
32
  // Varyings
33
33
  varying vec2 vUV;
34
34
  varying vec4 vColor;
35
+ #ifdef UV1
36
+ // The particle's own UV, never moved by the sheet (UV channel 1).
37
+ varying vec2 vUV1;
38
+ #endif
35
39
  #ifdef TILE_BLEND
36
40
  varying vec2 vUV2;
37
41
  varying float vTileBlend;
@@ -45,6 +49,7 @@ varying float linearDepth;
45
49
  // Smallest and largest size a particle may have on screen, as fractions of the viewport height.
46
50
  uniform vec2 sizeLimits;
47
51
  #endif
52
+
48
53
  ${fogVertexDeclarationGLSL}
49
54
  void main() {
50
55
  float lengthFactor = velocity.w;
@@ -52,15 +57,6 @@ void main() {
52
57
  // extent and mirrors its texture instead (see quadUV below).
53
58
  float avgSize = (abs(size.x) + abs(size.y)) * 0.5;
54
59
  vec4 mvPosition = view * world * vec4(offset, 1.0);
55
- #ifdef SIZE_LIMITS
56
- // The streak's width on screen as a fraction of the viewport height, held
57
- // between the limits; a perspective projection divides by the view depth.
58
- float limitDepth = abs(projection[2][3]) > 0.0 ? abs(mvPosition.z) : 1.0;
59
- float screenSize = avgSize * projection[1][1] / (2.0 * limitDepth);
60
- if (screenSize > 0.0) {
61
- avgSize *= clamp(screenSize, sizeLimits.x, sizeLimits.y) / screenSize;
62
- }
63
- #endif
64
60
  vec3 viewVelocity = mat3(view * world) * velocity.xyz;
65
61
  float vlength = length(viewVelocity);
66
62
  // Stretch direction is the velocity direction; only a genuinely motionless particle has none.
@@ -73,6 +69,15 @@ void main() {
73
69
  vec3 side = dot(sideRaw, sideRaw) > 1e-12 ? normalize(sideRaw) : vec3(1.0, 0.0, 0.0);
74
70
  #else
75
71
  vec3 side = normalize(cross(mvPosition.xyz, vdir));
72
+ #endif
73
+ #ifdef SIZE_LIMITS
74
+ // The streak's width on screen as a fraction of the viewport height, held
75
+ // between the limits; a perspective projection divides by the view depth.
76
+ float limitDepth = abs(projection[2][3]) > 0.0 ? abs(mvPosition.z) : 1.0;
77
+ float screenSize = avgSize * projection[1][1] / (2.0 * limitDepth);
78
+ if (screenSize > 0.0) {
79
+ avgSize *= clamp(screenSize, sizeLimits.x, sizeLimits.y) / screenSize;
80
+ }
76
81
  #endif
77
82
  mvPosition.xyz += position.y * side * avgSize;
78
83
  #ifdef BEAM
@@ -113,6 +118,9 @@ ${fogVertexGLSL('mvPosition.xyz')}
113
118
  vUV = quadUV;
114
119
  #endif
115
120
 
121
+ #ifdef UV1
122
+ vUV1 = quadUV;
123
+ #endif
116
124
  vColor = color;
117
125
  }
118
126
  `;
@@ -32,6 +32,10 @@ uniform pivot: vec3f;
32
32
  // Varyings
33
33
  varying vUV: vec2f;
34
34
  varying vColor: vec4f;
35
+ #ifdef UV1
36
+ // The particle's own UV, never moved by the sheet (UV channel 1).
37
+ varying vUV1: vec2f;
38
+ #endif
35
39
  #ifdef TILE_BLEND
36
40
  varying vUV2: vec2f;
37
41
  varying vTileBlend: f32;
@@ -45,6 +49,7 @@ varying linearDepth: f32;
45
49
  // Smallest and largest size a particle may have on screen, as fractions of the viewport height.
46
50
  uniform sizeLimits: vec2f;
47
51
  #endif
52
+
48
53
  ${fogVertexDeclarationWGSL}
49
54
  @vertex
50
55
  fn main(input: VertexInputs) -> FragmentInputs {
@@ -53,15 +58,6 @@ fn main(input: VertexInputs) -> FragmentInputs {
53
58
  // extent and mirrors its texture instead (see quadUV below).
54
59
  var avgSize = (abs(vertexInputs.size.x) + abs(vertexInputs.size.y)) * 0.5;
55
60
  var mvPosition = uniforms.view * uniforms.world * vec4f(vertexInputs.offset, 1.0);
56
- #ifdef SIZE_LIMITS
57
- // The streak's width on screen as a fraction of the viewport height, held
58
- // between the limits; a perspective projection divides by the view depth.
59
- let limitDepth = select(1.0, abs(mvPosition.z), abs(uniforms.projection[2][3]) > 0.0);
60
- let screenSize = avgSize * uniforms.projection[1][1] / (2.0 * limitDepth);
61
- if (screenSize > 0.0) {
62
- avgSize *= clamp(screenSize, uniforms.sizeLimits.x, uniforms.sizeLimits.y) / screenSize;
63
- }
64
- #endif
65
61
  let viewWorld = uniforms.view * uniforms.world;
66
62
  let viewWorld3 = mat3x3f(viewWorld[0].xyz, viewWorld[1].xyz, viewWorld[2].xyz);
67
63
  let viewVelocity = viewWorld3 * vertexInputs.velocity.xyz;
@@ -82,6 +78,15 @@ fn main(input: VertexInputs) -> FragmentInputs {
82
78
  }
83
79
  #else
84
80
  let side = normalize(cross(mvPosition.xyz, vdir));
81
+ #endif
82
+ #ifdef SIZE_LIMITS
83
+ // The streak's width on screen as a fraction of the viewport height, held
84
+ // between the limits; a perspective projection divides by the view depth.
85
+ let limitDepth = select(1.0, abs(mvPosition.z), abs(uniforms.projection[2][3]) > 0.0);
86
+ let screenSize = avgSize * uniforms.projection[1][1] / (2.0 * limitDepth);
87
+ if (screenSize > 0.0) {
88
+ avgSize *= clamp(screenSize, uniforms.sizeLimits.x, uniforms.sizeLimits.y) / screenSize;
89
+ }
85
90
  #endif
86
91
  let widthOffset = vertexInputs.position.y * side * avgSize;
87
92
  // Equivalent to viewVelocity * (1.0 + lengthFactor / vlength) for moving particles, but the
@@ -128,6 +133,9 @@ ${fogVertexWGSL('mvPosition.xyz')}
128
133
  vertexOutputs.vUV = quadUV;
129
134
  #endif
130
135
 
136
+ #ifdef UV1
137
+ vertexOutputs.vUV1 = quadUV;
138
+ #endif
131
139
  vertexOutputs.vColor = vertexInputs.color;
132
140
  }
133
141
  `;
@@ -4,6 +4,9 @@ import {surfaceColorGLSL, surfaceFragmentDeclarationGLSL} from './chunks/surface
4
4
  export default /* glsl */ `
5
5
  varying vec2 vUV;
6
6
  varying vec4 vColor;
7
+ #ifdef UV1
8
+ varying vec2 vUV1;
9
+ #endif
7
10
 
8
11
  #ifdef USE_MAP
9
12
  uniform sampler2D map;
@@ -4,6 +4,9 @@ import {surfaceColorWGSL, surfaceFragmentDeclarationWGSL} from './chunks/surface
4
4
  export default /* wgsl */ `
5
5
  varying vUV: vec2f;
6
6
  varying vColor: vec4f;
7
+ #ifdef UV1
8
+ varying vUV1: vec2f;
9
+ #endif
7
10
 
8
11
  #ifdef USE_MAP
9
12
  var mapSampler: sampler;
@@ -17,6 +17,11 @@ uniform float sizeAttenuation;
17
17
 
18
18
  varying vec2 vUV;
19
19
  varying vec4 vColor;
20
+ #ifdef UV1
21
+ // Along the trail and across it, without the sheet's cell (UV channel 1).
22
+ attribute vec2 uv2;
23
+ varying vec2 vUV1;
24
+ #endif
20
25
 
21
26
  vec2 fix(vec4 i, float aspect) {
22
27
  vec2 res = i.xy / i.w;
@@ -57,6 +62,9 @@ void main() {
57
62
  gl_Position = finalPosition;
58
63
  ${fogVertexGLSL('(view * vec4(position, 1.0)).xyz')}
59
64
  vUV = uv;
65
+ #ifdef UV1
66
+ vUV1 = uv2;
67
+ #endif
60
68
  vColor = color;
61
69
  }
62
70
  `;
@@ -17,6 +17,11 @@ uniform sizeAttenuation: f32;
17
17
 
18
18
  varying vUV: vec2f;
19
19
  varying vColor: vec4f;
20
+ #ifdef UV1
21
+ // Along the trail and across it, without the sheet's cell (UV channel 1).
22
+ attribute uv2: vec2f;
23
+ varying vUV1: vec2f;
24
+ #endif
20
25
 
21
26
  fn fix(i: vec4f, aspect: f32) -> vec2f {
22
27
  var res = i.xy / i.w;
@@ -62,6 +67,9 @@ fn main(input: VertexInputs) -> FragmentInputs {
62
67
  vertexOutputs.position = finalPosition;
63
68
  ${fogVertexWGSL('(uniforms.view * vec4f(vertexInputs.position, 1.0)).xyz')}
64
69
  vertexOutputs.vUV = vertexInputs.uv;
70
+ #ifdef UV1
71
+ vertexOutputs.vUV1 = vertexInputs.uv2;
72
+ #endif
65
73
  vertexOutputs.vColor = vertexInputs.color;
66
74
  }
67
75
  `;