@forgeax/engine-shader 0.1.20 → 0.1.21
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 +10 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/ShaderRegistry.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +52 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/ShaderRegistry.ts +65 -0
- package/src/__tests__/bloom-fxaa-tonemap.unit.test.ts +30 -4
- package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +7 -0
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +69 -24
- package/src/__tests__/lighting-point-spot-volume.unit.test.ts +8 -0
- package/src/__tests__/lighting-punctual.unit.test.ts +32 -6
- package/src/__tests__/material-derived-builtins.integration.test.ts +27 -0
- package/src/__tests__/reflection-probe-sampling.unit.test.ts +23 -0
- package/src/__tests__/scene-temporal.unit.test.ts +20 -0
- package/src/__tests__/shader.unit.test.ts +34 -13
- package/src/__tests__/shadow-pcss.unit.test.ts +131 -0
- package/src/__tests__/sprite-lit-shader.test.ts +24 -11
- package/src/__tests__/sprite-variants.unit.test.ts +1 -0
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +7 -1
- package/src/__tests__/transparent-pbr.unit.test.ts +7 -0
- package/src/common.wgsl +42 -12
- package/src/default-standard-pbr-skin.wgsl +30 -71
- package/src/default-standard-pbr.wgsl +92 -163
- package/src/fxaa.wgsl +27 -6
- package/src/ibl-sampling.wgsl +54 -0
- package/src/index.ts +6 -0
- package/src/lighting-directional.wgsl +157 -18
- package/src/lighting-punctual.wgsl +51 -21
- package/src/scene-temporal.wgsl +14 -8
- package/src/shadow-pcf.wgsl +44 -1
- package/src/sprite-lit.wgsl +36 -52
- package/src/{hdrp-cluster-forward.wgsl → standard-cluster.wgsl} +73 -60
- package/src/tonemap.wgsl +8 -3
- package/src/volume/volume-inject.wgsl +14 -2
- package/src/volume/volume-integrate.wgsl +1 -6
package/src/common.wgsl
CHANGED
|
@@ -36,6 +36,36 @@ fn linearToSrgbOetf(color : vec3<f32>) -> vec3<f32> {
|
|
|
36
36
|
return select(high, low, safe <= vec3<f32>(0.0031308));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// Shared final-surface dither. The output transform and FXAA are both
|
|
40
|
+
// display-encoded fullscreen writers, so they must use one owner-level
|
|
41
|
+
// algorithm rather than carrying subtly different copies in each pass.
|
|
42
|
+
// The bounded shift is ±0.5/255 per channel, matching Three.js's optional
|
|
43
|
+
// material dithering amplitude while the screen-space hash remains stable for
|
|
44
|
+
// a still frame. Callers decide whether this writer is the final 8-bit
|
|
45
|
+
// boundary; intermediate float targets must leave it disabled.
|
|
46
|
+
fn hash32(value : u32) -> u32 {
|
|
47
|
+
var hash = value;
|
|
48
|
+
hash = (hash ^ 61u) ^ (hash >> 16u);
|
|
49
|
+
hash = hash + (hash << 3u);
|
|
50
|
+
hash = hash ^ (hash >> 4u);
|
|
51
|
+
hash = hash * 668265261u;
|
|
52
|
+
hash = hash ^ (hash >> 15u);
|
|
53
|
+
return hash;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn ditherNoise(pixelPosition : vec2<f32>) -> f32 {
|
|
57
|
+
let pixel = vec2<u32>(pixelPosition);
|
|
58
|
+
let seed = (pixel.x * 1973u) ^ (pixel.y * 9277u) ^ 89173u;
|
|
59
|
+
return f32(hash32(seed) & 1023u) / 1023.0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fn ditherUnorm8(value : vec3<f32>, pixelPosition : vec2<f32>) -> vec3<f32> {
|
|
63
|
+
let noise = ditherNoise(pixelPosition);
|
|
64
|
+
var shift = vec3<f32>(0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0);
|
|
65
|
+
shift = mix(2.0 * shift, -2.0 * shift, noise);
|
|
66
|
+
return clamp(value + shift, vec3<f32>(0.0), vec3<f32>(1.0));
|
|
67
|
+
}
|
|
68
|
+
|
|
39
69
|
// @forgeax/engine-shader - common.wgsl (M5 T-18 feat-20260512-naga-oil-composition-hmr).
|
|
40
70
|
//
|
|
41
71
|
// Shared view + mesh structs extracted from pbr.wgsl + unlit.wgsl via
|
|
@@ -50,7 +80,7 @@ fn linearToSrgbOetf(color : vec3<f32>) -> vec3<f32> {
|
|
|
50
80
|
// aligned with Bevy's bevy_view::common and matches charter proposition 5
|
|
51
81
|
// (consistent abstraction - one View struct everywhere).
|
|
52
82
|
|
|
53
|
-
// View UBO byte layout (
|
|
83
|
+
// View UBO byte layout (960 B payload, 1024 B slot):
|
|
54
84
|
// [ 0.. 64) worldViewProj mat4x4<f32> (align 16, size 64)
|
|
55
85
|
// [ 64.. 80) lightDir vec3<f32> (align 16, 12+4 pad)
|
|
56
86
|
// [ 80.. 96) lightColor vec3<f32> (align 16, 12+4 pad)
|
|
@@ -68,8 +98,7 @@ fn linearToSrgbOetf(color : vec3<f32>) -> vec3<f32> {
|
|
|
68
98
|
// [500..504) cascadeBlend f32 (align 4, size 4)
|
|
69
99
|
// [504..508) depthBias f32 (align 4, size 4)
|
|
70
100
|
// [508..512) normalBias f32 (align 4, size 4)
|
|
71
|
-
// [512..
|
|
72
|
-
// [516..528) _align_pad — (mat4 array align=16, 12 B)
|
|
101
|
+
// [512..528) directional shadow carrier f32[4] (profile, radius, penumbra, reserved)
|
|
73
102
|
// [528..784) spotLightViewProj array<mat4x4<f32>,4> (align 16, size 256)
|
|
74
103
|
// WGSL prefix = 784 B. Host UBO = VIEW_UBO_BYTES = 960 B (renderer-factory.ts);
|
|
75
104
|
// view-ubo.ts writes the full 240 f32 payload. The spot matrix
|
|
@@ -99,14 +128,15 @@ fn linearToSrgbOetf(color : vec3<f32>) -> vec3<f32> {
|
|
|
99
128
|
// shadow_caster.wgsl indexes the 4 fields via `shadowCasterCascade.index`
|
|
100
129
|
// (binding 5, written per shadow pass).
|
|
101
130
|
//
|
|
102
|
-
// feat-
|
|
103
|
-
// the merged DirectionalLight's shadow bias +
|
|
104
|
-
// tail (depthBias / normalBias
|
|
105
|
-
// 126/127/128). Tail
|
|
131
|
+
// feat-20260827-directional-csm-pcss-quality M1:
|
|
132
|
+
// the merged DirectionalLight's shadow bias + accepted filter carrier occupy
|
|
133
|
+
// the existing tail (depthBias / normalBias at bytes 504/508, carrier at
|
|
134
|
+
// bytes 512..528, floats 126/127/128..131). Tail reuse only -- field order is
|
|
135
|
+
// byte-for-byte stable
|
|
106
136
|
// (charter P4); the prior 88 B host tail pad shrinks to 64 B, total stays
|
|
107
137
|
// View slot remains within the existing 1024 B stride. lighting-directional.wgsl drives the
|
|
108
|
-
// directional shadow bias (D-1: bias = max(normalBias*(1-N.L), depthBias))
|
|
109
|
-
//
|
|
138
|
+
// directional shadow bias (D-1: bias = max(normalBias*(1-N.L), depthBias));
|
|
139
|
+
// directional shadow carrier is the receiver-facing profile/radius/penumbra POD.
|
|
110
140
|
//
|
|
111
141
|
// feat-20260625-spot-light-shadow-mapping w25 (scope-amend webkit-fallback):
|
|
112
142
|
// the per-spot perspective `spotLightViewProj` matrices (4 lanes, fragment-read)
|
|
@@ -122,7 +152,7 @@ fn linearToSrgbOetf(color : vec3<f32>) -> vec3<f32> {
|
|
|
122
152
|
// matrix in the same frame (same-frame write contention); the FRAGMENT read
|
|
123
153
|
// channel has no such contention — the host writes all <=4 spot matrices once
|
|
124
154
|
// per frame before the forward pass, so the fold is safe. mat4 array align=16:
|
|
125
|
-
// the array lands at byte 528 (next 16 B-aligned offset after
|
|
155
|
+
// the array lands at byte 528 (next 16 B-aligned offset after the filter carrier at
|
|
126
156
|
// byte 512..516), spanning bytes 528..784. The host allocates VIEW_UBO_BYTES =
|
|
127
157
|
// 960 (renderer-factory.ts) and writes the full 240 f32
|
|
128
158
|
// payload (render-system-record.ts). Lane N = the spot with `shadowAtlasTile
|
|
@@ -142,10 +172,10 @@ struct View {
|
|
|
142
172
|
cascadeBlend : f32,
|
|
143
173
|
depthBias : f32,
|
|
144
174
|
normalBias : f32,
|
|
145
|
-
|
|
175
|
+
directionalShadowFilter : vec4<f32>,
|
|
146
176
|
// feat-20260625-spot-light-shadow-mapping w25: per-spot perspective
|
|
147
177
|
// light-view-projection matrices, fragment-read. Auto-aligned to byte 528
|
|
148
|
-
// (mat4 array align=16, after
|
|
178
|
+
// (mat4 array align=16, after the four directional carrier lanes at byte 512); spans bytes
|
|
149
179
|
// 528..784. Lane N = spot with shadowAtlasTile === N (cap = 4). Zeroed lanes
|
|
150
180
|
// are safe (sample gated on shadowAtlasTile >= 0 in default-standard-pbr.wgsl).
|
|
151
181
|
spotLightViewProj : array<mat4x4<f32>, 4>,
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
#import forgeax_view::common::{View, Mesh, InstanceData, view, meshes, instances,
|
|
1
|
+
#import forgeax_view::common::{View, Mesh, InstanceData, view, meshes, instances, sampleMaterialTexture}
|
|
2
|
+
#import forgeax_scene_temporal::{sceneViewZ}
|
|
2
3
|
#import forgeax_pbr::temporal::{projectPbrSceneTemporal}
|
|
3
4
|
#import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx}
|
|
4
5
|
#import forgeax_pbr::ibl_sampling::{sampleIblDiffuse, sampleIblSpecular}
|
|
5
6
|
#import forgeax_pbr::tbn::{decodeTangentSpaceNormalRg, scaleTangentSpaceNormal, applyTBN}
|
|
6
7
|
#import forgeax_pbr::lighting_directional::{evalDirectionalNoShadow, evalDirectionalShadowFactor}
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#import forgeax_pbr::lighting_punctual::{evalPointShadowed}
|
|
10
|
-
#import forgeax_view::common::{shadowParams}
|
|
8
|
+
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
9
|
+
#import forgeax_standard::cluster::{evaluateStandardClusterLights}
|
|
11
10
|
#endif
|
|
12
11
|
|
|
13
12
|
#define_import_path forgeax_material::pbr-skin
|
|
14
13
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
14
|
+
#pragma variant_axis CLUSTER_FORWARD_AVAILABLE
|
|
15
15
|
#pragma variant_axis VERTEX_COLOR_AVAILABLE
|
|
16
|
+
#pragma variant_axis DIRECTIONAL_PCSS_AVAILABLE
|
|
16
17
|
|
|
17
18
|
// @forgeax/engine-shader - default-standard-pbr-skin.wgsl
|
|
18
19
|
// (feat-20260523-skin-skeleton-animation M3 / T-29).
|
|
@@ -204,7 +205,6 @@ struct VsOut {
|
|
|
204
205
|
@location(1) worldNormal : vec3<f32>,
|
|
205
206
|
@location(2) uv : vec2<f32>,
|
|
206
207
|
@location(3) worldTangent : vec4<f32>,
|
|
207
|
-
@location(4) @interpolate(flat) instanceIdx : u32,
|
|
208
208
|
// feat-city-glb multi-UV tiling: second UV set varying at location 5
|
|
209
209
|
// (parity with default-standard-pbr.wgsl).
|
|
210
210
|
@location(5) uv1 : vec2<f32>,
|
|
@@ -217,6 +217,9 @@ struct VsOut {
|
|
|
217
217
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
218
218
|
@location(14) color : vec4<f32>,
|
|
219
219
|
#endif
|
|
220
|
+
// Vertex-produced Standard cluster coordinates. Fragment builtin position
|
|
221
|
+
// is framebuffer-space and cannot recover the original clip-space NDC.
|
|
222
|
+
@location(6) ndc : vec3<f32>,
|
|
220
223
|
@location(7) viewZ : f32,
|
|
221
224
|
};
|
|
222
225
|
|
|
@@ -298,11 +301,14 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
298
301
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
299
302
|
out.color = in.color;
|
|
300
303
|
#endif
|
|
301
|
-
out.instanceIdx = idx;
|
|
302
304
|
// feat-20260613-csm-cascaded-shadow-maps M5 / w19: viewZ replaces the
|
|
303
305
|
// prior light-space-position varying; evalDirectional picks the cascade
|
|
304
306
|
// matrix per fragment from viewZ + worldPos.
|
|
305
|
-
|
|
307
|
+
let clipPos = out.clip;
|
|
308
|
+
out.ndc = vec3(clipPos.xy / clipPos.w, clipPos.z / clipPos.w);
|
|
309
|
+
// Keep the cluster depth exactly aligned with the CPU binner for both
|
|
310
|
+
// perspective and off-axis orthographic projections.
|
|
311
|
+
out.viewZ = sceneViewZ(out.clip, view.temporalProjection);
|
|
306
312
|
return out;
|
|
307
313
|
}
|
|
308
314
|
|
|
@@ -397,65 +403,17 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
397
403
|
);
|
|
398
404
|
color = color + directionalShadow * material.clearcoat * directionalClearcoat;
|
|
399
405
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
in.worldPos, n, v, albedo, metallic, a, f0,
|
|
409
|
-
p.shadowAtlasLayer, lane.x, lane.y, 0.005, 0.05,
|
|
410
|
-
);
|
|
411
|
-
if (material.clearcoat != 0.0) {
|
|
412
|
-
color = color + material.clearcoat * evalPointShadowed(
|
|
413
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
414
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
415
|
-
p.shadowAtlasLayer, lane.x, lane.y, 0.005, 0.05,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
} else {
|
|
419
|
-
color = color + evalPoint(
|
|
420
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
421
|
-
in.worldPos, n, v, albedo, metallic, a, f0,
|
|
422
|
-
);
|
|
423
|
-
if (material.clearcoat != 0.0) {
|
|
424
|
-
color = color + material.clearcoat * evalPoint(
|
|
425
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
426
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
#else
|
|
431
|
-
color = color + evalPoint(
|
|
432
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
433
|
-
in.worldPos, n, v, albedo, metallic, a, f0,
|
|
434
|
-
);
|
|
435
|
-
if (material.clearcoat != 0.0) {
|
|
436
|
-
color = color + material.clearcoat * evalPoint(
|
|
437
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
438
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
#endif
|
|
442
|
-
}
|
|
443
|
-
let spotCount = spotLightsBuffer.count;
|
|
444
|
-
for (var i: u32 = 0u; i < spotCount; i = i + 1u) {
|
|
445
|
-
let s = spotLightsBuffer.slots[i];
|
|
446
|
-
color = color + evalSpot(
|
|
447
|
-
s.position, s.direction, s.colorTimesIntensity,
|
|
448
|
-
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
449
|
-
in.worldPos, n, v, albedo, metallic, a, f0,
|
|
406
|
+
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
407
|
+
color = color + evaluateStandardClusterLights(
|
|
408
|
+
in.ndc, in.viewZ, in.worldPos, n, v, albedo, metallic, a, f0, false,
|
|
409
|
+
);
|
|
410
|
+
if (material.clearcoat != 0.0) {
|
|
411
|
+
color = color + material.clearcoat * evaluateStandardClusterLights(
|
|
412
|
+
in.ndc, in.viewZ, in.worldPos, n, v,
|
|
413
|
+
vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04), false,
|
|
450
414
|
);
|
|
451
|
-
if (material.clearcoat != 0.0) {
|
|
452
|
-
color = color + material.clearcoat * evalSpot(
|
|
453
|
-
s.position, s.direction, s.colorTimesIntensity,
|
|
454
|
-
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
455
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
415
|
}
|
|
416
|
+
#endif
|
|
459
417
|
return vec4<f32>(color, alpha);
|
|
460
418
|
}
|
|
461
419
|
|
|
@@ -471,6 +429,7 @@ struct TemporalVsOut {
|
|
|
471
429
|
@location(7) uv7 : vec2<f32>,
|
|
472
430
|
@location(8) @interpolate(perspective) currentClip : vec4<f32>,
|
|
473
431
|
@location(9) @interpolate(perspective) previousClip : vec4<f32>,
|
|
432
|
+
@location(10) @interpolate(flat) reactive : f32,
|
|
474
433
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
475
434
|
@location(14) color : vec4<f32>,
|
|
476
435
|
#endif
|
|
@@ -492,6 +451,11 @@ fn vs_temporal(in : VsIn, @builtin(instance_index) idx : u32) -> TemporalVsOut {
|
|
|
492
451
|
out.currentClip = view.temporalCurrentViewProj * currentWorld;
|
|
493
452
|
out.clip = out.currentClip;
|
|
494
453
|
out.previousClip = view.temporalPreviousViewProj * previousWorld;
|
|
454
|
+
#if STORAGE_BUFFER_AVAILABLE == true
|
|
455
|
+
out.reactive = meshes[0].temporal.x;
|
|
456
|
+
#else
|
|
457
|
+
out.reactive = 1.0;
|
|
458
|
+
#endif
|
|
495
459
|
out.uv = in.uv;
|
|
496
460
|
out.uv1 = in.uv1;
|
|
497
461
|
out.uv2 = in.uv2;
|
|
@@ -517,11 +481,6 @@ fn temporalVertexAlpha(in : TemporalVsOut) -> f32 {
|
|
|
517
481
|
|
|
518
482
|
@fragment
|
|
519
483
|
fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
520
|
-
#if STORAGE_BUFFER_AVAILABLE == true
|
|
521
|
-
let reactive = meshes[0].temporal.x;
|
|
522
|
-
#else
|
|
523
|
-
let reactive = 1.0;
|
|
524
|
-
#endif
|
|
525
484
|
return projectPbrSceneTemporal(
|
|
526
485
|
material.baseColor.a * temporalVertexAlpha(in),
|
|
527
486
|
material.alphaCutoff,
|
|
@@ -532,7 +491,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
532
491
|
in.currentClip,
|
|
533
492
|
in.previousClip,
|
|
534
493
|
view.temporalProjection,
|
|
535
|
-
reactive,
|
|
494
|
+
in.reactive,
|
|
536
495
|
in.uv, in.uv1, in.uv2, in.uv3,
|
|
537
496
|
in.uv4, in.uv5, in.uv6, in.uv7,
|
|
538
497
|
);
|
|
@@ -1,46 +1,34 @@
|
|
|
1
1
|
#define_import_path forgeax_material::standard
|
|
2
|
-
#import forgeax_view::common::{View, Mesh, InstanceData, view, meshes, instances,
|
|
3
|
-
#
|
|
4
|
-
#import forgeax_view::common::{projectorTexture, projectorSampler}
|
|
5
|
-
#endif
|
|
2
|
+
#import forgeax_view::common::{View, Mesh, InstanceData, view, meshes, instances, sampleMaterialTexture}
|
|
3
|
+
#import forgeax_scene_temporal::{sceneViewZ}
|
|
6
4
|
#import forgeax_pbr::temporal::{projectPbrSceneTemporal}
|
|
7
5
|
#import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx}
|
|
8
|
-
#import forgeax_pbr::ibl_sampling::{sampleIblDiffuse, sampleIblSpecular}
|
|
6
|
+
#import forgeax_pbr::ibl_sampling::{box_project, sampleIblDiffuse, sampleIblSpecular, sampleReflectionProbeSpecular}
|
|
9
7
|
#import forgeax_pbr::tbn::{decodeTangentSpaceNormalRg, scaleTangentSpaceNormal, applyTBN}
|
|
10
8
|
#import forgeax_pbr::lighting_directional::{evalDirectionalNoShadow, evalDirectionalShadowFactor}
|
|
11
|
-
#import forgeax_pbr::lighting_punctual::{evalPoint, evalSpot, evalSpotShadowed, projectSpotUv}
|
|
12
|
-
#ifdef POINT_SHADOW_AVAILABLE
|
|
13
|
-
#import forgeax_pbr::lighting_punctual::{evalPointShadowed}
|
|
14
|
-
#import forgeax_view::common::{shadowParams}
|
|
15
|
-
#endif
|
|
16
|
-
|
|
17
|
-
fn sampleSpotProjector(lightViewProj : mat4x4<f32>, worldPos : vec3<f32>) -> vec3<f32> {
|
|
18
|
-
#ifdef PROJECTOR_AVAILABLE
|
|
19
|
-
let uv = projectSpotUv(lightViewProj, worldPos);
|
|
20
|
-
if (!(uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0)) {
|
|
21
|
-
// Three's SpotLight.map is a modulation inside the projected light
|
|
22
|
-
// frustum, not a second cone mask. Outside the map the original spot
|
|
23
|
-
// radiance remains intact; the cone/visibility terms already own whether
|
|
24
|
-
// the light reaches the fragment.
|
|
25
|
-
return vec3<f32>(1.0);
|
|
26
|
-
}
|
|
27
|
-
return textureSampleLevel(projectorTexture, projectorSampler, uv, 0.0).rgb;
|
|
28
|
-
#else
|
|
29
|
-
// Keep the analytic SpotLight contribution intact on adapters whose
|
|
30
|
-
// fragment-stage sampled-texture budget cannot carry the optional cookie.
|
|
31
|
-
return vec3<f32>(1.0);
|
|
32
|
-
#endif
|
|
33
|
-
}
|
|
34
9
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
35
|
-
#import
|
|
10
|
+
#import forgeax_standard::cluster::{evaluateStandardClusterLights, get_ssao_intensity}
|
|
36
11
|
#endif
|
|
37
12
|
|
|
38
13
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
39
14
|
#pragma variant_axis CLUSTER_FORWARD_AVAILABLE
|
|
40
15
|
#pragma variant_axis VERTEX_COLOR_AVAILABLE
|
|
41
16
|
#pragma variant_axis TRANSMISSION_AVAILABLE
|
|
17
|
+
#pragma variant_axis DIRECTIONAL_PCSS_AVAILABLE
|
|
42
18
|
#pragma variant_axis PROJECTOR_AVAILABLE
|
|
43
19
|
|
|
20
|
+
// Reflection probes remain a bounded scene-projected sampling lane. Their
|
|
21
|
+
// optional texture resources are the same Skylight inputs; when no probe is
|
|
22
|
+
// admitted, the explicit Skylight path below remains authoritative.
|
|
23
|
+
fn reflection_probe_sampling_contract(
|
|
24
|
+
worldPosition: vec3<f32>,
|
|
25
|
+
direction: vec3<f32>,
|
|
26
|
+
boxCenter: vec3<f32>,
|
|
27
|
+
boxExtents: vec3<f32>,
|
|
28
|
+
) -> vec3<f32> {
|
|
29
|
+
return box_project(worldPosition, direction, boxCenter, boxExtents);
|
|
30
|
+
}
|
|
31
|
+
|
|
44
32
|
// @forgeax/engine-shader - default-standard-pbr.wgsl
|
|
45
33
|
// (feat-20260523-shader-template-instance-split M5 / T04).
|
|
46
34
|
//
|
|
@@ -274,7 +262,11 @@ struct VsOut {
|
|
|
274
262
|
@location(1) worldNormal : vec3<f32>,
|
|
275
263
|
@location(2) uv : vec2<f32>,
|
|
276
264
|
@location(3) worldTangent : vec4<f32>,
|
|
277
|
-
|
|
265
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
266
|
+
// Keep mesh and instance storage vertex-only; transmission receives the
|
|
267
|
+
// transform basis from the vertex stage below.
|
|
268
|
+
@location(4) @interpolate(flat) transmissionBasis0 : vec4<f32>,
|
|
269
|
+
#endif
|
|
278
270
|
// feat-city-glb multi-UV tiling: second UV set inter-stage varying. Uses the
|
|
279
271
|
// previously-vacant @location(5) so @location(6)/(7) stay byte-stable with
|
|
280
272
|
// the prior layout (CSM M5/w19).
|
|
@@ -288,7 +280,7 @@ struct VsOut {
|
|
|
288
280
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
289
281
|
@location(14) color : vec4<f32>,
|
|
290
282
|
#endif
|
|
291
|
-
@location(6) ndc :
|
|
283
|
+
@location(6) ndc : vec4<f32>, // NDC for HDRP cluster lookup; .w = transmission basis tail
|
|
292
284
|
// feat-20260609-hdrp-cluster-fragment-ggx M4.5-followup: view-space z is
|
|
293
285
|
// needed by ndc_position_to_cluster (slice index uses log-z mapping that
|
|
294
286
|
// takes negative view_z, NOT NDC z which is [0,1]). Earlier `in.ndc.z`
|
|
@@ -297,6 +289,9 @@ struct VsOut {
|
|
|
297
289
|
// hot zone -- received zero light. Forward view_z explicitly. M5 / w19:
|
|
298
290
|
// also feeds CSM cascade selection in evalDirectional.
|
|
299
291
|
@location(7) viewZ : f32,
|
|
292
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
293
|
+
@location(15) @interpolate(flat) transmissionBasis1 : vec4<f32>,
|
|
294
|
+
#endif
|
|
300
295
|
};
|
|
301
296
|
|
|
302
297
|
fn pick_channel(rgba : vec4<f32>, channelIndex : u32) -> f32 {
|
|
@@ -358,6 +353,20 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
358
353
|
// (w=0); .w handedness preserved for bitangent reconstruction in fragment.
|
|
359
354
|
let worldTangentXyz = normalize((entityWorld * instanceLocal * vec4<f32>(in.tangent.xyz, 0.0)).xyz);
|
|
360
355
|
out.worldTangent = vec4<f32>(worldTangentXyz, in.tangent.w);
|
|
356
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
357
|
+
out.transmissionBasis0 = vec4<f32>(
|
|
358
|
+
localToWorld[0].x,
|
|
359
|
+
localToWorld[0].y,
|
|
360
|
+
localToWorld[0].z,
|
|
361
|
+
localToWorld[1].x,
|
|
362
|
+
);
|
|
363
|
+
out.transmissionBasis1 = vec4<f32>(
|
|
364
|
+
localToWorld[1].y,
|
|
365
|
+
localToWorld[1].z,
|
|
366
|
+
localToWorld[2].x,
|
|
367
|
+
localToWorld[2].y,
|
|
368
|
+
);
|
|
369
|
+
#endif
|
|
361
370
|
out.uv = in.uv;
|
|
362
371
|
out.uv1 = in.uv1;
|
|
363
372
|
out.uv2 = in.uv2;
|
|
@@ -369,7 +378,6 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
369
378
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
370
379
|
out.color = in.color;
|
|
371
380
|
#endif
|
|
372
|
-
out.instanceIdx = idx;
|
|
373
381
|
// feat-20260613-csm-cascaded-shadow-maps M5 / w19: the per-fragment
|
|
374
382
|
// light-space position varying is gone; evalDirectional computes
|
|
375
383
|
// per-cascade lightViewProj * worldPos in the fragment stage from
|
|
@@ -377,12 +385,14 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
377
385
|
// NDC for HDRP cluster lookup (feat-20260609-hdrp-cluster-fragment-ggx M2 / w10).
|
|
378
386
|
// Perspective divide on clip-space position; ndc.z retains depth-buffer value.
|
|
379
387
|
let clipPos = out.clip;
|
|
380
|
-
out.ndc =
|
|
388
|
+
out.ndc = vec4(clipPos.xy / clipPos.w, clipPos.z / clipPos.w, localToWorld[2].z);
|
|
381
389
|
// Standard WebGPU/GL perspective projection has clip.w = -view.z, so
|
|
382
390
|
// view_z = -clip.w (negative in front of the camera). M4.5-followup:
|
|
383
391
|
// ndc_position_to_cluster needs this for log-z slice mapping; passing
|
|
384
392
|
// ndc.z (in [0,1]) instead made every fragment land in slice 0.
|
|
385
|
-
|
|
393
|
+
// Keep the cluster depth exactly aligned with the CPU binner for both
|
|
394
|
+
// perspective and off-axis orthographic projections.
|
|
395
|
+
out.viewZ = sceneViewZ(clipPos, view.temporalProjection);
|
|
386
396
|
return out;
|
|
387
397
|
}
|
|
388
398
|
|
|
@@ -587,12 +597,26 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
587
597
|
// shadow-map PCF lookup (CSM, feat-20260613).
|
|
588
598
|
let kD = (vec3<f32>(1.0) - f_schlick(max(dot(n, v), 0.0), f0)) * (1.0 - metallic);
|
|
589
599
|
let iblRoughness = clamp(max(finiteScalar(material.roughness, 0.5), 0.04) * roughnessTex, 0.04, 1.0);
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
600
|
+
var irradiance = vec3<f32>(0.0);
|
|
601
|
+
var specularIbl = vec3<f32>(0.0);
|
|
602
|
+
if (skylight.intensity < 0.0) {
|
|
603
|
+
// A negative intensity is the renderer-owned probe sentinel. The four
|
|
604
|
+
// color/rotation lanes carry probe center and half extents in this mode;
|
|
605
|
+
// Skylight resources remain unchanged and therefore preserve the ABI.
|
|
606
|
+
specularIbl = sampleReflectionProbeSpecular(
|
|
607
|
+
n, v, iblRoughness, f0, in.worldPos,
|
|
608
|
+
vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB),
|
|
609
|
+
skylight.rotation.xyz, vec4<f32>(0.0, 0.0, 0.0, 1.0),
|
|
610
|
+
prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
|
|
611
|
+
);
|
|
612
|
+
} else {
|
|
613
|
+
irradiance = sampleIblDiffuse(n, skylight.rotation, irradianceMap, irradianceSampler);
|
|
614
|
+
specularIbl = sampleIblSpecular(
|
|
615
|
+
n, v, iblRoughness, f0,
|
|
616
|
+
skylight.rotation,
|
|
617
|
+
prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
|
|
618
|
+
);
|
|
619
|
+
}
|
|
596
620
|
let occlusionUv = transformedMaterialUv(material.occlusionTextureCoordinatesTransform, material.occlusionTextureCoordinatesMetadata, in);
|
|
597
621
|
let aoSample = sampleMaterialTexture(occlusionTexture, occlusionSampler, occlusionUv, material.occlusionTextureCoordinatesMetadata.zw);
|
|
598
622
|
let ao = mix(1.0, aoSample.r, material.occlusionStrength);
|
|
@@ -609,7 +633,11 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
609
633
|
);
|
|
610
634
|
ambient = ambient + clearcoatIbl * material.clearcoat;
|
|
611
635
|
}
|
|
612
|
-
|
|
636
|
+
if (skylight.intensity < 0.0) {
|
|
637
|
+
ambient = ambient * (-skylight.intensity) * ao;
|
|
638
|
+
} else {
|
|
639
|
+
ambient = ambient * skyColor * skylight.intensity * ao;
|
|
640
|
+
}
|
|
613
641
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
614
642
|
// feat-20260612-hdrp-ssao M2 round-1 + M7 round-2 (plan-strategy D-7 + D-B + D-C):
|
|
615
643
|
// SSAO ambient synthesis. Reads the half-res R8 `ssaoBlurredTexture` from
|
|
@@ -636,10 +664,17 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
636
664
|
// the fragment stage preserves that contract and keeps thickness in the
|
|
637
665
|
// glTF unit-scale mesh space before measuring the refracted segment in world
|
|
638
666
|
// metres.
|
|
639
|
-
let
|
|
640
|
-
let
|
|
641
|
-
|
|
642
|
-
|
|
667
|
+
let localToWorld0 = in.transmissionBasis0.xyz;
|
|
668
|
+
let localToWorld1 = vec3<f32>(
|
|
669
|
+
in.transmissionBasis0.w,
|
|
670
|
+
in.transmissionBasis1.x,
|
|
671
|
+
in.transmissionBasis1.y,
|
|
672
|
+
);
|
|
673
|
+
let localToWorld2 = vec3<f32>(
|
|
674
|
+
in.transmissionBasis1.z,
|
|
675
|
+
in.transmissionBasis1.w,
|
|
676
|
+
in.ndc.w,
|
|
677
|
+
);
|
|
643
678
|
let inverseCofactor0 = cross(localToWorld1, localToWorld2);
|
|
644
679
|
let inverseCofactor1 = cross(localToWorld2, localToWorld0);
|
|
645
680
|
let inverseCofactor2 = cross(localToWorld0, localToWorld1);
|
|
@@ -723,121 +758,13 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
723
758
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
724
759
|
// NDC from vertex shader (perspective-divided clip-space, interpolated).
|
|
725
760
|
// view_z: NDC depth for cluster Z-slice lookup.
|
|
726
|
-
color = color +
|
|
761
|
+
color = color + evaluateStandardClusterLights(
|
|
762
|
+
in.ndc.xyz, in.viewZ, in.worldPos, n, v, diffuseAlbedo, metallic, a, f0, false,
|
|
763
|
+
);
|
|
727
764
|
if (material.clearcoat != 0.0) {
|
|
728
|
-
color = color + material.clearcoat *
|
|
729
|
-
in.ndc, in.viewZ, in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
#else
|
|
733
|
-
let pointCount = pointLightsBuffer.count;
|
|
734
|
-
for (var i: u32 = 0u; i < pointCount; i = i + 1u) {
|
|
735
|
-
let p = pointLightsBuffer.slots[i];
|
|
736
|
-
#ifdef POINT_SHADOW_AVAILABLE
|
|
737
|
-
// feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1: gate between
|
|
738
|
-
// shadowed and unshadowed evaluation on the host-assigned
|
|
739
|
-
// shadowAtlasLayer (>= 0 means PointLightShadow companion exists; -1
|
|
740
|
-
// sentinel means no shadow). shadowParams[layer] = (near, far,
|
|
741
|
-
// 1/(far-near), 0) is bound at @group(0) @binding(6) (shadowParams
|
|
742
|
-
// declared in common.wgsl).
|
|
743
|
-
if (p.shadowAtlasLayer >= 0) {
|
|
744
|
-
let lane = shadowParams[p.shadowAtlasLayer];
|
|
745
|
-
color = color + evalPointShadowed(
|
|
746
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
747
|
-
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
748
|
-
p.shadowAtlasLayer, lane.x, lane.y, 0.005, 0.05,
|
|
749
|
-
);
|
|
750
|
-
if (material.clearcoat != 0.0) {
|
|
751
|
-
color = color + material.clearcoat * evalPointShadowed(
|
|
752
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
753
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
754
|
-
p.shadowAtlasLayer, lane.x, lane.y, 0.005, 0.05,
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
} else {
|
|
758
|
-
color = color + evalPoint(
|
|
759
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
760
|
-
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
761
|
-
);
|
|
762
|
-
if (material.clearcoat != 0.0) {
|
|
763
|
-
color = color + material.clearcoat * evalPoint(
|
|
764
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
765
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
#else
|
|
770
|
-
color = color + evalPoint(
|
|
771
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
772
|
-
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
773
|
-
);
|
|
774
|
-
if (material.clearcoat != 0.0) {
|
|
775
|
-
color = color + material.clearcoat * evalPoint(
|
|
776
|
-
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
777
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
|
-
#endif
|
|
781
|
-
}
|
|
782
|
-
let spotCount = spotLightsBuffer.count;
|
|
783
|
-
// Surface and volume consume the same projectSpotUv / projectorRevision
|
|
784
|
-
// tuple; an accepted projector is sampled with textureSampleLevel.
|
|
785
|
-
const PROJECTOR_ONLY_TILE : i32 = -2;
|
|
786
|
-
for (var i: u32 = 0u; i < spotCount; i = i + 1u) {
|
|
787
|
-
let s = spotLightsBuffer.slots[i];
|
|
788
|
-
// feat-20260625-spot-light-shadow-mapping M3 / w15 (plan-strategy D-1
|
|
789
|
-
// fragment side + D-3 + D-4): gate on the host-assigned shadowAtlasTile
|
|
790
|
-
// (>= 0 means a spot atlas tile was allocated; -1 sentinel means no
|
|
791
|
-
// shadow / clipped / direction-degenerate). The shadowed path reads the
|
|
792
|
-
// per-spot perspective matrix from the View UBO `view.spotLightViewProj`
|
|
793
|
-
// array (lane N = shadowAtlasTile N; folded from the standalone binding 9
|
|
794
|
-
// into the View UBO in feat-20260625 w25 for WebGL2 uniform-buffer budget);
|
|
795
|
-
// the unshadowed path stays on evalSpot. Receiver controls come from the
|
|
796
|
-
// same packed SpotLight snapshot used by the volume path.
|
|
797
|
-
if (s.shadowAtlasTile >= 0) {
|
|
798
|
-
let projector = sampleSpotProjector(view.spotLightViewProj[s.shadowAtlasTile], in.worldPos);
|
|
799
|
-
color = color + evalSpotShadowed(
|
|
800
|
-
s.position, s.direction, s.colorTimesIntensity,
|
|
801
|
-
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
802
|
-
in.worldPos, n, v, albedo, metallic, a, f0,
|
|
803
|
-
view.spotLightViewProj[s.shadowAtlasTile], s.shadowAtlasTile, s.depthBias, s.normalBias, s.pcfKernelSize, s.shadowIntensity,
|
|
804
|
-
) * projector;
|
|
805
|
-
if (material.clearcoat != 0.0) {
|
|
806
|
-
color = color + material.clearcoat * evalSpotShadowed(
|
|
807
|
-
s.position, s.direction, s.colorTimesIntensity,
|
|
808
|
-
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
809
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
810
|
-
view.spotLightViewProj[s.shadowAtlasTile], s.shadowAtlasTile, s.depthBias, s.normalBias, s.pcfKernelSize, s.shadowIntensity,
|
|
811
|
-
) * projector;
|
|
812
|
-
}
|
|
813
|
-
} else if (s.shadowAtlasTile == PROJECTOR_ONLY_TILE) {
|
|
814
|
-
let projector = sampleSpotProjector(view.spotLightViewProj[0], in.worldPos);
|
|
815
|
-
color = color + evalSpot(
|
|
816
|
-
s.position, s.direction, s.colorTimesIntensity,
|
|
817
|
-
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
818
|
-
in.worldPos, n, v, albedo, metallic, a, f0,
|
|
819
|
-
) * projector;
|
|
820
|
-
if (material.clearcoat != 0.0) {
|
|
821
|
-
color = color + material.clearcoat * evalSpot(
|
|
822
|
-
s.position, s.direction, s.colorTimesIntensity,
|
|
823
|
-
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
824
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
825
|
-
) * projector;
|
|
826
|
-
}
|
|
827
|
-
} else {
|
|
828
|
-
color = color + evalSpot(
|
|
829
|
-
s.position, s.direction, s.colorTimesIntensity,
|
|
830
|
-
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
831
|
-
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
765
|
+
color = color + material.clearcoat * evaluateStandardClusterLights(
|
|
766
|
+
in.ndc.xyz, in.viewZ, in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04), false,
|
|
832
767
|
);
|
|
833
|
-
if (material.clearcoat != 0.0) {
|
|
834
|
-
color = color + material.clearcoat * evalSpot(
|
|
835
|
-
s.position, s.direction, s.colorTimesIntensity,
|
|
836
|
-
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
837
|
-
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
838
|
-
);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
768
|
}
|
|
842
769
|
#endif // CLUSTER_FORWARD_AVAILABLE
|
|
843
770
|
let emissiveUv = transformedMaterialUv(material.emissiveTextureCoordinatesTransform, material.emissiveTextureCoordinatesMetadata, in);
|
|
@@ -931,6 +858,7 @@ struct TemporalVsOut {
|
|
|
931
858
|
@location(7) uv7 : vec2<f32>,
|
|
932
859
|
@location(8) @interpolate(perspective) currentClip : vec4<f32>,
|
|
933
860
|
@location(9) @interpolate(perspective) previousClip : vec4<f32>,
|
|
861
|
+
@location(10) @interpolate(flat) reactive : f32,
|
|
934
862
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
935
863
|
@location(14) color : vec4<f32>,
|
|
936
864
|
#endif
|
|
@@ -949,6 +877,11 @@ fn vs_temporal(in : VsIn, @builtin(instance_index) idx : u32) -> TemporalVsOut {
|
|
|
949
877
|
out.currentClip = view.temporalCurrentViewProj * currentWorld;
|
|
950
878
|
out.clip = out.currentClip;
|
|
951
879
|
out.previousClip = view.temporalPreviousViewProj * previousWorld;
|
|
880
|
+
#if STORAGE_BUFFER_AVAILABLE == true
|
|
881
|
+
out.reactive = meshes[0].temporal.x;
|
|
882
|
+
#else
|
|
883
|
+
out.reactive = 1.0;
|
|
884
|
+
#endif
|
|
952
885
|
out.uv = in.uv;
|
|
953
886
|
out.uv1 = in.uv1;
|
|
954
887
|
out.uv2 = in.uv2;
|
|
@@ -973,10 +906,6 @@ fn temporalVertexAlpha(in : TemporalVsOut) -> f32 {
|
|
|
973
906
|
|
|
974
907
|
@fragment
|
|
975
908
|
fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
976
|
-
var reactive = 0.0;
|
|
977
|
-
#if STORAGE_BUFFER_AVAILABLE == true
|
|
978
|
-
reactive = meshes[0].temporal.x;
|
|
979
|
-
#endif
|
|
980
909
|
return projectPbrSceneTemporal(
|
|
981
910
|
material.baseColor.a * temporalVertexAlpha(in),
|
|
982
911
|
material.alphaCutoff,
|
|
@@ -987,7 +916,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
987
916
|
in.currentClip,
|
|
988
917
|
in.previousClip,
|
|
989
918
|
view.temporalProjection,
|
|
990
|
-
reactive,
|
|
919
|
+
in.reactive,
|
|
991
920
|
in.uv, in.uv1, in.uv2, in.uv3,
|
|
992
921
|
in.uv4, in.uv5, in.uv6, in.uv7,
|
|
993
922
|
);
|