@forgeax/engine-shader 0.1.7 → 0.1.20
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 +185 -0
- package/src/__tests__/fog-oracle.unit.test.ts +110 -114
- package/src/__tests__/fog-orthographic-ray.unit.test.ts +12 -37
- package/src/__tests__/fog-producer-matrix.integration.test.ts +16 -51
- package/src/__tests__/lighting-point-spot-volume.unit.test.ts +48 -0
- package/src/__tests__/lighting-punctual.unit.test.ts +1 -1
- package/src/__tests__/lighting-spot-volume-attenuation.unit.test.ts +26 -0
- package/src/__tests__/lighting-spot-volume.unit.test.ts +25 -0
- 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 +44 -3
- package/src/__tests__/sprite-variants.unit.test.ts +20 -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 +34 -65
- package/src/default-standard-pbr-skin.wgsl +29 -28
- package/src/default-standard-pbr.wgsl +257 -57
- package/src/fxaa.wgsl +5 -6
- package/src/hdrp-cluster-forward.wgsl +41 -3
- package/src/hdrp-deferred-lighting.wgsl +3 -27
- package/src/index.ts +3 -0
- package/src/lighting-attenuation.wgsl +58 -0
- package/src/lighting-directional.wgsl +31 -3
- package/src/lighting-punctual.wgsl +57 -12
- package/src/material-schemas.ts +7 -0
- package/src/motion-blur.wgsl +77 -0
- package/src/msdf-text.wgsl +4 -23
- package/src/pbr-temporal.wgsl +15 -2
- package/src/scene-temporal.wgsl +54 -0
- package/src/shadow-pcf.wgsl +31 -10
- package/src/shadow_caster.wgsl +27 -2
- package/src/skybox.wgsl +2 -7
- package/src/sprite-lit.wgsl +7 -25
- package/src/sprite.wgsl +7 -25
- package/src/taa-resolve.wgsl +76 -81
- package/src/tonemap.wgsl +7 -9
- package/src/unlit.wgsl +7 -28
- package/src/volume/volume-composite.wgsl +113 -0
- package/src/volume/volume-inject.wgsl +249 -0
- package/src/volume/volume-integrate.wgsl +342 -0
- package/src/volume/volume-temporal.wgsl +70 -0
- 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
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
#define_import_path forgeax_material::standard
|
|
2
|
-
#import forgeax_view::common::{View,
|
|
2
|
+
#import forgeax_view::common::{View, Mesh, InstanceData, view, meshes, instances, PointLight, SpotLight, pointLightsBuffer, spotLightsBuffer, shadowMap, shadowSampler, sampleMaterialTexture}
|
|
3
|
+
#ifdef PROJECTOR_AVAILABLE
|
|
4
|
+
#import forgeax_view::common::{projectorTexture, projectorSampler}
|
|
5
|
+
#endif
|
|
3
6
|
#import forgeax_pbr::temporal::{projectPbrSceneTemporal}
|
|
4
7
|
#import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx}
|
|
5
8
|
#import forgeax_pbr::ibl_sampling::{sampleIblDiffuse, sampleIblSpecular}
|
|
6
9
|
#import forgeax_pbr::tbn::{decodeTangentSpaceNormalRg, scaleTangentSpaceNormal, applyTBN}
|
|
7
10
|
#import forgeax_pbr::lighting_directional::{evalDirectionalNoShadow, evalDirectionalShadowFactor}
|
|
8
|
-
#import forgeax_pbr::lighting_punctual::{evalPoint, evalSpot, evalSpotShadowed}
|
|
9
|
-
#import forgeax_view::fog::{apply_fog}
|
|
11
|
+
#import forgeax_pbr::lighting_punctual::{evalPoint, evalSpot, evalSpotShadowed, projectSpotUv}
|
|
10
12
|
#ifdef POINT_SHADOW_AVAILABLE
|
|
11
13
|
#import forgeax_pbr::lighting_punctual::{evalPointShadowed}
|
|
12
14
|
#import forgeax_view::common::{shadowParams}
|
|
13
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
|
+
}
|
|
14
34
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
15
35
|
#import forgeax_hdrp::cluster_forward::{evaluate_cluster_lights, get_ssao_intensity}
|
|
16
36
|
#endif
|
|
@@ -18,6 +38,8 @@
|
|
|
18
38
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
19
39
|
#pragma variant_axis CLUSTER_FORWARD_AVAILABLE
|
|
20
40
|
#pragma variant_axis VERTEX_COLOR_AVAILABLE
|
|
41
|
+
#pragma variant_axis TRANSMISSION_AVAILABLE
|
|
42
|
+
#pragma variant_axis PROJECTOR_AVAILABLE
|
|
21
43
|
|
|
22
44
|
// @forgeax/engine-shader - default-standard-pbr.wgsl
|
|
23
45
|
// (feat-20260523-shader-template-instance-split M5 / T04).
|
|
@@ -32,8 +54,8 @@
|
|
|
32
54
|
//
|
|
33
55
|
// Bindings (4 BG layout slots; View + Mesh bindings inherited from
|
|
34
56
|
// forgeax_view::common; shadow map + comparison sampler at view BG
|
|
35
|
-
// @binding(3..4) per shadow-mapping feat; Skylight 7 bindings merged
|
|
36
|
-
// the
|
|
57
|
+
// @binding(3..4) per shadow-mapping feat; Skylight 7 bindings merged after
|
|
58
|
+
// the Standard user region per D-5 round-4:
|
|
37
59
|
//
|
|
38
60
|
// @group(0) @binding(0) view uniform (see common.wgsl)
|
|
39
61
|
// @group(1) @binding(0) material uniform (baseColor vec4
|
|
@@ -112,6 +134,11 @@ struct Material {
|
|
|
112
134
|
clearcoatRoughness : f32,
|
|
113
135
|
specularTint : vec3<f32>,
|
|
114
136
|
normalScale : f32,
|
|
137
|
+
transmission : f32,
|
|
138
|
+
ior : f32,
|
|
139
|
+
thickness : f32,
|
|
140
|
+
attenuationColor : vec3<f32>,
|
|
141
|
+
attenuationDistance : f32,
|
|
115
142
|
baseColorTextureCoordinatesTransform : vec4<f32>,
|
|
116
143
|
baseColorTextureCoordinatesMetadata : vec4<f32>,
|
|
117
144
|
metallicRoughnessTextureCoordinatesTransform : vec4<f32>,
|
|
@@ -124,6 +151,10 @@ struct Material {
|
|
|
124
151
|
emissiveTextureCoordinatesMetadata : vec4<f32>,
|
|
125
152
|
occlusionTextureCoordinatesTransform : vec4<f32>,
|
|
126
153
|
occlusionTextureCoordinatesMetadata : vec4<f32>,
|
|
154
|
+
transmissionTextureCoordinatesTransform : vec4<f32>,
|
|
155
|
+
transmissionTextureCoordinatesMetadata : vec4<f32>,
|
|
156
|
+
thicknessTextureCoordinatesTransform : vec4<f32>,
|
|
157
|
+
thicknessTextureCoordinatesMetadata : vec4<f32>,
|
|
127
158
|
};
|
|
128
159
|
|
|
129
160
|
@group(1) @binding(0) var<uniform> material : Material;
|
|
@@ -139,6 +170,14 @@ struct Material {
|
|
|
139
170
|
@group(1) @binding(10) var emissiveTexture : texture_2d<f32>;
|
|
140
171
|
@group(1) @binding(11) var occlusionSampler : sampler;
|
|
141
172
|
@group(1) @binding(12) var occlusionTexture : texture_2d<f32>;
|
|
173
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
174
|
+
@group(1) @binding(13) var transmissionSampler : sampler;
|
|
175
|
+
@group(1) @binding(14) var transmissionTexture : texture_2d<f32>;
|
|
176
|
+
@group(1) @binding(15) var thicknessSampler : sampler;
|
|
177
|
+
@group(1) @binding(16) var thicknessTexture : texture_2d<f32>;
|
|
178
|
+
@group(1) @binding(24) var transmissionBackdropSampler : sampler;
|
|
179
|
+
@group(1) @binding(25) var transmissionBackdropTexture : texture_2d<f32>;
|
|
180
|
+
#endif
|
|
142
181
|
|
|
143
182
|
// Naga reflection does not retain filtering usage through the generic shared
|
|
144
183
|
// helper. These compile-time-only witnesses retain the binding contract while
|
|
@@ -156,6 +195,12 @@ fn materialTextureFilteringWitness() {
|
|
|
156
195
|
let specularTintWitness = textureSample(specularTint, specularTintSampler, vec2<f32>(0.0));
|
|
157
196
|
let emissiveWitness = textureSample(emissive, emissiveSampler, vec2<f32>(0.0));
|
|
158
197
|
let occlusionWitness = textureSample(occlusion, occlusionSampler, vec2<f32>(0.0));
|
|
198
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
199
|
+
let transmission = transmissionTexture;
|
|
200
|
+
let thickness = thicknessTexture;
|
|
201
|
+
let transmissionWitness = textureSample(transmission, transmissionSampler, vec2<f32>(0.0));
|
|
202
|
+
let thicknessWitness = textureSample(thickness, thicknessSampler, vec2<f32>(0.0));
|
|
203
|
+
#endif
|
|
159
204
|
}
|
|
160
205
|
|
|
161
206
|
// Skylight bindings merged into the PBR material BGL
|
|
@@ -163,10 +208,10 @@ fn materialTextureFilteringWitness() {
|
|
|
163
208
|
// round-4 REVISED). The round-2 stand-alone group 4 Skylight BGL collided
|
|
164
209
|
// with WebGPU's default maxBindGroups=4 in chrome-beta and blocked pbr-pl
|
|
165
210
|
// pipeline-layout creation; round-4 appends the 7 Skylight entries to the
|
|
166
|
-
// PBR material BindGroupLayout
|
|
211
|
+
// PBR material BindGroupLayout after the eight Standard texture pairs. The material BG factory
|
|
167
212
|
// (mergeSkylightIntoMaterialBgl in
|
|
168
213
|
// packages/runtime/src/ibl/skylight-bind-group.ts) extends the layout to
|
|
169
|
-
//
|
|
214
|
+
// merged entries; render-system-record assembles a single merged material
|
|
170
215
|
// BindGroup (no extra setBindGroup(4) call). Identity (default) resources
|
|
171
216
|
// produce ambient = 0; with a real Skylight, ibl_sampling helpers project
|
|
172
217
|
// the IBL irradiance + split-sum specular into `ambient` below.
|
|
@@ -182,13 +227,13 @@ struct SkylightUniforms {
|
|
|
182
227
|
colorB : f32,
|
|
183
228
|
rotation : vec4<f32>,
|
|
184
229
|
};
|
|
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(
|
|
230
|
+
@group(1) @binding(17) var irradianceMap : texture_cube<f32>;
|
|
231
|
+
@group(1) @binding(18) var irradianceSampler : sampler;
|
|
232
|
+
@group(1) @binding(19) var prefilterMap : texture_cube<f32>;
|
|
233
|
+
@group(1) @binding(20) var prefilterSampler : sampler;
|
|
234
|
+
@group(1) @binding(21) var brdfLut : texture_2d<f32>;
|
|
235
|
+
@group(1) @binding(22) var brdfLutSampler : sampler;
|
|
236
|
+
@group(1) @binding(23) var<uniform> skylight : SkylightUniforms;
|
|
192
237
|
|
|
193
238
|
// feat-20260612-hdrp-ssao M7 (round 2) D-B + D-C, scope-amend-webgl2-ubo:
|
|
194
239
|
// SSAO sampling lives on the HDRP unified BGL @group(2) alongside the
|
|
@@ -265,25 +310,6 @@ fn pick_channel(rgba : vec4<f32>, channelIndex : u32) -> f32 {
|
|
|
265
310
|
}
|
|
266
311
|
}
|
|
267
312
|
|
|
268
|
-
fn applySceneFog(viewParams : View, color : vec3<f32>, alpha : f32, worldPos : vec3<f32>) -> vec4<f32> {
|
|
269
|
-
var origin = viewParams.cameraPos;
|
|
270
|
-
var direction = normalize(worldPos - origin);
|
|
271
|
-
var rayDistance = length(worldPos - origin);
|
|
272
|
-
if (viewParams.temporalProjection.z >= 0.5) {
|
|
273
|
-
let nearH = viewParams.inverseViewProj * vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
|
274
|
-
let farH = viewParams.inverseViewProj * vec4<f32>(0.0, 0.0, 1.0, 1.0);
|
|
275
|
-
let nearPoint = nearH.xyz / nearH.w;
|
|
276
|
-
let farPoint = farH.xyz / farH.w;
|
|
277
|
-
direction = normalize(farPoint - nearPoint);
|
|
278
|
-
// Orthographic rays are parallel, but their origins differ per fragment.
|
|
279
|
-
// Project the current fragment onto the camera plane instead of reusing
|
|
280
|
-
// the center NDC ray origin for every pixel.
|
|
281
|
-
origin = worldPos - direction * dot(worldPos - viewParams.cameraPos, direction);
|
|
282
|
-
rayDistance = max(dot(worldPos - origin, direction), 0.0);
|
|
283
|
-
}
|
|
284
|
-
return apply_fog(viewParams.fog, FogRay(origin, direction, rayDistance), vec4<f32>(color, alpha));
|
|
285
|
-
}
|
|
286
|
-
|
|
287
313
|
// Light evaluators live in forgeax_pbr::lighting_directional +
|
|
288
314
|
// forgeax_pbr::lighting_punctual (M5 / T02). evalDirectional consumes the
|
|
289
315
|
// host's view UBO + shadowMap (LO 3.1.3 slope-scaled-bias 3x3 PCF;
|
|
@@ -305,7 +331,8 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
305
331
|
// Combine: entity_world * per_instance_local.
|
|
306
332
|
let instanceLocal = instances[idx].localFromInstance;
|
|
307
333
|
let entityWorld = meshes[0].worldFromLocal;
|
|
308
|
-
let
|
|
334
|
+
let localToWorld = entityWorld * instanceLocal;
|
|
335
|
+
let world = localToWorld * vec4<f32>(in.pos, 1.0);
|
|
309
336
|
var out : VsOut;
|
|
310
337
|
out.clip = view.worldViewProj * world;
|
|
311
338
|
out.worldPos = world.xyz;
|
|
@@ -387,6 +414,19 @@ fn materialVertexColor(in : VsOut) -> vec4<f32> {
|
|
|
387
414
|
#endif
|
|
388
415
|
}
|
|
389
416
|
|
|
417
|
+
fn finiteScalar(value : f32, fallback : f32) -> f32 {
|
|
418
|
+
let bounded = clamp(value, -65504.0, 65504.0);
|
|
419
|
+
return select(fallback, bounded, value == value);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
fn finiteColor(value : vec3<f32>, fallback : vec3<f32>) -> vec3<f32> {
|
|
423
|
+
return vec3<f32>(
|
|
424
|
+
finiteScalar(value.x, fallback.x),
|
|
425
|
+
finiteScalar(value.y, fallback.y),
|
|
426
|
+
finiteScalar(value.z, fallback.z),
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
390
430
|
// linearColorDomain: transparent source and destination values are blended
|
|
391
431
|
// before any display encoding. The fixed-function material blend state uses
|
|
392
432
|
// this same equation for the render target's declared linear domain.
|
|
@@ -423,8 +463,17 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
423
463
|
// at the pick_channel call site; values stay in {0,1,2,3}.
|
|
424
464
|
let mrUv = transformedMaterialUv(material.metallicRoughnessTextureCoordinatesTransform, material.metallicRoughnessTextureCoordinatesMetadata, in);
|
|
425
465
|
let mrSample = sampleMaterialTexture(metallicRoughnessTexture, metallicRoughnessSampler, mrUv, material.metallicRoughnessTextureCoordinatesMetadata.zw);
|
|
426
|
-
let metallic =
|
|
427
|
-
|
|
466
|
+
let metallic = clamp(
|
|
467
|
+
finiteScalar(material.metallic, 0.0) *
|
|
468
|
+
finiteScalar(pick_channel(mrSample, u32(material.metallicChannel)), 1.0),
|
|
469
|
+
0.0,
|
|
470
|
+
1.0,
|
|
471
|
+
);
|
|
472
|
+
let roughnessTex = clamp(
|
|
473
|
+
finiteScalar(pick_channel(mrSample, u32(material.roughnessChannel)), 1.0),
|
|
474
|
+
0.0,
|
|
475
|
+
1.0,
|
|
476
|
+
);
|
|
428
477
|
|
|
429
478
|
// Layer-2 fail-fast: shader internal clamp keeps D_GGX finite for
|
|
430
479
|
// roughness=0 even if the asset somehow bypasses layer-1 register
|
|
@@ -433,7 +482,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
433
482
|
// so the final roughness >= 0.04 floor is preserved (texture sample is
|
|
434
483
|
// a 1.0-default white when metallicRoughnessTexture is absent, so
|
|
435
484
|
// multiplying after the clamp keeps the floor intact).
|
|
436
|
-
var a = max(material.roughness, 0.04);
|
|
485
|
+
var a = max(finiteScalar(material.roughness, 0.5), 0.04);
|
|
437
486
|
a = a * roughnessTex;
|
|
438
487
|
a = a * a;
|
|
439
488
|
|
|
@@ -452,7 +501,57 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
452
501
|
let specularTint = material.specularTint * sampleMaterialTexture(
|
|
453
502
|
specularTintTexture, specularTintSampler, specularUv, material.specularTintTextureCoordinatesMetadata.zw,
|
|
454
503
|
).rgb;
|
|
455
|
-
let
|
|
504
|
+
let safeIor = max(finiteScalar(material.ior, 1.5), 1.0);
|
|
505
|
+
let dielectricF0 = pow((safeIor - 1.0) / (safeIor + 1.0), 2.0);
|
|
506
|
+
let f0 = mix(vec3<f32>(dielectricF0) * specularTint, albedo, metallic);
|
|
507
|
+
var diffuseAlbedo = albedo;
|
|
508
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
509
|
+
let transmissionUv = transformedMaterialUv(
|
|
510
|
+
material.transmissionTextureCoordinatesTransform,
|
|
511
|
+
material.transmissionTextureCoordinatesMetadata,
|
|
512
|
+
in,
|
|
513
|
+
);
|
|
514
|
+
let thicknessUv = transformedMaterialUv(
|
|
515
|
+
material.thicknessTextureCoordinatesTransform,
|
|
516
|
+
material.thicknessTextureCoordinatesMetadata,
|
|
517
|
+
in,
|
|
518
|
+
);
|
|
519
|
+
let transmissionSample = sampleMaterialTexture(
|
|
520
|
+
transmissionTexture,
|
|
521
|
+
transmissionSampler,
|
|
522
|
+
transmissionUv,
|
|
523
|
+
material.transmissionTextureCoordinatesMetadata.zw,
|
|
524
|
+
).r;
|
|
525
|
+
let thicknessSample = sampleMaterialTexture(
|
|
526
|
+
thicknessTexture,
|
|
527
|
+
thicknessSampler,
|
|
528
|
+
thicknessUv,
|
|
529
|
+
material.thicknessTextureCoordinatesMetadata.zw,
|
|
530
|
+
).g;
|
|
531
|
+
let transmissionFactor = clamp(
|
|
532
|
+
finiteScalar(material.transmission, 0.0) * finiteScalar(transmissionSample, 1.0),
|
|
533
|
+
0.0,
|
|
534
|
+
1.0,
|
|
535
|
+
);
|
|
536
|
+
let viewDot = finiteScalar(dot(n, v), 0.0);
|
|
537
|
+
let refractionFromInside = viewDot < 0.0;
|
|
538
|
+
let refractionNormal = select(n, -n, refractionFromInside);
|
|
539
|
+
let refractionEta = select(1.0 / safeIor, safeIor, refractionFromInside);
|
|
540
|
+
let incident = -v;
|
|
541
|
+
let refracted = refract(incident, refractionNormal, refractionEta);
|
|
542
|
+
let refractedLengthSquared = dot(refracted, refracted);
|
|
543
|
+
let viewCos = clamp(abs(viewDot), 0.0, 1.0);
|
|
544
|
+
let fresnel = clamp(f_schlick(viewCos, vec3<f32>(dielectricF0)).x, 0.0, 1.0);
|
|
545
|
+
// `refract` returns the zero vector for total internal reflection. Keep the
|
|
546
|
+
// reflection/IBL term above authoritative and remove transmission energy
|
|
547
|
+
// instead of sampling and adding a second reflected environment.
|
|
548
|
+
let transmittedEnergy = select(
|
|
549
|
+
0.0,
|
|
550
|
+
transmissionFactor * (1.0 - metallic) * (1.0 - fresnel),
|
|
551
|
+
refractedLengthSquared > 1e-6,
|
|
552
|
+
);
|
|
553
|
+
diffuseAlbedo = albedo * (1.0 - transmittedEnergy);
|
|
554
|
+
#endif
|
|
456
555
|
let coatRoughness = max(material.clearcoatRoughness, 0.04);
|
|
457
556
|
let coatAlpha = coatRoughness * coatRoughness;
|
|
458
557
|
let coatF = f_schlick(max(dot(n, v), 0.0), vec3<f32>(0.04)) * material.clearcoat;
|
|
@@ -487,7 +586,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
487
586
|
// through the per-cascade light-space matrix in the fragment stage for
|
|
488
587
|
// shadow-map PCF lookup (CSM, feat-20260613).
|
|
489
588
|
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;
|
|
589
|
+
let iblRoughness = clamp(max(finiteScalar(material.roughness, 0.5), 0.04) * roughnessTex, 0.04, 1.0);
|
|
491
590
|
let irradiance = sampleIblDiffuse(n, skylight.rotation, irradianceMap, irradianceSampler);
|
|
492
591
|
let specularIbl = sampleIblSpecular(
|
|
493
592
|
n, v, iblRoughness, f0,
|
|
@@ -501,7 +600,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
501
600
|
// CLUSTER_FORWARD_AVAILABLE branch below can `ambient *=` the SSAO
|
|
502
601
|
// factor. The non-HDRP path leaves ambient untouched.
|
|
503
602
|
let skyColor = vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB);
|
|
504
|
-
var ambient = (kD * irradiance *
|
|
603
|
+
var ambient = (kD * irradiance * diffuseAlbedo + specularIbl) * (vec3<f32>(1.0) - coatF);
|
|
505
604
|
if (material.clearcoat != 0.0) {
|
|
506
605
|
let clearcoatIbl = sampleIblSpecular(
|
|
507
606
|
n, v, coatRoughness, vec3<f32>(0.04),
|
|
@@ -529,8 +628,91 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
529
628
|
ambient *= mix(1.0, ssaoFactor * ao, ssaoIntensity);
|
|
530
629
|
#endif
|
|
531
630
|
var color = ambient;
|
|
631
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
632
|
+
let screenUv = in.ndc.xy * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
|
|
633
|
+
// Read the same renderer-owned transform tables that the vertex stage uses
|
|
634
|
+
// instead of adding six more interstage varyings. WebGPU guarantees only 16
|
|
635
|
+
// user-defined vertex output locations; keeping the affine conversion in
|
|
636
|
+
// the fragment stage preserves that contract and keeps thickness in the
|
|
637
|
+
// glTF unit-scale mesh space before measuring the refracted segment in world
|
|
638
|
+
// metres.
|
|
639
|
+
let localToWorld = meshes[0].worldFromLocal * instances[in.instanceIdx].localFromInstance;
|
|
640
|
+
let localToWorld0 = localToWorld[0].xyz;
|
|
641
|
+
let localToWorld1 = localToWorld[1].xyz;
|
|
642
|
+
let localToWorld2 = localToWorld[2].xyz;
|
|
643
|
+
let inverseCofactor0 = cross(localToWorld1, localToWorld2);
|
|
644
|
+
let inverseCofactor1 = cross(localToWorld2, localToWorld0);
|
|
645
|
+
let inverseCofactor2 = cross(localToWorld0, localToWorld1);
|
|
646
|
+
let localToWorldDet = dot(localToWorld0, inverseCofactor0);
|
|
647
|
+
let safeLocalToWorldDet = select(1.0, localToWorldDet, abs(localToWorldDet) >= 1e-6);
|
|
648
|
+
let worldToLocal0 = vec3<f32>(
|
|
649
|
+
inverseCofactor0.x,
|
|
650
|
+
inverseCofactor1.x,
|
|
651
|
+
inverseCofactor2.x,
|
|
652
|
+
) / safeLocalToWorldDet;
|
|
653
|
+
let worldToLocal1 = vec3<f32>(
|
|
654
|
+
inverseCofactor0.y,
|
|
655
|
+
inverseCofactor1.y,
|
|
656
|
+
inverseCofactor2.y,
|
|
657
|
+
) / safeLocalToWorldDet;
|
|
658
|
+
let worldToLocal2 = vec3<f32>(
|
|
659
|
+
inverseCofactor0.z,
|
|
660
|
+
inverseCofactor1.z,
|
|
661
|
+
inverseCofactor2.z,
|
|
662
|
+
) / safeLocalToWorldDet;
|
|
663
|
+
let refractedLocal = normalize(
|
|
664
|
+
worldToLocal0 * refracted.x +
|
|
665
|
+
worldToLocal1 * refracted.y +
|
|
666
|
+
worldToLocal2 * refracted.z,
|
|
667
|
+
);
|
|
668
|
+
let worldRefractedDirection =
|
|
669
|
+
localToWorld0 * refractedLocal.x +
|
|
670
|
+
localToWorld1 * refractedLocal.y +
|
|
671
|
+
localToWorld2 * refractedLocal.z;
|
|
672
|
+
let worldThickness = max(
|
|
673
|
+
finiteScalar(material.thickness, 0.0) * length(worldRefractedDirection) *
|
|
674
|
+
finiteScalar(thicknessSample, 1.0),
|
|
675
|
+
0.0,
|
|
676
|
+
);
|
|
677
|
+
let refractedUv = screenUv + (refracted.xy - incident.xy) * worldThickness * 0.25;
|
|
678
|
+
let guardBand = 0.02;
|
|
679
|
+
let insideGuardBand = all(refractedUv >= vec2<f32>(guardBand)) &&
|
|
680
|
+
all(refractedUv <= vec2<f32>(1.0 - guardBand));
|
|
681
|
+
let backdropMipCount = textureNumLevels(transmissionBackdropTexture);
|
|
682
|
+
let backdropMaxLod = max(f32(backdropMipCount) - 1.0, 0.0);
|
|
683
|
+
let backdropLod = clamp(iblRoughness * iblRoughness * backdropMaxLod, 0.0, backdropMaxLod);
|
|
684
|
+
let unrefractedBackdrop = textureSampleLevel(
|
|
685
|
+
transmissionBackdropTexture,
|
|
686
|
+
transmissionBackdropSampler,
|
|
687
|
+
clamp(screenUv, vec2<f32>(0.0), vec2<f32>(1.0)),
|
|
688
|
+
backdropLod,
|
|
689
|
+
).rgb;
|
|
690
|
+
var transmittedBackdrop = unrefractedBackdrop;
|
|
691
|
+
if (refractedLengthSquared > 1e-6 && insideGuardBand) {
|
|
692
|
+
transmittedBackdrop = textureSampleLevel(
|
|
693
|
+
transmissionBackdropTexture,
|
|
694
|
+
transmissionBackdropSampler,
|
|
695
|
+
clamp(refractedUv, vec2<f32>(0.0), vec2<f32>(1.0)),
|
|
696
|
+
backdropLod,
|
|
697
|
+
).rgb;
|
|
698
|
+
}
|
|
699
|
+
let safeAttenuationColor = clamp(
|
|
700
|
+
finiteColor(material.attenuationColor, vec3<f32>(1.0)),
|
|
701
|
+
vec3<f32>(0.0),
|
|
702
|
+
vec3<f32>(1.0),
|
|
703
|
+
);
|
|
704
|
+
let safeAttenuationDistance = max(finiteScalar(material.attenuationDistance, 0.0), 0.0);
|
|
705
|
+
let attenuationExponent = worldThickness / max(safeAttenuationDistance, 1e-6);
|
|
706
|
+
let beerAttenuation = select(
|
|
707
|
+
vec3<f32>(1.0),
|
|
708
|
+
pow(safeAttenuationColor, vec3<f32>(attenuationExponent)),
|
|
709
|
+
safeAttenuationDistance > 1e-6 && worldThickness > 0.0,
|
|
710
|
+
);
|
|
711
|
+
color = color + finiteColor(transmittedBackdrop, vec3<f32>(0.0)) *
|
|
712
|
+
transmittedEnergy * beerAttenuation;
|
|
713
|
+
#endif
|
|
532
714
|
let directionalShadow = evalDirectionalShadowFactor(n, in.worldPos, in.viewZ);
|
|
533
|
-
let directionalBase = evalDirectionalNoShadow(n, v,
|
|
715
|
+
let directionalBase = evalDirectionalNoShadow(n, v, diffuseAlbedo, metallic, a, f0);
|
|
534
716
|
color = color + directionalShadow * directionalBase;
|
|
535
717
|
if (material.clearcoat != 0.0) {
|
|
536
718
|
let directionalClearcoat = evalDirectionalNoShadow(
|
|
@@ -541,7 +723,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
541
723
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
542
724
|
// NDC from vertex shader (perspective-divided clip-space, interpolated).
|
|
543
725
|
// view_z: NDC depth for cluster Z-slice lookup.
|
|
544
|
-
color = color + evaluate_cluster_lights(in.ndc, in.viewZ, in.worldPos, n, v,
|
|
726
|
+
color = color + evaluate_cluster_lights(in.ndc, in.viewZ, in.worldPos, n, v, diffuseAlbedo, metallic, a, f0);
|
|
545
727
|
if (material.clearcoat != 0.0) {
|
|
546
728
|
color = color + material.clearcoat * evaluate_cluster_lights(
|
|
547
729
|
in.ndc, in.viewZ, in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
@@ -562,7 +744,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
562
744
|
let lane = shadowParams[p.shadowAtlasLayer];
|
|
563
745
|
color = color + evalPointShadowed(
|
|
564
746
|
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
565
|
-
in.worldPos, n, v,
|
|
747
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
566
748
|
p.shadowAtlasLayer, lane.x, lane.y, 0.005, 0.05,
|
|
567
749
|
);
|
|
568
750
|
if (material.clearcoat != 0.0) {
|
|
@@ -575,7 +757,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
575
757
|
} else {
|
|
576
758
|
color = color + evalPoint(
|
|
577
759
|
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
578
|
-
in.worldPos, n, v,
|
|
760
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
579
761
|
);
|
|
580
762
|
if (material.clearcoat != 0.0) {
|
|
581
763
|
color = color + material.clearcoat * evalPoint(
|
|
@@ -587,7 +769,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
587
769
|
#else
|
|
588
770
|
color = color + evalPoint(
|
|
589
771
|
p.position, p.colorTimesIntensity, p.invRangeSquared,
|
|
590
|
-
in.worldPos, n, v,
|
|
772
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
591
773
|
);
|
|
592
774
|
if (material.clearcoat != 0.0) {
|
|
593
775
|
color = color + material.clearcoat * evalPoint(
|
|
@@ -598,6 +780,9 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
598
780
|
#endif
|
|
599
781
|
}
|
|
600
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;
|
|
601
786
|
for (var i: u32 = 0u; i < spotCount; i = i + 1u) {
|
|
602
787
|
let s = spotLightsBuffer.slots[i];
|
|
603
788
|
// feat-20260625-spot-light-shadow-mapping M3 / w15 (plan-strategy D-1
|
|
@@ -607,29 +792,43 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
607
792
|
// per-spot perspective matrix from the View UBO `view.spotLightViewProj`
|
|
608
793
|
// array (lane N = shadowAtlasTile N; folded from the standalone binding 9
|
|
609
794
|
// into the View UBO in feat-20260625 w25 for WebGL2 uniform-buffer budget);
|
|
610
|
-
// the unshadowed path stays on evalSpot.
|
|
611
|
-
//
|
|
612
|
-
// evalSpotShadowed, not here.
|
|
795
|
+
// the unshadowed path stays on evalSpot. Receiver controls come from the
|
|
796
|
+
// same packed SpotLight snapshot used by the volume path.
|
|
613
797
|
if (s.shadowAtlasTile >= 0) {
|
|
798
|
+
let projector = sampleSpotProjector(view.spotLightViewProj[s.shadowAtlasTile], in.worldPos);
|
|
614
799
|
color = color + evalSpotShadowed(
|
|
615
800
|
s.position, s.direction, s.colorTimesIntensity,
|
|
616
801
|
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
617
802
|
in.worldPos, n, v, albedo, metallic, a, f0,
|
|
618
|
-
view.spotLightViewProj[s.shadowAtlasTile], s.shadowAtlasTile,
|
|
619
|
-
);
|
|
803
|
+
view.spotLightViewProj[s.shadowAtlasTile], s.shadowAtlasTile, s.depthBias, s.normalBias, s.pcfKernelSize, s.shadowIntensity,
|
|
804
|
+
) * projector;
|
|
620
805
|
if (material.clearcoat != 0.0) {
|
|
621
806
|
color = color + material.clearcoat * evalSpotShadowed(
|
|
622
807
|
s.position, s.direction, s.colorTimesIntensity,
|
|
623
808
|
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
624
809
|
in.worldPos, n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
625
|
-
view.spotLightViewProj[s.shadowAtlasTile], s.shadowAtlasTile,
|
|
626
|
-
);
|
|
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;
|
|
627
826
|
}
|
|
628
827
|
} else {
|
|
629
828
|
color = color + evalSpot(
|
|
630
829
|
s.position, s.direction, s.colorTimesIntensity,
|
|
631
830
|
s.cosInner, s.cosOuter, s.invRangeSquared,
|
|
632
|
-
in.worldPos, n, v,
|
|
831
|
+
in.worldPos, n, v, diffuseAlbedo, metallic, a, f0,
|
|
633
832
|
);
|
|
634
833
|
if (material.clearcoat != 0.0) {
|
|
635
834
|
color = color + material.clearcoat * evalSpot(
|
|
@@ -644,7 +843,7 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
644
843
|
let emissiveUv = transformedMaterialUv(material.emissiveTextureCoordinatesTransform, material.emissiveTextureCoordinatesMetadata, in);
|
|
645
844
|
let emissiveSample = sampleMaterialTexture(emissiveTexture, emissiveSampler, emissiveUv, material.emissiveTextureCoordinatesMetadata.zw).rgb;
|
|
646
845
|
color = color + material.emissive * material.emissiveIntensity * emissiveSample;
|
|
647
|
-
return
|
|
846
|
+
return vec4<f32>(color, alpha);
|
|
648
847
|
}
|
|
649
848
|
|
|
650
849
|
// ── G-buffer output struct (feat-20260612-hdrp-deferred-shading M2 / w12) ──
|
|
@@ -730,8 +929,8 @@ struct TemporalVsOut {
|
|
|
730
929
|
@location(5) uv5 : vec2<f32>,
|
|
731
930
|
@location(6) uv6 : vec2<f32>,
|
|
732
931
|
@location(7) uv7 : vec2<f32>,
|
|
733
|
-
@location(8) @interpolate(
|
|
734
|
-
@location(9) @interpolate(
|
|
932
|
+
@location(8) @interpolate(perspective) currentClip : vec4<f32>,
|
|
933
|
+
@location(9) @interpolate(perspective) previousClip : vec4<f32>,
|
|
735
934
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
736
935
|
@location(14) color : vec4<f32>,
|
|
737
936
|
#endif
|
|
@@ -774,7 +973,7 @@ fn temporalVertexAlpha(in : TemporalVsOut) -> f32 {
|
|
|
774
973
|
|
|
775
974
|
@fragment
|
|
776
975
|
fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
777
|
-
var reactive =
|
|
976
|
+
var reactive = 0.0;
|
|
778
977
|
#if STORAGE_BUFFER_AVAILABLE == true
|
|
779
978
|
reactive = meshes[0].temporal.x;
|
|
780
979
|
#endif
|
|
@@ -787,6 +986,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
787
986
|
material.baseColorTextureCoordinatesMetadata,
|
|
788
987
|
in.currentClip,
|
|
789
988
|
in.previousClip,
|
|
989
|
+
view.temporalProjection,
|
|
790
990
|
reactive,
|
|
791
991
|
in.uv, in.uv1, in.uv2, in.uv3,
|
|
792
992
|
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
|
}
|
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
#define_import_path forgeax_hdrp::cluster_forward
|
|
8
8
|
|
|
9
|
-
#import forgeax_pbr::lighting_punctual::{evalPoint, evalSpot, evalSpotShadowed}
|
|
9
|
+
#import forgeax_pbr::lighting_punctual::{evalPoint, evalSpot, evalSpotShadowed, projectSpotUv}
|
|
10
10
|
#import forgeax_view::common::{view}
|
|
11
|
+
#ifdef PROJECTOR_AVAILABLE
|
|
12
|
+
#import forgeax_view::common::{projectorTexture, projectorSampler}
|
|
13
|
+
#endif
|
|
11
14
|
#ifdef POINT_SHADOW_AVAILABLE
|
|
12
15
|
#import forgeax_pbr::lighting_punctual::{evalPointShadowed}
|
|
13
16
|
#endif
|
|
@@ -16,13 +19,15 @@
|
|
|
16
19
|
|
|
17
20
|
const KIND_POINT: u32 = 0u;
|
|
18
21
|
const KIND_SPOT: u32 = 1u;
|
|
22
|
+
const PROJECTOR_ONLY_TILE: i32 = -2;
|
|
19
23
|
|
|
20
24
|
// LightSlot is the byte-frozen 64B std430/std140 transport contract.
|
|
21
25
|
// [0..2] position, [3] invRangeSquared
|
|
22
26
|
// [4..6] colorTimesIntensity, [7] cosInner
|
|
23
27
|
// [8..10] direction, [11] cosOuter
|
|
24
28
|
// [12] raw kind u32, [13] shadow identity i32
|
|
25
|
-
// [14
|
|
29
|
+
// [14] point near / spot shadow intensity f32 bits
|
|
30
|
+
// [15] point far / spot PCF kernel width f32 bits
|
|
26
31
|
struct LightSlot {
|
|
27
32
|
position : vec4<f32>,
|
|
28
33
|
color : vec4<f32>,
|
|
@@ -32,6 +37,23 @@ struct LightSlot {
|
|
|
32
37
|
|
|
33
38
|
const LIGHTSLOT_BYTE_SIZE: u32 = 64u;
|
|
34
39
|
|
|
40
|
+
fn sampleClusterSpotProjector(lightViewProj : mat4x4<f32>, worldPos : vec3<f32>) -> vec3<f32> {
|
|
41
|
+
#ifdef PROJECTOR_AVAILABLE
|
|
42
|
+
let clip = lightViewProj * vec4<f32>(worldPos, 1.0);
|
|
43
|
+
// The projector is a cookie, not an extra cone. A fragment that cannot be
|
|
44
|
+
// projected (or lies outside the tile) keeps the analytic Spot contribution
|
|
45
|
+
// intact, matching Three's SpotLightNode fail-open map gate.
|
|
46
|
+
if (abs(clip.w) < 1e-6) { return vec3<f32>(1.0); }
|
|
47
|
+
let uv = projectSpotUv(lightViewProj, worldPos);
|
|
48
|
+
if (any(uv < vec2<f32>(0.0)) || any(uv > vec2<f32>(1.0))) {
|
|
49
|
+
return vec3<f32>(1.0);
|
|
50
|
+
}
|
|
51
|
+
return textureSampleLevel(projectorTexture, projectorSampler, uv, 0.0).rgb;
|
|
52
|
+
#else
|
|
53
|
+
return vec3<f32>(1.0);
|
|
54
|
+
#endif
|
|
55
|
+
}
|
|
56
|
+
|
|
35
57
|
struct ClusterUniform {
|
|
36
58
|
grid : vec4<u32>,
|
|
37
59
|
near_far_log : vec4<f32>,
|
|
@@ -119,6 +141,7 @@ fn evaluate_cluster_light(
|
|
|
119
141
|
if (kind == KIND_SPOT) {
|
|
120
142
|
let tile = bitcast<i32>(light.kind_and_shadow.y);
|
|
121
143
|
if (tile >= 0) {
|
|
144
|
+
let projector = sampleClusterSpotProjector(view.spotLightViewProj[tile], world_pos);
|
|
122
145
|
return evalSpotShadowed(
|
|
123
146
|
light.position.xyz, light.direction.xyz, light.color.xyz,
|
|
124
147
|
// LightSlot packs cosInner in color.w and cosOuter in direction.w;
|
|
@@ -126,7 +149,22 @@ fn evaluate_cluster_light(
|
|
|
126
149
|
light.color.w, light.direction.w, light.position.w,
|
|
127
150
|
world_pos, normal, view_dir, base_color, metallic, alpha_sq, f0,
|
|
128
151
|
view.spotLightViewProj[tile], tile, 0.005, 0.05,
|
|
129
|
-
|
|
152
|
+
clamp(
|
|
153
|
+
round(select(3.0, bitcast<f32>(light.kind_and_shadow.w),
|
|
154
|
+
bitcast<f32>(light.kind_and_shadow.w) > 0.5)),
|
|
155
|
+
1.0,
|
|
156
|
+
5.0,
|
|
157
|
+
),
|
|
158
|
+
clamp(bitcast<f32>(light.kind_and_shadow.z), 0.0, 1.0),
|
|
159
|
+
) * projector;
|
|
160
|
+
}
|
|
161
|
+
if (tile == PROJECTOR_ONLY_TILE) {
|
|
162
|
+
let projector = sampleClusterSpotProjector(view.spotLightViewProj[0], world_pos);
|
|
163
|
+
return evalSpot(
|
|
164
|
+
light.position.xyz, light.direction.xyz, light.color.xyz,
|
|
165
|
+
light.color.w, light.direction.w, light.position.w,
|
|
166
|
+
world_pos, normal, view_dir, base_color, metallic, alpha_sq, f0,
|
|
167
|
+
) * projector;
|
|
130
168
|
}
|
|
131
169
|
return evalSpot(
|
|
132
170
|
light.position.xyz, light.direction.xyz, light.color.xyz,
|