@forgeax/engine-shader 0.1.7 → 0.1.19
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 +34 -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 +53 -2
- package/dist/index.mjs.map +1 -1
- package/dist/material-schemas.d.ts.map +1 -1
- package/package.json +6 -3
- package/src/ShaderRegistry.ts +50 -0
- package/src/__tests__/bloom-fxaa-tonemap.unit.test.ts +86 -12
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +181 -0
- package/src/__tests__/lighting-punctual.unit.test.ts +1 -1
- package/src/__tests__/material-builtins.unit.test.ts +38 -1
- package/src/__tests__/material-contract.unit.test.ts +123 -0
- package/src/__tests__/motion-blur.unit.test.ts +44 -0
- package/src/__tests__/scene-temporal.unit.test.ts +23 -5
- package/src/__tests__/shader-manifest-transmission.unit.test.ts +75 -0
- package/src/__tests__/shader.unit.test.ts +43 -3
- package/src/__tests__/sprite-variants.unit.test.ts +19 -0
- package/src/__tests__/taa-resolve.unit.test.ts +75 -3
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +19 -0
- package/src/common.wgsl +30 -30
- package/src/default-standard-pbr-skin.wgsl +27 -9
- package/src/default-standard-pbr.wgsl +209 -28
- package/src/fxaa.wgsl +5 -6
- package/src/index.ts +3 -0
- package/src/material-schemas.ts +7 -0
- package/src/motion-blur.wgsl +77 -0
- package/src/pbr-temporal.wgsl +15 -2
- package/src/scene-temporal.wgsl +54 -0
- package/src/shadow_caster.wgsl +27 -2
- package/src/sprite-lit.wgsl +5 -4
- package/src/sprite.wgsl +5 -4
- package/src/taa-resolve.wgsl +76 -81
- package/src/tonemap.wgsl +7 -9
- package/src/unlit.wgsl +6 -5
- package/dist/__tests__/atmosphere-background.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-background.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-cubemap-orientation.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-cubemap-orientation.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham-numeric.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham-numeric.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham-red.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham-red.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-three-r184-reference.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-three-r184-reference.test.d.ts.map +0 -1
- package/dist/__tests__/bloom-fxaa-tonemap.unit.test.d.ts +0 -2
- package/dist/__tests__/bloom-fxaa-tonemap.unit.test.d.ts.map +0 -1
- package/dist/__tests__/builtin-texture-sampling-contract.test.d.ts +0 -2
- package/dist/__tests__/builtin-texture-sampling-contract.test.d.ts.map +0 -1
- package/dist/__tests__/default-standard-pbr-alpha.unit.test.d.ts +0 -2
- package/dist/__tests__/default-standard-pbr-alpha.unit.test.d.ts.map +0 -1
- package/dist/__tests__/deferred-lighting-ssao.test.d.ts +0 -2
- package/dist/__tests__/deferred-lighting-ssao.test.d.ts.map +0 -1
- package/dist/__tests__/fog-oracle.unit.test.d.ts +0 -2
- package/dist/__tests__/fog-oracle.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-orthographic-ray.unit.test.d.ts +0 -2
- package/dist/__tests__/fog-orthographic-ray.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-matrix.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-matrix.integration.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-compile.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-compile.test.d.ts.map +0 -1
- package/dist/__tests__/ibl-irradiance.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl-irradiance.unit.test.d.ts.map +0 -1
- package/dist/__tests__/ibl-sampling.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl-sampling.unit.test.d.ts.map +0 -1
- package/dist/__tests__/lighting-punctual.unit.test.d.ts +0 -2
- package/dist/__tests__/lighting-punctual.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-artifact-dedup.unit.test.d.ts +0 -2
- package/dist/__tests__/material-artifact-dedup.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-builtins.unit.test.d.ts +0 -2
- package/dist/__tests__/material-builtins.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-contract.unit.test.d.ts +0 -2
- package/dist/__tests__/material-contract.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-derived-builtins.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-builtins.integration.test.d.ts.map +0 -1
- package/dist/__tests__/points-lines-shader.unit.test.d.ts +0 -2
- package/dist/__tests__/points-lines-shader.unit.test.d.ts.map +0 -1
- package/dist/__tests__/public-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/public-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/scene-temporal.unit.test.d.ts +0 -2
- package/dist/__tests__/scene-temporal.unit.test.d.ts.map +0 -1
- package/dist/__tests__/shader-registry-transaction.unit.test.d.ts +0 -2
- package/dist/__tests__/shader-registry-transaction.unit.test.d.ts.map +0 -1
- package/dist/__tests__/shader.unit.test.d.ts +0 -2
- package/dist/__tests__/shader.unit.test.d.ts.map +0 -1
- package/dist/__tests__/sprite-lit-shader.test.d.ts +0 -2
- package/dist/__tests__/sprite-lit-shader.test.d.ts.map +0 -1
- package/dist/__tests__/sprite-variants.unit.test.d.ts +0 -2
- package/dist/__tests__/sprite-variants.unit.test.d.ts.map +0 -1
- package/dist/__tests__/taa-resolve.unit.test.d.ts +0 -2
- package/dist/__tests__/taa-resolve.unit.test.d.ts.map +0 -1
- package/dist/__tests__/transparent-pbr.unit.test.d.ts +0 -2
- package/dist/__tests__/transparent-pbr.unit.test.d.ts.map +0 -1
- package/dist/__tests__/vertex-color-variant.unit.test.d.ts +0 -2
- package/dist/__tests__/vertex-color-variant.unit.test.d.ts.map +0 -1
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
19
19
|
#pragma variant_axis CLUSTER_FORWARD_AVAILABLE
|
|
20
20
|
#pragma variant_axis VERTEX_COLOR_AVAILABLE
|
|
21
|
+
#pragma variant_axis TRANSMISSION_AVAILABLE
|
|
21
22
|
|
|
22
23
|
// @forgeax/engine-shader - default-standard-pbr.wgsl
|
|
23
24
|
// (feat-20260523-shader-template-instance-split M5 / T04).
|
|
@@ -32,8 +33,8 @@
|
|
|
32
33
|
//
|
|
33
34
|
// Bindings (4 BG layout slots; View + Mesh bindings inherited from
|
|
34
35
|
// forgeax_view::common; shadow map + comparison sampler at view BG
|
|
35
|
-
// @binding(3..4) per shadow-mapping feat; Skylight 7 bindings merged
|
|
36
|
-
// the
|
|
36
|
+
// @binding(3..4) per shadow-mapping feat; Skylight 7 bindings merged after
|
|
37
|
+
// the Standard user region per D-5 round-4:
|
|
37
38
|
//
|
|
38
39
|
// @group(0) @binding(0) view uniform (see common.wgsl)
|
|
39
40
|
// @group(1) @binding(0) material uniform (baseColor vec4
|
|
@@ -112,6 +113,11 @@ struct Material {
|
|
|
112
113
|
clearcoatRoughness : f32,
|
|
113
114
|
specularTint : vec3<f32>,
|
|
114
115
|
normalScale : f32,
|
|
116
|
+
transmission : f32,
|
|
117
|
+
ior : f32,
|
|
118
|
+
thickness : f32,
|
|
119
|
+
attenuationColor : vec3<f32>,
|
|
120
|
+
attenuationDistance : f32,
|
|
115
121
|
baseColorTextureCoordinatesTransform : vec4<f32>,
|
|
116
122
|
baseColorTextureCoordinatesMetadata : vec4<f32>,
|
|
117
123
|
metallicRoughnessTextureCoordinatesTransform : vec4<f32>,
|
|
@@ -124,6 +130,10 @@ struct Material {
|
|
|
124
130
|
emissiveTextureCoordinatesMetadata : vec4<f32>,
|
|
125
131
|
occlusionTextureCoordinatesTransform : vec4<f32>,
|
|
126
132
|
occlusionTextureCoordinatesMetadata : vec4<f32>,
|
|
133
|
+
transmissionTextureCoordinatesTransform : vec4<f32>,
|
|
134
|
+
transmissionTextureCoordinatesMetadata : vec4<f32>,
|
|
135
|
+
thicknessTextureCoordinatesTransform : vec4<f32>,
|
|
136
|
+
thicknessTextureCoordinatesMetadata : vec4<f32>,
|
|
127
137
|
};
|
|
128
138
|
|
|
129
139
|
@group(1) @binding(0) var<uniform> material : Material;
|
|
@@ -139,6 +149,14 @@ struct Material {
|
|
|
139
149
|
@group(1) @binding(10) var emissiveTexture : texture_2d<f32>;
|
|
140
150
|
@group(1) @binding(11) var occlusionSampler : sampler;
|
|
141
151
|
@group(1) @binding(12) var occlusionTexture : texture_2d<f32>;
|
|
152
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
153
|
+
@group(1) @binding(13) var transmissionSampler : sampler;
|
|
154
|
+
@group(1) @binding(14) var transmissionTexture : texture_2d<f32>;
|
|
155
|
+
@group(1) @binding(15) var thicknessSampler : sampler;
|
|
156
|
+
@group(1) @binding(16) var thicknessTexture : texture_2d<f32>;
|
|
157
|
+
@group(1) @binding(24) var transmissionBackdropSampler : sampler;
|
|
158
|
+
@group(1) @binding(25) var transmissionBackdropTexture : texture_2d<f32>;
|
|
159
|
+
#endif
|
|
142
160
|
|
|
143
161
|
// Naga reflection does not retain filtering usage through the generic shared
|
|
144
162
|
// helper. These compile-time-only witnesses retain the binding contract while
|
|
@@ -156,6 +174,12 @@ fn materialTextureFilteringWitness() {
|
|
|
156
174
|
let specularTintWitness = textureSample(specularTint, specularTintSampler, vec2<f32>(0.0));
|
|
157
175
|
let emissiveWitness = textureSample(emissive, emissiveSampler, vec2<f32>(0.0));
|
|
158
176
|
let occlusionWitness = textureSample(occlusion, occlusionSampler, vec2<f32>(0.0));
|
|
177
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
178
|
+
let transmission = transmissionTexture;
|
|
179
|
+
let thickness = thicknessTexture;
|
|
180
|
+
let transmissionWitness = textureSample(transmission, transmissionSampler, vec2<f32>(0.0));
|
|
181
|
+
let thicknessWitness = textureSample(thickness, thicknessSampler, vec2<f32>(0.0));
|
|
182
|
+
#endif
|
|
159
183
|
}
|
|
160
184
|
|
|
161
185
|
// Skylight bindings merged into the PBR material BGL
|
|
@@ -163,10 +187,10 @@ fn materialTextureFilteringWitness() {
|
|
|
163
187
|
// round-4 REVISED). The round-2 stand-alone group 4 Skylight BGL collided
|
|
164
188
|
// with WebGPU's default maxBindGroups=4 in chrome-beta and blocked pbr-pl
|
|
165
189
|
// pipeline-layout creation; round-4 appends the 7 Skylight entries to the
|
|
166
|
-
// PBR material BindGroupLayout
|
|
190
|
+
// PBR material BindGroupLayout after the eight Standard texture pairs. The material BG factory
|
|
167
191
|
// (mergeSkylightIntoMaterialBgl in
|
|
168
192
|
// packages/runtime/src/ibl/skylight-bind-group.ts) extends the layout to
|
|
169
|
-
//
|
|
193
|
+
// merged entries; render-system-record assembles a single merged material
|
|
170
194
|
// BindGroup (no extra setBindGroup(4) call). Identity (default) resources
|
|
171
195
|
// produce ambient = 0; with a real Skylight, ibl_sampling helpers project
|
|
172
196
|
// the IBL irradiance + split-sum specular into `ambient` below.
|
|
@@ -182,13 +206,13 @@ struct SkylightUniforms {
|
|
|
182
206
|
colorB : f32,
|
|
183
207
|
rotation : vec4<f32>,
|
|
184
208
|
};
|
|
185
|
-
@group(1) @binding(
|
|
186
|
-
@group(1) @binding(
|
|
187
|
-
@group(1) @binding(
|
|
188
|
-
@group(1) @binding(
|
|
189
|
-
@group(1) @binding(
|
|
190
|
-
@group(1) @binding(
|
|
191
|
-
@group(1) @binding(
|
|
209
|
+
@group(1) @binding(17) var irradianceMap : texture_cube<f32>;
|
|
210
|
+
@group(1) @binding(18) var irradianceSampler : sampler;
|
|
211
|
+
@group(1) @binding(19) var prefilterMap : texture_cube<f32>;
|
|
212
|
+
@group(1) @binding(20) var prefilterSampler : sampler;
|
|
213
|
+
@group(1) @binding(21) var brdfLut : texture_2d<f32>;
|
|
214
|
+
@group(1) @binding(22) var brdfLutSampler : sampler;
|
|
215
|
+
@group(1) @binding(23) var<uniform> skylight : SkylightUniforms;
|
|
192
216
|
|
|
193
217
|
// feat-20260612-hdrp-ssao M7 (round 2) D-B + D-C, scope-amend-webgl2-ubo:
|
|
194
218
|
// SSAO sampling lives on the HDRP unified BGL @group(2) alongside the
|
|
@@ -305,7 +329,8 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
305
329
|
// Combine: entity_world * per_instance_local.
|
|
306
330
|
let instanceLocal = instances[idx].localFromInstance;
|
|
307
331
|
let entityWorld = meshes[0].worldFromLocal;
|
|
308
|
-
let
|
|
332
|
+
let localToWorld = entityWorld * instanceLocal;
|
|
333
|
+
let world = localToWorld * vec4<f32>(in.pos, 1.0);
|
|
309
334
|
var out : VsOut;
|
|
310
335
|
out.clip = view.worldViewProj * world;
|
|
311
336
|
out.worldPos = world.xyz;
|
|
@@ -387,6 +412,19 @@ fn materialVertexColor(in : VsOut) -> vec4<f32> {
|
|
|
387
412
|
#endif
|
|
388
413
|
}
|
|
389
414
|
|
|
415
|
+
fn finiteScalar(value : f32, fallback : f32) -> f32 {
|
|
416
|
+
let bounded = clamp(value, -65504.0, 65504.0);
|
|
417
|
+
return select(fallback, bounded, value == value);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
fn finiteColor(value : vec3<f32>, fallback : vec3<f32>) -> vec3<f32> {
|
|
421
|
+
return vec3<f32>(
|
|
422
|
+
finiteScalar(value.x, fallback.x),
|
|
423
|
+
finiteScalar(value.y, fallback.y),
|
|
424
|
+
finiteScalar(value.z, fallback.z),
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
390
428
|
// linearColorDomain: transparent source and destination values are blended
|
|
391
429
|
// before any display encoding. The fixed-function material blend state uses
|
|
392
430
|
// this same equation for the render target's declared linear domain.
|
|
@@ -423,8 +461,17 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
423
461
|
// at the pick_channel call site; values stay in {0,1,2,3}.
|
|
424
462
|
let mrUv = transformedMaterialUv(material.metallicRoughnessTextureCoordinatesTransform, material.metallicRoughnessTextureCoordinatesMetadata, in);
|
|
425
463
|
let mrSample = sampleMaterialTexture(metallicRoughnessTexture, metallicRoughnessSampler, mrUv, material.metallicRoughnessTextureCoordinatesMetadata.zw);
|
|
426
|
-
let metallic =
|
|
427
|
-
|
|
464
|
+
let metallic = clamp(
|
|
465
|
+
finiteScalar(material.metallic, 0.0) *
|
|
466
|
+
finiteScalar(pick_channel(mrSample, u32(material.metallicChannel)), 1.0),
|
|
467
|
+
0.0,
|
|
468
|
+
1.0,
|
|
469
|
+
);
|
|
470
|
+
let roughnessTex = clamp(
|
|
471
|
+
finiteScalar(pick_channel(mrSample, u32(material.roughnessChannel)), 1.0),
|
|
472
|
+
0.0,
|
|
473
|
+
1.0,
|
|
474
|
+
);
|
|
428
475
|
|
|
429
476
|
// Layer-2 fail-fast: shader internal clamp keeps D_GGX finite for
|
|
430
477
|
// roughness=0 even if the asset somehow bypasses layer-1 register
|
|
@@ -433,7 +480,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
433
480
|
// so the final roughness >= 0.04 floor is preserved (texture sample is
|
|
434
481
|
// a 1.0-default white when metallicRoughnessTexture is absent, so
|
|
435
482
|
// multiplying after the clamp keeps the floor intact).
|
|
436
|
-
var a = max(material.roughness, 0.04);
|
|
483
|
+
var a = max(finiteScalar(material.roughness, 0.5), 0.04);
|
|
437
484
|
a = a * roughnessTex;
|
|
438
485
|
a = a * a;
|
|
439
486
|
|
|
@@ -452,7 +499,57 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
452
499
|
let specularTint = material.specularTint * sampleMaterialTexture(
|
|
453
500
|
specularTintTexture, specularTintSampler, specularUv, material.specularTintTextureCoordinatesMetadata.zw,
|
|
454
501
|
).rgb;
|
|
455
|
-
let
|
|
502
|
+
let safeIor = max(finiteScalar(material.ior, 1.5), 1.0);
|
|
503
|
+
let dielectricF0 = pow((safeIor - 1.0) / (safeIor + 1.0), 2.0);
|
|
504
|
+
let f0 = mix(vec3<f32>(dielectricF0) * specularTint, albedo, metallic);
|
|
505
|
+
var diffuseAlbedo = albedo;
|
|
506
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
507
|
+
let transmissionUv = transformedMaterialUv(
|
|
508
|
+
material.transmissionTextureCoordinatesTransform,
|
|
509
|
+
material.transmissionTextureCoordinatesMetadata,
|
|
510
|
+
in,
|
|
511
|
+
);
|
|
512
|
+
let thicknessUv = transformedMaterialUv(
|
|
513
|
+
material.thicknessTextureCoordinatesTransform,
|
|
514
|
+
material.thicknessTextureCoordinatesMetadata,
|
|
515
|
+
in,
|
|
516
|
+
);
|
|
517
|
+
let transmissionSample = sampleMaterialTexture(
|
|
518
|
+
transmissionTexture,
|
|
519
|
+
transmissionSampler,
|
|
520
|
+
transmissionUv,
|
|
521
|
+
material.transmissionTextureCoordinatesMetadata.zw,
|
|
522
|
+
).r;
|
|
523
|
+
let thicknessSample = sampleMaterialTexture(
|
|
524
|
+
thicknessTexture,
|
|
525
|
+
thicknessSampler,
|
|
526
|
+
thicknessUv,
|
|
527
|
+
material.thicknessTextureCoordinatesMetadata.zw,
|
|
528
|
+
).g;
|
|
529
|
+
let transmissionFactor = clamp(
|
|
530
|
+
finiteScalar(material.transmission, 0.0) * finiteScalar(transmissionSample, 1.0),
|
|
531
|
+
0.0,
|
|
532
|
+
1.0,
|
|
533
|
+
);
|
|
534
|
+
let viewDot = finiteScalar(dot(n, v), 0.0);
|
|
535
|
+
let refractionFromInside = viewDot < 0.0;
|
|
536
|
+
let refractionNormal = select(n, -n, refractionFromInside);
|
|
537
|
+
let refractionEta = select(1.0 / safeIor, safeIor, refractionFromInside);
|
|
538
|
+
let incident = -v;
|
|
539
|
+
let refracted = refract(incident, refractionNormal, refractionEta);
|
|
540
|
+
let refractedLengthSquared = dot(refracted, refracted);
|
|
541
|
+
let viewCos = clamp(abs(viewDot), 0.0, 1.0);
|
|
542
|
+
let fresnel = clamp(f_schlick(viewCos, vec3<f32>(dielectricF0)).x, 0.0, 1.0);
|
|
543
|
+
// `refract` returns the zero vector for total internal reflection. Keep the
|
|
544
|
+
// reflection/IBL term above authoritative and remove transmission energy
|
|
545
|
+
// instead of sampling and adding a second reflected environment.
|
|
546
|
+
let transmittedEnergy = select(
|
|
547
|
+
0.0,
|
|
548
|
+
transmissionFactor * (1.0 - metallic) * (1.0 - fresnel),
|
|
549
|
+
refractedLengthSquared > 1e-6,
|
|
550
|
+
);
|
|
551
|
+
diffuseAlbedo = albedo * (1.0 - transmittedEnergy);
|
|
552
|
+
#endif
|
|
456
553
|
let coatRoughness = max(material.clearcoatRoughness, 0.04);
|
|
457
554
|
let coatAlpha = coatRoughness * coatRoughness;
|
|
458
555
|
let coatF = f_schlick(max(dot(n, v), 0.0), vec3<f32>(0.04)) * material.clearcoat;
|
|
@@ -487,7 +584,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
487
584
|
// through the per-cascade light-space matrix in the fragment stage for
|
|
488
585
|
// shadow-map PCF lookup (CSM, feat-20260613).
|
|
489
586
|
let kD = (vec3<f32>(1.0) - f_schlick(max(dot(n, v), 0.0), f0)) * (1.0 - metallic);
|
|
490
|
-
let iblRoughness = max(material.roughness, 0.04) * roughnessTex;
|
|
587
|
+
let iblRoughness = clamp(max(finiteScalar(material.roughness, 0.5), 0.04) * roughnessTex, 0.04, 1.0);
|
|
491
588
|
let irradiance = sampleIblDiffuse(n, skylight.rotation, irradianceMap, irradianceSampler);
|
|
492
589
|
let specularIbl = sampleIblSpecular(
|
|
493
590
|
n, v, iblRoughness, f0,
|
|
@@ -501,7 +598,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
501
598
|
// CLUSTER_FORWARD_AVAILABLE branch below can `ambient *=` the SSAO
|
|
502
599
|
// factor. The non-HDRP path leaves ambient untouched.
|
|
503
600
|
let skyColor = vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB);
|
|
504
|
-
var ambient = (kD * irradiance *
|
|
601
|
+
var ambient = (kD * irradiance * diffuseAlbedo + specularIbl) * (vec3<f32>(1.0) - coatF);
|
|
505
602
|
if (material.clearcoat != 0.0) {
|
|
506
603
|
let clearcoatIbl = sampleIblSpecular(
|
|
507
604
|
n, v, coatRoughness, vec3<f32>(0.04),
|
|
@@ -529,8 +626,91 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
529
626
|
ambient *= mix(1.0, ssaoFactor * ao, ssaoIntensity);
|
|
530
627
|
#endif
|
|
531
628
|
var color = ambient;
|
|
629
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
630
|
+
let screenUv = in.ndc.xy * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
|
|
631
|
+
// Read the same renderer-owned transform tables that the vertex stage uses
|
|
632
|
+
// instead of adding six more interstage varyings. WebGPU guarantees only 16
|
|
633
|
+
// user-defined vertex output locations; keeping the affine conversion in
|
|
634
|
+
// the fragment stage preserves that contract and keeps thickness in the
|
|
635
|
+
// glTF unit-scale mesh space before measuring the refracted segment in world
|
|
636
|
+
// metres.
|
|
637
|
+
let localToWorld = meshes[0].worldFromLocal * instances[in.instanceIdx].localFromInstance;
|
|
638
|
+
let localToWorld0 = localToWorld[0].xyz;
|
|
639
|
+
let localToWorld1 = localToWorld[1].xyz;
|
|
640
|
+
let localToWorld2 = localToWorld[2].xyz;
|
|
641
|
+
let inverseCofactor0 = cross(localToWorld1, localToWorld2);
|
|
642
|
+
let inverseCofactor1 = cross(localToWorld2, localToWorld0);
|
|
643
|
+
let inverseCofactor2 = cross(localToWorld0, localToWorld1);
|
|
644
|
+
let localToWorldDet = dot(localToWorld0, inverseCofactor0);
|
|
645
|
+
let safeLocalToWorldDet = select(1.0, localToWorldDet, abs(localToWorldDet) >= 1e-6);
|
|
646
|
+
let worldToLocal0 = vec3<f32>(
|
|
647
|
+
inverseCofactor0.x,
|
|
648
|
+
inverseCofactor1.x,
|
|
649
|
+
inverseCofactor2.x,
|
|
650
|
+
) / safeLocalToWorldDet;
|
|
651
|
+
let worldToLocal1 = vec3<f32>(
|
|
652
|
+
inverseCofactor0.y,
|
|
653
|
+
inverseCofactor1.y,
|
|
654
|
+
inverseCofactor2.y,
|
|
655
|
+
) / safeLocalToWorldDet;
|
|
656
|
+
let worldToLocal2 = vec3<f32>(
|
|
657
|
+
inverseCofactor0.z,
|
|
658
|
+
inverseCofactor1.z,
|
|
659
|
+
inverseCofactor2.z,
|
|
660
|
+
) / safeLocalToWorldDet;
|
|
661
|
+
let refractedLocal = normalize(
|
|
662
|
+
worldToLocal0 * refracted.x +
|
|
663
|
+
worldToLocal1 * refracted.y +
|
|
664
|
+
worldToLocal2 * refracted.z,
|
|
665
|
+
);
|
|
666
|
+
let worldRefractedDirection =
|
|
667
|
+
localToWorld0 * refractedLocal.x +
|
|
668
|
+
localToWorld1 * refractedLocal.y +
|
|
669
|
+
localToWorld2 * refractedLocal.z;
|
|
670
|
+
let worldThickness = max(
|
|
671
|
+
finiteScalar(material.thickness, 0.0) * length(worldRefractedDirection) *
|
|
672
|
+
finiteScalar(thicknessSample, 1.0),
|
|
673
|
+
0.0,
|
|
674
|
+
);
|
|
675
|
+
let refractedUv = screenUv + (refracted.xy - incident.xy) * worldThickness * 0.25;
|
|
676
|
+
let guardBand = 0.02;
|
|
677
|
+
let insideGuardBand = all(refractedUv >= vec2<f32>(guardBand)) &&
|
|
678
|
+
all(refractedUv <= vec2<f32>(1.0 - guardBand));
|
|
679
|
+
let backdropMipCount = textureNumLevels(transmissionBackdropTexture);
|
|
680
|
+
let backdropMaxLod = max(f32(backdropMipCount) - 1.0, 0.0);
|
|
681
|
+
let backdropLod = clamp(iblRoughness * iblRoughness * backdropMaxLod, 0.0, backdropMaxLod);
|
|
682
|
+
let unrefractedBackdrop = textureSampleLevel(
|
|
683
|
+
transmissionBackdropTexture,
|
|
684
|
+
transmissionBackdropSampler,
|
|
685
|
+
clamp(screenUv, vec2<f32>(0.0), vec2<f32>(1.0)),
|
|
686
|
+
backdropLod,
|
|
687
|
+
).rgb;
|
|
688
|
+
var transmittedBackdrop = unrefractedBackdrop;
|
|
689
|
+
if (refractedLengthSquared > 1e-6 && insideGuardBand) {
|
|
690
|
+
transmittedBackdrop = textureSampleLevel(
|
|
691
|
+
transmissionBackdropTexture,
|
|
692
|
+
transmissionBackdropSampler,
|
|
693
|
+
clamp(refractedUv, vec2<f32>(0.0), vec2<f32>(1.0)),
|
|
694
|
+
backdropLod,
|
|
695
|
+
).rgb;
|
|
696
|
+
}
|
|
697
|
+
let safeAttenuationColor = clamp(
|
|
698
|
+
finiteColor(material.attenuationColor, vec3<f32>(1.0)),
|
|
699
|
+
vec3<f32>(0.0),
|
|
700
|
+
vec3<f32>(1.0),
|
|
701
|
+
);
|
|
702
|
+
let safeAttenuationDistance = max(finiteScalar(material.attenuationDistance, 0.0), 0.0);
|
|
703
|
+
let attenuationExponent = worldThickness / max(safeAttenuationDistance, 1e-6);
|
|
704
|
+
let beerAttenuation = select(
|
|
705
|
+
vec3<f32>(1.0),
|
|
706
|
+
pow(safeAttenuationColor, vec3<f32>(attenuationExponent)),
|
|
707
|
+
safeAttenuationDistance > 1e-6 && worldThickness > 0.0,
|
|
708
|
+
);
|
|
709
|
+
color = color + finiteColor(transmittedBackdrop, vec3<f32>(0.0)) *
|
|
710
|
+
transmittedEnergy * beerAttenuation;
|
|
711
|
+
#endif
|
|
532
712
|
let directionalShadow = evalDirectionalShadowFactor(n, in.worldPos, in.viewZ);
|
|
533
|
-
let directionalBase = evalDirectionalNoShadow(n, v,
|
|
713
|
+
let directionalBase = evalDirectionalNoShadow(n, v, diffuseAlbedo, metallic, a, f0);
|
|
534
714
|
color = color + directionalShadow * directionalBase;
|
|
535
715
|
if (material.clearcoat != 0.0) {
|
|
536
716
|
let directionalClearcoat = evalDirectionalNoShadow(
|
|
@@ -541,7 +721,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
541
721
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
542
722
|
// NDC from vertex shader (perspective-divided clip-space, interpolated).
|
|
543
723
|
// view_z: NDC depth for cluster Z-slice lookup.
|
|
544
|
-
color = color + evaluate_cluster_lights(in.ndc, in.viewZ, in.worldPos, n, v,
|
|
724
|
+
color = color + evaluate_cluster_lights(in.ndc, in.viewZ, in.worldPos, n, v, diffuseAlbedo, metallic, a, f0);
|
|
545
725
|
if (material.clearcoat != 0.0) {
|
|
546
726
|
color = color + material.clearcoat * evaluate_cluster_lights(
|
|
547
727
|
in.ndc, in.viewZ, in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
@@ -562,7 +742,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
562
742
|
let lane = shadowParams[p.shadowAtlasLayer];
|
|
563
743
|
color = color + evalPointShadowed(
|
|
564
744
|
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
565
|
-
in.worldPos, n, v,
|
|
745
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
566
746
|
p.shadowAtlasLayer, lane.x, lane.y, 0.005, 0.05,
|
|
567
747
|
);
|
|
568
748
|
if (material.clearcoat != 0.0) {
|
|
@@ -575,7 +755,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
575
755
|
} else {
|
|
576
756
|
color = color + evalPoint(
|
|
577
757
|
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
578
|
-
in.worldPos, n, v,
|
|
758
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
579
759
|
);
|
|
580
760
|
if (material.clearcoat != 0.0) {
|
|
581
761
|
color = color + material.clearcoat * evalPoint(
|
|
@@ -587,7 +767,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
587
767
|
#else
|
|
588
768
|
color = color + evalPoint(
|
|
589
769
|
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
590
|
-
in.worldPos, n, v,
|
|
770
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
591
771
|
);
|
|
592
772
|
if (material.clearcoat != 0.0) {
|
|
593
773
|
color = color + material.clearcoat * evalPoint(
|
|
@@ -614,7 +794,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
614
794
|
color = color + evalSpotShadowed(
|
|
615
795
|
s.position, s.direction, s.colorTimesIntensity,
|
|
616
796
|
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
617
|
-
in.worldPos, n, v,
|
|
797
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
618
798
|
view.spotLightViewProj[s.shadowAtlasTile], s.shadowAtlasTile, 0.005, 0.05,
|
|
619
799
|
);
|
|
620
800
|
if (material.clearcoat != 0.0) {
|
|
@@ -629,7 +809,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
629
809
|
color = color + evalSpot(
|
|
630
810
|
s.position, s.direction, s.colorTimesIntensity,
|
|
631
811
|
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
632
|
-
in.worldPos, n, v,
|
|
812
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
633
813
|
);
|
|
634
814
|
if (material.clearcoat != 0.0) {
|
|
635
815
|
color = color + material.clearcoat * evalSpot(
|
|
@@ -730,8 +910,8 @@ struct TemporalVsOut {
|
|
|
730
910
|
@location(5) uv5 : vec2<f32>,
|
|
731
911
|
@location(6) uv6 : vec2<f32>,
|
|
732
912
|
@location(7) uv7 : vec2<f32>,
|
|
733
|
-
@location(8) @interpolate(
|
|
734
|
-
@location(9) @interpolate(
|
|
913
|
+
@location(8) @interpolate(perspective) currentClip : vec4<f32>,
|
|
914
|
+
@location(9) @interpolate(perspective) previousClip : vec4<f32>,
|
|
735
915
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
736
916
|
@location(14) color : vec4<f32>,
|
|
737
917
|
#endif
|
|
@@ -774,7 +954,7 @@ fn temporalVertexAlpha(in : TemporalVsOut) -> f32 {
|
|
|
774
954
|
|
|
775
955
|
@fragment
|
|
776
956
|
fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
777
|
-
var reactive =
|
|
957
|
+
var reactive = 0.0;
|
|
778
958
|
#if STORAGE_BUFFER_AVAILABLE == true
|
|
779
959
|
reactive = meshes[0].temporal.x;
|
|
780
960
|
#endif
|
|
@@ -787,6 +967,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
787
967
|
material.baseColorTextureCoordinatesMetadata,
|
|
788
968
|
in.currentClip,
|
|
789
969
|
in.previousClip,
|
|
970
|
+
view.temporalProjection,
|
|
790
971
|
reactive,
|
|
791
972
|
in.uv, in.uv1, in.uv2, in.uv3,
|
|
792
973
|
in.uv4, in.uv5, in.uv6, in.uv7,
|
package/src/fxaa.wgsl
CHANGED
|
@@ -33,14 +33,13 @@
|
|
|
33
33
|
// holes in shaded surfaces -- the bug this rewrite fixes.
|
|
34
34
|
//
|
|
35
35
|
// Bindings (group 0):
|
|
36
|
-
// @binding(0) screenTexture : texture_2d<f32> -- sampled
|
|
36
|
+
// @binding(0) screenTexture : texture_2d<f32> -- sampled display-encoded input
|
|
37
37
|
// @binding(1) samp : sampler -- linear filterable sampler
|
|
38
|
-
//
|
|
39
|
-
//
|
|
38
|
+
// display-encoded: FXAA samples and writes display-encoded values; the Output
|
|
39
|
+
// Transform owns the sole linear-to-sRGB conversion before this pass.
|
|
40
40
|
|
|
41
41
|
#import forgeax_view::common::FullscreenOutput
|
|
42
42
|
#import forgeax_view::common::fullscreen_triangle
|
|
43
|
-
#import forgeax_view::common::linearToSrgbOetf
|
|
44
43
|
|
|
45
44
|
const EDGE_THRESHOLD_MIN: f32 = 0.0312;
|
|
46
45
|
const EDGE_THRESHOLD_MAX: f32 = 0.125;
|
|
@@ -106,7 +105,7 @@ fn fs_main(in: FullscreenOutput) -> @location(0) vec4<f32> {
|
|
|
106
105
|
|
|
107
106
|
// Not on an edge (or in a near-uniform region): pass the centre through.
|
|
108
107
|
if lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax * EDGE_THRESHOLD_MAX) {
|
|
109
|
-
return vec4<f32>(
|
|
108
|
+
return vec4<f32>(centerColor, 1.0);
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
// Lumas of the 4 corners.
|
|
@@ -255,5 +254,5 @@ fn fs_main(in: FullscreenOutput) -> @location(0) vec4<f32> {
|
|
|
255
254
|
}
|
|
256
255
|
|
|
257
256
|
let finalColor = sampleColor(finalUv);
|
|
258
|
-
return vec4<f32>(
|
|
257
|
+
return vec4<f32>(finalColor, 1.0);
|
|
259
258
|
}
|
package/src/index.ts
CHANGED
|
@@ -84,6 +84,9 @@ export const BUILTIN_MATERIAL_MODULES = {
|
|
|
84
84
|
sprite: 'forgeax_material::sprite',
|
|
85
85
|
} as const;
|
|
86
86
|
|
|
87
|
+
/** Stable build-time module id for the temporal-v1 accessor owner. */
|
|
88
|
+
export const SCENE_DATA_TEMPORAL_V1_SHADER_MODULE = 'forgeax_scene_temporal' as const;
|
|
89
|
+
|
|
87
90
|
/**
|
|
88
91
|
* Material modules whose shader source and parameter contract are owned by
|
|
89
92
|
* the Engine. They live in the runtime ShaderRegistry, so a Pack containing
|
package/src/material-schemas.ts
CHANGED
|
@@ -19,12 +19,19 @@ export const DEFAULT_STANDARD_PBR_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [
|
|
|
19
19
|
{ name: 'clearcoatRoughness', type: 'f32', default: 0.5 },
|
|
20
20
|
{ name: 'specularTint', type: 'vec3', colorSpace: 'srgb', default: [1, 1, 1] },
|
|
21
21
|
{ name: 'normalScale', type: 'f32', default: 1 },
|
|
22
|
+
{ name: 'transmission', type: 'f32', default: 0 },
|
|
23
|
+
{ name: 'ior', type: 'f32', default: 1.5 },
|
|
24
|
+
{ name: 'thickness', type: 'f32', default: 0 },
|
|
25
|
+
{ name: 'attenuationColor', type: 'vec3', colorSpace: 'linear', default: [1, 1, 1] },
|
|
26
|
+
{ name: 'attenuationDistance', type: 'f32' },
|
|
22
27
|
{ name: 'baseColorTexture', type: 'texture2d' },
|
|
23
28
|
{ name: 'metallicRoughnessTexture', type: 'texture2d' },
|
|
24
29
|
{ name: 'normalTexture', type: 'texture2d' },
|
|
25
30
|
{ name: 'specularTintTexture', type: 'texture2d' },
|
|
26
31
|
{ name: 'emissiveTexture', type: 'texture2d' },
|
|
27
32
|
{ name: 'occlusionTexture', type: 'texture2d' },
|
|
33
|
+
{ name: 'transmissionTexture', type: 'texture2d' },
|
|
34
|
+
{ name: 'thicknessTexture', type: 'texture2d' },
|
|
28
35
|
];
|
|
29
36
|
|
|
30
37
|
export const DEFAULT_UNLIT_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#define_import_path forgeax_view::motion_blur
|
|
2
|
+
|
|
3
|
+
#import forgeax_view::common::FullscreenOutput
|
|
4
|
+
#import forgeax_view::common::fullscreen_triangle
|
|
5
|
+
#import forgeax_scene_temporal::{unpackSceneTemporalV1}
|
|
6
|
+
|
|
7
|
+
struct MotionBlurParams {
|
|
8
|
+
shutterAngle : f32,
|
|
9
|
+
maxRadiusPixels : f32,
|
|
10
|
+
sampleCount : u32,
|
|
11
|
+
reset : u32,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
@group(1) @binding(0) var currentColor : texture_2d<f32>;
|
|
15
|
+
@group(1) @binding(1) var linearSampler : sampler;
|
|
16
|
+
@group(1) @binding(2) var<uniform> params : MotionBlurParams;
|
|
17
|
+
@group(1) @binding(3) var sceneTemporal : texture_2d<f32>;
|
|
18
|
+
|
|
19
|
+
fn depthReject(center : f32, sample : f32) -> bool {
|
|
20
|
+
return abs(center - sample) > max(0.01, center * 0.01);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fn symmetricOffset(index : u32, count : u32, motion : vec2<f32>) -> vec2<f32> {
|
|
24
|
+
let denominator = max(f32(count), 1.0);
|
|
25
|
+
let signedIndex = f32(index) - (denominator - 1.0) * 0.5;
|
|
26
|
+
return motion * (signedIndex / denominator);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@vertex
|
|
30
|
+
fn vs_main(@builtin(vertex_index) vertexIndex : u32) -> FullscreenOutput {
|
|
31
|
+
return fullscreen_triangle(vertexIndex);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@fragment
|
|
35
|
+
fn fs_main(in : FullscreenOutput) -> @location(0) vec4<f32> {
|
|
36
|
+
let current = textureSampleLevel(currentColor, linearSampler, in.uv, 0.0);
|
|
37
|
+
let packed = textureSampleLevel(sceneTemporal, linearSampler, in.uv, 0.0);
|
|
38
|
+
let temporal = unpackSceneTemporalV1(packed);
|
|
39
|
+
let dimensions = vec2<f32>(textureDimensions(currentColor));
|
|
40
|
+
let pixelMotion = temporal.motionUv * dimensions;
|
|
41
|
+
let shutter = clamp(params.shutterAngle / 360.0, 0.0, 1.0);
|
|
42
|
+
let radius = min(length(pixelMotion) * shutter, params.maxRadiusPixels);
|
|
43
|
+
let motion = normalize(select(vec2<f32>(0.0), pixelMotion, radius > 1e-5));
|
|
44
|
+
let invalidDepth = !temporal.validDepth;
|
|
45
|
+
let reactive = clamp(temporal.reactive, 0.0, 1.0);
|
|
46
|
+
let subpixel = radius <= 1e-5;
|
|
47
|
+
let reset = params.reset != 0u;
|
|
48
|
+
var output = current;
|
|
49
|
+
output.a = current.a;
|
|
50
|
+
if (invalidDepth || subpixel || reset || params.sampleCount < 4u) {
|
|
51
|
+
return output;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
var accum = vec3<f32>(0.0);
|
|
55
|
+
var weight = 0.0;
|
|
56
|
+
let temporalDimensions = vec2<i32>(textureDimensions(sceneTemporal));
|
|
57
|
+
for (var index = 0u; index < 16u; index += 1u) {
|
|
58
|
+
if (index >= params.sampleCount) { break; }
|
|
59
|
+
let offset = symmetricOffset(index, params.sampleCount, motion * radius) / dimensions;
|
|
60
|
+
let sampleUv = clamp(in.uv + offset, vec2<f32>(0.0), vec2<f32>(1.0));
|
|
61
|
+
let samplePixel = clamp(
|
|
62
|
+
vec2<i32>(sampleUv * dimensions),
|
|
63
|
+
vec2<i32>(0),
|
|
64
|
+
temporalDimensions - vec2<i32>(1),
|
|
65
|
+
);
|
|
66
|
+
let sampleTemporal = unpackSceneTemporalV1(textureLoad(sceneTemporal, samplePixel, 0));
|
|
67
|
+
if (sampleTemporal.validDepth && !depthReject(temporal.viewDepth, sampleTemporal.viewDepth)) {
|
|
68
|
+
accum += textureSampleLevel(currentColor, linearSampler, sampleUv, 0.0).rgb;
|
|
69
|
+
weight += 1.0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let gathered = accum / max(weight, 1.0);
|
|
73
|
+
let blurWeight = (1.0 - reactive) * select(0.0, 1.0, weight > 0.0);
|
|
74
|
+
output = vec4<f32>(mix(current.rgb, gathered, blurWeight), current.a);
|
|
75
|
+
output.a = current.a;
|
|
76
|
+
return output;
|
|
77
|
+
}
|
package/src/pbr-temporal.wgsl
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#define_import_path forgeax_pbr::temporal
|
|
2
2
|
|
|
3
|
-
#import forgeax_view::common::{sampleMaterialTexture
|
|
3
|
+
#import forgeax_view::common::{sampleMaterialTexture}
|
|
4
|
+
#import forgeax_scene_temporal::{packSceneTemporalV1}
|
|
4
5
|
|
|
5
6
|
fn transformedPbrTemporalUv(
|
|
6
7
|
transform : vec4<f32>,
|
|
@@ -29,6 +30,16 @@ fn transformedPbrTemporalUv(
|
|
|
29
30
|
return vec2<f32>(scaled.x * c - scaled.y * s, scaled.x * s + scaled.y * c) + transform.xy;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
fn resolvePbrTemporalReactive(
|
|
34
|
+
reactive : f32,
|
|
35
|
+
baseColorAlpha : f32,
|
|
36
|
+
sampledAlpha : f32,
|
|
37
|
+
) -> f32 {
|
|
38
|
+
let coverage = clamp(baseColorAlpha * sampledAlpha, 0.0, 1.0);
|
|
39
|
+
let coverageReactive = 1.0 - coverage;
|
|
40
|
+
return max(clamp(reactive, 0.0, 1.0), coverageReactive);
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
fn projectPbrSceneTemporal(
|
|
33
44
|
baseColorAlpha : f32,
|
|
34
45
|
alphaCutoff : f32,
|
|
@@ -38,6 +49,7 @@ fn projectPbrSceneTemporal(
|
|
|
38
49
|
metadata : vec4<f32>,
|
|
39
50
|
currentClip : vec4<f32>,
|
|
40
51
|
previousClip : vec4<f32>,
|
|
52
|
+
temporalProjection : vec4<f32>,
|
|
41
53
|
reactive : f32,
|
|
42
54
|
uv0 : vec2<f32>,
|
|
43
55
|
uv1 : vec2<f32>,
|
|
@@ -69,5 +81,6 @@ fn projectPbrSceneTemporal(
|
|
|
69
81
|
if (alphaCutoff > 0.0 && baseColorAlpha * baseSample.a <= alphaCutoff) {
|
|
70
82
|
discard;
|
|
71
83
|
}
|
|
72
|
-
|
|
84
|
+
let reactiveCoverage = resolvePbrTemporalReactive(reactive, baseColorAlpha, baseSample.a);
|
|
85
|
+
return packSceneTemporalV1(currentClip, previousClip, temporalProjection, reactiveCoverage);
|
|
73
86
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#define_import_path forgeax_scene_temporal
|
|
2
|
+
|
|
3
|
+
// The sole shader interpretation of forgeax::scene-data::temporal-v1.
|
|
4
|
+
// The packed target is rgba16float at render resolution and is sampled only.
|
|
5
|
+
|
|
6
|
+
const SCENE_DATA_TEMPORAL_V1_CLEAR : vec4<f32> = vec4<f32>(0.0, 0.0, -1.0, 1.0);
|
|
7
|
+
|
|
8
|
+
struct SceneTemporalV1 {
|
|
9
|
+
motionUv : vec2<f32>,
|
|
10
|
+
viewDepth : f32,
|
|
11
|
+
reactive : f32,
|
|
12
|
+
validDepth : bool,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
fn unpackSceneTemporalV1(packed : vec4<f32>) -> SceneTemporalV1 {
|
|
16
|
+
let invalidDepth = packed.z < 0.0;
|
|
17
|
+
let validDepth = !invalidDepth;
|
|
18
|
+
let viewDepth = select(0.0, exp2(packed.z) - 1.0, validDepth);
|
|
19
|
+
let motionUv = packed.xy;
|
|
20
|
+
let reactive = clamp(packed.w, 0.0, 1.0);
|
|
21
|
+
return SceneTemporalV1(motionUv, viewDepth, reactive, validDepth);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fn sceneTemporalUv(clip : vec4<f32>) -> vec2<f32> {
|
|
25
|
+
let safeW = select(1e-6, clip.w, abs(clip.w) >= 1e-6);
|
|
26
|
+
let ndc = clip.xy / safeW;
|
|
27
|
+
return vec2<f32>(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn sceneTemporalViewDepth(clip : vec4<f32>, temporalProjection : vec4<f32>) -> f32 {
|
|
31
|
+
let perspectiveDepth = max(clip.w, 0.0);
|
|
32
|
+
let ndcDepth = clip.z / max(abs(clip.w), 1e-6);
|
|
33
|
+
let orthographicDepth = temporalProjection.x +
|
|
34
|
+
ndcDepth * (temporalProjection.y - temporalProjection.x);
|
|
35
|
+
let viewDepth = select(
|
|
36
|
+
perspectiveDepth,
|
|
37
|
+
max(orthographicDepth, 0.0),
|
|
38
|
+
temporalProjection.z >= 0.5,
|
|
39
|
+
);
|
|
40
|
+
return log2(1.0 + viewDepth);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fn packSceneTemporalV1(
|
|
44
|
+
currentClip : vec4<f32>,
|
|
45
|
+
previousClip : vec4<f32>,
|
|
46
|
+
temporalProjection : vec4<f32>,
|
|
47
|
+
reactive : f32,
|
|
48
|
+
) -> vec4<f32> {
|
|
49
|
+
return vec4<f32>(
|
|
50
|
+
sceneTemporalUv(currentClip) - sceneTemporalUv(previousClip),
|
|
51
|
+
sceneTemporalViewDepth(currentClip, temporalProjection),
|
|
52
|
+
clamp(reactive, 0.0, 1.0),
|
|
53
|
+
);
|
|
54
|
+
}
|