babylon.quarks 0.23.0 → 0.24.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.
- package/CHANGELOG.md +101 -0
- package/dist/babylon.quarks.cjs +1 -1
- package/dist/babylon.quarks.cjs.map +1 -1
- package/dist/babylon.quarks.esm.js +1 -1
- package/dist/babylon.quarks.esm.js.map +1 -1
- package/dist/babylon.quarks.umd.min.js +1 -1
- package/dist/babylon.quarks.umd.min.js.map +1 -1
- package/dist/types/index.d.ts +529 -20
- package/package.json +2 -2
- package/src/BatchedRenderer.ts +146 -2
- package/src/EffectPool.ts +165 -0
- package/src/MeshSurfaceEmitter.ts +4 -4
- package/src/ParticleEmitter.ts +2 -1
- package/src/ParticleSystem.ts +592 -15
- package/src/QuarksUtil.ts +55 -7
- package/src/SpriteBatch.ts +127 -3
- package/src/TrailBatch.ts +49 -12
- package/src/VFXBatch.ts +154 -3
- package/src/index.ts +4 -1
- package/src/instanceSort.ts +121 -0
- package/src/shaders/chunks/fog.ts +127 -0
- package/src/shaders/local_particle_physics_vert.glsl.ts +4 -0
- package/src/shaders/local_particle_physics_vert.wgsl.ts +4 -0
- package/src/shaders/particle_frag.glsl.ts +4 -0
- package/src/shaders/particle_frag.wgsl.ts +4 -0
- package/src/shaders/particle_physics_frag.glsl.ts +4 -0
- package/src/shaders/particle_physics_frag.wgsl.ts +4 -0
- package/src/shaders/particle_vert.glsl.ts +23 -4
- package/src/shaders/particle_vert.wgsl.ts +23 -4
- package/src/shaders/stretched_bb_particle_vert.glsl.ts +17 -0
- package/src/shaders/stretched_bb_particle_vert.wgsl.ts +18 -1
- package/src/shaders/trail_frag.glsl.ts +4 -0
- package/src/shaders/trail_frag.wgsl.ts +4 -0
- package/src/shaders/trail_vert.glsl.ts +4 -0
- package/src/shaders/trail_vert.wgsl.ts +4 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sorting a batch's instances by a per-instance key, fast enough to run every
|
|
3
|
+
* frame over tens of thousands of particles: a three-pass radix sort on 32-bit
|
|
4
|
+
* keys (no comparator calls, linear time) and a gather of each attribute buffer
|
|
5
|
+
* in the resulting order.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const RADIX_BITS = 11;
|
|
9
|
+
const RADIX_SIZE = 1 << RADIX_BITS;
|
|
10
|
+
const RADIX_MASK = RADIX_SIZE - 1;
|
|
11
|
+
|
|
12
|
+
/** Scratch space for {@link sortByKey}, sized for a given instance count. */
|
|
13
|
+
export class SortScratch {
|
|
14
|
+
keys: Uint32Array;
|
|
15
|
+
orderA: Uint32Array;
|
|
16
|
+
orderB: Uint32Array;
|
|
17
|
+
readonly counts = new Uint32Array(RADIX_SIZE);
|
|
18
|
+
private readonly floatView = new Float32Array(1);
|
|
19
|
+
private readonly uintView = new Uint32Array(this.floatView.buffer);
|
|
20
|
+
|
|
21
|
+
constructor(capacity: number) {
|
|
22
|
+
this.keys = new Uint32Array(capacity);
|
|
23
|
+
this.orderA = new Uint32Array(capacity);
|
|
24
|
+
this.orderB = new Uint32Array(capacity);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
ensure(capacity: number): void {
|
|
28
|
+
if (this.keys.length < capacity) {
|
|
29
|
+
this.keys = new Uint32Array(capacity);
|
|
30
|
+
this.orderA = new Uint32Array(capacity);
|
|
31
|
+
this.orderB = new Uint32Array(capacity);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The bits of a non-negative float, which order the same way the float
|
|
37
|
+
* does, so they can be radix sorted. `descending` flips the order.
|
|
38
|
+
*/
|
|
39
|
+
floatKey(value: number, descending: boolean): number {
|
|
40
|
+
this.floatView[0] = value;
|
|
41
|
+
const bits = this.uintView[0];
|
|
42
|
+
return descending ? ~bits >>> 0 : bits;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fills `order` with the instance indices `[0, count)` sorted by ascending
|
|
48
|
+
* `scratch.keys`. Returns the array holding the result, one of the two order
|
|
49
|
+
* buffers of the scratch.
|
|
50
|
+
*/
|
|
51
|
+
export function sortByKey(scratch: SortScratch, count: number): Uint32Array {
|
|
52
|
+
const keys = scratch.keys;
|
|
53
|
+
const counts = scratch.counts;
|
|
54
|
+
let from = scratch.orderA;
|
|
55
|
+
let to = scratch.orderB;
|
|
56
|
+
for (let i = 0; i < count; i++) {
|
|
57
|
+
from[i] = i;
|
|
58
|
+
}
|
|
59
|
+
for (let shift = 0; shift < 32; shift += RADIX_BITS) {
|
|
60
|
+
counts.fill(0);
|
|
61
|
+
for (let i = 0; i < count; i++) {
|
|
62
|
+
counts[(keys[from[i]] >>> shift) & RADIX_MASK]++;
|
|
63
|
+
}
|
|
64
|
+
// A pass whose keys all share one digit changes nothing; skip the scatter.
|
|
65
|
+
if (counts[(keys[from[0]] >>> shift) & RADIX_MASK] === count) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
let sum = 0;
|
|
69
|
+
for (let b = 0; b < RADIX_SIZE; b++) {
|
|
70
|
+
const c = counts[b];
|
|
71
|
+
counts[b] = sum;
|
|
72
|
+
sum += c;
|
|
73
|
+
}
|
|
74
|
+
for (let i = 0; i < count; i++) {
|
|
75
|
+
const index = from[i];
|
|
76
|
+
to[counts[(keys[index] >>> shift) & RADIX_MASK]++] = index;
|
|
77
|
+
}
|
|
78
|
+
const swap = from;
|
|
79
|
+
from = to;
|
|
80
|
+
to = swap;
|
|
81
|
+
}
|
|
82
|
+
return from;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Copies `count` instances of `stride` floats from `source` into `target` in `order`. */
|
|
86
|
+
export function gather(
|
|
87
|
+
source: Float32Array,
|
|
88
|
+
target: Float32Array,
|
|
89
|
+
order: Uint32Array,
|
|
90
|
+
count: number,
|
|
91
|
+
stride: number
|
|
92
|
+
): void {
|
|
93
|
+
if (stride === 1) {
|
|
94
|
+
for (let i = 0; i < count; i++) {
|
|
95
|
+
target[i] = source[order[i]];
|
|
96
|
+
}
|
|
97
|
+
} else if (stride === 3) {
|
|
98
|
+
for (let i = 0, o = 0; i < count; i++, o += 3) {
|
|
99
|
+
const s = order[i] * 3;
|
|
100
|
+
target[o] = source[s];
|
|
101
|
+
target[o + 1] = source[s + 1];
|
|
102
|
+
target[o + 2] = source[s + 2];
|
|
103
|
+
}
|
|
104
|
+
} else if (stride === 4) {
|
|
105
|
+
for (let i = 0, o = 0; i < count; i++, o += 4) {
|
|
106
|
+
const s = order[i] * 4;
|
|
107
|
+
target[o] = source[s];
|
|
108
|
+
target[o + 1] = source[s + 1];
|
|
109
|
+
target[o + 2] = source[s + 2];
|
|
110
|
+
target[o + 3] = source[s + 3];
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
for (let i = 0; i < count; i++) {
|
|
114
|
+
const s = order[i] * stride;
|
|
115
|
+
const o = i * stride;
|
|
116
|
+
for (let k = 0; k < stride; k++) {
|
|
117
|
+
target[o + k] = source[s + k];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scene fog for the particle shaders, in both shader languages.
|
|
3
|
+
*
|
|
4
|
+
* Babylon's own fog includes expect variables the particle shaders do not have
|
|
5
|
+
* (`worldPos`, a `color` the fragment writes), so the few lines involved live
|
|
6
|
+
* here instead and are spliced into each shader. The maths is Babylon's
|
|
7
|
+
* (`fogFragmentDeclaration`): the fog mode, start, end and density arrive in
|
|
8
|
+
* `vFogInfos`, the colour in `vFogColor`, and the distance is the view-space
|
|
9
|
+
* position's length. Mode 0 is "no fog", so a scene that turns its fog off
|
|
10
|
+
* needs no recompile — the uniform does it.
|
|
11
|
+
*
|
|
12
|
+
* Additive particles cannot fade toward a fog colour (that would add fog to
|
|
13
|
+
* the whole screen), so under `FOG_ADDITIVE` they fade toward black instead,
|
|
14
|
+
* which is what Unity does for additive materials.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Vertex-side declaration, GLSL. */
|
|
18
|
+
export const fogVertexDeclarationGLSL = /* glsl */ `
|
|
19
|
+
#ifdef FOG
|
|
20
|
+
varying vec3 vFogDistance;
|
|
21
|
+
#endif
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
/** Writes the view-space position the fragment measures its distance from, GLSL. */
|
|
25
|
+
export function fogVertexGLSL(viewPosition: string): string {
|
|
26
|
+
return /* glsl */ `
|
|
27
|
+
#ifdef FOG
|
|
28
|
+
vFogDistance = ${viewPosition};
|
|
29
|
+
#endif
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Fragment-side declaration and fog factor, GLSL. */
|
|
34
|
+
export const fogFragmentDeclarationGLSL = /* glsl */ `
|
|
35
|
+
#ifdef FOG
|
|
36
|
+
uniform vec4 vFogInfos;
|
|
37
|
+
uniform vec3 vFogColor;
|
|
38
|
+
varying vec3 vFogDistance;
|
|
39
|
+
|
|
40
|
+
float quarksFogFactor() {
|
|
41
|
+
float fogCoeff = 1.0;
|
|
42
|
+
float fogStart = vFogInfos.y;
|
|
43
|
+
float fogEnd = vFogInfos.z;
|
|
44
|
+
float fogDensity = vFogInfos.w;
|
|
45
|
+
float fogDistance = length(vFogDistance);
|
|
46
|
+
if (vFogInfos.x == 3.0) {
|
|
47
|
+
fogCoeff = (fogEnd - fogDistance) / (fogEnd - fogStart);
|
|
48
|
+
} else if (vFogInfos.x == 1.0) {
|
|
49
|
+
fogCoeff = exp(-fogDistance * fogDensity);
|
|
50
|
+
} else if (vFogInfos.x == 2.0) {
|
|
51
|
+
fogCoeff = exp(-fogDistance * fogDistance * fogDensity * fogDensity);
|
|
52
|
+
}
|
|
53
|
+
return clamp(fogCoeff, 0.0, 1.0);
|
|
54
|
+
}
|
|
55
|
+
#endif
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
/** Applies the fog to an rgb lvalue (`color` or `baseColor.rgb`), GLSL. */
|
|
59
|
+
export function fogFragmentGLSL(rgb: string): string {
|
|
60
|
+
return /* glsl */ `
|
|
61
|
+
#ifdef FOG
|
|
62
|
+
{
|
|
63
|
+
float fogFactor = quarksFogFactor();
|
|
64
|
+
#ifdef FOG_ADDITIVE
|
|
65
|
+
${rgb} *= fogFactor;
|
|
66
|
+
#else
|
|
67
|
+
${rgb} = mix(vFogColor, ${rgb}, fogFactor);
|
|
68
|
+
#endif
|
|
69
|
+
}
|
|
70
|
+
#endif
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Vertex-side declaration, WGSL. */
|
|
75
|
+
export const fogVertexDeclarationWGSL = /* wgsl */ `
|
|
76
|
+
#ifdef FOG
|
|
77
|
+
varying vFogDistance: vec3f;
|
|
78
|
+
#endif
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
/** Writes the view-space position the fragment measures its distance from, WGSL. */
|
|
82
|
+
export function fogVertexWGSL(viewPosition: string): string {
|
|
83
|
+
return /* wgsl */ `
|
|
84
|
+
#ifdef FOG
|
|
85
|
+
vertexOutputs.vFogDistance = ${viewPosition};
|
|
86
|
+
#endif
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Fragment-side declaration and fog function, WGSL. */
|
|
91
|
+
export const fogFragmentDeclarationWGSL = /* wgsl */ `
|
|
92
|
+
#ifdef FOG
|
|
93
|
+
uniform vFogInfos: vec4f;
|
|
94
|
+
uniform vFogColor: vec3f;
|
|
95
|
+
varying vFogDistance: vec3f;
|
|
96
|
+
|
|
97
|
+
fn quarksFog(rgb: vec3f, fogDistanceVector: vec3f) -> vec3f {
|
|
98
|
+
var fogCoeff = 1.0;
|
|
99
|
+
let fogStart = uniforms.vFogInfos.y;
|
|
100
|
+
let fogEnd = uniforms.vFogInfos.z;
|
|
101
|
+
let fogDensity = uniforms.vFogInfos.w;
|
|
102
|
+
let fogDistance = length(fogDistanceVector);
|
|
103
|
+
if (uniforms.vFogInfos.x == 3.0) {
|
|
104
|
+
fogCoeff = (fogEnd - fogDistance) / (fogEnd - fogStart);
|
|
105
|
+
} else if (uniforms.vFogInfos.x == 1.0) {
|
|
106
|
+
fogCoeff = exp(-fogDistance * fogDensity);
|
|
107
|
+
} else if (uniforms.vFogInfos.x == 2.0) {
|
|
108
|
+
fogCoeff = exp(-fogDistance * fogDistance * fogDensity * fogDensity);
|
|
109
|
+
}
|
|
110
|
+
let fogFactor = clamp(fogCoeff, 0.0, 1.0);
|
|
111
|
+
#ifdef FOG_ADDITIVE
|
|
112
|
+
return rgb * fogFactor;
|
|
113
|
+
#else
|
|
114
|
+
return mix(uniforms.vFogColor, rgb, fogFactor);
|
|
115
|
+
#endif
|
|
116
|
+
}
|
|
117
|
+
#endif
|
|
118
|
+
`;
|
|
119
|
+
|
|
120
|
+
/** Applies the fog to a `var` vec4 colour, WGSL. */
|
|
121
|
+
export function fogFragmentWGSL(color: string): string {
|
|
122
|
+
return /* wgsl */ `
|
|
123
|
+
#ifdef FOG
|
|
124
|
+
${color} = vec4f(quarksFog(${color}.rgb, fragmentInputs.vFogDistance), ${color}.a);
|
|
125
|
+
#endif
|
|
126
|
+
`;
|
|
127
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogVertexDeclarationGLSL, fogVertexGLSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* glsl */ `
|
|
2
4
|
attribute vec3 position;
|
|
3
5
|
attribute vec2 uv;
|
|
@@ -39,6 +41,7 @@ vec3 applyQuaternion(vec3 v, vec4 q) {
|
|
|
39
41
|
return v + qW * t + cross(qVec, t);
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
${fogVertexDeclarationGLSL}
|
|
42
45
|
void main() {
|
|
43
46
|
vec3 scaledPosition = position * size;
|
|
44
47
|
vec3 rotatedPosition = applyQuaternion(scaledPosition, rotation);
|
|
@@ -48,6 +51,7 @@ void main() {
|
|
|
48
51
|
vec4 worldPos = world * vec4(localPos, 1.0);
|
|
49
52
|
vec4 viewPos = view * worldPos;
|
|
50
53
|
gl_Position = projection * viewPos;
|
|
54
|
+
${fogVertexGLSL('viewPos.xyz')}
|
|
51
55
|
|
|
52
56
|
vWorldPos = worldPos.xyz;
|
|
53
57
|
vNormal = normalize((world * vec4(rotatedNormal, 0.0)).xyz);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogVertexDeclarationWGSL, fogVertexWGSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* wgsl */ `
|
|
2
4
|
attribute position: vec3f;
|
|
3
5
|
attribute uv: vec2f;
|
|
@@ -39,6 +41,7 @@ fn applyQuaternion(v: vec3f, q: vec4f) -> vec3f {
|
|
|
39
41
|
return v + qW * t + cross(qVec, t);
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
${fogVertexDeclarationWGSL}
|
|
42
45
|
@vertex
|
|
43
46
|
fn main(input: VertexInputs) -> FragmentInputs {
|
|
44
47
|
let scaledPosition = vertexInputs.position * vertexInputs.size;
|
|
@@ -50,6 +53,7 @@ fn main(input: VertexInputs) -> FragmentInputs {
|
|
|
50
53
|
let viewPos = uniforms.view * worldPos;
|
|
51
54
|
let clipPosition = uniforms.projection * viewPos;
|
|
52
55
|
vertexOutputs.position = clipPosition;
|
|
56
|
+
${fogVertexWGSL('viewPos.xyz')}
|
|
53
57
|
|
|
54
58
|
vertexOutputs.vWorldPos = worldPos.xyz;
|
|
55
59
|
vertexOutputs.vNormal = normalize((uniforms.world * vec4f(rotatedNormal, 0.0)).xyz);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogFragmentDeclarationGLSL, fogFragmentGLSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* glsl */ `
|
|
2
4
|
varying vec2 vUV;
|
|
3
5
|
varying vec4 vColor;
|
|
@@ -23,6 +25,7 @@ varying float linearDepth;
|
|
|
23
25
|
uniform float alphaTest;
|
|
24
26
|
#endif
|
|
25
27
|
|
|
28
|
+
${fogFragmentDeclarationGLSL}
|
|
26
29
|
void main() {
|
|
27
30
|
vec4 baseColor = vColor;
|
|
28
31
|
|
|
@@ -45,6 +48,7 @@ void main() {
|
|
|
45
48
|
if (baseColor.a < 0.01) discard;
|
|
46
49
|
#endif
|
|
47
50
|
|
|
51
|
+
${fogFragmentGLSL('baseColor.rgb')}
|
|
48
52
|
gl_FragColor = baseColor;
|
|
49
53
|
|
|
50
54
|
#ifdef SOFT_PARTICLES
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogFragmentDeclarationWGSL, fogFragmentWGSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* wgsl */ `
|
|
2
4
|
varying vUV: vec2f;
|
|
3
5
|
varying vColor: vec4f;
|
|
@@ -25,6 +27,7 @@ varying linearDepth: f32;
|
|
|
25
27
|
uniform alphaTest: f32;
|
|
26
28
|
#endif
|
|
27
29
|
|
|
30
|
+
${fogFragmentDeclarationWGSL}
|
|
28
31
|
@fragment
|
|
29
32
|
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
30
33
|
var baseColor = fragmentInputs.vColor;
|
|
@@ -59,6 +62,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
59
62
|
baseColor *= fade;
|
|
60
63
|
#endif
|
|
61
64
|
|
|
65
|
+
${fogFragmentWGSL('baseColor')}
|
|
62
66
|
fragmentOutputs.color = baseColor;
|
|
63
67
|
}
|
|
64
68
|
`;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogFragmentDeclarationGLSL, fogFragmentGLSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* glsl */ `
|
|
2
4
|
varying vec2 vUV;
|
|
3
5
|
varying vec4 vColor;
|
|
@@ -85,6 +87,7 @@ varying float linearDepth;
|
|
|
85
87
|
uniform float alphaTest;
|
|
86
88
|
#endif
|
|
87
89
|
|
|
90
|
+
${fogFragmentDeclarationGLSL}
|
|
88
91
|
void main() {
|
|
89
92
|
vec4 baseColor = vColor;
|
|
90
93
|
|
|
@@ -131,6 +134,7 @@ void main() {
|
|
|
131
134
|
#endif
|
|
132
135
|
#endif
|
|
133
136
|
|
|
137
|
+
${fogFragmentGLSL('color')}
|
|
134
138
|
gl_FragColor = vec4(color, baseColor.a);
|
|
135
139
|
|
|
136
140
|
#ifdef SOFT_PARTICLES
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogFragmentDeclarationWGSL, fogFragmentWGSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* wgsl */ `
|
|
2
4
|
varying vUV: vec2f;
|
|
3
5
|
varying vColor: vec4f;
|
|
@@ -85,6 +87,7 @@ varying linearDepth: f32;
|
|
|
85
87
|
uniform alphaTest: f32;
|
|
86
88
|
#endif
|
|
87
89
|
|
|
90
|
+
${fogFragmentDeclarationWGSL}
|
|
88
91
|
@fragment
|
|
89
92
|
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
90
93
|
var baseColor = fragmentInputs.vColor;
|
|
@@ -145,6 +148,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
145
148
|
finalColor *= fade;
|
|
146
149
|
#endif
|
|
147
150
|
|
|
151
|
+
${fogFragmentWGSL('finalColor')}
|
|
148
152
|
fragmentOutputs.color = finalColor;
|
|
149
153
|
}
|
|
150
154
|
`;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogVertexDeclarationGLSL, fogVertexGLSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* glsl */ `
|
|
2
4
|
// Per-vertex attributes
|
|
3
5
|
attribute vec3 position;
|
|
@@ -38,15 +40,31 @@ varying vec4 projPosition;
|
|
|
38
40
|
varying float linearDepth;
|
|
39
41
|
#endif
|
|
40
42
|
|
|
43
|
+
#ifdef SIZE_LIMITS
|
|
44
|
+
// Smallest and largest size a particle may have on screen, as fractions of the viewport height.
|
|
45
|
+
uniform vec2 sizeLimits;
|
|
46
|
+
#endif
|
|
47
|
+
${fogVertexDeclarationGLSL}
|
|
41
48
|
void main() {
|
|
42
49
|
float c = cos(rotation);
|
|
43
50
|
float s = sin(rotation);
|
|
44
51
|
|
|
52
|
+
#ifdef SIZE_LIMITS
|
|
53
|
+
// The particle's size on screen as a fraction of the viewport height, held
|
|
54
|
+
// between the limits; a perspective projection divides by the view depth.
|
|
55
|
+
vec4 limitCenter = view * world * vec4(offset, 1.0);
|
|
56
|
+
float limitDepth = abs(projection[2][3]) > 0.0 ? abs(limitCenter.z) : 1.0;
|
|
57
|
+
float screenSize = max(abs(size.x), abs(size.y)) * projection[1][1] / (2.0 * limitDepth);
|
|
58
|
+
float sizeScale = screenSize > 0.0 ? clamp(screenSize, sizeLimits.x, sizeLimits.y) / screenSize : 1.0;
|
|
59
|
+
vec3 limitedSize = size * sizeScale;
|
|
60
|
+
#else
|
|
61
|
+
vec3 limitedSize = size;
|
|
62
|
+
#endif
|
|
45
63
|
// A negative size mirrors the quad, which is how flipped particles arrive.
|
|
46
|
-
vec2 alignedPosition = position.xy *
|
|
64
|
+
vec2 alignedPosition = position.xy * limitedSize.xy;
|
|
47
65
|
#ifdef PIVOT
|
|
48
66
|
// The pivot turns with the particle but is not mirrored with it.
|
|
49
|
-
alignedPosition += pivot.xy * abs(
|
|
67
|
+
alignedPosition += pivot.xy * abs(limitedSize.xy);
|
|
50
68
|
#endif
|
|
51
69
|
vec2 rotatedPosition;
|
|
52
70
|
rotatedPosition.x = c * alignedPosition.x - s * alignedPosition.y;
|
|
@@ -57,7 +75,7 @@ void main() {
|
|
|
57
75
|
mvPosition.x += rotatedPosition.x;
|
|
58
76
|
mvPosition.z -= rotatedPosition.y;
|
|
59
77
|
#ifdef PIVOT
|
|
60
|
-
mvPosition.y += pivot.z * abs(
|
|
78
|
+
mvPosition.y += pivot.z * abs(limitedSize.x);
|
|
61
79
|
#endif
|
|
62
80
|
mvPosition = view * mvPosition;
|
|
63
81
|
#elif defined(VERTICAL)
|
|
@@ -73,11 +91,12 @@ void main() {
|
|
|
73
91
|
#ifndef HORIZONTAL
|
|
74
92
|
// Toward the camera along its view axis, whichever way the view space
|
|
75
93
|
// faces: anything in front of the camera has the sign of the far side.
|
|
76
|
-
mvPosition.z -= sign(mvPosition.z) * pivot.z * abs(
|
|
94
|
+
mvPosition.z -= sign(mvPosition.z) * pivot.z * abs(limitedSize.x);
|
|
77
95
|
#endif
|
|
78
96
|
#endif
|
|
79
97
|
|
|
80
98
|
gl_Position = projection * mvPosition;
|
|
99
|
+
${fogVertexGLSL('mvPosition.xyz')}
|
|
81
100
|
#ifdef SOFT_PARTICLES
|
|
82
101
|
projPosition = gl_Position;
|
|
83
102
|
linearDepth = -mvPosition.z;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogVertexDeclarationWGSL, fogVertexWGSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* wgsl */ `
|
|
2
4
|
// Per-vertex attributes
|
|
3
5
|
attribute position: vec3f;
|
|
@@ -37,16 +39,32 @@ varying projPosition: vec4f;
|
|
|
37
39
|
varying linearDepth: f32;
|
|
38
40
|
#endif
|
|
39
41
|
|
|
42
|
+
#ifdef SIZE_LIMITS
|
|
43
|
+
// Smallest and largest size a particle may have on screen, as fractions of the viewport height.
|
|
44
|
+
uniform sizeLimits: vec2f;
|
|
45
|
+
#endif
|
|
46
|
+
${fogVertexDeclarationWGSL}
|
|
40
47
|
@vertex
|
|
41
48
|
fn main(input: VertexInputs) -> FragmentInputs {
|
|
42
49
|
let c = cos(vertexInputs.rotation);
|
|
43
50
|
let s = sin(vertexInputs.rotation);
|
|
44
51
|
|
|
52
|
+
#ifdef SIZE_LIMITS
|
|
53
|
+
// The particle's size on screen as a fraction of the viewport height, held
|
|
54
|
+
// between the limits; a perspective projection divides by the view depth.
|
|
55
|
+
let limitCenter = uniforms.view * uniforms.world * vec4f(vertexInputs.offset, 1.0);
|
|
56
|
+
let limitDepth = select(1.0, abs(limitCenter.z), abs(uniforms.projection[2][3]) > 0.0);
|
|
57
|
+
let screenSize = max(abs(vertexInputs.size.x), abs(vertexInputs.size.y)) * uniforms.projection[1][1] / (2.0 * limitDepth);
|
|
58
|
+
let sizeScale = select(1.0, clamp(screenSize, uniforms.sizeLimits.x, uniforms.sizeLimits.y) / screenSize, screenSize > 0.0);
|
|
59
|
+
let limitedSize = vertexInputs.size * sizeScale;
|
|
60
|
+
#else
|
|
61
|
+
let limitedSize = vertexInputs.size;
|
|
62
|
+
#endif
|
|
45
63
|
// A negative size mirrors the quad, which is how flipped particles arrive.
|
|
46
|
-
var alignedPosition = vertexInputs.position.xy *
|
|
64
|
+
var alignedPosition = vertexInputs.position.xy * limitedSize.xy;
|
|
47
65
|
#ifdef PIVOT
|
|
48
66
|
// The pivot turns with the particle but is not mirrored with it.
|
|
49
|
-
alignedPosition += uniforms.pivot.xy * abs(
|
|
67
|
+
alignedPosition += uniforms.pivot.xy * abs(limitedSize.xy);
|
|
50
68
|
#endif
|
|
51
69
|
let rotatedPosition = vec2f(
|
|
52
70
|
c * alignedPosition.x - s * alignedPosition.y,
|
|
@@ -57,7 +75,7 @@ fn main(input: VertexInputs) -> FragmentInputs {
|
|
|
57
75
|
mvPosition.x += rotatedPosition.x;
|
|
58
76
|
mvPosition.z -= rotatedPosition.y;
|
|
59
77
|
#ifdef PIVOT
|
|
60
|
-
mvPosition.y += uniforms.pivot.z * abs(
|
|
78
|
+
mvPosition.y += uniforms.pivot.z * abs(limitedSize.x);
|
|
61
79
|
#endif
|
|
62
80
|
mvPosition = uniforms.view * mvPosition;
|
|
63
81
|
#elif defined(VERTICAL)
|
|
@@ -74,12 +92,13 @@ fn main(input: VertexInputs) -> FragmentInputs {
|
|
|
74
92
|
#ifndef HORIZONTAL
|
|
75
93
|
// Toward the camera along its view axis, whichever way the view space
|
|
76
94
|
// faces: anything in front of the camera has the sign of the far side.
|
|
77
|
-
mvPosition.z -= sign(mvPosition.z) * uniforms.pivot.z * abs(
|
|
95
|
+
mvPosition.z -= sign(mvPosition.z) * uniforms.pivot.z * abs(limitedSize.x);
|
|
78
96
|
#endif
|
|
79
97
|
#endif
|
|
80
98
|
|
|
81
99
|
let clipPosition = uniforms.projection * mvPosition;
|
|
82
100
|
vertexOutputs.position = clipPosition;
|
|
101
|
+
${fogVertexWGSL('mvPosition.xyz')}
|
|
83
102
|
#ifdef SOFT_PARTICLES
|
|
84
103
|
vertexOutputs.projPosition = clipPosition;
|
|
85
104
|
vertexOutputs.linearDepth = -mvPosition.z;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogVertexDeclarationGLSL, fogVertexGLSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* glsl */ `
|
|
2
4
|
// Per-vertex attributes
|
|
3
5
|
attribute vec3 position;
|
|
@@ -39,12 +41,26 @@ varying vec4 projPosition;
|
|
|
39
41
|
varying float linearDepth;
|
|
40
42
|
#endif
|
|
41
43
|
|
|
44
|
+
#ifdef SIZE_LIMITS
|
|
45
|
+
// Smallest and largest size a particle may have on screen, as fractions of the viewport height.
|
|
46
|
+
uniform vec2 sizeLimits;
|
|
47
|
+
#endif
|
|
48
|
+
${fogVertexDeclarationGLSL}
|
|
42
49
|
void main() {
|
|
43
50
|
float lengthFactor = velocity.w;
|
|
44
51
|
// A flipped particle arrives with a negative size: the streak keeps its
|
|
45
52
|
// extent and mirrors its texture instead (see quadUV below).
|
|
46
53
|
float avgSize = (abs(size.x) + abs(size.y)) * 0.5;
|
|
47
54
|
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
|
|
48
64
|
vec3 viewVelocity = mat3(view * world) * velocity.xyz;
|
|
49
65
|
float vlength = length(viewVelocity);
|
|
50
66
|
// Stretch direction is the velocity direction; only a genuinely motionless particle has none.
|
|
@@ -61,6 +77,7 @@ void main() {
|
|
|
61
77
|
mvPosition.xyz += (pivot.y * side - pivot.x * vdir) * avgSize;
|
|
62
78
|
#endif
|
|
63
79
|
gl_Position = projection * mvPosition;
|
|
80
|
+
${fogVertexGLSL('mvPosition.xyz')}
|
|
64
81
|
vec2 quadUV = vec2(size.x < 0.0 ? 1.0 - uv.x : uv.x, size.y < 0.0 ? 1.0 - uv.y : uv.y);
|
|
65
82
|
#ifdef SOFT_PARTICLES
|
|
66
83
|
projPosition = gl_Position;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogVertexDeclarationWGSL, fogVertexWGSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* wgsl */ `
|
|
2
4
|
// Per-vertex attributes
|
|
3
5
|
attribute position: vec3f;
|
|
@@ -39,13 +41,27 @@ varying projPosition: vec4f;
|
|
|
39
41
|
varying linearDepth: f32;
|
|
40
42
|
#endif
|
|
41
43
|
|
|
44
|
+
#ifdef SIZE_LIMITS
|
|
45
|
+
// Smallest and largest size a particle may have on screen, as fractions of the viewport height.
|
|
46
|
+
uniform sizeLimits: vec2f;
|
|
47
|
+
#endif
|
|
48
|
+
${fogVertexDeclarationWGSL}
|
|
42
49
|
@vertex
|
|
43
50
|
fn main(input: VertexInputs) -> FragmentInputs {
|
|
44
51
|
let lengthFactor = vertexInputs.velocity.w;
|
|
45
52
|
// A flipped particle arrives with a negative size: the streak keeps its
|
|
46
53
|
// extent and mirrors its texture instead (see quadUV below).
|
|
47
|
-
|
|
54
|
+
var avgSize = (abs(vertexInputs.size.x) + abs(vertexInputs.size.y)) * 0.5;
|
|
48
55
|
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
|
|
49
65
|
let viewWorld = uniforms.view * uniforms.world;
|
|
50
66
|
let viewWorld3 = mat3x3f(viewWorld[0].xyz, viewWorld[1].xyz, viewWorld[2].xyz);
|
|
51
67
|
let viewVelocity = viewWorld3 * vertexInputs.velocity.xyz;
|
|
@@ -73,6 +89,7 @@ fn main(input: VertexInputs) -> FragmentInputs {
|
|
|
73
89
|
|
|
74
90
|
let clipPosition = uniforms.projection * mvPosition;
|
|
75
91
|
vertexOutputs.position = clipPosition;
|
|
92
|
+
${fogVertexWGSL('mvPosition.xyz')}
|
|
76
93
|
let quadUV = select(vertexInputs.uv, 1.0 - vertexInputs.uv, vertexInputs.size.xy < vec2f(0.0));
|
|
77
94
|
#ifdef SOFT_PARTICLES
|
|
78
95
|
vertexOutputs.projPosition = clipPosition;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogFragmentDeclarationGLSL, fogFragmentGLSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* glsl */ `
|
|
2
4
|
varying vec2 vUV;
|
|
3
5
|
varying vec4 vColor;
|
|
@@ -6,6 +8,7 @@ varying vec4 vColor;
|
|
|
6
8
|
uniform sampler2D map;
|
|
7
9
|
#endif
|
|
8
10
|
|
|
11
|
+
${fogFragmentDeclarationGLSL}
|
|
9
12
|
void main() {
|
|
10
13
|
vec4 baseColor = vColor;
|
|
11
14
|
|
|
@@ -16,6 +19,7 @@ void main() {
|
|
|
16
19
|
|
|
17
20
|
if (baseColor.a < 0.01) discard;
|
|
18
21
|
|
|
22
|
+
${fogFragmentGLSL('baseColor.rgb')}
|
|
19
23
|
gl_FragColor = baseColor;
|
|
20
24
|
}
|
|
21
25
|
`;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogFragmentDeclarationWGSL, fogFragmentWGSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* wgsl */ `
|
|
2
4
|
varying vUV: vec2f;
|
|
3
5
|
varying vColor: vec4f;
|
|
@@ -7,6 +9,7 @@ var mapSampler: sampler;
|
|
|
7
9
|
var map: texture_2d<f32>;
|
|
8
10
|
#endif
|
|
9
11
|
|
|
12
|
+
${fogFragmentDeclarationWGSL}
|
|
10
13
|
@fragment
|
|
11
14
|
fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
12
15
|
var baseColor = fragmentInputs.vColor;
|
|
@@ -18,6 +21,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
|
|
|
18
21
|
|
|
19
22
|
if (baseColor.a < 0.01) { discard; }
|
|
20
23
|
|
|
24
|
+
${fogFragmentWGSL('baseColor')}
|
|
21
25
|
fragmentOutputs.color = baseColor;
|
|
22
26
|
}
|
|
23
27
|
`;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogVertexDeclarationGLSL, fogVertexGLSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* glsl */ `
|
|
2
4
|
attribute vec3 position;
|
|
3
5
|
attribute vec3 previous;
|
|
@@ -22,6 +24,7 @@ vec2 fix(vec4 i, float aspect) {
|
|
|
22
24
|
return res;
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
${fogVertexDeclarationGLSL}
|
|
25
28
|
void main() {
|
|
26
29
|
float aspect = resolution.x / resolution.y;
|
|
27
30
|
vec4 finalPosition = projection * view * vec4(position, 1.0);
|
|
@@ -52,6 +55,7 @@ void main() {
|
|
|
52
55
|
}
|
|
53
56
|
finalPosition.xy += normal.xy * side;
|
|
54
57
|
gl_Position = finalPosition;
|
|
58
|
+
${fogVertexGLSL('(view * vec4(position, 1.0)).xyz')}
|
|
55
59
|
vUV = uv;
|
|
56
60
|
vColor = color;
|
|
57
61
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {fogVertexDeclarationWGSL, fogVertexWGSL} from './chunks/fog';
|
|
2
|
+
|
|
1
3
|
export default /* wgsl */ `
|
|
2
4
|
attribute position: vec3f;
|
|
3
5
|
attribute previous: vec3f;
|
|
@@ -22,6 +24,7 @@ fn fix(i: vec4f, aspect: f32) -> vec2f {
|
|
|
22
24
|
return res;
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
${fogVertexDeclarationWGSL}
|
|
25
28
|
@vertex
|
|
26
29
|
fn main(input: VertexInputs) -> FragmentInputs {
|
|
27
30
|
let aspect = uniforms.resolution.x / uniforms.resolution.y;
|
|
@@ -57,6 +60,7 @@ fn main(input: VertexInputs) -> FragmentInputs {
|
|
|
57
60
|
finalPosition.y += normal.y * vertexInputs.side;
|
|
58
61
|
|
|
59
62
|
vertexOutputs.position = finalPosition;
|
|
63
|
+
${fogVertexWGSL('(uniforms.view * vec4f(vertexInputs.position, 1.0)).xyz')}
|
|
60
64
|
vertexOutputs.vUV = vertexInputs.uv;
|
|
61
65
|
vertexOutputs.vColor = vertexInputs.color;
|
|
62
66
|
}
|