@forgeax/engine-shader 0.1.21 → 0.1.24
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 +14 -5
- package/dist/ShaderRegistry.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +33042 -55
- package/dist/index.mjs.map +1 -1
- package/dist/ltc/provenance.d.ts +17 -0
- package/dist/ltc/provenance.d.ts.map +1 -0
- package/dist/ltc/tables.d.ts +22 -0
- package/dist/ltc/tables.d.ts.map +1 -0
- package/dist/material/artifact-types.d.ts +52 -0
- package/dist/material/artifact-types.d.ts.map +1 -1
- package/dist/material-schemas.d.ts +36 -0
- package/dist/material-schemas.d.ts.map +1 -1
- package/dist/register-default-sprite-lit.d.ts +3 -6
- package/dist/register-default-sprite-lit.d.ts.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/ShaderRegistry.ts +0 -17
- package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +9 -3
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +67 -122
- package/src/__tests__/deferred-lighting-ssao.test.ts +3 -3
- package/src/__tests__/direct-light-layout.unit.test.ts +40 -0
- package/src/__tests__/ibl-irradiance.unit.test.ts +16 -4
- package/src/__tests__/ibl-sampling.unit.test.ts +3 -4
- package/src/__tests__/lighting-directional.unit.test.ts +36 -0
- package/src/__tests__/lighting-point-spot-volume.unit.test.ts +8 -2
- package/src/__tests__/lighting-punctual.unit.test.ts +40 -5
- package/src/__tests__/lighting-spot-volume-attenuation.unit.test.ts +1 -1
- package/src/__tests__/ltc-provenance.unit.test.ts +33 -0
- package/src/__tests__/manifest.fixture.json +33 -0
- package/src/__tests__/material-builtins.unit.test.ts +21 -3
- package/src/__tests__/material-contract.unit.test.ts +188 -14
- package/src/__tests__/physical-clearcoat.unit.test.ts +65 -0
- package/src/__tests__/probe-lighting-composition.unit.test.ts +39 -0
- package/src/__tests__/rect-area-brdf.unit.test.ts +36 -0
- package/src/__tests__/reflection-probe-sampling.unit.test.ts +24 -2
- package/src/__tests__/shader-manifest-transmission.unit.test.ts +8 -10
- package/src/__tests__/shader.unit.test.ts +54 -85
- package/src/__tests__/spot-cookie-composition.unit.test.ts +58 -0
- package/src/__tests__/spot-modifier-composition.unit.test.ts +19 -0
- package/src/__tests__/sprite-variants.unit.test.ts +4 -1
- package/src/__tests__/standard-webgl2-varying-budget.unit.test.ts +17 -0
- package/src/__tests__/surface-v1-negative.unit.test.ts +37 -0
- package/src/__tests__/surface-v1.unit.test.ts +83 -0
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +2 -6
- package/src/__tests__/vertex-color-variant.unit.test.ts +16 -5
- package/src/brdf.wgsl +115 -1
- package/src/common.wgsl +53 -79
- package/src/default-standard-pbr-skin.wgsl +687 -144
- package/src/default-standard-pbr.wgsl +600 -247
- package/src/default_standard_surface.wgsl +88 -0
- package/src/hdrp-cluster-forward.wgsl +225 -0
- package/src/ibl-irradiance.wgsl +47 -9
- package/src/ibl-prefilter.wgsl +13 -3
- package/src/ibl-sampling.wgsl +2 -2
- package/src/ibl-shared.wgsl +9 -2
- package/src/index.ts +23 -0
- package/src/lighting-directional.wgsl +9 -117
- package/src/lighting-probe.wgsl +51 -0
- package/src/lighting-punctual.wgsl +21 -11
- package/src/lighting-rect-area.wgsl +162 -0
- package/src/lighting-spot-modifiers.wgsl +100 -0
- package/src/lighting-spot-projector.wgsl +31 -0
- package/src/ltc/provenance.ts +19 -0
- package/src/ltc/tables.ts +2831 -0
- package/src/material/artifact-types.ts +137 -0
- package/src/material/physical/anisotropy.wgsl +26 -0
- package/src/material/physical/clearcoat.wgsl +24 -0
- package/src/material/physical/iridescence.wgsl +23 -0
- package/src/material/physical/sheen.wgsl +17 -0
- package/src/material-schemas.ts +77 -30
- package/src/register-default-sprite-lit.ts +3 -6
- package/src/register-default-standard-pbr-skin.ts +2 -2
- package/src/sprite-lit.wgsl +3 -3
- package/src/sprite.wgsl +1 -1
- package/src/standard-cluster.wgsl +80 -78
- package/src/surface_v1.wgsl +26 -0
- package/src/types.ts +2 -1
- package/src/volume/volume-inject.wgsl +34 -36
- package/src/volume/volume-integrate.wgsl +48 -38
- package/dist/.tsbuildinfo +0 -1
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
#define_import_path forgeax_material::standard
|
|
2
|
-
#
|
|
2
|
+
#pragma material_slot surface
|
|
3
|
+
#import forgeax_material::slot::surface::{evaluate_surface}
|
|
4
|
+
#import forgeax_material::surface_v1::{SurfaceInput, SurfaceData}
|
|
5
|
+
#import forgeax_view::common::{View, Mesh, InstanceData, view, meshes, instances, shadowMap, shadowSampler, sampleMaterialTexture}
|
|
3
6
|
#import forgeax_scene_temporal::{sceneViewZ}
|
|
7
|
+
#ifdef EXTENDED_LIGHTING_AVAILABLE
|
|
8
|
+
#import forgeax_view::common::{spotModifierSampler, iesProfileTexture, cookieTexture, cookieMatrices}
|
|
9
|
+
#endif
|
|
10
|
+
#ifdef PROJECTOR_AVAILABLE
|
|
11
|
+
#ifndef EXTENDED_LIGHTING_AVAILABLE
|
|
12
|
+
#import forgeax_view::common::{projectorTexture, projectorSampler}
|
|
13
|
+
#endif
|
|
14
|
+
#endif
|
|
4
15
|
#import forgeax_pbr::temporal::{projectPbrSceneTemporal}
|
|
5
16
|
#import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx}
|
|
6
17
|
#import forgeax_pbr::ibl_sampling::{box_project, sampleIblDiffuse, sampleIblSpecular, sampleReflectionProbeSpecular}
|
|
18
|
+
#import forgeax_pbr::lighting_probe::{evaluateProbeDiffuse}
|
|
7
19
|
#import forgeax_pbr::tbn::{decodeTangentSpaceNormalRg, scaleTangentSpaceNormal, applyTBN}
|
|
20
|
+
#ifdef CLEARCOAT_AVAILABLE
|
|
21
|
+
#import forgeax_pbr::clearcoat::{evaluateClearcoatLayer}
|
|
22
|
+
#endif
|
|
23
|
+
#ifdef ANISOTROPY_AVAILABLE
|
|
24
|
+
#import forgeax_pbr::anisotropy::{evaluateAnisotropicNormal}
|
|
25
|
+
#endif
|
|
26
|
+
#ifdef SHEEN_AVAILABLE
|
|
27
|
+
#import forgeax_pbr::sheen::{evaluateSheenLayer}
|
|
28
|
+
#endif
|
|
29
|
+
#ifdef IRIDESCENCE_AVAILABLE
|
|
30
|
+
#import forgeax_pbr::iridescence::{evaluateIridescenceFresnel}
|
|
31
|
+
#endif
|
|
8
32
|
#import forgeax_pbr::lighting_directional::{evalDirectionalNoShadow, evalDirectionalShadowFactor}
|
|
9
33
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
10
34
|
#import forgeax_standard::cluster::{evaluateStandardClusterLights, get_ssao_intensity}
|
|
@@ -13,13 +37,20 @@
|
|
|
13
37
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
14
38
|
#pragma variant_axis CLUSTER_FORWARD_AVAILABLE
|
|
15
39
|
#pragma variant_axis VERTEX_COLOR_AVAILABLE
|
|
40
|
+
#pragma variant_axis PROBE_BLEND_AVAILABLE
|
|
41
|
+
#pragma variant_axis EXTENDED_LIGHTING_AVAILABLE
|
|
16
42
|
#pragma variant_axis TRANSMISSION_AVAILABLE
|
|
17
43
|
#pragma variant_axis DIRECTIONAL_PCSS_AVAILABLE
|
|
18
44
|
#pragma variant_axis PROJECTOR_AVAILABLE
|
|
45
|
+
#pragma variant_axis REFLECTION_FALLBACK_AVAILABLE
|
|
19
46
|
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
47
|
+
// reflection_probe is a scene-projected Standard material lane. Its optional
|
|
48
|
+
// texture arguments stay binding-free here; the host selects the bounded table
|
|
49
|
+
// row and falls back to the Skylight helpers when no candidate is admitted.
|
|
50
|
+
// Keep this compile-time witness in the probe variant only: authored physical
|
|
51
|
+
// aliases intentionally flatten renderer-owned probe axes, and naga_oil must
|
|
52
|
+
// not resolve a probe-only helper for those roots.
|
|
53
|
+
#ifdef PROBE_BLEND_AVAILABLE
|
|
23
54
|
fn reflection_probe_sampling_contract(
|
|
24
55
|
worldPosition: vec3<f32>,
|
|
25
56
|
direction: vec3<f32>,
|
|
@@ -28,6 +59,7 @@ fn reflection_probe_sampling_contract(
|
|
|
28
59
|
) -> vec3<f32> {
|
|
29
60
|
return box_project(worldPosition, direction, boxCenter, boxExtents);
|
|
30
61
|
}
|
|
62
|
+
#endif
|
|
31
63
|
|
|
32
64
|
// @forgeax/engine-shader - default-standard-pbr.wgsl
|
|
33
65
|
// (feat-20260523-shader-template-instance-split M5 / T04).
|
|
@@ -50,14 +82,13 @@ fn reflection_probe_sampling_contract(
|
|
|
50
82
|
// + metallic + roughness
|
|
51
83
|
// + 4 channel selectors f32
|
|
52
84
|
// + emissive vec3 + emissiveIntensity
|
|
53
|
-
// + uv/alpha/
|
|
85
|
+
// + uv/alpha/specularColor = 104 B)
|
|
54
86
|
// @group(1) @binding(1) baseColorSampler sampler
|
|
55
87
|
// @group(1) @binding(2) baseColorTexture texture_2d<f32>
|
|
56
88
|
// @group(1) @binding(3) metallicRoughnessSampler sampler
|
|
57
89
|
// @group(1) @binding(4) metallicRoughnessTexture texture_2d<f32>
|
|
58
90
|
// @group(1) @binding(5) normalSampler sampler
|
|
59
91
|
// @group(1) @binding(6) normalTexture texture_2d<f32>
|
|
60
|
-
// @group(1) @binding(7) specularTintSampler + @binding(8) specularTintTexture
|
|
61
92
|
// @group(1) @binding(9..15) Skylight (irradiance / prefilter / brdfLut +
|
|
62
93
|
// samplers) + skylight uniform (intensity)
|
|
63
94
|
// @group(2) @binding(0) meshes storage (worldFromLocal mat4
|
|
@@ -97,63 +128,16 @@ fn reflection_probe_sampling_contract(
|
|
|
97
128
|
// and tolerates RGB normal maps (b is dropped, z is recomputed --
|
|
98
129
|
// equivalent for unit vectors).
|
|
99
130
|
|
|
100
|
-
struct
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Channel selectors (D-8): 4 independent f32 entries split out of the
|
|
105
|
-
// legacy `channelMap : vec4<u32>`. Each value is a small integer encoded
|
|
106
|
-
// as f32 in {0.0, 1.0, 2.0, 3.0} indexing into {r,g,b,a}. Default glTF 2.0
|
|
107
|
-
// packing = (metallicChannel=2, roughnessChannel=1, aoChannel=0,
|
|
108
|
-
// extraChannel=0). The fragment casts to u32 at the pick site -- f32 is
|
|
109
|
-
// chosen so the schema entry type aligns with the 14-tuple
|
|
110
|
-
// MaterialParamType numeric-run packing rule (4-byte stride, 16 B span).
|
|
111
|
-
metallicChannel : f32,
|
|
112
|
-
roughnessChannel : f32,
|
|
113
|
-
aoChannel : f32,
|
|
114
|
-
extraChannel : f32,
|
|
115
|
-
// vec3 align=16 inserts 8 implicit padding bytes after extraChannel
|
|
116
|
-
// (offsets 40..48) so emissive lands at offset 48.
|
|
117
|
-
emissive : vec3<f32>,
|
|
118
|
-
emissiveIntensity : f32,
|
|
119
|
-
occlusionStrength : f32,
|
|
120
|
-
alphaCutoff : f32,
|
|
121
|
-
clearcoat : f32,
|
|
122
|
-
clearcoatRoughness : f32,
|
|
123
|
-
specularTint : vec3<f32>,
|
|
124
|
-
normalScale : f32,
|
|
125
|
-
transmission : f32,
|
|
126
|
-
ior : f32,
|
|
127
|
-
thickness : f32,
|
|
128
|
-
attenuationColor : vec3<f32>,
|
|
129
|
-
attenuationDistance : f32,
|
|
130
|
-
baseColorTextureCoordinatesTransform : vec4<f32>,
|
|
131
|
-
baseColorTextureCoordinatesMetadata : vec4<f32>,
|
|
132
|
-
metallicRoughnessTextureCoordinatesTransform : vec4<f32>,
|
|
133
|
-
metallicRoughnessTextureCoordinatesMetadata : vec4<f32>,
|
|
134
|
-
normalTextureCoordinatesTransform : vec4<f32>,
|
|
135
|
-
normalTextureCoordinatesMetadata : vec4<f32>,
|
|
136
|
-
specularTintTextureCoordinatesTransform : vec4<f32>,
|
|
137
|
-
specularTintTextureCoordinatesMetadata : vec4<f32>,
|
|
138
|
-
emissiveTextureCoordinatesTransform : vec4<f32>,
|
|
139
|
-
emissiveTextureCoordinatesMetadata : vec4<f32>,
|
|
140
|
-
occlusionTextureCoordinatesTransform : vec4<f32>,
|
|
141
|
-
occlusionTextureCoordinatesMetadata : vec4<f32>,
|
|
142
|
-
transmissionTextureCoordinatesTransform : vec4<f32>,
|
|
143
|
-
transmissionTextureCoordinatesMetadata : vec4<f32>,
|
|
144
|
-
thicknessTextureCoordinatesTransform : vec4<f32>,
|
|
145
|
-
thicknessTextureCoordinatesMetadata : vec4<f32>,
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
@group(1) @binding(0) var<uniform> material : Material;
|
|
131
|
+
// The MaterialParameters struct and its binding-0 declaration are generated
|
|
132
|
+
// from the root ParamSchema during composition. Keeping the ABI out of this
|
|
133
|
+
// template prevents a second handwritten interface from drifting from the
|
|
134
|
+
// runtime UBO writer.
|
|
149
135
|
@group(1) @binding(1) var baseColorSampler : sampler;
|
|
150
136
|
@group(1) @binding(2) var baseColorTexture : texture_2d<f32>;
|
|
151
137
|
@group(1) @binding(3) var metallicRoughnessSampler : sampler;
|
|
152
138
|
@group(1) @binding(4) var metallicRoughnessTexture : texture_2d<f32>;
|
|
153
139
|
@group(1) @binding(5) var normalSampler : sampler;
|
|
154
140
|
@group(1) @binding(6) var normalTexture : texture_2d<f32>;
|
|
155
|
-
@group(1) @binding(7) var specularTintSampler : sampler;
|
|
156
|
-
@group(1) @binding(8) var specularTintTexture : texture_2d<f32>;
|
|
157
141
|
@group(1) @binding(9) var emissiveSampler : sampler;
|
|
158
142
|
@group(1) @binding(10) var emissiveTexture : texture_2d<f32>;
|
|
159
143
|
@group(1) @binding(11) var occlusionSampler : sampler;
|
|
@@ -166,6 +150,46 @@ struct Material {
|
|
|
166
150
|
@group(1) @binding(24) var transmissionBackdropSampler : sampler;
|
|
167
151
|
@group(1) @binding(25) var transmissionBackdropTexture : texture_2d<f32>;
|
|
168
152
|
#endif
|
|
153
|
+
#ifdef CLEARCOAT_TEXTURE_AVAILABLE
|
|
154
|
+
@group(1) @binding(26) var clearcoatSampler : sampler;
|
|
155
|
+
@group(1) @binding(27) var clearcoatTexture : texture_2d<f32>;
|
|
156
|
+
#endif
|
|
157
|
+
#ifdef CLEARCOAT_ROUGHNESS_TEXTURE_AVAILABLE
|
|
158
|
+
@group(1) @binding(28) var clearcoatRoughnessSampler : sampler;
|
|
159
|
+
@group(1) @binding(29) var clearcoatRoughnessTexture : texture_2d<f32>;
|
|
160
|
+
#endif
|
|
161
|
+
#ifdef CLEARCOAT_NORMAL_TEXTURE_AVAILABLE
|
|
162
|
+
@group(1) @binding(30) var clearcoatNormalSampler : sampler;
|
|
163
|
+
@group(1) @binding(31) var clearcoatNormalTexture : texture_2d<f32>;
|
|
164
|
+
#endif
|
|
165
|
+
#ifdef ANISOTROPY_TEXTURE_AVAILABLE
|
|
166
|
+
@group(1) @binding(32) var anisotropySampler : sampler;
|
|
167
|
+
@group(1) @binding(33) var anisotropyTexture : texture_2d<f32>;
|
|
168
|
+
#endif
|
|
169
|
+
#ifdef SHEEN_COLOR_TEXTURE_AVAILABLE
|
|
170
|
+
@group(1) @binding(34) var sheenColorSampler : sampler;
|
|
171
|
+
@group(1) @binding(35) var sheenColorTexture : texture_2d<f32>;
|
|
172
|
+
#endif
|
|
173
|
+
#ifdef SHEEN_ROUGHNESS_TEXTURE_AVAILABLE
|
|
174
|
+
@group(1) @binding(36) var sheenRoughnessSampler : sampler;
|
|
175
|
+
@group(1) @binding(37) var sheenRoughnessTexture : texture_2d<f32>;
|
|
176
|
+
#endif
|
|
177
|
+
#ifdef IRIDESCENCE_TEXTURE_AVAILABLE
|
|
178
|
+
@group(1) @binding(38) var iridescenceSampler : sampler;
|
|
179
|
+
@group(1) @binding(39) var iridescenceTexture : texture_2d<f32>;
|
|
180
|
+
#endif
|
|
181
|
+
#ifdef IRIDESCENCE_THICKNESS_TEXTURE_AVAILABLE
|
|
182
|
+
@group(1) @binding(40) var iridescenceThicknessSampler : sampler;
|
|
183
|
+
@group(1) @binding(41) var iridescenceThicknessTexture : texture_2d<f32>;
|
|
184
|
+
#endif
|
|
185
|
+
#ifdef SPECULAR_TEXTURE_AVAILABLE
|
|
186
|
+
@group(1) @binding(42) var specularTextureSampler : sampler;
|
|
187
|
+
@group(1) @binding(43) var specularTexture : texture_2d<f32>;
|
|
188
|
+
#endif
|
|
189
|
+
#ifdef SPECULAR_COLOR_TEXTURE_AVAILABLE
|
|
190
|
+
@group(1) @binding(44) var specularColorTextureSampler : sampler;
|
|
191
|
+
@group(1) @binding(45) var specularColorTexture : texture_2d<f32>;
|
|
192
|
+
#endif
|
|
169
193
|
|
|
170
194
|
// Naga reflection does not retain filtering usage through the generic shared
|
|
171
195
|
// helper. These compile-time-only witnesses retain the binding contract while
|
|
@@ -174,15 +198,52 @@ fn materialTextureFilteringWitness() {
|
|
|
174
198
|
let base = baseColorTexture;
|
|
175
199
|
let metallicRoughness = metallicRoughnessTexture;
|
|
176
200
|
let normal = normalTexture;
|
|
177
|
-
let specularTint = specularTintTexture;
|
|
178
201
|
let emissive = emissiveTexture;
|
|
179
202
|
let occlusion = occlusionTexture;
|
|
203
|
+
#ifdef CLEARCOAT_TEXTURE_AVAILABLE
|
|
204
|
+
let clearcoat = clearcoatTexture;
|
|
205
|
+
#endif
|
|
206
|
+
#ifdef CLEARCOAT_ROUGHNESS_TEXTURE_AVAILABLE
|
|
207
|
+
let clearcoatRoughness = clearcoatRoughnessTexture;
|
|
208
|
+
#endif
|
|
209
|
+
#ifdef CLEARCOAT_NORMAL_TEXTURE_AVAILABLE
|
|
210
|
+
let clearcoatNormal = clearcoatNormalTexture;
|
|
211
|
+
#endif
|
|
180
212
|
let baseWitness = textureSample(base, baseColorSampler, vec2<f32>(0.0));
|
|
181
213
|
let metallicRoughnessWitness = textureSample(metallicRoughness, metallicRoughnessSampler, vec2<f32>(0.0));
|
|
182
214
|
let normalWitness = textureSample(normal, normalSampler, vec2<f32>(0.0));
|
|
183
|
-
let specularTintWitness = textureSample(specularTint, specularTintSampler, vec2<f32>(0.0));
|
|
184
215
|
let emissiveWitness = textureSample(emissive, emissiveSampler, vec2<f32>(0.0));
|
|
185
216
|
let occlusionWitness = textureSample(occlusion, occlusionSampler, vec2<f32>(0.0));
|
|
217
|
+
#ifdef CLEARCOAT_TEXTURE_AVAILABLE
|
|
218
|
+
let clearcoatWitness = textureSample(clearcoat, clearcoatSampler, vec2<f32>(0.0));
|
|
219
|
+
#endif
|
|
220
|
+
#ifdef CLEARCOAT_ROUGHNESS_TEXTURE_AVAILABLE
|
|
221
|
+
let clearcoatRoughnessWitness = textureSample(clearcoatRoughness, clearcoatRoughnessSampler, vec2<f32>(0.0));
|
|
222
|
+
#endif
|
|
223
|
+
#ifdef CLEARCOAT_NORMAL_TEXTURE_AVAILABLE
|
|
224
|
+
let clearcoatNormalWitness = textureSample(clearcoatNormal, clearcoatNormalSampler, vec2<f32>(0.0));
|
|
225
|
+
#endif
|
|
226
|
+
#ifdef ANISOTROPY_TEXTURE_AVAILABLE
|
|
227
|
+
let anisotropyWitness = textureSample(anisotropyTexture, anisotropySampler, vec2<f32>(0.0));
|
|
228
|
+
#endif
|
|
229
|
+
#ifdef SHEEN_COLOR_TEXTURE_AVAILABLE
|
|
230
|
+
let sheenColorWitness = textureSample(sheenColorTexture, sheenColorSampler, vec2<f32>(0.0));
|
|
231
|
+
#endif
|
|
232
|
+
#ifdef SHEEN_ROUGHNESS_TEXTURE_AVAILABLE
|
|
233
|
+
let sheenRoughnessWitness = textureSample(sheenRoughnessTexture, sheenRoughnessSampler, vec2<f32>(0.0));
|
|
234
|
+
#endif
|
|
235
|
+
#ifdef IRIDESCENCE_TEXTURE_AVAILABLE
|
|
236
|
+
let iridescenceWitness = textureSample(iridescenceTexture, iridescenceSampler, vec2<f32>(0.0));
|
|
237
|
+
#endif
|
|
238
|
+
#ifdef IRIDESCENCE_THICKNESS_TEXTURE_AVAILABLE
|
|
239
|
+
let iridescenceThicknessWitness = textureSample(iridescenceThicknessTexture, iridescenceThicknessSampler, vec2<f32>(0.0));
|
|
240
|
+
#endif
|
|
241
|
+
#ifdef SPECULAR_TEXTURE_AVAILABLE
|
|
242
|
+
let specularWeightWitness = textureSample(specularTexture, specularTextureSampler, vec2<f32>(0.0));
|
|
243
|
+
#endif
|
|
244
|
+
#ifdef SPECULAR_COLOR_TEXTURE_AVAILABLE
|
|
245
|
+
let specularColorWitness = textureSample(specularColorTexture, specularColorTextureSampler, vec2<f32>(0.0));
|
|
246
|
+
#endif
|
|
186
247
|
#ifdef TRANSMISSION_AVAILABLE
|
|
187
248
|
let transmission = transmissionTexture;
|
|
188
249
|
let thickness = thicknessTexture;
|
|
@@ -223,6 +284,14 @@ struct SkylightUniforms {
|
|
|
223
284
|
@group(1) @binding(22) var brdfLutSampler : sampler;
|
|
224
285
|
@group(1) @binding(23) var<uniform> skylight : SkylightUniforms;
|
|
225
286
|
|
|
287
|
+
#ifdef PROBE_BLEND_AVAILABLE
|
|
288
|
+
// Optional consumer lane. The host provides the retained ProbeBlendRecord
|
|
289
|
+
// storage for the object selected by this draw. Keeping it in group(3)
|
|
290
|
+
// preserves the mesh/instance ownership of groups (2)/(3) without inventing a
|
|
291
|
+
// second probe bind group.
|
|
292
|
+
@group(3) @binding(1) var<storage, read> probeBlendRecords : array<vec4<f32>>;
|
|
293
|
+
#endif
|
|
294
|
+
|
|
226
295
|
// feat-20260612-hdrp-ssao M7 (round 2) D-B + D-C, scope-amend-webgl2-ubo:
|
|
227
296
|
// SSAO sampling lives on the HDRP unified BGL @group(2) alongside the
|
|
228
297
|
// cluster bindings (binding 7 = ssao texture, binding 8 = sampler). The
|
|
@@ -266,6 +335,10 @@ struct VsOut {
|
|
|
266
335
|
// Keep mesh and instance storage vertex-only; transmission receives the
|
|
267
336
|
// transform basis from the vertex stage below.
|
|
268
337
|
@location(4) @interpolate(flat) transmissionBasis0 : vec4<f32>,
|
|
338
|
+
#else
|
|
339
|
+
// WebGL2 exposes only inter-stage locations 0..14. Pack the object-space
|
|
340
|
+
// Surface position with its cluster/CSM depth in one varying.
|
|
341
|
+
@location(7) positionOSAndViewZ : vec4<f32>,
|
|
269
342
|
#endif
|
|
270
343
|
// feat-city-glb multi-UV tiling: second UV set inter-stage varying. Uses the
|
|
271
344
|
// previously-vacant @location(5) so @location(6)/(7) stay byte-stable with
|
|
@@ -275,8 +348,9 @@ struct VsOut {
|
|
|
275
348
|
@location(9) uv3 : vec2<f32>,
|
|
276
349
|
@location(10) uv4 : vec2<f32>,
|
|
277
350
|
@location(11) uv5 : vec2<f32>,
|
|
278
|
-
|
|
279
|
-
|
|
351
|
+
// UV6/UV7 share one vec4 slot so the transmission basis tail can stay
|
|
352
|
+
// inside WebGL2's 14 user varying locations.
|
|
353
|
+
@location(12) uv6And7 : vec4<f32>,
|
|
280
354
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
281
355
|
@location(14) color : vec4<f32>,
|
|
282
356
|
#endif
|
|
@@ -288,23 +362,14 @@ struct VsOut {
|
|
|
288
362
|
// surfaces -- whose cluster cells were unrelated to the floor's slice 0
|
|
289
363
|
// hot zone -- received zero light. Forward view_z explicitly. M5 / w19:
|
|
290
364
|
// also feeds CSM cascade selection in evalDirectional.
|
|
291
|
-
|
|
365
|
+
// Surface position and clustered/CSM view depth share one varying so the
|
|
366
|
+
// composed Standard shader stays within WebGL2's 14 inter-stage locations.
|
|
292
367
|
#ifdef TRANSMISSION_AVAILABLE
|
|
293
|
-
@location(
|
|
368
|
+
@location(7) viewZ : f32,
|
|
369
|
+
@location(13) @interpolate(flat) transmissionBasis1 : vec4<f32>,
|
|
294
370
|
#endif
|
|
295
371
|
};
|
|
296
372
|
|
|
297
|
-
fn pick_channel(rgba : vec4<f32>, channelIndex : u32) -> f32 {
|
|
298
|
-
// Branch-free channel pick. WGSL has no array<f32, 4>(rgba) addressable
|
|
299
|
-
// helper; manual switch keeps the path uniform.
|
|
300
|
-
switch (channelIndex) {
|
|
301
|
-
case 0u: { return rgba.r; }
|
|
302
|
-
case 1u: { return rgba.g; }
|
|
303
|
-
case 2u: { return rgba.b; }
|
|
304
|
-
default: { return rgba.a; }
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
373
|
// Light evaluators live in forgeax_pbr::lighting_directional +
|
|
309
374
|
// forgeax_pbr::lighting_punctual (M5 / T02). evalDirectional consumes the
|
|
310
375
|
// host's view UBO + shadowMap (LO 3.1.3 slope-scaled-bias 3x3 PCF;
|
|
@@ -315,8 +380,7 @@ fn pick_channel(rgba : vec4<f32>, channelIndex : u32) -> f32 {
|
|
|
315
380
|
// Charter P4: spot light is a thin cone-multiplier on top of the punctual
|
|
316
381
|
// body, point light is the body unchanged -- no magic-value collapse.
|
|
317
382
|
|
|
318
|
-
|
|
319
|
-
fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
383
|
+
fn vs_main_impl(in : VsIn, meshIndex : u32, instanceIndex : u32) -> VsOut {
|
|
320
384
|
// feat-20260604-instances-per-instance-transform-shader-group3-bin M1 / w4:
|
|
321
385
|
// Entity world from meshes[0] — the @group(2) dynamic-offset window has
|
|
322
386
|
// already been aimed at this entity's slot (render-system-record.ts:2996
|
|
@@ -324,12 +388,15 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
324
388
|
// local from instances[idx] — @group(3) is a flat per-instance buffer indexed
|
|
325
389
|
// directly by instance_index (firstInstance=0, render-system-record.ts:2898).
|
|
326
390
|
// Combine: entity_world * per_instance_local.
|
|
327
|
-
let instanceLocal = instances[
|
|
328
|
-
let entityWorld = meshes[
|
|
391
|
+
let instanceLocal = instances[instanceIndex].localFromInstance;
|
|
392
|
+
let entityWorld = meshes[meshIndex].worldFromLocal;
|
|
329
393
|
let localToWorld = entityWorld * instanceLocal;
|
|
330
394
|
let world = localToWorld * vec4<f32>(in.pos, 1.0);
|
|
331
395
|
var out : VsOut;
|
|
332
396
|
out.clip = view.worldViewProj * world;
|
|
397
|
+
#ifndef TRANSMISSION_AVAILABLE
|
|
398
|
+
out.positionOSAndViewZ = vec4<f32>(in.pos, sceneViewZ(out.clip, view.temporalProjection));
|
|
399
|
+
#endif
|
|
333
400
|
out.worldPos = world.xyz;
|
|
334
401
|
// Normal: derive the inverse-transpose from the world matrix columns. This
|
|
335
402
|
// keeps non-uniform entity scale correct while avoiding the Mesh.normalMatrix
|
|
@@ -353,6 +420,16 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
353
420
|
// (w=0); .w handedness preserved for bitangent reconstruction in fragment.
|
|
354
421
|
let worldTangentXyz = normalize((entityWorld * instanceLocal * vec4<f32>(in.tangent.xyz, 0.0)).xyz);
|
|
355
422
|
out.worldTangent = vec4<f32>(worldTangentXyz, in.tangent.w);
|
|
423
|
+
out.uv = in.uv;
|
|
424
|
+
out.uv1 = in.uv1;
|
|
425
|
+
out.uv2 = in.uv2;
|
|
426
|
+
out.uv3 = in.uv3;
|
|
427
|
+
out.uv4 = in.uv4;
|
|
428
|
+
out.uv5 = in.uv5;
|
|
429
|
+
out.uv6And7 = vec4<f32>(in.uv6, in.uv7);
|
|
430
|
+
#ifdef VERTEX_COLOR_AVAILABLE
|
|
431
|
+
out.color = in.color;
|
|
432
|
+
#endif
|
|
356
433
|
#ifdef TRANSMISSION_AVAILABLE
|
|
357
434
|
out.transmissionBasis0 = vec4<f32>(
|
|
358
435
|
localToWorld[0].x,
|
|
@@ -366,17 +443,6 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
366
443
|
localToWorld[2].x,
|
|
367
444
|
localToWorld[2].y,
|
|
368
445
|
);
|
|
369
|
-
#endif
|
|
370
|
-
out.uv = in.uv;
|
|
371
|
-
out.uv1 = in.uv1;
|
|
372
|
-
out.uv2 = in.uv2;
|
|
373
|
-
out.uv3 = in.uv3;
|
|
374
|
-
out.uv4 = in.uv4;
|
|
375
|
-
out.uv5 = in.uv5;
|
|
376
|
-
out.uv6 = in.uv6;
|
|
377
|
-
out.uv7 = in.uv7;
|
|
378
|
-
#ifdef VERTEX_COLOR_AVAILABLE
|
|
379
|
-
out.color = in.color;
|
|
380
446
|
#endif
|
|
381
447
|
// feat-20260613-csm-cascaded-shadow-maps M5 / w19: the per-fragment
|
|
382
448
|
// light-space position varying is gone; evalDirectional computes
|
|
@@ -386,16 +452,35 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
|
386
452
|
// Perspective divide on clip-space position; ndc.z retains depth-buffer value.
|
|
387
453
|
let clipPos = out.clip;
|
|
388
454
|
out.ndc = vec4(clipPos.xy / clipPos.w, clipPos.z / clipPos.w, localToWorld[2].z);
|
|
389
|
-
// Standard WebGPU/GL perspective projection has clip.w = -view.z, so
|
|
390
|
-
// view_z = -clip.w (negative in front of the camera). M4.5-followup:
|
|
391
|
-
// ndc_position_to_cluster needs this for log-z slice mapping; passing
|
|
392
|
-
// ndc.z (in [0,1]) instead made every fragment land in slice 0.
|
|
393
455
|
// Keep the cluster depth exactly aligned with the CPU binner for both
|
|
394
456
|
// perspective and off-axis orthographic projections.
|
|
457
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
395
458
|
out.viewZ = sceneViewZ(clipPos, view.temporalProjection);
|
|
459
|
+
#endif
|
|
396
460
|
return out;
|
|
397
461
|
}
|
|
398
462
|
|
|
463
|
+
fn standardViewZ(in : VsOut) -> f32 {
|
|
464
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
465
|
+
return in.viewZ;
|
|
466
|
+
#else
|
|
467
|
+
return in.positionOSAndViewZ.w;
|
|
468
|
+
#endif
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
@vertex
|
|
472
|
+
fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
473
|
+
return vs_main_impl(in, 0u, idx);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
@vertex
|
|
477
|
+
fn vs_scene_index(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
478
|
+
// GPU-driven scene projection packs one Mesh row per visible candidate and
|
|
479
|
+
// uses the identity instance row. The scene index therefore selects the
|
|
480
|
+
// projected Mesh directly while retaining the Standard PBR varyings.
|
|
481
|
+
return vs_main_impl(in, idx, 0u);
|
|
482
|
+
}
|
|
483
|
+
|
|
399
484
|
fn transformedMaterialUv(transform : vec4<f32>, metadata : vec4<f32>, in : VsOut) -> vec2<f32> {
|
|
400
485
|
var source = in.uv;
|
|
401
486
|
if (metadata.x >= 1.0) { source = in.uv1; }
|
|
@@ -403,8 +488,8 @@ fn transformedMaterialUv(transform : vec4<f32>, metadata : vec4<f32>, in : VsOut
|
|
|
403
488
|
if (metadata.x >= 3.0) { source = in.uv3; }
|
|
404
489
|
if (metadata.x >= 4.0) { source = in.uv4; }
|
|
405
490
|
if (metadata.x >= 5.0) { source = in.uv5; }
|
|
406
|
-
if (metadata.x >= 6.0) { source = in.
|
|
407
|
-
if (metadata.x >= 7.0) { source = in.
|
|
491
|
+
if (metadata.x >= 6.0) { source = in.uv6And7.xy; }
|
|
492
|
+
if (metadata.x >= 7.0) { source = in.uv6And7.zw; }
|
|
408
493
|
let scaled = source * transform.zw;
|
|
409
494
|
let angle = metadata.y;
|
|
410
495
|
let c = cos(angle);
|
|
@@ -412,10 +497,6 @@ fn transformedMaterialUv(transform : vec4<f32>, metadata : vec4<f32>, in : VsOut
|
|
|
412
497
|
return vec2<f32>(scaled.x * c - scaled.y * s, scaled.x * s + scaled.y * c) + transform.xy;
|
|
413
498
|
}
|
|
414
499
|
|
|
415
|
-
fn materialAlpha(baseSample : vec4<f32>) -> f32 {
|
|
416
|
-
return material.baseColor.a * baseSample.a;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
500
|
fn materialVertexColor(in : VsOut) -> vec4<f32> {
|
|
420
501
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
421
502
|
return in.color;
|
|
@@ -424,6 +505,79 @@ fn materialVertexColor(in : VsOut) -> vec4<f32> {
|
|
|
424
505
|
#endif
|
|
425
506
|
}
|
|
426
507
|
|
|
508
|
+
// Keep the cooked artifact content-addressable per declared capability set.
|
|
509
|
+
// Some capability axes only change an imported helper's resource surface; the
|
|
510
|
+
// witness makes that distinction explicit in the composed source as well, so
|
|
511
|
+
// two valid manifest keys can never alias one artifact hash.
|
|
512
|
+
fn standardVariantIdentity() -> f32 {
|
|
513
|
+
var identity = 0.0;
|
|
514
|
+
#ifdef STORAGE_BUFFER_AVAILABLE
|
|
515
|
+
identity = identity + 1.0;
|
|
516
|
+
#endif
|
|
517
|
+
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
518
|
+
identity = identity + 2.0;
|
|
519
|
+
#endif
|
|
520
|
+
#ifdef VERTEX_COLOR_AVAILABLE
|
|
521
|
+
identity = identity + 4.0;
|
|
522
|
+
#endif
|
|
523
|
+
#ifdef PROBE_BLEND_AVAILABLE
|
|
524
|
+
identity = identity + 8.0;
|
|
525
|
+
#endif
|
|
526
|
+
#ifdef EXTENDED_LIGHTING_AVAILABLE
|
|
527
|
+
identity = identity + 16.0;
|
|
528
|
+
#endif
|
|
529
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
530
|
+
identity = identity + 32.0;
|
|
531
|
+
#endif
|
|
532
|
+
#ifdef DIRECTIONAL_PCSS_AVAILABLE
|
|
533
|
+
identity = identity + 64.0;
|
|
534
|
+
#endif
|
|
535
|
+
#ifdef PROJECTOR_AVAILABLE
|
|
536
|
+
identity = identity + 128.0;
|
|
537
|
+
#endif
|
|
538
|
+
#ifdef REFLECTION_FALLBACK_AVAILABLE
|
|
539
|
+
identity = identity + 256.0;
|
|
540
|
+
#endif
|
|
541
|
+
return identity;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Standard owns the lighting and pass policy; the selected Surface owns only
|
|
545
|
+
// the base facts exchanged through surface_v1.
|
|
546
|
+
fn evaluateStandardSurface(in : VsOut, frontFacing : bool) -> SurfaceData {
|
|
547
|
+
let viewDirectionWS = normalize(view.cameraPos - in.worldPos);
|
|
548
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
549
|
+
let positionOS = in.worldPos;
|
|
550
|
+
#else
|
|
551
|
+
let positionOS = in.positionOSAndViewZ.xyz;
|
|
552
|
+
#endif
|
|
553
|
+
return evaluate_surface(SurfaceInput(
|
|
554
|
+
positionOS,
|
|
555
|
+
in.worldPos,
|
|
556
|
+
in.worldNormal,
|
|
557
|
+
in.worldTangent,
|
|
558
|
+
viewDirectionWS,
|
|
559
|
+
in.uv,
|
|
560
|
+
in.uv1,
|
|
561
|
+
materialVertexColor(in),
|
|
562
|
+
frontFacing,
|
|
563
|
+
));
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// The renderer supplies the retained ProbeBlendRecord at the per-object
|
|
567
|
+
// consumer boundary. This helper deliberately accepts only diffuse inputs;
|
|
568
|
+
// Skylight/ReflectionProbe continue to own specular.
|
|
569
|
+
fn composeProbeDiffuse(
|
|
570
|
+
shPreblend : array<vec4<f32>, 9>,
|
|
571
|
+
localBlendFraction : f32,
|
|
572
|
+
normal : vec3<f32>,
|
|
573
|
+
skyIrradiance : vec3<f32>,
|
|
574
|
+
kD : vec3<f32>,
|
|
575
|
+
albedo : vec3<f32>,
|
|
576
|
+
metallic : f32,
|
|
577
|
+
) -> vec3<f32> {
|
|
578
|
+
return evaluateProbeDiffuse(shPreblend, localBlendFraction, normal, skyIrradiance, kD, albedo, metallic);
|
|
579
|
+
}
|
|
580
|
+
|
|
427
581
|
fn finiteScalar(value : f32, fallback : f32) -> f32 {
|
|
428
582
|
let bounded = clamp(value, -65504.0, 65504.0);
|
|
429
583
|
return select(fallback, bounded, value == value);
|
|
@@ -451,69 +605,139 @@ fn blendLinearTransparent(
|
|
|
451
605
|
// linearHdrColorDomain: fs_main writes linear HDR when its target is HDR.
|
|
452
606
|
// toneStageInput: the value remains linear until the fullscreen tone stage.
|
|
453
607
|
|
|
454
|
-
fn
|
|
455
|
-
if (
|
|
608
|
+
fn alphaTestSurface(surface : SurfaceData) {
|
|
609
|
+
if (surface.alphaClipThreshold > 0.0 && surface.opacity <= surface.alphaClipThreshold) {
|
|
456
610
|
discard;
|
|
457
611
|
}
|
|
458
612
|
}
|
|
459
613
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
// Metallic-roughness texture sampling with per-field channel selectors
|
|
470
|
-
// (D-8): glTF 2.0 default layout B=metallic, G=roughness, R=occlusion is
|
|
471
|
-
// encoded by the host as 4 independent f32 selectors in the merged UBO
|
|
472
|
-
// (metallicChannel/roughnessChannel/aoChannel/extraChannel). Cast to u32
|
|
473
|
-
// at the pick_channel call site; values stay in {0,1,2,3}.
|
|
474
|
-
let mrUv = transformedMaterialUv(material.metallicRoughnessTextureCoordinatesTransform, material.metallicRoughnessTextureCoordinatesMetadata, in);
|
|
475
|
-
let mrSample = sampleMaterialTexture(metallicRoughnessTexture, metallicRoughnessSampler, mrUv, material.metallicRoughnessTextureCoordinatesMetadata.zw);
|
|
476
|
-
let metallic = clamp(
|
|
477
|
-
finiteScalar(material.metallic, 0.0) *
|
|
478
|
-
finiteScalar(pick_channel(mrSample, u32(material.metallicChannel)), 1.0),
|
|
479
|
-
0.0,
|
|
480
|
-
1.0,
|
|
481
|
-
);
|
|
482
|
-
let roughnessTex = clamp(
|
|
483
|
-
finiteScalar(pick_channel(mrSample, u32(material.roughnessChannel)), 1.0),
|
|
484
|
-
0.0,
|
|
485
|
-
1.0,
|
|
486
|
-
);
|
|
487
|
-
|
|
488
|
-
// Layer-2 fail-fast: shader internal clamp keeps D_GGX finite for
|
|
489
|
-
// roughness=0 even if the asset somehow bypasses layer-1 register
|
|
490
|
-
// fail-fast (plan-strategy section 5.3 + AC-02 (b)+(c)). Apply to the
|
|
491
|
-
// material scalar prior to the channelMap-extracted texture multiplier
|
|
492
|
-
// so the final roughness >= 0.04 floor is preserved (texture sample is
|
|
493
|
-
// a 1.0-default white when metallicRoughnessTexture is absent, so
|
|
494
|
-
// multiplying after the clamp keeps the floor intact).
|
|
495
|
-
var a = max(finiteScalar(material.roughness, 0.5), 0.04);
|
|
496
|
-
a = a * roughnessTex;
|
|
497
|
-
a = a * a;
|
|
614
|
+
struct StandardPbrOutput {
|
|
615
|
+
@location(0) color : vec4<f32>,
|
|
616
|
+
#ifdef REFLECTION_FALLBACK_AVAILABLE
|
|
617
|
+
// Linear HDR environment contribution from this same Standard BRDF callsite.
|
|
618
|
+
// The detached producer names this second target S_fallback.
|
|
619
|
+
@location(1) reflectionFallback : vec4<f32>,
|
|
620
|
+
#endif
|
|
621
|
+
};
|
|
498
622
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
let
|
|
503
|
-
|
|
504
|
-
let
|
|
505
|
-
|
|
506
|
-
);
|
|
507
|
-
let
|
|
623
|
+
@fragment
|
|
624
|
+
fn fs_main(in : VsOut, @builtin(front_facing) frontFacing : bool) -> StandardPbrOutput {
|
|
625
|
+
let _variantIdentity = standardVariantIdentity();
|
|
626
|
+
let surface = evaluateStandardSurface(in, frontFacing);
|
|
627
|
+
alphaTestSurface(surface);
|
|
628
|
+
let alpha = surface.opacity;
|
|
629
|
+
let albedo = surface.baseColor;
|
|
630
|
+
let metallic = clamp(finiteScalar(surface.metallic, 0.0), 0.0, 1.0);
|
|
631
|
+
let iblRoughness = clamp(finiteScalar(surface.roughness, 0.5), 0.04, 1.0);
|
|
632
|
+
let a = iblRoughness * iblRoughness;
|
|
633
|
+
let n = normalize(surface.normalWS);
|
|
508
634
|
|
|
509
635
|
let v = normalize(view.cameraPos - in.worldPos);
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
636
|
+
var specularColor = material.specularColor;
|
|
637
|
+
#ifdef SPECULAR_COLOR_TEXTURE_AVAILABLE
|
|
638
|
+
let specularUv = transformedMaterialUv(
|
|
639
|
+
material.specularColorTextureCoordinatesTransform,
|
|
640
|
+
material.specularColorTextureCoordinatesMetadata,
|
|
641
|
+
in,
|
|
642
|
+
);
|
|
643
|
+
specularColor = specularColor * sampleMaterialTexture(
|
|
644
|
+
specularColorTexture,
|
|
645
|
+
specularColorTextureSampler,
|
|
646
|
+
specularUv,
|
|
647
|
+
material.specularColorTextureCoordinatesMetadata.zw,
|
|
513
648
|
).rgb;
|
|
649
|
+
#endif
|
|
650
|
+
var specularWeight = clamp(finiteScalar(material.specular, 1.0), 0.0, 1.0);
|
|
651
|
+
#ifdef SPECULAR_TEXTURE_AVAILABLE
|
|
652
|
+
let specularWeightUv = transformedMaterialUv(
|
|
653
|
+
material.specularTextureCoordinatesTransform,
|
|
654
|
+
material.specularTextureCoordinatesMetadata,
|
|
655
|
+
in,
|
|
656
|
+
);
|
|
657
|
+
specularWeight = specularWeight * sampleMaterialTexture(
|
|
658
|
+
specularTexture,
|
|
659
|
+
specularTextureSampler,
|
|
660
|
+
specularWeightUv,
|
|
661
|
+
material.specularTextureCoordinatesMetadata.zw,
|
|
662
|
+
).a;
|
|
663
|
+
#endif
|
|
514
664
|
let safeIor = max(finiteScalar(material.ior, 1.5), 1.0);
|
|
515
665
|
let dielectricF0 = pow((safeIor - 1.0) / (safeIor + 1.0), 2.0);
|
|
516
|
-
|
|
666
|
+
var f0 = mix(vec3<f32>(dielectricF0) * specularColor * specularWeight, albedo, metallic);
|
|
667
|
+
var physicalNormal = n;
|
|
668
|
+
#ifdef ANISOTROPY_AVAILABLE
|
|
669
|
+
var anisotropyStrength = finiteScalar(material.anisotropyStrength, 0.0);
|
|
670
|
+
var anisotropyRotation = finiteScalar(material.anisotropyRotation, 0.0);
|
|
671
|
+
var anisotropyDirection = vec2<f32>(1.0, 0.0);
|
|
672
|
+
#ifdef ANISOTROPY_TEXTURE_AVAILABLE
|
|
673
|
+
let anisotropyUv = transformedMaterialUv(
|
|
674
|
+
material.anisotropyTextureCoordinatesTransform,
|
|
675
|
+
material.anisotropyTextureCoordinatesMetadata,
|
|
676
|
+
in,
|
|
677
|
+
);
|
|
678
|
+
let anisotropySample = sampleMaterialTexture(
|
|
679
|
+
anisotropyTexture,
|
|
680
|
+
anisotropySampler,
|
|
681
|
+
anisotropyUv,
|
|
682
|
+
material.anisotropyTextureCoordinatesMetadata.zw,
|
|
683
|
+
);
|
|
684
|
+
let encodedAnisotropyDirection = 2.0 * anisotropySample.rg - vec2<f32>(1.0, 1.0);
|
|
685
|
+
if (dot(encodedAnisotropyDirection, encodedAnisotropyDirection) >= 1e-6) {
|
|
686
|
+
anisotropyDirection = normalize(encodedAnisotropyDirection);
|
|
687
|
+
}
|
|
688
|
+
anisotropyStrength = anisotropyStrength * anisotropySample.b;
|
|
689
|
+
#endif
|
|
690
|
+
physicalNormal = evaluateAnisotropicNormal(
|
|
691
|
+
n,
|
|
692
|
+
in.worldTangent,
|
|
693
|
+
anisotropyStrength,
|
|
694
|
+
anisotropyRotation,
|
|
695
|
+
anisotropyDirection,
|
|
696
|
+
);
|
|
697
|
+
#endif
|
|
698
|
+
#ifdef IRIDESCENCE_AVAILABLE
|
|
699
|
+
var iridescenceStrength = finiteScalar(material.iridescence, 0.0);
|
|
700
|
+
var iridescenceThicknessFactor = 1.0;
|
|
701
|
+
#ifdef IRIDESCENCE_TEXTURE_AVAILABLE
|
|
702
|
+
let iridescenceUv = transformedMaterialUv(
|
|
703
|
+
material.iridescenceTextureCoordinatesTransform,
|
|
704
|
+
material.iridescenceTextureCoordinatesMetadata,
|
|
705
|
+
in,
|
|
706
|
+
);
|
|
707
|
+
let iridescenceSample = sampleMaterialTexture(
|
|
708
|
+
iridescenceTexture,
|
|
709
|
+
iridescenceSampler,
|
|
710
|
+
iridescenceUv,
|
|
711
|
+
material.iridescenceTextureCoordinatesMetadata.zw,
|
|
712
|
+
);
|
|
713
|
+
iridescenceStrength = iridescenceStrength * iridescenceSample.r;
|
|
714
|
+
#endif
|
|
715
|
+
#ifdef IRIDESCENCE_THICKNESS_TEXTURE_AVAILABLE
|
|
716
|
+
let iridescenceThicknessUv = transformedMaterialUv(
|
|
717
|
+
material.iridescenceThicknessTextureCoordinatesTransform,
|
|
718
|
+
material.iridescenceThicknessTextureCoordinatesMetadata,
|
|
719
|
+
in,
|
|
720
|
+
);
|
|
721
|
+
let iridescenceThicknessSample = sampleMaterialTexture(
|
|
722
|
+
iridescenceThicknessTexture,
|
|
723
|
+
iridescenceThicknessSampler,
|
|
724
|
+
iridescenceThicknessUv,
|
|
725
|
+
material.iridescenceThicknessTextureCoordinatesMetadata.zw,
|
|
726
|
+
);
|
|
727
|
+
iridescenceThicknessFactor = clamp(iridescenceThicknessSample.g, 0.0, 1.0);
|
|
728
|
+
#endif
|
|
729
|
+
let filmThickness = mix(
|
|
730
|
+
finiteScalar(material.iridescenceThicknessMinimum, 100.0),
|
|
731
|
+
finiteScalar(material.iridescenceThicknessMaximum, 400.0),
|
|
732
|
+
iridescenceThicknessFactor,
|
|
733
|
+
);
|
|
734
|
+
f0 = evaluateIridescenceFresnel(
|
|
735
|
+
f0,
|
|
736
|
+
iridescenceStrength,
|
|
737
|
+
finiteScalar(material.iridescenceIor, 1.3),
|
|
738
|
+
filmThickness,
|
|
739
|
+
);
|
|
740
|
+
#endif
|
|
517
741
|
var diffuseAlbedo = albedo;
|
|
518
742
|
#ifdef TRANSMISSION_AVAILABLE
|
|
519
743
|
let transmissionUv = transformedMaterialUv(
|
|
@@ -562,10 +786,6 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
562
786
|
);
|
|
563
787
|
diffuseAlbedo = albedo * (1.0 - transmittedEnergy);
|
|
564
788
|
#endif
|
|
565
|
-
let coatRoughness = max(material.clearcoatRoughness, 0.04);
|
|
566
|
-
let coatAlpha = coatRoughness * coatRoughness;
|
|
567
|
-
let coatF = f_schlick(max(dot(n, v), 0.0), vec3<f32>(0.04)) * material.clearcoat;
|
|
568
|
-
|
|
569
789
|
// Ambient (IBL) + 1 + N + N accumulation
|
|
570
790
|
// (feat-20260520-skylight-ibl-cubemap M3 / t48 +
|
|
571
791
|
// feat-20260520-directional-light-shadow-mapping +
|
|
@@ -595,49 +815,83 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
595
815
|
// diffuse form. The directional path additionally projects worldPos
|
|
596
816
|
// through the per-cascade light-space matrix in the fragment stage for
|
|
597
817
|
// shadow-map PCF lookup (CSM, feat-20260613).
|
|
598
|
-
let kD = (vec3<f32>(1.0) - f_schlick(max(dot(
|
|
599
|
-
|
|
818
|
+
let kD = (vec3<f32>(1.0) - f_schlick(max(dot(physicalNormal, v), 0.0), f0)) * (1.0 - metallic);
|
|
819
|
+
var coatRoughness = 0.04;
|
|
820
|
+
var coatAlpha = coatRoughness * coatRoughness;
|
|
821
|
+
var coatF = vec3<f32>(0.0);
|
|
822
|
+
#ifdef CLEARCOAT_AVAILABLE
|
|
823
|
+
coatRoughness = max(finiteScalar(material.clearcoatRoughness, 0.04), 0.04);
|
|
824
|
+
coatAlpha = coatRoughness * coatRoughness;
|
|
825
|
+
coatF = f_schlick(max(dot(physicalNormal, v), 0.0), vec3<f32>(0.04)) *
|
|
826
|
+
clamp(finiteScalar(material.clearcoat, 0.0), 0.0, 1.0);
|
|
827
|
+
#endif
|
|
600
828
|
var irradiance = vec3<f32>(0.0);
|
|
601
829
|
var specularIbl = vec3<f32>(0.0);
|
|
602
830
|
if (skylight.intensity < 0.0) {
|
|
603
831
|
// A negative intensity is the renderer-owned probe sentinel. The four
|
|
604
|
-
// color/rotation lanes carry probe center and half extents
|
|
605
|
-
// Skylight resources remain unchanged
|
|
832
|
+
// color/rotation lanes carry the selected probe center and half extents;
|
|
833
|
+
// ordinary Skylight resources remain unchanged, preserving the ABI.
|
|
606
834
|
specularIbl = sampleReflectionProbeSpecular(
|
|
607
|
-
|
|
835
|
+
physicalNormal, v, iblRoughness, f0, in.worldPos,
|
|
608
836
|
vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB),
|
|
609
837
|
skylight.rotation.xyz, vec4<f32>(0.0, 0.0, 0.0, 1.0),
|
|
610
838
|
prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
|
|
611
839
|
);
|
|
612
840
|
} else {
|
|
613
|
-
irradiance = sampleIblDiffuse(
|
|
841
|
+
irradiance = sampleIblDiffuse(physicalNormal, skylight.rotation, irradianceMap, irradianceSampler);
|
|
614
842
|
specularIbl = sampleIblSpecular(
|
|
615
|
-
|
|
843
|
+
physicalNormal, v, iblRoughness, f0,
|
|
616
844
|
skylight.rotation,
|
|
617
845
|
prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
|
|
618
846
|
);
|
|
619
847
|
}
|
|
620
|
-
let
|
|
621
|
-
let aoSample = sampleMaterialTexture(occlusionTexture, occlusionSampler, occlusionUv, material.occlusionTextureCoordinatesMetadata.zw);
|
|
622
|
-
let ao = mix(1.0, aoSample.r, material.occlusionStrength);
|
|
848
|
+
let ao = surface.occlusion;
|
|
623
849
|
// feat-20260612-hdrp-ssao M7 round-2: `var` (mutable) so the
|
|
624
850
|
// CLUSTER_FORWARD_AVAILABLE branch below can `ambient *=` the SSAO
|
|
625
851
|
// factor. The non-HDRP path leaves ambient untouched.
|
|
626
852
|
let skyColor = vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
853
|
+
let skyFactor = skyColor * skylight.intensity;
|
|
854
|
+
var reflectionFallback = specularIbl * (vec3<f32>(1.0) - coatF);
|
|
855
|
+
var ambient = (kD * irradiance * diffuseAlbedo + specularIbl) *
|
|
856
|
+
(vec3<f32>(1.0) - coatF);
|
|
857
|
+
#ifdef PROBE_BLEND_AVAILABLE
|
|
858
|
+
// group(3) is the per-object bind group; record zero is the object selected
|
|
859
|
+
// by this draw, independent of instance count.
|
|
860
|
+
let probeShPreblend = array<vec4<f32>, 9>(
|
|
861
|
+
probeBlendRecords[1], probeBlendRecords[2], probeBlendRecords[3],
|
|
862
|
+
probeBlendRecords[4], probeBlendRecords[5], probeBlendRecords[6],
|
|
863
|
+
probeBlendRecords[7], probeBlendRecords[8], probeBlendRecords[9],
|
|
864
|
+
);
|
|
865
|
+
let probeLocalBlendFraction = probeBlendRecords[0].z;
|
|
866
|
+
let probeDiffuseK = kD / max(vec3<f32>(1.0 - metallic), vec3<f32>(0.0001));
|
|
867
|
+
let probeDiffuse = composeProbeDiffuse(probeShPreblend,
|
|
868
|
+
probeLocalBlendFraction,
|
|
869
|
+
physicalNormal,
|
|
870
|
+
irradiance * skyFactor,
|
|
871
|
+
probeDiffuseK,
|
|
872
|
+
diffuseAlbedo,
|
|
873
|
+
metallic,
|
|
874
|
+
);
|
|
875
|
+
ambient = (probeDiffuse + specularIbl * skyFactor) *
|
|
876
|
+
(vec3<f32>(1.0) - coatF);
|
|
877
|
+
#endif
|
|
878
|
+
#ifdef PROBE_BLEND_AVAILABLE
|
|
879
|
+
ambient = ambient * ao;
|
|
880
|
+
#else
|
|
881
|
+
// In the regular Skylight path tint/intensity are applied after sampling.
|
|
882
|
+
// In the probe-sentinel path intensity is encoded as -(probeIntensity + 1),
|
|
883
|
+
// so decode only the non-negative payload and keep zero-intensity probes
|
|
884
|
+
// distinct from an absent Skylight.
|
|
636
885
|
if (skylight.intensity < 0.0) {
|
|
637
|
-
|
|
886
|
+
let environmentScale = max(-skylight.intensity - 1.0, 0.0) * ao;
|
|
887
|
+
ambient = ambient * environmentScale;
|
|
888
|
+
reflectionFallback = reflectionFallback * environmentScale;
|
|
638
889
|
} else {
|
|
639
|
-
|
|
890
|
+
let environmentScale = skyColor * skylight.intensity * ao;
|
|
891
|
+
ambient = ambient * environmentScale;
|
|
892
|
+
reflectionFallback = reflectionFallback * environmentScale;
|
|
640
893
|
}
|
|
894
|
+
#endif
|
|
641
895
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
642
896
|
// feat-20260612-hdrp-ssao M2 round-1 + M7 round-2 (plan-strategy D-7 + D-B + D-C):
|
|
643
897
|
// SSAO ambient synthesis. Reads the half-res R8 `ssaoBlurredTexture` from
|
|
@@ -654,16 +908,15 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
654
908
|
// scope-amend-webgl2-ubo: intensity packed into cluster_uniform.near_far_log.w.
|
|
655
909
|
let ssaoIntensity = get_ssao_intensity();
|
|
656
910
|
ambient *= mix(1.0, ssaoFactor * ao, ssaoIntensity);
|
|
911
|
+
let ssaoScale = mix(1.0, ssaoFactor * ao, ssaoIntensity);
|
|
912
|
+
reflectionFallback *= ssaoScale;
|
|
657
913
|
#endif
|
|
658
914
|
var color = ambient;
|
|
659
915
|
#ifdef TRANSMISSION_AVAILABLE
|
|
660
916
|
let screenUv = in.ndc.xy * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
|
|
661
|
-
// Read the
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
// the fragment stage preserves that contract and keeps thickness in the
|
|
665
|
-
// glTF unit-scale mesh space before measuring the refracted segment in world
|
|
666
|
-
// metres.
|
|
917
|
+
// Read the affine basis forwarded by the vertex stage. This keeps the
|
|
918
|
+
// fragment path within the inter-stage location budget and preserves the
|
|
919
|
+
// scene-index vertex entry used by the GPU-driven lane.
|
|
667
920
|
let localToWorld0 = in.transmissionBasis0.xyz;
|
|
668
921
|
let localToWorld1 = vec3<f32>(
|
|
669
922
|
in.transmissionBasis0.w,
|
|
@@ -746,31 +999,164 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
746
999
|
color = color + finiteColor(transmittedBackdrop, vec3<f32>(0.0)) *
|
|
747
1000
|
transmittedEnergy * beerAttenuation;
|
|
748
1001
|
#endif
|
|
749
|
-
let directionalShadow = evalDirectionalShadowFactor(
|
|
750
|
-
|
|
1002
|
+
let directionalShadow = evalDirectionalShadowFactor(
|
|
1003
|
+
physicalNormal,
|
|
1004
|
+
in.worldPos,
|
|
1005
|
+
standardViewZ(in),
|
|
1006
|
+
);
|
|
1007
|
+
let directionalBase = evalDirectionalNoShadow(physicalNormal, v, diffuseAlbedo, metallic, a, f0);
|
|
751
1008
|
color = color + directionalShadow * directionalBase;
|
|
752
|
-
if (material.clearcoat != 0.0) {
|
|
753
|
-
let directionalClearcoat = evalDirectionalNoShadow(
|
|
754
|
-
n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
755
|
-
);
|
|
756
|
-
color = color + directionalShadow * material.clearcoat * directionalClearcoat;
|
|
757
|
-
}
|
|
758
1009
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
759
1010
|
// NDC from vertex shader (perspective-divided clip-space, interpolated).
|
|
760
1011
|
// view_z: NDC depth for cluster Z-slice lookup.
|
|
761
1012
|
color = color + evaluateStandardClusterLights(
|
|
762
|
-
in.ndc.xyz, in
|
|
1013
|
+
in.ndc.xyz, standardViewZ(in), in.worldPos, physicalNormal, v,
|
|
1014
|
+
diffuseAlbedo, metallic, a, f0, false,
|
|
1015
|
+
);
|
|
1016
|
+
#endif // CLUSTER_FORWARD_AVAILABLE
|
|
1017
|
+
// Emissive is part of the lower-energy stack and is attenuated by the
|
|
1018
|
+
// topcoat just like diffuse/specular radiance.
|
|
1019
|
+
color = color + surface.emissive;
|
|
1020
|
+
#ifdef SHEEN_AVAILABLE
|
|
1021
|
+
var sheenColor = material.sheenColor;
|
|
1022
|
+
var sheenRoughnessFactor = 1.0;
|
|
1023
|
+
#ifdef SHEEN_COLOR_TEXTURE_AVAILABLE
|
|
1024
|
+
let sheenColorUv = transformedMaterialUv(
|
|
1025
|
+
material.sheenColorTextureCoordinatesTransform,
|
|
1026
|
+
material.sheenColorTextureCoordinatesMetadata,
|
|
1027
|
+
in,
|
|
1028
|
+
);
|
|
1029
|
+
sheenColor = sheenColor * sampleMaterialTexture(
|
|
1030
|
+
sheenColorTexture,
|
|
1031
|
+
sheenColorSampler,
|
|
1032
|
+
sheenColorUv,
|
|
1033
|
+
material.sheenColorTextureCoordinatesMetadata.zw,
|
|
1034
|
+
).rgb;
|
|
1035
|
+
#endif
|
|
1036
|
+
#ifdef SHEEN_ROUGHNESS_TEXTURE_AVAILABLE
|
|
1037
|
+
let sheenRoughnessUv = transformedMaterialUv(
|
|
1038
|
+
material.sheenRoughnessTextureCoordinatesTransform,
|
|
1039
|
+
material.sheenRoughnessTextureCoordinatesMetadata,
|
|
1040
|
+
in,
|
|
1041
|
+
);
|
|
1042
|
+
sheenRoughnessFactor = sampleMaterialTexture(
|
|
1043
|
+
sheenRoughnessTexture,
|
|
1044
|
+
sheenRoughnessSampler,
|
|
1045
|
+
sheenRoughnessUv,
|
|
1046
|
+
material.sheenRoughnessTextureCoordinatesMetadata.zw,
|
|
1047
|
+
).a;
|
|
1048
|
+
#endif
|
|
1049
|
+
let sheenRoughness = clamp(
|
|
1050
|
+
finiteScalar(material.sheenRoughness, 0.0) * sheenRoughnessFactor,
|
|
1051
|
+
0.0,
|
|
1052
|
+
1.0,
|
|
1053
|
+
);
|
|
1054
|
+
color = evaluateSheenLayer(color, sheenColor, sheenRoughness, dot(n, v));
|
|
1055
|
+
#endif
|
|
1056
|
+
#ifdef CLEARCOAT_AVAILABLE
|
|
1057
|
+
var clearcoatFactor = clamp(material.clearcoat, 0.0, 1.0);
|
|
1058
|
+
#ifdef CLEARCOAT_TEXTURE_AVAILABLE
|
|
1059
|
+
let clearcoatUv = transformedMaterialUv(material.clearcoatTextureCoordinatesTransform, material.clearcoatTextureCoordinatesMetadata, in);
|
|
1060
|
+
clearcoatFactor = clamp(
|
|
1061
|
+
material.clearcoat * sampleMaterialTexture(
|
|
1062
|
+
clearcoatTexture,
|
|
1063
|
+
clearcoatSampler,
|
|
1064
|
+
clearcoatUv,
|
|
1065
|
+
material.clearcoatTextureCoordinatesMetadata.zw,
|
|
1066
|
+
).r,
|
|
1067
|
+
0.0,
|
|
1068
|
+
1.0,
|
|
1069
|
+
);
|
|
1070
|
+
#endif
|
|
1071
|
+
var clearcoatRoughnessValue = clamp(max(material.clearcoatRoughness, 0.04), 0.04, 1.0);
|
|
1072
|
+
#ifdef CLEARCOAT_ROUGHNESS_TEXTURE_AVAILABLE
|
|
1073
|
+
let clearcoatRoughnessUv = transformedMaterialUv(material.clearcoatRoughnessTextureCoordinatesTransform, material.clearcoatRoughnessTextureCoordinatesMetadata, in);
|
|
1074
|
+
clearcoatRoughnessValue = clamp(
|
|
1075
|
+
max(material.clearcoatRoughness, 0.04) * sampleMaterialTexture(
|
|
1076
|
+
clearcoatRoughnessTexture,
|
|
1077
|
+
clearcoatRoughnessSampler,
|
|
1078
|
+
clearcoatRoughnessUv,
|
|
1079
|
+
material.clearcoatRoughnessTextureCoordinatesMetadata.zw,
|
|
1080
|
+
).g,
|
|
1081
|
+
0.04,
|
|
1082
|
+
1.0,
|
|
763
1083
|
);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1084
|
+
#endif
|
|
1085
|
+
var clearcoatNormalValue = in.worldNormal;
|
|
1086
|
+
#ifdef CLEARCOAT_NORMAL_TEXTURE_AVAILABLE
|
|
1087
|
+
let clearcoatNormalUv = transformedMaterialUv(material.clearcoatNormalTextureCoordinatesTransform, material.clearcoatNormalTextureCoordinatesMetadata, in);
|
|
1088
|
+
clearcoatNormalValue = applyTBN(
|
|
1089
|
+
in.worldNormal,
|
|
1090
|
+
in.worldTangent,
|
|
1091
|
+
scaleTangentSpaceNormal(
|
|
1092
|
+
decodeTangentSpaceNormalRg(sampleMaterialTexture(
|
|
1093
|
+
clearcoatNormalTexture,
|
|
1094
|
+
clearcoatNormalSampler,
|
|
1095
|
+
clearcoatNormalUv,
|
|
1096
|
+
material.clearcoatNormalTextureCoordinatesMetadata.zw,
|
|
1097
|
+
).rg),
|
|
1098
|
+
material.clearcoatNormalScale,
|
|
1099
|
+
),
|
|
1100
|
+
);
|
|
1101
|
+
#endif
|
|
1102
|
+
var clearcoatIbl = vec3<f32>(0.0);
|
|
1103
|
+
if (skylight.intensity < 0.0) {
|
|
1104
|
+
clearcoatIbl = sampleReflectionProbeSpecular(
|
|
1105
|
+
clearcoatNormalValue,
|
|
1106
|
+
v,
|
|
1107
|
+
clearcoatRoughnessValue,
|
|
1108
|
+
vec3<f32>(0.04),
|
|
1109
|
+
in.worldPos,
|
|
1110
|
+
vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB),
|
|
1111
|
+
skylight.rotation.xyz,
|
|
1112
|
+
vec4<f32>(0.0, 0.0, 0.0, 1.0),
|
|
1113
|
+
prefilterMap,
|
|
1114
|
+
prefilterSampler,
|
|
1115
|
+
brdfLut,
|
|
1116
|
+
brdfLutSampler,
|
|
1117
|
+
);
|
|
1118
|
+
} else {
|
|
1119
|
+
clearcoatIbl = sampleIblSpecular(
|
|
1120
|
+
clearcoatNormalValue,
|
|
1121
|
+
v,
|
|
1122
|
+
clearcoatRoughnessValue,
|
|
1123
|
+
vec3<f32>(0.04),
|
|
1124
|
+
skylight.rotation,
|
|
1125
|
+
prefilterMap,
|
|
1126
|
+
prefilterSampler,
|
|
1127
|
+
brdfLut,
|
|
1128
|
+
brdfLutSampler,
|
|
767
1129
|
);
|
|
768
1130
|
}
|
|
769
|
-
|
|
770
|
-
let
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1131
|
+
let clearcoatAlpha = clearcoatRoughnessValue * clearcoatRoughnessValue;
|
|
1132
|
+
let clearcoatDirect = directionalShadow * evalDirectionalNoShadow(
|
|
1133
|
+
clearcoatNormalValue,
|
|
1134
|
+
v,
|
|
1135
|
+
vec3<f32>(0.0),
|
|
1136
|
+
1.0,
|
|
1137
|
+
clearcoatAlpha,
|
|
1138
|
+
vec3<f32>(0.04),
|
|
1139
|
+
);
|
|
1140
|
+
let directionalClearcoat = clearcoatDirect;
|
|
1141
|
+
let clearcoatContribution = clearcoatIbl * clearcoatFactor;
|
|
1142
|
+
var clearcoatEnvironment = clearcoatIbl * skyColor * skylight.intensity;
|
|
1143
|
+
if (skylight.intensity < 0.0) {
|
|
1144
|
+
clearcoatEnvironment = clearcoatIbl;
|
|
1145
|
+
}
|
|
1146
|
+
color = evaluateClearcoatLayer(
|
|
1147
|
+
color,
|
|
1148
|
+
clearcoatEnvironment + directionalClearcoat,
|
|
1149
|
+
dot(clearcoatNormalValue, v),
|
|
1150
|
+
clearcoatFactor,
|
|
1151
|
+
);
|
|
1152
|
+
reflectionFallback = reflectionFallback + clearcoatContribution;
|
|
1153
|
+
#endif
|
|
1154
|
+
var output : StandardPbrOutput;
|
|
1155
|
+
output.color = vec4<f32>(color, alpha);
|
|
1156
|
+
#ifdef REFLECTION_FALLBACK_AVAILABLE
|
|
1157
|
+
output.reflectionFallback = vec4<f32>(reflectionFallback, 1.0);
|
|
1158
|
+
#endif
|
|
1159
|
+
return output;
|
|
774
1160
|
}
|
|
775
1161
|
|
|
776
1162
|
// ── G-buffer output struct (feat-20260612-hdrp-deferred-shading M2 / w12) ──
|
|
@@ -800,49 +1186,18 @@ struct GBufferOutput {
|
|
|
800
1186
|
/// pipeline's g-buffer render pass binds this entry point via the shader's
|
|
801
1187
|
/// multi-entry support (passKind='deferred' selects `fs_gbuffer`).
|
|
802
1188
|
@fragment
|
|
803
|
-
fn fs_gbuffer(in : VsOut) -> GBufferOutput {
|
|
804
|
-
let
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
);
|
|
809
|
-
let
|
|
810
|
-
let vertexColor = materialVertexColor(in);
|
|
811
|
-
alphaTest(material.baseColor.a * baseSample.a * vertexColor.a);
|
|
812
|
-
let alpha = materialAlpha(baseSample) * vertexColor.a;
|
|
813
|
-
let albedo = material.baseColor.rgb * baseSample.rgb * vertexColor.rgb;
|
|
814
|
-
|
|
815
|
-
let mrUv = transformedMaterialUv(
|
|
816
|
-
material.metallicRoughnessTextureCoordinatesTransform,
|
|
817
|
-
material.metallicRoughnessTextureCoordinatesMetadata,
|
|
818
|
-
in,
|
|
819
|
-
);
|
|
820
|
-
let mrSample = sampleMaterialTexture(metallicRoughnessTexture, metallicRoughnessSampler, mrUv, material.metallicRoughnessTextureCoordinatesMetadata.zw);
|
|
821
|
-
let metallic = material.metallic * pick_channel(mrSample, u32(material.metallicChannel));
|
|
822
|
-
let roughnessTex = pick_channel(mrSample, u32(material.roughnessChannel));
|
|
823
|
-
|
|
824
|
-
var a = max(material.roughness, 0.04);
|
|
825
|
-
a = a * roughnessTex;
|
|
826
|
-
|
|
827
|
-
let normalUv = transformedMaterialUv(material.normalTextureCoordinatesTransform, material.normalTextureCoordinatesMetadata, in);
|
|
828
|
-
let normSampleRg = sampleMaterialTexture(normalTexture, normalSampler, normalUv, material.normalTextureCoordinatesMetadata.zw).rg;
|
|
829
|
-
let normTangent = scaleTangentSpaceNormal(
|
|
830
|
-
decodeTangentSpaceNormalRg(normSampleRg), material.normalScale,
|
|
831
|
-
);
|
|
832
|
-
let n = applyTBN(in.worldNormal, in.worldTangent, normTangent);
|
|
833
|
-
|
|
834
|
-
let emissiveUv = transformedMaterialUv(material.emissiveTextureCoordinatesTransform, material.emissiveTextureCoordinatesMetadata, in);
|
|
835
|
-
let emissiveSample = sampleMaterialTexture(emissiveTexture, emissiveSampler, emissiveUv, material.emissiveTextureCoordinatesMetadata.zw).rgb;
|
|
836
|
-
let emissive = material.emissive * material.emissiveIntensity * emissiveSample;
|
|
837
|
-
|
|
838
|
-
let occlusionUv = transformedMaterialUv(material.occlusionTextureCoordinatesTransform, material.occlusionTextureCoordinatesMetadata, in);
|
|
839
|
-
let aoSample = sampleMaterialTexture(occlusionTexture, occlusionSampler, occlusionUv, material.occlusionTextureCoordinatesMetadata.zw);
|
|
840
|
-
let ao = mix(1.0, aoSample.r, material.occlusionStrength);
|
|
1189
|
+
fn fs_gbuffer(in : VsOut, @builtin(front_facing) frontFacing : bool) -> GBufferOutput {
|
|
1190
|
+
let surface = evaluateStandardSurface(in, frontFacing);
|
|
1191
|
+
alphaTestSurface(surface);
|
|
1192
|
+
let albedo = surface.baseColor;
|
|
1193
|
+
let metallic = clamp(surface.metallic, 0.0, 1.0);
|
|
1194
|
+
let roughness = clamp(surface.roughness, 0.04, 1.0);
|
|
1195
|
+
let n = normalize(surface.normalWS);
|
|
841
1196
|
|
|
842
1197
|
var out : GBufferOutput;
|
|
843
|
-
out.normal_roughness = vec4<f32>(n * 0.5 + 0.5,
|
|
1198
|
+
out.normal_roughness = vec4<f32>(n * 0.5 + 0.5, roughness);
|
|
844
1199
|
out.albedo_metallic = vec4<f32>(albedo, metallic);
|
|
845
|
-
out.emissive_ao = vec4<f32>(emissive,
|
|
1200
|
+
out.emissive_ao = vec4<f32>(surface.emissive, surface.occlusion);
|
|
846
1201
|
return out;
|
|
847
1202
|
}
|
|
848
1203
|
|
|
@@ -858,7 +1213,6 @@ struct TemporalVsOut {
|
|
|
858
1213
|
@location(7) uv7 : vec2<f32>,
|
|
859
1214
|
@location(8) @interpolate(perspective) currentClip : vec4<f32>,
|
|
860
1215
|
@location(9) @interpolate(perspective) previousClip : vec4<f32>,
|
|
861
|
-
@location(10) @interpolate(flat) reactive : f32,
|
|
862
1216
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
863
1217
|
@location(14) color : vec4<f32>,
|
|
864
1218
|
#endif
|
|
@@ -877,11 +1231,6 @@ fn vs_temporal(in : VsIn, @builtin(instance_index) idx : u32) -> TemporalVsOut {
|
|
|
877
1231
|
out.currentClip = view.temporalCurrentViewProj * currentWorld;
|
|
878
1232
|
out.clip = out.currentClip;
|
|
879
1233
|
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
|
|
885
1234
|
out.uv = in.uv;
|
|
886
1235
|
out.uv1 = in.uv1;
|
|
887
1236
|
out.uv2 = in.uv2;
|
|
@@ -906,6 +1255,10 @@ fn temporalVertexAlpha(in : TemporalVsOut) -> f32 {
|
|
|
906
1255
|
|
|
907
1256
|
@fragment
|
|
908
1257
|
fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
1258
|
+
var reactive = 0.0;
|
|
1259
|
+
#if STORAGE_BUFFER_AVAILABLE == true
|
|
1260
|
+
reactive = meshes[0].temporal.x;
|
|
1261
|
+
#endif
|
|
909
1262
|
return projectPbrSceneTemporal(
|
|
910
1263
|
material.baseColor.a * temporalVertexAlpha(in),
|
|
911
1264
|
material.alphaCutoff,
|
|
@@ -916,7 +1269,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
916
1269
|
in.currentClip,
|
|
917
1270
|
in.previousClip,
|
|
918
1271
|
view.temporalProjection,
|
|
919
|
-
|
|
1272
|
+
reactive,
|
|
920
1273
|
in.uv, in.uv1, in.uv2, in.uv3,
|
|
921
1274
|
in.uv4, in.uv5, in.uv6, in.uv7,
|
|
922
1275
|
);
|