@forgeax/engine-vfx-render 0.1.2

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 (91) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +163 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/billboard-advanced.integration.test.d.ts +2 -0
  5. package/dist/__tests__/billboard-advanced.integration.test.d.ts.map +1 -0
  6. package/dist/__tests__/data-interface-providers.unit.test.d.ts +2 -0
  7. package/dist/__tests__/data-interface-providers.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/data-interface-public-api.test-d.d.ts +2 -0
  9. package/dist/__tests__/data-interface-public-api.test-d.d.ts.map +1 -0
  10. package/dist/__tests__/event-resources.unit.test.d.ts +2 -0
  11. package/dist/__tests__/event-resources.unit.test.d.ts.map +1 -0
  12. package/dist/__tests__/fog-producer.integration.test.d.ts +2 -0
  13. package/dist/__tests__/fog-producer.integration.test.d.ts.map +1 -0
  14. package/dist/__tests__/gpu-host.integration.test.d.ts +2 -0
  15. package/dist/__tests__/gpu-host.integration.test.d.ts.map +1 -0
  16. package/dist/__tests__/inspect-snapshot.integration.test.d.ts +2 -0
  17. package/dist/__tests__/inspect-snapshot.integration.test.d.ts.map +1 -0
  18. package/dist/__tests__/particle-resources.unit.test.d.ts +2 -0
  19. package/dist/__tests__/particle-resources.unit.test.d.ts.map +1 -0
  20. package/dist/__tests__/render-vocabulary-owner.test-d.d.ts +2 -0
  21. package/dist/__tests__/render-vocabulary-owner.test-d.d.ts.map +1 -0
  22. package/dist/__tests__/stage-plan.integration.test.d.ts +2 -0
  23. package/dist/__tests__/stage-plan.integration.test.d.ts.map +1 -0
  24. package/dist/__tests__/stage-recovery.integration.test.d.ts +2 -0
  25. package/dist/__tests__/stage-recovery.integration.test.d.ts.map +1 -0
  26. package/dist/__tests__/stage-resource-access-owner.test-d.d.ts +2 -0
  27. package/dist/__tests__/stage-resource-access-owner.test-d.d.ts.map +1 -0
  28. package/dist/__tests__/stage-resource-layout.unit.test.d.ts +2 -0
  29. package/dist/__tests__/stage-resource-layout.unit.test.d.ts.map +1 -0
  30. package/dist/__tests__/topology-capacity.unit.test.d.ts +2 -0
  31. package/dist/__tests__/topology-capacity.unit.test.d.ts.map +1 -0
  32. package/dist/__tests__/topology-resources.unit.test.d.ts +2 -0
  33. package/dist/__tests__/topology-resources.unit.test.d.ts.map +1 -0
  34. package/dist/feature/camera.d.ts +11 -0
  35. package/dist/feature/camera.d.ts.map +1 -0
  36. package/dist/feature/event-resources.d.ts +9 -0
  37. package/dist/feature/event-resources.d.ts.map +1 -0
  38. package/dist/feature/gpu-particle-feature.d.ts +96 -0
  39. package/dist/feature/gpu-particle-feature.d.ts.map +1 -0
  40. package/dist/feature/particle-resources.d.ts +66 -0
  41. package/dist/feature/particle-resources.d.ts.map +1 -0
  42. package/dist/feature/stage-plan.d.ts +54 -0
  43. package/dist/feature/stage-plan.d.ts.map +1 -0
  44. package/dist/feature/topologies/beam.d.ts +16 -0
  45. package/dist/feature/topologies/beam.d.ts.map +1 -0
  46. package/dist/feature/topologies/ribbon.d.ts +15 -0
  47. package/dist/feature/topologies/ribbon.d.ts.map +1 -0
  48. package/dist/feature/topologies/trail.d.ts +19 -0
  49. package/dist/feature/topologies/trail.d.ts.map +1 -0
  50. package/dist/host/data-interface-providers.d.ts +19 -0
  51. package/dist/host/data-interface-providers.d.ts.map +1 -0
  52. package/dist/host/vfx-runtime-host.d.ts +87 -0
  53. package/dist/host/vfx-runtime-host.d.ts.map +1 -0
  54. package/dist/index.d.ts +12 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.mjs +1572 -0
  57. package/dist/index.mjs.map +1 -0
  58. package/package.json +63 -0
  59. package/src/__tests__/billboard-advanced.integration.test.ts +69 -0
  60. package/src/__tests__/data-interface-providers.unit.test.ts +55 -0
  61. package/src/__tests__/data-interface-public-api.test-d.ts +58 -0
  62. package/src/__tests__/event-resources.unit.test.ts +71 -0
  63. package/src/__tests__/fog-producer.integration.test.ts +59 -0
  64. package/src/__tests__/gpu-host.integration.test.ts +441 -0
  65. package/src/__tests__/inspect-snapshot.integration.test.ts +26 -0
  66. package/src/__tests__/particle-resources.unit.test.ts +75 -0
  67. package/src/__tests__/render-vocabulary-owner.test-d.ts +81 -0
  68. package/src/__tests__/stage-plan.integration.test.ts +16 -0
  69. package/src/__tests__/stage-recovery.integration.test.ts +24 -0
  70. package/src/__tests__/stage-resource-access-owner.test-d.ts +26 -0
  71. package/src/__tests__/stage-resource-layout.unit.test.ts +40 -0
  72. package/src/__tests__/topology-capacity.unit.test.ts +59 -0
  73. package/src/__tests__/topology-resources.unit.test.ts +29 -0
  74. package/src/feature/camera.ts +12 -0
  75. package/src/feature/event-resources.ts +62 -0
  76. package/src/feature/gpu-particle-feature.ts +1225 -0
  77. package/src/feature/particle-resources.ts +203 -0
  78. package/src/feature/stage-plan.ts +282 -0
  79. package/src/feature/topologies/beam.ts +41 -0
  80. package/src/feature/topologies/ribbon.ts +31 -0
  81. package/src/feature/topologies/trail.ts +43 -0
  82. package/src/host/data-interface-providers.ts +119 -0
  83. package/src/host/vfx-runtime-host.ts +402 -0
  84. package/src/index.ts +57 -0
  85. package/src/shaders/beam.wgsl +69 -0
  86. package/src/shaders/billboard.wgsl +132 -0
  87. package/src/shaders/event.wgsl +11 -0
  88. package/src/shaders/mesh.wgsl +98 -0
  89. package/src/shaders/ribbon.wgsl +69 -0
  90. package/src/shaders/stage.wgsl +5 -0
  91. package/src/shaders/trail.wgsl +77 -0
@@ -0,0 +1,132 @@
1
+ #define_import_path forgeax::vfx-render.particles.billboard
2
+ #import forgeax_view::common::{View, FogViewParams, FogRay, view}
3
+ #import forgeax_view::fog::{apply_fog}
4
+
5
+ @group(0) @binding(1) var scene_depth: texture_depth_2d;
6
+
7
+ struct VertexOutput {
8
+ @builtin(position) position: vec4<f32>,
9
+ @location(0) color: vec4<f32>,
10
+ @location(1) local: vec2<f32>,
11
+ @location(2) emissive_intensity: vec4<f32>,
12
+ @location(3) surface: vec4<f32>,
13
+ @location(4) sheet_uv: vec2<f32>,
14
+ @location(5) sheet_frame: f32,
15
+ @location(6) fade_distance: f32,
16
+ @location(7) clip_position: vec3<f32>,
17
+ };
18
+
19
+ fn textureSheetFrame(age: f32, frameRate: f32, frameCount: u32) -> u32 {
20
+ if (frameCount == 0u || frameRate <= 0.0) { return 0u; }
21
+ return min(frameCount - 1u, u32(max(0.0, floor(age * frameRate))));
22
+ }
23
+
24
+ fn textureSheetUv(local: vec2<f32>, frame: u32, columns: u32, rows: u32) -> vec2<f32> {
25
+ let safeColumns = max(columns, 1u);
26
+ let safeRows = max(rows, 1u);
27
+ let cell = vec2<u32>(frame % safeColumns, frame / safeColumns);
28
+ return (local + vec2<f32>(1.0)) * 0.5 / vec2<f32>(f32(safeColumns), f32(safeRows)) +
29
+ vec2<f32>(f32(cell.x) / f32(safeColumns), f32(cell.y) / f32(safeRows));
30
+ }
31
+
32
+ fn billboardPivot(corner: vec2<f32>, pivot: vec2<f32>) -> vec2<f32> {
33
+ return corner + pivot * 2.0;
34
+ }
35
+
36
+ fn softParticleFactor(particleDepth: f32, sceneDepth: f32, fadeDistance: f32) -> f32 {
37
+ if (fadeDistance <= 0.0) { return 1.0; }
38
+ return clamp((sceneDepth - particleDepth) / fadeDistance, 0.0, 1.0);
39
+ }
40
+
41
+ fn billboardSortingKey(depth: f32, mode: u32) -> f32 {
42
+ return select(0.0, depth, mode == 2u);
43
+ }
44
+
45
+ fn softParticle(position: vec4<f32>, alpha: f32, fadeDistance: f32) -> f32 {
46
+ let pixel = vec2<i32>(position.xy);
47
+ let sceneDepth = textureLoad(scene_depth, pixel, 0);
48
+ if (fadeDistance <= 0.0) {
49
+ return select(alpha, 0.0, position.z > sceneDepth);
50
+ }
51
+ return alpha * softParticleFactor(position.z, sceneDepth, fadeDistance);
52
+ }
53
+
54
+ fn fogWorldPoint(ndc: vec3<f32>) -> vec3<f32> {
55
+ let homogeneous = view.inverseViewProj * vec4<f32>(ndc, 1.0);
56
+ let divisor = select(1.0, homogeneous.w, abs(homogeneous.w) > 0.000001);
57
+ return homogeneous.xyz / divisor;
58
+ }
59
+
60
+ fn fogRayFromNdc(ndc: vec3<f32>) -> FogRay {
61
+ let worldPosition = fogWorldPoint(ndc);
62
+ let nearPosition = fogWorldPoint(vec3<f32>(ndc.xy, 0.0));
63
+ let farPosition = fogWorldPoint(vec3<f32>(ndc.xy, 1.0));
64
+ let perspective = view.temporalProjection.z < 0.5;
65
+ let perspectiveVector = worldPosition - view.cameraPos;
66
+ let orthographicVector = farPosition - nearPosition;
67
+ let direction = normalize(select(orthographicVector, perspectiveVector, perspective));
68
+ let origin = select(nearPosition, view.cameraPos, perspective);
69
+ let ray_distance = select(
70
+ max(dot(worldPosition - nearPosition, direction), 0.0),
71
+ length(perspectiveVector),
72
+ perspective,
73
+ );
74
+ return FogRay(origin, direction, ray_distance);
75
+ }
76
+
77
+ struct VertexInput {
78
+ @location(0) position: vec3<f32>,
79
+ @location(1) right: vec2<f32>,
80
+ @location(2) up: vec2<f32>,
81
+ @location(3) particle_color: vec4<f32>,
82
+ @location(4) base_color: vec4<f32>,
83
+ @location(5) emissive_intensity: vec4<f32>,
84
+ @location(6) surface: vec4<f32>,
85
+ @location(7) advanced: vec4<f32>,
86
+ @location(8) texture_sheet: vec4<f32>,
87
+ };
88
+
89
+ @vertex
90
+ fn vs_main(input: VertexInput, @builtin(vertex_index) vertex_index: u32) -> VertexOutput {
91
+ let corners = array<vec2<f32>, 6>(
92
+ vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0),
93
+ vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, 1.0), vec2<f32>(-1.0, 1.0)
94
+ );
95
+ let corner = billboardPivot(corners[vertex_index], input.advanced.xy);
96
+ var output: VertexOutput;
97
+ let clipPosition = vec3<f32>(
98
+ input.position.xy + input.right * corner.x + input.up * corner.y,
99
+ input.position.z,
100
+ );
101
+ output.position = vec4<f32>(clipPosition, 1.0);
102
+ output.clip_position = clipPosition;
103
+ output.color = input.particle_color * input.base_color;
104
+ output.local = corner;
105
+ output.emissive_intensity = input.emissive_intensity;
106
+ output.surface = input.surface;
107
+ output.sheet_uv = textureSheetUv(
108
+ corners[vertex_index],
109
+ u32(input.advanced.z),
110
+ u32(input.texture_sheet.x),
111
+ u32(input.texture_sheet.y),
112
+ );
113
+ output.sheet_frame = input.advanced.z;
114
+ output.fade_distance = input.texture_sheet.z;
115
+ return output;
116
+ }
117
+
118
+ @fragment
119
+ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
120
+ let radius = length(input.local);
121
+ let edge = 1.0 - smoothstep(0.45, 1.0, radius);
122
+ let core = 1.0 - smoothstep(0.0, 0.42, radius);
123
+ let roughness = clamp(input.surface.y, 0.04, 1.0);
124
+ let clearcoat = clamp(input.surface.z, 0.0, 1.0);
125
+ let highlight = core * clearcoat * (1.0 - roughness * 0.65);
126
+ let emissive = input.emissive_intensity.rgb * input.emissive_intensity.a;
127
+ let alpha = softParticle(input.position, input.color.a * edge, input.fade_distance);
128
+ let sheetPulse = 0.82 + 0.18 * fract(input.sheet_frame * 0.618 + input.sheet_uv.x + input.sheet_uv.y);
129
+ let rgb = (input.color.rgb + emissive * (0.35 + core * 0.65) + vec3<f32>(highlight)) * sheetPulse;
130
+ let fogged = apply_fog(view.fog, fogRayFromNdc(input.clip_position), vec4<f32>(rgb, alpha));
131
+ return vec4<f32>(fogged.rgb * fogged.a, fogged.a);
132
+ }
@@ -0,0 +1,11 @@
1
+ struct ForgeaxVfxEventShaderRecord {
2
+ position: vec4<f32>,
3
+ strength: f32,
4
+ sequence: u32,
5
+ channel: u32,
6
+ fan_out: u32,
7
+ }
8
+
9
+ fn forgeax_vfx_event_strength(record: ForgeaxVfxEventShaderRecord) -> f32 {
10
+ return max(record.strength, 0.0);
11
+ }
@@ -0,0 +1,98 @@
1
+ #define_import_path forgeax::vfx-render.particles.mesh
2
+ #import forgeax_view::common::{View, FogViewParams, FogRay, view}
3
+ #import forgeax_view::fog::{apply_fog}
4
+
5
+ struct VertexOutput {
6
+ @builtin(position) position: vec4<f32>,
7
+ @location(0) color: vec4<f32>,
8
+ @location(1) normal: vec3<f32>,
9
+ @location(2) emissive_intensity: vec4<f32>,
10
+ @location(3) surface: vec4<f32>,
11
+ @location(4) clip_position: vec3<f32>,
12
+ };
13
+
14
+ struct VertexInput {
15
+ @location(0) geometry_position: vec3<f32>,
16
+ @location(1) geometry_normal: vec3<f32>,
17
+ @location(2) geometry_uv: vec2<f32>,
18
+ @location(3) geometry_tangent: vec4<f32>,
19
+ @location(4) center: vec3<f32>,
20
+ @location(5) right: vec3<f32>,
21
+ @location(6) up: vec3<f32>,
22
+ @location(7) forward: vec3<f32>,
23
+ @location(8) particle_color: vec4<f32>,
24
+ @location(9) base_color: vec4<f32>,
25
+ @location(10) emissive_intensity: vec4<f32>,
26
+ @location(11) surface: vec4<f32>,
27
+ };
28
+
29
+ fn fogWorldPoint(ndc: vec3<f32>) -> vec3<f32> {
30
+ let homogeneous = view.inverseViewProj * vec4<f32>(ndc, 1.0);
31
+ let divisor = select(1.0, homogeneous.w, abs(homogeneous.w) > 0.000001);
32
+ return homogeneous.xyz / divisor;
33
+ }
34
+
35
+ fn fogRayFromNdc(ndc: vec3<f32>) -> FogRay {
36
+ let worldPosition = fogWorldPoint(ndc);
37
+ let nearPosition = fogWorldPoint(vec3<f32>(ndc.xy, 0.0));
38
+ let farPosition = fogWorldPoint(vec3<f32>(ndc.xy, 1.0));
39
+ let perspective = view.temporalProjection.z < 0.5;
40
+ let perspectiveVector = worldPosition - view.cameraPos;
41
+ let orthographicVector = farPosition - nearPosition;
42
+ let direction = normalize(select(orthographicVector, perspectiveVector, perspective));
43
+ let origin = select(nearPosition, view.cameraPos, perspective);
44
+ let ray_distance = select(
45
+ max(dot(worldPosition - nearPosition, direction), 0.0),
46
+ length(perspectiveVector),
47
+ perspective,
48
+ );
49
+ return FogRay(origin, direction, ray_distance);
50
+ }
51
+
52
+ @vertex
53
+ fn vs_main(input: VertexInput) -> VertexOutput {
54
+ var output: VertexOutput;
55
+ let offset =
56
+ input.right * input.geometry_position.x +
57
+ input.up * input.geometry_position.y +
58
+ input.forward * input.geometry_position.z;
59
+ let clipPosition = input.center + offset;
60
+ output.position = vec4<f32>(clipPosition, 1.0);
61
+ output.clip_position = clipPosition;
62
+ output.color = input.particle_color * input.base_color;
63
+ output.normal = normalize(
64
+ input.right * input.geometry_normal.x +
65
+ input.up * input.geometry_normal.y +
66
+ input.forward * input.geometry_normal.z
67
+ );
68
+ output.emissive_intensity = input.emissive_intensity;
69
+ output.surface = input.surface;
70
+ return output;
71
+ }
72
+
73
+ @fragment
74
+ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
75
+ let normal = normalize(input.normal);
76
+ let light_direction = normalize(vec3<f32>(0.35, 0.7, 0.55));
77
+ let view_direction = vec3<f32>(0.0, 0.0, 1.0);
78
+ let half_direction = normalize(light_direction + view_direction);
79
+ let diffuse = 0.2 + 0.8 * max(dot(normal, light_direction), 0.0);
80
+ let metallic = clamp(input.surface.x, 0.0, 1.0);
81
+ let roughness = clamp(input.surface.y, 0.04, 1.0);
82
+ let clearcoat = clamp(input.surface.z, 0.0, 1.0);
83
+ let clearcoat_roughness = clamp(input.surface.w, 0.04, 1.0);
84
+ let specular_power = mix(96.0, 4.0, roughness);
85
+ let coat_power = mix(192.0, 8.0, clearcoat_roughness);
86
+ let specular = pow(max(dot(normal, half_direction), 0.0), specular_power);
87
+ let coat = clearcoat * pow(max(dot(normal, half_direction), 0.0), coat_power);
88
+ let dielectric = vec3<f32>(0.04);
89
+ let specular_color = mix(dielectric, input.color.rgb, metallic);
90
+ let lit = input.color.rgb * diffuse * (1.0 - metallic * 0.55);
91
+ let emissive = input.emissive_intensity.rgb * input.emissive_intensity.a;
92
+ let fogged = apply_fog(
93
+ view.fog,
94
+ fogRayFromNdc(input.clip_position),
95
+ vec4<f32>(lit + specular_color * specular + vec3<f32>(coat) + emissive, input.color.a),
96
+ );
97
+ return vec4<f32>(fogged.rgb * fogged.a, fogged.a);
98
+ }
@@ -0,0 +1,69 @@
1
+ #define_import_path forgeax::vfx-render.particles.ribbon
2
+ #import forgeax_view::common::{View, FogViewParams, FogRay, view}
3
+ #import forgeax_view::fog::{apply_fog}
4
+
5
+ struct SegmentInput {
6
+ @location(0) start: vec3<f32>,
7
+ @location(1) endpoint: vec3<f32>,
8
+ @location(2) color: vec4<f32>,
9
+ @location(3) properties: vec2<f32>,
10
+ }
11
+
12
+ struct VertexOutput {
13
+ @builtin(position) position: vec4<f32>,
14
+ @location(0) color: vec4<f32>,
15
+ @location(1) clip_position: vec3<f32>,
16
+ }
17
+
18
+ fn fogWorldPoint(ndc: vec3<f32>) -> vec3<f32> {
19
+ let homogeneous = view.inverseViewProj * vec4<f32>(ndc, 1.0);
20
+ let divisor = select(1.0, homogeneous.w, abs(homogeneous.w) > 0.000001);
21
+ return homogeneous.xyz / divisor;
22
+ }
23
+
24
+ fn fogRayFromNdc(ndc: vec3<f32>) -> FogRay {
25
+ let worldPosition = fogWorldPoint(ndc);
26
+ let nearPosition = fogWorldPoint(vec3<f32>(ndc.xy, 0.0));
27
+ let farPosition = fogWorldPoint(vec3<f32>(ndc.xy, 1.0));
28
+ let perspective = view.temporalProjection.z < 0.5;
29
+ let perspectiveVector = worldPosition - view.cameraPos;
30
+ let orthographicVector = farPosition - nearPosition;
31
+ let direction = normalize(select(orthographicVector, perspectiveVector, perspective));
32
+ let origin = select(nearPosition, view.cameraPos, perspective);
33
+ let ray_distance = select(
34
+ max(dot(worldPosition - nearPosition, direction), 0.0),
35
+ length(perspectiveVector),
36
+ perspective,
37
+ );
38
+ return FogRay(origin, direction, ray_distance);
39
+ }
40
+
41
+ @vertex
42
+ fn vs_main(input: SegmentInput, @builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
43
+ let corners = array<vec2<f32>, 6>(
44
+ vec2<f32>(0.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0),
45
+ vec2<f32>(0.0, -1.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0)
46
+ );
47
+ let corner = corners[vertexIndex];
48
+ let delta = input.endpoint.xy - input.start.xy;
49
+ let normal = normalize(vec2<f32>(-delta.y, delta.x) + vec2<f32>(0.000001, 0.0));
50
+ let point = mix(input.start, input.endpoint, corner.x);
51
+ let clipPosition = vec3<f32>(point.xy + normal * corner.y * input.properties.x, point.z);
52
+ var output: VertexOutput;
53
+ output.position = vec4<f32>(clipPosition, 1.0);
54
+ output.color = input.color;
55
+ output.clip_position = clipPosition;
56
+ return output;
57
+ }
58
+
59
+ @fragment
60
+ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
61
+ let base = vec4<f32>(0.2, 0.7, 1.0, 0.9);
62
+ let alpha = base.a * input.color.a;
63
+ let fogged = apply_fog(
64
+ view.fog,
65
+ fogRayFromNdc(input.clip_position),
66
+ vec4<f32>(base.rgb * input.color.rgb, alpha),
67
+ );
68
+ return fogged;
69
+ }
@@ -0,0 +1,5 @@
1
+ // Managed particle stages are emitted by the compiler and dispatched by the RenderFeature.
2
+ // This shared ABI helper keeps the particle-domain bounds explicit at the shader boundary.
3
+ fn forgeax_vfx_stage_in_bounds(index: u32, capacity: u32) -> bool {
4
+ return index < capacity;
5
+ }
@@ -0,0 +1,77 @@
1
+ #define_import_path forgeax::vfx-render.particles.trail
2
+ #import forgeax_view::common::{View, FogViewParams, FogRay, view}
3
+ #import forgeax_view::fog::{apply_fog}
4
+
5
+ struct SegmentInput {
6
+ @location(0) start: vec3<f32>,
7
+ @location(1) endpoint: vec3<f32>,
8
+ @location(2) color: vec4<f32>,
9
+ @location(3) properties: vec2<f32>,
10
+ }
11
+
12
+ struct VertexOutput {
13
+ @builtin(position) position: vec4<f32>,
14
+ @location(0) color: vec4<f32>,
15
+ @location(1) clip_position: vec3<f32>,
16
+ }
17
+
18
+ fn fogWorldPoint(ndc: vec3<f32>) -> vec3<f32> {
19
+ let homogeneous = view.inverseViewProj * vec4<f32>(ndc, 1.0);
20
+ let divisor = select(1.0, homogeneous.w, abs(homogeneous.w) > 0.000001);
21
+ return homogeneous.xyz / divisor;
22
+ }
23
+
24
+ fn fogRayFromNdc(ndc: vec3<f32>) -> FogRay {
25
+ let worldPosition = fogWorldPoint(ndc);
26
+ let nearPosition = fogWorldPoint(vec3<f32>(ndc.xy, 0.0));
27
+ let farPosition = fogWorldPoint(vec3<f32>(ndc.xy, 1.0));
28
+ let perspective = view.temporalProjection.z < 0.5;
29
+ let perspectiveVector = worldPosition - view.cameraPos;
30
+ let orthographicVector = farPosition - nearPosition;
31
+ let direction = normalize(select(orthographicVector, perspectiveVector, perspective));
32
+ let origin = select(nearPosition, view.cameraPos, perspective);
33
+ let ray_distance = select(
34
+ max(dot(worldPosition - nearPosition, direction), 0.0),
35
+ length(perspectiveVector),
36
+ perspective,
37
+ );
38
+ return FogRay(origin, direction, ray_distance);
39
+ }
40
+
41
+ @vertex
42
+ fn vs_main(input: SegmentInput, @builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
43
+ let corners = array<vec2<f32>, 6>(
44
+ vec2<f32>(0.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0),
45
+ vec2<f32>(0.0, -1.0), vec2<f32>(1.0, 1.0), vec2<f32>(0.0, 1.0)
46
+ );
47
+ let corner = corners[vertexIndex];
48
+ let delta = input.endpoint.xy - input.start.xy;
49
+ let normal = normalize(vec2<f32>(-delta.y, delta.x) + vec2<f32>(0.000001, 0.0));
50
+ let tangent = normalize(delta + vec2<f32>(0.000001, 0.0));
51
+ let taper = max(0.0, 1.0 - input.properties.y);
52
+ let point = mix(input.start, input.endpoint, corner.x) + vec3<f32>(
53
+ tangent * ((corner.x * 2.0 - 1.0) * input.properties.x),
54
+ 0.0,
55
+ );
56
+ let clipPosition = vec3<f32>(
57
+ point.xy + normal * corner.y * input.properties.x * taper,
58
+ point.z,
59
+ );
60
+ var output: VertexOutput;
61
+ output.position = vec4<f32>(clipPosition, 1.0);
62
+ output.color = input.color;
63
+ output.clip_position = clipPosition;
64
+ return output;
65
+ }
66
+
67
+ @fragment
68
+ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
69
+ let base = vec4<f32>(0.8, 0.35, 0.1, 1.0);
70
+ let alpha = base.a * input.color.a;
71
+ let fogged = apply_fog(
72
+ view.fog,
73
+ fogRayFromNdc(input.clip_position),
74
+ vec4<f32>(base.rgb * input.color.rgb, alpha),
75
+ );
76
+ return vec4<f32>(fogged.rgb * fogged.a, fogged.a);
77
+ }