@forgeax/engine-vfx-render 0.1.27 → 0.1.29
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/README.md +55 -10
- package/dist/__tests__/persistent-ticks.integration.test.d.ts +2 -0
- package/dist/__tests__/persistent-ticks.integration.test.d.ts.map +1 -0
- package/dist/feature/event-resources.d.ts +10 -3
- package/dist/feature/event-resources.d.ts.map +1 -1
- package/dist/feature/gpu-particle-feature.d.ts +18 -5
- package/dist/feature/gpu-particle-feature.d.ts.map +1 -1
- package/dist/feature/particle-resources.d.ts +43 -6
- package/dist/feature/particle-resources.d.ts.map +1 -1
- package/dist/host/data-interface-providers.d.ts +4 -1
- package/dist/host/data-interface-providers.d.ts.map +1 -1
- package/dist/host/vfx-runtime-host.d.ts +21 -2
- package/dist/host/vfx-runtime-host.d.ts.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1027 -307
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -9
- package/src/__tests__/billboard-advanced.integration.test.ts +1 -1
- package/src/__tests__/data-interface-providers.unit.test.ts +4 -0
- package/src/__tests__/data-interface-public-api.test-d.ts +1 -0
- package/src/__tests__/gpu-host.integration.test.ts +1471 -59
- package/src/__tests__/particle-resources.unit.test.ts +75 -0
- package/src/__tests__/persistent-ticks.integration.test.ts +162 -0
- package/src/__tests__/render-vocabulary-owner.test-d.ts +7 -7
- package/src/feature/event-resources.ts +18 -4
- package/src/feature/gpu-particle-feature.ts +1153 -376
- package/src/feature/particle-resources.ts +223 -11
- package/src/host/data-interface-providers.ts +19 -0
- package/src/host/vfx-runtime-host.ts +82 -2
- package/src/index.ts +8 -0
- package/src/shaders/beam-inputs.wgsl +40 -0
- package/src/shaders/beam.wgsl +4 -3
- package/src/shaders/billboard-inputs.wgsl +85 -0
- package/src/shaders/mesh-inputs.wgsl +95 -0
- package/src/shaders/mesh-shadow.wgsl +18 -0
- package/src/shaders/mesh.wgsl +64 -40
- package/src/shaders/ribbon-inputs.wgsl +39 -0
- package/src/shaders/trail-inputs.wgsl +45 -0
- package/src/shaders/trail.wgsl +9 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#define_import_path forgeax::vfx-render.particles.mesh-inputs
|
|
2
|
+
#import forgeax_view::common::{View, view}
|
|
3
|
+
#import forgeax_scene_temporal::{sceneViewZ}
|
|
4
|
+
#import forgeax_material::standard_surface::{VsOut, StandardSurfaceFactors, evaluateStandardSurface, material}
|
|
5
|
+
#import forgeax_material::standard_surface::{materialTextureFilteringWitness}
|
|
6
|
+
|
|
7
|
+
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
8
|
+
#pragma variant_axis CLUSTER_FORWARD_AVAILABLE
|
|
9
|
+
#pragma variant_axis EXTENDED_LIGHTING_AVAILABLE
|
|
10
|
+
#pragma variant_axis DIRECTIONAL_PCSS_AVAILABLE
|
|
11
|
+
#pragma variant_axis PROJECTOR_AVAILABLE
|
|
12
|
+
|
|
13
|
+
// Retain the full declared material ABI in reflection, including inactive inputs.
|
|
14
|
+
fn materialInterfaceWitness() { materialTextureFilteringWitness(); }
|
|
15
|
+
|
|
16
|
+
struct VertexOutput {
|
|
17
|
+
@builtin(position) position: vec4<f32>,
|
|
18
|
+
@location(0) color: vec4<f32>,
|
|
19
|
+
@location(1) normal: vec3<f32>,
|
|
20
|
+
@location(2) @interpolate(flat) render_controls: vec2<f32>,
|
|
21
|
+
@location(4) world_position: vec3<f32>,
|
|
22
|
+
@location(5) uv: vec2<f32>,
|
|
23
|
+
@location(6) tangent: vec4<f32>,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
struct VertexInput {
|
|
27
|
+
@location(0) geometry_position: vec3<f32>,
|
|
28
|
+
@location(1) geometry_normal: vec3<f32>,
|
|
29
|
+
@location(2) geometry_uv: vec2<f32>,
|
|
30
|
+
@location(3) geometry_tangent: vec4<f32>,
|
|
31
|
+
@location(4) center: vec3<f32>,
|
|
32
|
+
@location(5) right: vec3<f32>,
|
|
33
|
+
@location(6) up: vec3<f32>,
|
|
34
|
+
@location(7) forward: vec3<f32>,
|
|
35
|
+
@location(8) particle_color: vec4<f32>,
|
|
36
|
+
@location(9) render_controls: vec2<f32>,
|
|
37
|
+
@location(10) particle_inputs: vec4<f32>,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
fn safeNormalize(value: vec3<f32>, fallback: vec3<f32>) -> vec3<f32> {
|
|
41
|
+
let magnitude = length(value);
|
|
42
|
+
if (magnitude > 0.000001) { return value / magnitude; }
|
|
43
|
+
return fallback;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@vertex
|
|
47
|
+
fn vs_main(input: VertexInput) -> VertexOutput {
|
|
48
|
+
var output: VertexOutput;
|
|
49
|
+
output.world_position = input.center + input.right * input.geometry_position.x +
|
|
50
|
+
input.up * input.geometry_position.y + input.forward * input.geometry_position.z;
|
|
51
|
+
output.position = view.worldViewProj * vec4<f32>(output.world_position, 1.0);
|
|
52
|
+
let heat = clamp(input.particle_inputs.x, 0.0, 1.0);
|
|
53
|
+
output.color = input.particle_color * (0.35 + 0.65 * heat);
|
|
54
|
+
let normalBasisX = cross(input.up, input.forward);
|
|
55
|
+
let normalBasisY = cross(input.forward, input.right);
|
|
56
|
+
let normalBasisZ = cross(input.right, input.up);
|
|
57
|
+
// Cofactors require determinant sign for reflected/non-uniform Scale3.
|
|
58
|
+
let handedness = select(-1.0, 1.0, dot(input.right, normalBasisX) >= 0.0);
|
|
59
|
+
output.normal = safeNormalize(handedness * (
|
|
60
|
+
normalBasisX * input.geometry_normal.x + normalBasisY * input.geometry_normal.y +
|
|
61
|
+
normalBasisZ * input.geometry_normal.z), vec3<f32>(0.0, 1.0, 0.0));
|
|
62
|
+
let tangent = input.right * input.geometry_tangent.x + input.up * input.geometry_tangent.y +
|
|
63
|
+
input.forward * input.geometry_tangent.z;
|
|
64
|
+
output.tangent = vec4<f32>(safeNormalize(tangent - output.normal * dot(output.normal, tangent),
|
|
65
|
+
vec3<f32>(1.0, 0.0, 0.0)), input.geometry_tangent.w * handedness);
|
|
66
|
+
output.uv = input.geometry_uv;
|
|
67
|
+
output.render_controls = input.render_controls;
|
|
68
|
+
return output;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@fragment
|
|
72
|
+
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
73
|
+
var surface: VsOut;
|
|
74
|
+
let clip = view.worldViewProj * vec4<f32>(input.world_position, 1.0);
|
|
75
|
+
surface.clip = clip;
|
|
76
|
+
surface.worldPos = input.world_position;
|
|
77
|
+
surface.worldNormal = input.normal;
|
|
78
|
+
surface.worldTangent = input.tangent;
|
|
79
|
+
surface.uv = input.uv;
|
|
80
|
+
surface.uvOne = input.uv;
|
|
81
|
+
surface.uvTwo = input.uv;
|
|
82
|
+
surface.uvThree = input.uv;
|
|
83
|
+
surface.uvFour = input.uv;
|
|
84
|
+
surface.uvFive = input.uv;
|
|
85
|
+
surface.uvSix = input.uv;
|
|
86
|
+
surface.uvSeven = input.uv;
|
|
87
|
+
surface.ndc = vec4<f32>(clip.xyz / clip.w, 0.0);
|
|
88
|
+
surface.viewZ = sceneViewZ(clip, view.temporalProjection);
|
|
89
|
+
return evaluateStandardSurface(surface, StandardSurfaceFactors(
|
|
90
|
+
input.color * material.baseColor, material.metallic, material.roughness,
|
|
91
|
+
material.emissive, material.emissiveIntensity,
|
|
92
|
+
material.clearcoat, material.clearcoatRoughness,
|
|
93
|
+
input.render_controls.x != 0.0, input.render_controls.y != 0.0,
|
|
94
|
+
)).color;
|
|
95
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#define_import_path forgeax::vfx-render.particles.mesh-shadow
|
|
2
|
+
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
3
|
+
#import forgeax_shadow::surface::{projectShadowPosition}
|
|
4
|
+
|
|
5
|
+
struct MeshShadowInput {
|
|
6
|
+
@location(0) position: vec3<f32>,
|
|
7
|
+
@location(4) center: vec3<f32>,
|
|
8
|
+
@location(5) right: vec3<f32>,
|
|
9
|
+
@location(6) up: vec3<f32>,
|
|
10
|
+
@location(7) forward: vec3<f32>,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
@vertex
|
|
14
|
+
fn vs_main(input: MeshShadowInput) -> @builtin(position) vec4<f32> {
|
|
15
|
+
let worldPosition = input.center + input.right * input.position.x +
|
|
16
|
+
input.up * input.position.y + input.forward * input.position.z;
|
|
17
|
+
return projectShadowPosition(vec4<f32>(worldPosition, 1.0));
|
|
18
|
+
}
|
package/src/shaders/mesh.wgsl
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
#define_import_path forgeax::vfx-render.particles.mesh
|
|
2
|
+
#import forgeax_view::common::{View, view}
|
|
3
|
+
#import forgeax_scene_temporal::{sceneViewZ}
|
|
4
|
+
#import forgeax_material::standard_surface::{VsOut, StandardSurfaceFactors, evaluateStandardSurface, material}
|
|
5
|
+
#import forgeax_material::standard_surface::{materialTextureFilteringWitness}
|
|
6
|
+
|
|
7
|
+
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
8
|
+
#pragma variant_axis CLUSTER_FORWARD_AVAILABLE
|
|
9
|
+
#pragma variant_axis EXTENDED_LIGHTING_AVAILABLE
|
|
10
|
+
#pragma variant_axis DIRECTIONAL_PCSS_AVAILABLE
|
|
11
|
+
#pragma variant_axis PROJECTOR_AVAILABLE
|
|
12
|
+
|
|
13
|
+
// Retain the full declared material ABI in reflection, including inactive inputs.
|
|
14
|
+
fn materialInterfaceWitness() { materialTextureFilteringWitness(); }
|
|
2
15
|
|
|
3
16
|
struct VertexOutput {
|
|
4
17
|
@builtin(position) position: vec4<f32>,
|
|
5
18
|
@location(0) color: vec4<f32>,
|
|
6
19
|
@location(1) normal: vec3<f32>,
|
|
7
|
-
@location(2)
|
|
8
|
-
@location(
|
|
9
|
-
@location(
|
|
20
|
+
@location(2) @interpolate(flat) render_controls: vec2<f32>,
|
|
21
|
+
@location(4) world_position: vec3<f32>,
|
|
22
|
+
@location(5) uv: vec2<f32>,
|
|
23
|
+
@location(6) tangent: vec4<f32>,
|
|
10
24
|
};
|
|
11
25
|
|
|
12
26
|
struct VertexInput {
|
|
@@ -19,51 +33,61 @@ struct VertexInput {
|
|
|
19
33
|
@location(6) up: vec3<f32>,
|
|
20
34
|
@location(7) forward: vec3<f32>,
|
|
21
35
|
@location(8) particle_color: vec4<f32>,
|
|
22
|
-
@location(9)
|
|
23
|
-
@location(10) emissive_intensity: vec4<f32>,
|
|
24
|
-
@location(11) surface: vec4<f32>,
|
|
36
|
+
@location(9) render_controls: vec2<f32>,
|
|
25
37
|
};
|
|
26
38
|
|
|
39
|
+
fn safeNormalize(value: vec3<f32>, fallback: vec3<f32>) -> vec3<f32> {
|
|
40
|
+
let magnitude = length(value);
|
|
41
|
+
if (magnitude > 0.000001) { return value / magnitude; }
|
|
42
|
+
return fallback;
|
|
43
|
+
}
|
|
44
|
+
|
|
27
45
|
@vertex
|
|
28
46
|
fn vs_main(input: VertexInput) -> VertexOutput {
|
|
29
47
|
var output: VertexOutput;
|
|
30
|
-
|
|
31
|
-
input.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
let
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
input.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
output.
|
|
48
|
+
output.world_position = input.center + input.right * input.geometry_position.x +
|
|
49
|
+
input.up * input.geometry_position.y + input.forward * input.geometry_position.z;
|
|
50
|
+
output.position = view.worldViewProj * vec4<f32>(output.world_position, 1.0);
|
|
51
|
+
output.color = input.particle_color;
|
|
52
|
+
let normalBasisX = cross(input.up, input.forward);
|
|
53
|
+
let normalBasisY = cross(input.forward, input.right);
|
|
54
|
+
let normalBasisZ = cross(input.right, input.up);
|
|
55
|
+
// Cofactors require determinant sign for reflected/non-uniform Scale3.
|
|
56
|
+
let handedness = select(-1.0, 1.0, dot(input.right, normalBasisX) >= 0.0);
|
|
57
|
+
output.normal = safeNormalize(handedness * (
|
|
58
|
+
normalBasisX * input.geometry_normal.x + normalBasisY * input.geometry_normal.y +
|
|
59
|
+
normalBasisZ * input.geometry_normal.z), vec3<f32>(0.0, 1.0, 0.0));
|
|
60
|
+
let tangent = input.right * input.geometry_tangent.x + input.up * input.geometry_tangent.y +
|
|
61
|
+
input.forward * input.geometry_tangent.z;
|
|
62
|
+
output.tangent = vec4<f32>(safeNormalize(tangent - output.normal * dot(output.normal, tangent),
|
|
63
|
+
vec3<f32>(1.0, 0.0, 0.0)), input.geometry_tangent.w * handedness);
|
|
64
|
+
output.uv = input.geometry_uv;
|
|
65
|
+
output.render_controls = input.render_controls;
|
|
45
66
|
return output;
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
@fragment
|
|
49
70
|
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
50
|
-
|
|
51
|
-
let
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
var surface: VsOut;
|
|
72
|
+
let clip = view.worldViewProj * vec4<f32>(input.world_position, 1.0);
|
|
73
|
+
surface.clip = clip;
|
|
74
|
+
surface.worldPos = input.world_position;
|
|
75
|
+
surface.worldNormal = input.normal;
|
|
76
|
+
surface.worldTangent = input.tangent;
|
|
77
|
+
surface.uv = input.uv;
|
|
78
|
+
surface.uvOne = input.uv;
|
|
79
|
+
surface.uvTwo = input.uv;
|
|
80
|
+
surface.uvThree = input.uv;
|
|
81
|
+
surface.uvFour = input.uv;
|
|
82
|
+
surface.uvFive = input.uv;
|
|
83
|
+
surface.uvSix = input.uv;
|
|
84
|
+
surface.uvSeven = input.uv;
|
|
85
|
+
surface.ndc = vec4<f32>(clip.xyz / clip.w, 0.0);
|
|
86
|
+
surface.viewZ = sceneViewZ(clip, view.temporalProjection);
|
|
87
|
+
return evaluateStandardSurface(surface, StandardSurfaceFactors(
|
|
88
|
+
input.color * material.baseColor, material.metallic, material.roughness,
|
|
89
|
+
material.emissive, material.emissiveIntensity,
|
|
90
|
+
material.clearcoat, material.clearcoatRoughness,
|
|
91
|
+
input.render_controls.x != 0.0, input.render_controls.y != 0.0,
|
|
92
|
+
)).color;
|
|
69
93
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#define_import_path forgeax::vfx-render.particles.ribbon-inputs
|
|
2
|
+
|
|
3
|
+
struct SegmentInput {
|
|
4
|
+
@location(0) start: vec3<f32>,
|
|
5
|
+
@location(1) endpoint: vec3<f32>,
|
|
6
|
+
@location(2) color: vec4<f32>,
|
|
7
|
+
@location(3) properties: vec2<f32>,
|
|
8
|
+
@location(4) particle_inputs: vec4<f32>,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
struct VertexOutput {
|
|
12
|
+
@builtin(position) position: vec4<f32>,
|
|
13
|
+
@location(0) color: vec4<f32>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@vertex
|
|
17
|
+
fn vs_main(input: SegmentInput, @builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
18
|
+
let corners = array<vec2<f32>, 6>(
|
|
19
|
+
vec2<f32>(0.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0),
|
|
20
|
+
vec2<f32>(0.0, -1.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0)
|
|
21
|
+
);
|
|
22
|
+
let corner = corners[vertexIndex];
|
|
23
|
+
let delta = input.endpoint.xy - input.start.xy;
|
|
24
|
+
let normal = normalize(vec2<f32>(-delta.y, delta.x) + vec2<f32>(0.000001, 0.0));
|
|
25
|
+
let point = mix(input.start, input.endpoint, corner.x);
|
|
26
|
+
let clipPosition = vec3<f32>(point.xy + normal * corner.y * input.properties.x, point.z);
|
|
27
|
+
var output: VertexOutput;
|
|
28
|
+
output.position = vec4<f32>(clipPosition, 1.0);
|
|
29
|
+
let heat = clamp(input.particle_inputs.x, 0.0, 1.0);
|
|
30
|
+
output.color = input.color * (0.35 + 0.65 * heat);
|
|
31
|
+
return output;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@fragment
|
|
35
|
+
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
36
|
+
let base = vec4<f32>(0.2, 0.7, 1.0, 0.9);
|
|
37
|
+
let alpha = base.a * input.color.a;
|
|
38
|
+
return vec4<f32>(base.rgb * input.color.rgb * alpha, alpha);
|
|
39
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#define_import_path forgeax::vfx-render.particles.trail-inputs
|
|
2
|
+
|
|
3
|
+
struct SegmentInput {
|
|
4
|
+
@location(0) start: vec3<f32>,
|
|
5
|
+
@location(1) endpoint: vec3<f32>,
|
|
6
|
+
@location(2) color: vec4<f32>,
|
|
7
|
+
@location(3) properties: vec2<f32>,
|
|
8
|
+
@location(4) particle_inputs: vec4<f32>,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
struct VertexOutput {
|
|
12
|
+
@builtin(position) position: vec4<f32>,
|
|
13
|
+
@location(0) color: vec4<f32>,
|
|
14
|
+
@location(1) uv: vec2<f32>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@vertex
|
|
18
|
+
fn vs_main(input: SegmentInput, @builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
19
|
+
let corners = array<vec2<f32>, 6>(
|
|
20
|
+
vec2<f32>(0.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0),
|
|
21
|
+
vec2<f32>(0.0, -1.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0)
|
|
22
|
+
);
|
|
23
|
+
let corner = corners[vertexIndex];
|
|
24
|
+
let delta = input.endpoint.xy - input.start.xy;
|
|
25
|
+
let normal = normalize(vec2<f32>(-delta.y, delta.x) + vec2<f32>(0.000001, 0.0));
|
|
26
|
+
let tangent = normalize(delta + vec2<f32>(0.000001, 0.0));
|
|
27
|
+
let taper = max(0.0, 1.0 - input.properties.y);
|
|
28
|
+
let point = mix(input.start, input.endpoint, corner.x) + vec3<f32>(
|
|
29
|
+
tangent * ((corner.x * 2.0 - 1.0) * input.properties.x), 0.0,
|
|
30
|
+
);
|
|
31
|
+
let clipPosition = vec3<f32>(point.xy + normal * corner.y * input.properties.x * taper, point.z);
|
|
32
|
+
var output: VertexOutput;
|
|
33
|
+
output.position = vec4<f32>(clipPosition, 1.0);
|
|
34
|
+
let heat = clamp(input.particle_inputs.x, 0.0, 1.0);
|
|
35
|
+
output.color = input.color * (0.35 + 0.65 * heat);
|
|
36
|
+
output.uv = corner;
|
|
37
|
+
return output;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@fragment
|
|
41
|
+
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
42
|
+
let edge = 1.0 - smoothstep(0.58, 1.0, abs(input.uv.y));
|
|
43
|
+
let alpha = input.color.a * edge;
|
|
44
|
+
return vec4<f32>(input.color.rgb * alpha, alpha);
|
|
45
|
+
}
|
package/src/shaders/trail.wgsl
CHANGED
|
@@ -11,6 +11,7 @@ struct VertexOutput {
|
|
|
11
11
|
@builtin(position) position: vec4<f32>,
|
|
12
12
|
@location(0) color: vec4<f32>,
|
|
13
13
|
@location(1) clip_position: vec3<f32>,
|
|
14
|
+
@location(2) uv: vec2<f32>,
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
@vertex
|
|
@@ -36,12 +37,17 @@ fn vs_main(input: SegmentInput, @builtin(vertex_index) vertexIndex: u32) -> Vert
|
|
|
36
37
|
output.position = vec4<f32>(clipPosition, 1.0);
|
|
37
38
|
output.color = input.color;
|
|
38
39
|
output.clip_position = clipPosition;
|
|
40
|
+
output.uv = corner;
|
|
39
41
|
return output;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
@fragment
|
|
43
45
|
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
// Keep the topology's authored particle color authoritative and feather
|
|
47
|
+
// the strip edge. A solid quad makes short history segments read as a
|
|
48
|
+
// block when several particles overlap; this analytic cross-section keeps
|
|
49
|
+
// the existing clip-space ABI while providing a continuous wake.
|
|
50
|
+
let edge = 1.0 - smoothstep(0.58, 1.0, abs(input.uv.y));
|
|
51
|
+
let alpha = input.color.a * edge;
|
|
52
|
+
return vec4<f32>(input.color.rgb * alpha, alpha);
|
|
47
53
|
}
|