@forgeax/engine-shader 0.1.23 → 0.1.25
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 +110 -4
- package/dist/ShaderRegistry.d.ts.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +59 -55
- package/dist/index.mjs.map +1 -1
- package/dist/material/artifact-registry.d.ts +1 -0
- package/dist/material/artifact-registry.d.ts.map +1 -1
- package/dist/material-schemas.d.ts +31 -0
- package/dist/material-schemas.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/ShaderRegistry.ts +0 -17
- package/src/__tests__/builtin-texture-sampling-contract.test.ts +10 -1
- package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +9 -3
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +48 -166
- package/src/__tests__/deferred-lighting-ssao.test.ts +7 -4
- package/src/__tests__/ibl-irradiance.unit.test.ts +9 -0
- package/src/__tests__/lighting-directional.unit.test.ts +36 -0
- package/src/__tests__/lighting-punctual.unit.test.ts +12 -4
- package/src/__tests__/material-builtins.unit.test.ts +21 -3
- package/src/__tests__/material-contract.unit.test.ts +131 -17
- package/src/__tests__/physical-clearcoat.unit.test.ts +65 -0
- package/src/__tests__/probe-lighting-composition.unit.test.ts +3 -2
- package/src/__tests__/shader-manifest-transmission.unit.test.ts +8 -10
- package/src/__tests__/shader.unit.test.ts +52 -87
- package/src/__tests__/spot-cookie-composition.unit.test.ts +2 -1
- package/src/__tests__/standard-pbr-webgl2-interface.unit.test.ts +33 -0
- package/src/__tests__/standard-physical-layer.unit.test.ts +16 -0
- package/src/__tests__/standard-surface-pass-evaluation.integration.test.ts +38 -0
- package/src/__tests__/standard-webgl2-varying-budget.unit.test.ts +17 -0
- package/src/__tests__/surface-lighting-provenance.integration.test.ts +59 -0
- package/src/__tests__/surface-pass-templates.integration.test.ts +28 -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/default-standard-pbr-skin.wgsl +704 -192
- package/src/default-standard-pbr.wgsl +563 -279
- package/src/default_standard_surface.wgsl +88 -0
- package/src/hdrp-cluster-forward.wgsl +225 -0
- package/src/ibl-irradiance.wgsl +43 -8
- package/src/ibl-prefilter.wgsl +13 -3
- package/src/ibl-shared.wgsl +9 -2
- package/src/index.ts +37 -3
- package/src/lighting-directional.wgsl +3 -1
- package/src/lighting-spot-modifiers.wgsl +8 -1
- package/src/lighting-spot-projector.wgsl +31 -0
- package/src/material/artifact-registry.ts +20 -1
- package/src/material/artifact-types.ts +2 -2
- 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/standard-physical-layer.wgsl +12 -0
- package/src/material-schemas.ts +65 -30
- package/src/register-default-standard-pbr-skin.ts +2 -2
- package/src/shadow_caster.wgsl +80 -6
- package/src/standard-cluster.wgsl +1 -36
- package/src/surface_v1.wgsl +26 -0
- package/src/unlit.wgsl +12 -0
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
#define_import_path forgeax_material::standard
|
|
2
|
+
#pragma material_slot surface
|
|
3
|
+
#import forgeax_material::slot::surface::{evaluate_surface}
|
|
4
|
+
#import forgeax_material::surface_v1::{SurfaceInput, SurfaceData}
|
|
2
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
|
-
#import forgeax_pbr::ibl_sampling::{sampleIblDiffuse, sampleIblSpecular, sampleReflectionProbeSpecular}
|
|
17
|
+
#import forgeax_pbr::ibl_sampling::{box_project, sampleIblDiffuse, sampleIblSpecular, sampleReflectionProbeSpecular}
|
|
7
18
|
#import forgeax_pbr::lighting_probe::{evaluateProbeDiffuse}
|
|
8
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
|
|
9
32
|
#import forgeax_pbr::lighting_directional::{evalDirectionalNoShadow, evalDirectionalShadowFactor}
|
|
10
33
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
11
34
|
#import forgeax_standard::cluster::{evaluateStandardClusterLights, get_ssao_intensity}
|
|
@@ -21,6 +44,23 @@
|
|
|
21
44
|
#pragma variant_axis PROJECTOR_AVAILABLE
|
|
22
45
|
#pragma variant_axis REFLECTION_FALLBACK_AVAILABLE
|
|
23
46
|
|
|
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
|
|
54
|
+
fn reflection_probe_sampling_contract(
|
|
55
|
+
worldPosition: vec3<f32>,
|
|
56
|
+
direction: vec3<f32>,
|
|
57
|
+
boxCenter: vec3<f32>,
|
|
58
|
+
boxExtents: vec3<f32>,
|
|
59
|
+
) -> vec3<f32> {
|
|
60
|
+
return box_project(worldPosition, direction, boxCenter, boxExtents);
|
|
61
|
+
}
|
|
62
|
+
#endif
|
|
63
|
+
|
|
24
64
|
// @forgeax/engine-shader - default-standard-pbr.wgsl
|
|
25
65
|
// (feat-20260523-shader-template-instance-split M5 / T04).
|
|
26
66
|
//
|
|
@@ -42,14 +82,13 @@
|
|
|
42
82
|
// + metallic + roughness
|
|
43
83
|
// + 4 channel selectors f32
|
|
44
84
|
// + emissive vec3 + emissiveIntensity
|
|
45
|
-
// + uv/alpha/
|
|
85
|
+
// + uv/alpha/specularColor = 104 B)
|
|
46
86
|
// @group(1) @binding(1) baseColorSampler sampler
|
|
47
87
|
// @group(1) @binding(2) baseColorTexture texture_2d<f32>
|
|
48
88
|
// @group(1) @binding(3) metallicRoughnessSampler sampler
|
|
49
89
|
// @group(1) @binding(4) metallicRoughnessTexture texture_2d<f32>
|
|
50
90
|
// @group(1) @binding(5) normalSampler sampler
|
|
51
91
|
// @group(1) @binding(6) normalTexture texture_2d<f32>
|
|
52
|
-
// @group(1) @binding(7) specularTintSampler + @binding(8) specularTintTexture
|
|
53
92
|
// @group(1) @binding(9..15) Skylight (irradiance / prefilter / brdfLut +
|
|
54
93
|
// samplers) + skylight uniform (intensity)
|
|
55
94
|
// @group(2) @binding(0) meshes storage (worldFromLocal mat4
|
|
@@ -89,63 +128,16 @@
|
|
|
89
128
|
// and tolerates RGB normal maps (b is dropped, z is recomputed --
|
|
90
129
|
// equivalent for unit vectors).
|
|
91
130
|
|
|
92
|
-
struct
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// Channel selectors (D-8): 4 independent f32 entries split out of the
|
|
97
|
-
// legacy `channelMap : vec4<u32>`. Each value is a small integer encoded
|
|
98
|
-
// as f32 in {0.0, 1.0, 2.0, 3.0} indexing into {r,g,b,a}. Default glTF 2.0
|
|
99
|
-
// packing = (metallicChannel=2, roughnessChannel=1, aoChannel=0,
|
|
100
|
-
// extraChannel=0). The fragment casts to u32 at the pick site -- f32 is
|
|
101
|
-
// chosen so the schema entry type aligns with the 14-tuple
|
|
102
|
-
// MaterialParamType numeric-run packing rule (4-byte stride, 16 B span).
|
|
103
|
-
metallicChannel : f32,
|
|
104
|
-
roughnessChannel : f32,
|
|
105
|
-
aoChannel : f32,
|
|
106
|
-
extraChannel : f32,
|
|
107
|
-
// vec3 align=16 inserts 8 implicit padding bytes after extraChannel
|
|
108
|
-
// (offsets 40..48) so emissive lands at offset 48.
|
|
109
|
-
emissive : vec3<f32>,
|
|
110
|
-
emissiveIntensity : f32,
|
|
111
|
-
occlusionStrength : f32,
|
|
112
|
-
alphaCutoff : f32,
|
|
113
|
-
clearcoat : f32,
|
|
114
|
-
clearcoatRoughness : f32,
|
|
115
|
-
specularTint : vec3<f32>,
|
|
116
|
-
normalScale : f32,
|
|
117
|
-
transmission : f32,
|
|
118
|
-
ior : f32,
|
|
119
|
-
thickness : f32,
|
|
120
|
-
attenuationColor : vec3<f32>,
|
|
121
|
-
attenuationDistance : f32,
|
|
122
|
-
baseColorTextureCoordinatesTransform : vec4<f32>,
|
|
123
|
-
baseColorTextureCoordinatesMetadata : vec4<f32>,
|
|
124
|
-
metallicRoughnessTextureCoordinatesTransform : vec4<f32>,
|
|
125
|
-
metallicRoughnessTextureCoordinatesMetadata : vec4<f32>,
|
|
126
|
-
normalTextureCoordinatesTransform : vec4<f32>,
|
|
127
|
-
normalTextureCoordinatesMetadata : vec4<f32>,
|
|
128
|
-
specularTintTextureCoordinatesTransform : vec4<f32>,
|
|
129
|
-
specularTintTextureCoordinatesMetadata : vec4<f32>,
|
|
130
|
-
emissiveTextureCoordinatesTransform : vec4<f32>,
|
|
131
|
-
emissiveTextureCoordinatesMetadata : vec4<f32>,
|
|
132
|
-
occlusionTextureCoordinatesTransform : vec4<f32>,
|
|
133
|
-
occlusionTextureCoordinatesMetadata : vec4<f32>,
|
|
134
|
-
transmissionTextureCoordinatesTransform : vec4<f32>,
|
|
135
|
-
transmissionTextureCoordinatesMetadata : vec4<f32>,
|
|
136
|
-
thicknessTextureCoordinatesTransform : vec4<f32>,
|
|
137
|
-
thicknessTextureCoordinatesMetadata : vec4<f32>,
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
@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.
|
|
141
135
|
@group(1) @binding(1) var baseColorSampler : sampler;
|
|
142
136
|
@group(1) @binding(2) var baseColorTexture : texture_2d<f32>;
|
|
143
137
|
@group(1) @binding(3) var metallicRoughnessSampler : sampler;
|
|
144
138
|
@group(1) @binding(4) var metallicRoughnessTexture : texture_2d<f32>;
|
|
145
139
|
@group(1) @binding(5) var normalSampler : sampler;
|
|
146
140
|
@group(1) @binding(6) var normalTexture : texture_2d<f32>;
|
|
147
|
-
@group(1) @binding(7) var specularTintSampler : sampler;
|
|
148
|
-
@group(1) @binding(8) var specularTintTexture : texture_2d<f32>;
|
|
149
141
|
@group(1) @binding(9) var emissiveSampler : sampler;
|
|
150
142
|
@group(1) @binding(10) var emissiveTexture : texture_2d<f32>;
|
|
151
143
|
@group(1) @binding(11) var occlusionSampler : sampler;
|
|
@@ -158,6 +150,46 @@ struct Material {
|
|
|
158
150
|
@group(1) @binding(24) var transmissionBackdropSampler : sampler;
|
|
159
151
|
@group(1) @binding(25) var transmissionBackdropTexture : texture_2d<f32>;
|
|
160
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
|
|
161
193
|
|
|
162
194
|
// Naga reflection does not retain filtering usage through the generic shared
|
|
163
195
|
// helper. These compile-time-only witnesses retain the binding contract while
|
|
@@ -166,15 +198,52 @@ fn materialTextureFilteringWitness() {
|
|
|
166
198
|
let base = baseColorTexture;
|
|
167
199
|
let metallicRoughness = metallicRoughnessTexture;
|
|
168
200
|
let normal = normalTexture;
|
|
169
|
-
let specularTint = specularTintTexture;
|
|
170
201
|
let emissive = emissiveTexture;
|
|
171
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
|
|
172
212
|
let baseWitness = textureSample(base, baseColorSampler, vec2<f32>(0.0));
|
|
173
213
|
let metallicRoughnessWitness = textureSample(metallicRoughness, metallicRoughnessSampler, vec2<f32>(0.0));
|
|
174
214
|
let normalWitness = textureSample(normal, normalSampler, vec2<f32>(0.0));
|
|
175
|
-
let specularTintWitness = textureSample(specularTint, specularTintSampler, vec2<f32>(0.0));
|
|
176
215
|
let emissiveWitness = textureSample(emissive, emissiveSampler, vec2<f32>(0.0));
|
|
177
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
|
|
178
247
|
#ifdef TRANSMISSION_AVAILABLE
|
|
179
248
|
let transmission = transmissionTexture;
|
|
180
249
|
let thickness = thicknessTexture;
|
|
@@ -216,8 +285,10 @@ struct SkylightUniforms {
|
|
|
216
285
|
@group(1) @binding(23) var<uniform> skylight : SkylightUniforms;
|
|
217
286
|
|
|
218
287
|
#ifdef PROBE_BLEND_AVAILABLE
|
|
219
|
-
// Optional consumer lane. The host
|
|
220
|
-
//
|
|
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.
|
|
221
292
|
@group(3) @binding(1) var<storage, read> probeBlendRecords : array<vec4<f32>>;
|
|
222
293
|
#endif
|
|
223
294
|
|
|
@@ -264,6 +335,10 @@ struct VsOut {
|
|
|
264
335
|
// Keep mesh and instance storage vertex-only; transmission receives the
|
|
265
336
|
// transform basis from the vertex stage below.
|
|
266
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>,
|
|
267
342
|
#endif
|
|
268
343
|
// feat-city-glb multi-UV tiling: second UV set inter-stage varying. Uses the
|
|
269
344
|
// previously-vacant @location(5) so @location(6)/(7) stay byte-stable with
|
|
@@ -273,8 +348,9 @@ struct VsOut {
|
|
|
273
348
|
@location(9) uv3 : vec2<f32>,
|
|
274
349
|
@location(10) uv4 : vec2<f32>,
|
|
275
350
|
@location(11) uv5 : vec2<f32>,
|
|
276
|
-
|
|
277
|
-
|
|
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>,
|
|
278
354
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
279
355
|
@location(14) color : vec4<f32>,
|
|
280
356
|
#endif
|
|
@@ -286,27 +362,23 @@ struct VsOut {
|
|
|
286
362
|
// surfaces -- whose cluster cells were unrelated to the floor's slice 0
|
|
287
363
|
// hot zone -- received zero light. Forward view_z explicitly. M5 / w19:
|
|
288
364
|
// also feeds CSM cascade selection in evalDirectional.
|
|
289
|
-
|
|
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.
|
|
290
367
|
#ifdef TRANSMISSION_AVAILABLE
|
|
291
|
-
@location(
|
|
368
|
+
@location(7) viewZ : f32,
|
|
369
|
+
@location(13) @interpolate(flat) transmissionBasis1 : vec4<f32>,
|
|
292
370
|
#endif
|
|
293
371
|
};
|
|
294
372
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Directional lighting lives in forgeax_pbr::lighting_directional. Point,
|
|
307
|
-
// Spot, and Rect local lights are evaluated by the Standard Cluster module
|
|
308
|
-
// below from one indexed light_data corpus; no per-kind fixed-slot loop is
|
|
309
|
-
// present in this material shader.
|
|
373
|
+
// Light evaluators live in forgeax_pbr::lighting_directional +
|
|
374
|
+
// forgeax_pbr::lighting_punctual (M5 / T02). evalDirectional consumes the
|
|
375
|
+
// host's view UBO + shadowMap (LO 3.1.3 slope-scaled-bias 3x3 PCF;
|
|
376
|
+
// feat-20260520-directional-light-shadow-mapping byte-equivalent). evalPoint
|
|
377
|
+
// / evalSpot share evalPunctualBody (GGX specular + Lambertian diffuse +
|
|
378
|
+
// KHR_lights_punctual quartic range attenuation;
|
|
379
|
+
// feat-20260519-light-casters-point-spot-pbr M4 / w22 byte-equivalent).
|
|
380
|
+
// Charter P4: spot light is a thin cone-multiplier on top of the punctual
|
|
381
|
+
// body, point light is the body unchanged -- no magic-value collapse.
|
|
310
382
|
|
|
311
383
|
fn vs_main_impl(in : VsIn, meshIndex : u32, instanceIndex : u32) -> VsOut {
|
|
312
384
|
// feat-20260604-instances-per-instance-transform-shader-group3-bin M1 / w4:
|
|
@@ -322,6 +394,9 @@ fn vs_main_impl(in : VsIn, meshIndex : u32, instanceIndex : u32) -> VsOut {
|
|
|
322
394
|
let world = localToWorld * vec4<f32>(in.pos, 1.0);
|
|
323
395
|
var out : VsOut;
|
|
324
396
|
out.clip = view.worldViewProj * world;
|
|
397
|
+
#ifndef TRANSMISSION_AVAILABLE
|
|
398
|
+
out.positionOSAndViewZ = vec4<f32>(in.pos, sceneViewZ(out.clip, view.temporalProjection));
|
|
399
|
+
#endif
|
|
325
400
|
out.worldPos = world.xyz;
|
|
326
401
|
// Normal: derive the inverse-transpose from the world matrix columns. This
|
|
327
402
|
// keeps non-uniform entity scale correct while avoiding the Mesh.normalMatrix
|
|
@@ -345,6 +420,16 @@ fn vs_main_impl(in : VsIn, meshIndex : u32, instanceIndex : u32) -> VsOut {
|
|
|
345
420
|
// (w=0); .w handedness preserved for bitangent reconstruction in fragment.
|
|
346
421
|
let worldTangentXyz = normalize((entityWorld * instanceLocal * vec4<f32>(in.tangent.xyz, 0.0)).xyz);
|
|
347
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
|
|
348
433
|
#ifdef TRANSMISSION_AVAILABLE
|
|
349
434
|
out.transmissionBasis0 = vec4<f32>(
|
|
350
435
|
localToWorld[0].x,
|
|
@@ -358,17 +443,6 @@ fn vs_main_impl(in : VsIn, meshIndex : u32, instanceIndex : u32) -> VsOut {
|
|
|
358
443
|
localToWorld[2].x,
|
|
359
444
|
localToWorld[2].y,
|
|
360
445
|
);
|
|
361
|
-
#endif
|
|
362
|
-
out.uv = in.uv;
|
|
363
|
-
out.uv1 = in.uv1;
|
|
364
|
-
out.uv2 = in.uv2;
|
|
365
|
-
out.uv3 = in.uv3;
|
|
366
|
-
out.uv4 = in.uv4;
|
|
367
|
-
out.uv5 = in.uv5;
|
|
368
|
-
out.uv6 = in.uv6;
|
|
369
|
-
out.uv7 = in.uv7;
|
|
370
|
-
#ifdef VERTEX_COLOR_AVAILABLE
|
|
371
|
-
out.color = in.color;
|
|
372
446
|
#endif
|
|
373
447
|
// feat-20260613-csm-cascaded-shadow-maps M5 / w19: the per-fragment
|
|
374
448
|
// light-space position varying is gone; evalDirectional computes
|
|
@@ -378,16 +452,22 @@ fn vs_main_impl(in : VsIn, meshIndex : u32, instanceIndex : u32) -> VsOut {
|
|
|
378
452
|
// Perspective divide on clip-space position; ndc.z retains depth-buffer value.
|
|
379
453
|
let clipPos = out.clip;
|
|
380
454
|
out.ndc = vec4(clipPos.xy / clipPos.w, clipPos.z / clipPos.w, localToWorld[2].z);
|
|
381
|
-
// Standard WebGPU/GL perspective projection has clip.w = -view.z, so
|
|
382
|
-
// view_z = -clip.w (negative in front of the camera). M4.5-followup:
|
|
383
|
-
// ndc_position_to_cluster needs this for log-z slice mapping; passing
|
|
384
|
-
// ndc.z (in [0,1]) instead made every fragment land in slice 0.
|
|
385
455
|
// Keep the cluster depth exactly aligned with the CPU binner for both
|
|
386
456
|
// perspective and off-axis orthographic projections.
|
|
457
|
+
#ifdef TRANSMISSION_AVAILABLE
|
|
387
458
|
out.viewZ = sceneViewZ(clipPos, view.temporalProjection);
|
|
459
|
+
#endif
|
|
388
460
|
return out;
|
|
389
461
|
}
|
|
390
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
|
+
|
|
391
471
|
@vertex
|
|
392
472
|
fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
|
|
393
473
|
return vs_main_impl(in, 0u, idx);
|
|
@@ -408,8 +488,8 @@ fn transformedMaterialUv(transform : vec4<f32>, metadata : vec4<f32>, in : VsOut
|
|
|
408
488
|
if (metadata.x >= 3.0) { source = in.uv3; }
|
|
409
489
|
if (metadata.x >= 4.0) { source = in.uv4; }
|
|
410
490
|
if (metadata.x >= 5.0) { source = in.uv5; }
|
|
411
|
-
if (metadata.x >= 6.0) { source = in.
|
|
412
|
-
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; }
|
|
413
493
|
let scaled = source * transform.zw;
|
|
414
494
|
let angle = metadata.y;
|
|
415
495
|
let c = cos(angle);
|
|
@@ -417,10 +497,6 @@ fn transformedMaterialUv(transform : vec4<f32>, metadata : vec4<f32>, in : VsOut
|
|
|
417
497
|
return vec2<f32>(scaled.x * c - scaled.y * s, scaled.x * s + scaled.y * c) + transform.xy;
|
|
418
498
|
}
|
|
419
499
|
|
|
420
|
-
fn materialAlpha(baseSample : vec4<f32>) -> f32 {
|
|
421
|
-
return material.baseColor.a * baseSample.a;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
500
|
fn materialVertexColor(in : VsOut) -> vec4<f32> {
|
|
425
501
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
426
502
|
return in.color;
|
|
@@ -429,6 +505,79 @@ fn materialVertexColor(in : VsOut) -> vec4<f32> {
|
|
|
429
505
|
#endif
|
|
430
506
|
}
|
|
431
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
|
+
|
|
432
581
|
fn finiteScalar(value : f32, fallback : f32) -> f32 {
|
|
433
582
|
let bounded = clamp(value, -65504.0, 65504.0);
|
|
434
583
|
return select(fallback, bounded, value == value);
|
|
@@ -456,8 +605,8 @@ fn blendLinearTransparent(
|
|
|
456
605
|
// linearHdrColorDomain: fs_main writes linear HDR when its target is HDR.
|
|
457
606
|
// toneStageInput: the value remains linear until the fullscreen tone stage.
|
|
458
607
|
|
|
459
|
-
fn
|
|
460
|
-
if (
|
|
608
|
+
fn alphaTestSurface(surface : SurfaceData) {
|
|
609
|
+
if (surface.alphaClipThreshold > 0.0 && surface.opacity <= surface.alphaClipThreshold) {
|
|
461
610
|
discard;
|
|
462
611
|
}
|
|
463
612
|
}
|
|
@@ -472,62 +621,123 @@ struct StandardPbrOutput {
|
|
|
472
621
|
};
|
|
473
622
|
|
|
474
623
|
@fragment
|
|
475
|
-
fn fs_main(in : VsOut) -> StandardPbrOutput {
|
|
476
|
-
let
|
|
477
|
-
let
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
let
|
|
481
|
-
let
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
// encoded by the host as 4 independent f32 selectors in the merged UBO
|
|
486
|
-
// (metallicChannel/roughnessChannel/aoChannel/extraChannel). Cast to u32
|
|
487
|
-
// at the pick_channel call site; values stay in {0,1,2,3}.
|
|
488
|
-
let mrUv = transformedMaterialUv(material.metallicRoughnessTextureCoordinatesTransform, material.metallicRoughnessTextureCoordinatesMetadata, in);
|
|
489
|
-
let mrSample = sampleMaterialTexture(metallicRoughnessTexture, metallicRoughnessSampler, mrUv, material.metallicRoughnessTextureCoordinatesMetadata.zw);
|
|
490
|
-
let metallic = clamp(
|
|
491
|
-
finiteScalar(material.metallic, 0.0) *
|
|
492
|
-
finiteScalar(pick_channel(mrSample, u32(material.metallicChannel)), 1.0),
|
|
493
|
-
0.0,
|
|
494
|
-
1.0,
|
|
495
|
-
);
|
|
496
|
-
let roughnessTex = clamp(
|
|
497
|
-
finiteScalar(pick_channel(mrSample, u32(material.roughnessChannel)), 1.0),
|
|
498
|
-
0.0,
|
|
499
|
-
1.0,
|
|
500
|
-
);
|
|
501
|
-
|
|
502
|
-
// Layer-2 fail-fast: shader internal clamp keeps D_GGX finite for
|
|
503
|
-
// roughness=0 even if the asset somehow bypasses layer-1 register
|
|
504
|
-
// fail-fast (plan-strategy section 5.3 + AC-02 (b)+(c)). Apply to the
|
|
505
|
-
// material scalar prior to the channelMap-extracted texture multiplier
|
|
506
|
-
// so the final roughness >= 0.04 floor is preserved (texture sample is
|
|
507
|
-
// a 1.0-default white when metallicRoughnessTexture is absent, so
|
|
508
|
-
// multiplying after the clamp keeps the floor intact).
|
|
509
|
-
var a = max(finiteScalar(material.roughness, 0.5), 0.04);
|
|
510
|
-
a = a * roughnessTex;
|
|
511
|
-
a = a * a;
|
|
512
|
-
|
|
513
|
-
// TBN basis composed via forgeax_pbr::tbn helpers; default fallback
|
|
514
|
-
// (defaultNormalTextureView) RG=(128,128) -> tangent (0,0,1) -> world n
|
|
515
|
-
// unchanged.
|
|
516
|
-
let normalUv = transformedMaterialUv(material.normalTextureCoordinatesTransform, material.normalTextureCoordinatesMetadata, in);
|
|
517
|
-
let normSampleRg = sampleMaterialTexture(normalTexture, normalSampler, normalUv, material.normalTextureCoordinatesMetadata.zw).rg;
|
|
518
|
-
let normTangent = scaleTangentSpaceNormal(
|
|
519
|
-
decodeTangentSpaceNormalRg(normSampleRg), material.normalScale,
|
|
520
|
-
);
|
|
521
|
-
let n = applyTBN(in.worldNormal, in.worldTangent, normTangent);
|
|
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);
|
|
522
634
|
|
|
523
635
|
let v = normalize(view.cameraPos - in.worldPos);
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
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,
|
|
527
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
|
|
528
664
|
let safeIor = max(finiteScalar(material.ior, 1.5), 1.0);
|
|
529
665
|
let dielectricF0 = pow((safeIor - 1.0) / (safeIor + 1.0), 2.0);
|
|
530
|
-
|
|
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
|
|
531
741
|
var diffuseAlbedo = albedo;
|
|
532
742
|
#ifdef TRANSMISSION_AVAILABLE
|
|
533
743
|
let transmissionUv = transformedMaterialUv(
|
|
@@ -576,16 +786,12 @@ fn fs_main(in : VsOut) -> StandardPbrOutput {
|
|
|
576
786
|
);
|
|
577
787
|
diffuseAlbedo = albedo * (1.0 - transmittedEnergy);
|
|
578
788
|
#endif
|
|
579
|
-
let coatRoughness = max(material.clearcoatRoughness, 0.04);
|
|
580
|
-
let coatAlpha = coatRoughness * coatRoughness;
|
|
581
|
-
let coatF = f_schlick(max(dot(n, v), 0.0), vec3<f32>(0.04)) * material.clearcoat;
|
|
582
|
-
|
|
583
789
|
// Ambient (IBL) + 1 + N + N accumulation
|
|
584
790
|
// (feat-20260520-skylight-ibl-cubemap M3 / t48 +
|
|
585
791
|
// feat-20260520-directional-light-shadow-mapping +
|
|
586
792
|
// feat-20260518-pbr-direct-lighting-mvp Finding 4):
|
|
587
793
|
//
|
|
588
|
-
// color = ambient(IBL) + directional(shadowed) +
|
|
794
|
+
// color = ambient(IBL) + directional(shadowed) + sum(point) + sum(spot)
|
|
589
795
|
//
|
|
590
796
|
// When the host Skylight bind group provides default-zero resources the IBL
|
|
591
797
|
// helpers sample to vec3(0) and ambient = 0, so the shader falls through to
|
|
@@ -601,43 +807,53 @@ fn fs_main(in : VsOut) -> StandardPbrOutput {
|
|
|
601
807
|
// split-sum mip lookup, so we re-derive the post-shader-clamp roughness
|
|
602
808
|
// here (matches sampleIblSpecular's `mip = roughness * 4.0` expectation).
|
|
603
809
|
//
|
|
604
|
-
//
|
|
605
|
-
//
|
|
606
|
-
//
|
|
607
|
-
|
|
608
|
-
|
|
810
|
+
// Direct lights (1 + N + N): one directional + N point + N spot, summed
|
|
811
|
+
// sequentially. LIGHT_ARRAY_MAX_SLOTS = 4 host-side bounds the loop trip
|
|
812
|
+
// counts. GGX BRDF helpers (d_ggx / v_smith / f_schlick from
|
|
813
|
+
// forgeax_pbr::brdf) run inside each evalDirectional / evalPoint / evalSpot
|
|
814
|
+
// so all three light types share the same microfacet specular + Lambertian
|
|
815
|
+
// diffuse form. The directional path additionally projects worldPos
|
|
816
|
+
// through the per-cascade light-space matrix in the fragment stage for
|
|
817
|
+
// shadow-map PCF lookup (CSM, feat-20260613).
|
|
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
|
|
609
828
|
var irradiance = vec3<f32>(0.0);
|
|
610
829
|
var specularIbl = vec3<f32>(0.0);
|
|
611
830
|
if (skylight.intensity < 0.0) {
|
|
612
|
-
// A negative intensity is the renderer-owned probe sentinel. The
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
// carry probe center and half extents in this mode; Skylight resources
|
|
616
|
-
// remain unchanged and therefore preserve the ABI.
|
|
831
|
+
// A negative intensity is the renderer-owned probe sentinel. The four
|
|
832
|
+
// color/rotation lanes carry the selected probe center and half extents;
|
|
833
|
+
// ordinary Skylight resources remain unchanged, preserving the ABI.
|
|
617
834
|
specularIbl = sampleReflectionProbeSpecular(
|
|
618
|
-
|
|
835
|
+
physicalNormal, v, iblRoughness, f0, in.worldPos,
|
|
619
836
|
vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB),
|
|
620
837
|
skylight.rotation.xyz, vec4<f32>(0.0, 0.0, 0.0, 1.0),
|
|
621
838
|
prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
|
|
622
839
|
);
|
|
623
840
|
} else {
|
|
624
|
-
irradiance = sampleIblDiffuse(
|
|
841
|
+
irradiance = sampleIblDiffuse(physicalNormal, skylight.rotation, irradianceMap, irradianceSampler);
|
|
625
842
|
specularIbl = sampleIblSpecular(
|
|
626
|
-
|
|
843
|
+
physicalNormal, v, iblRoughness, f0,
|
|
627
844
|
skylight.rotation,
|
|
628
845
|
prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
|
|
629
846
|
);
|
|
630
847
|
}
|
|
631
|
-
let
|
|
632
|
-
let aoSample = sampleMaterialTexture(occlusionTexture, occlusionSampler, occlusionUv, material.occlusionTextureCoordinatesMetadata.zw);
|
|
633
|
-
let ao = mix(1.0, aoSample.r, material.occlusionStrength);
|
|
848
|
+
let ao = surface.occlusion;
|
|
634
849
|
// feat-20260612-hdrp-ssao M7 round-2: `var` (mutable) so the
|
|
635
850
|
// CLUSTER_FORWARD_AVAILABLE branch below can `ambient *=` the SSAO
|
|
636
851
|
// factor. The non-HDRP path leaves ambient untouched.
|
|
637
852
|
let skyColor = vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB);
|
|
638
853
|
let skyFactor = skyColor * skylight.intensity;
|
|
639
854
|
var reflectionFallback = specularIbl * (vec3<f32>(1.0) - coatF);
|
|
640
|
-
var ambient = (kD * irradiance * diffuseAlbedo + specularIbl) *
|
|
855
|
+
var ambient = (kD * irradiance * diffuseAlbedo + specularIbl) *
|
|
856
|
+
(vec3<f32>(1.0) - coatF);
|
|
641
857
|
#ifdef PROBE_BLEND_AVAILABLE
|
|
642
858
|
// group(3) is the per-object bind group; record zero is the object selected
|
|
643
859
|
// by this draw, independent of instance count.
|
|
@@ -648,44 +864,24 @@ fn fs_main(in : VsOut) -> StandardPbrOutput {
|
|
|
648
864
|
);
|
|
649
865
|
let probeLocalBlendFraction = probeBlendRecords[0].z;
|
|
650
866
|
let probeDiffuseK = kD / max(vec3<f32>(1.0 - metallic), vec3<f32>(0.0001));
|
|
651
|
-
let probeDiffuse =
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
clearcoatIbl = sampleReflectionProbeSpecular(
|
|
662
|
-
n, v, coatRoughness, vec3<f32>(0.04), in.worldPos,
|
|
663
|
-
vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB),
|
|
664
|
-
skylight.rotation.xyz, vec4<f32>(0.0, 0.0, 0.0, 1.0),
|
|
665
|
-
prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
|
|
666
|
-
);
|
|
667
|
-
} else {
|
|
668
|
-
clearcoatIbl = sampleIblSpecular(
|
|
669
|
-
n, v, coatRoughness, vec3<f32>(0.04),
|
|
670
|
-
skylight.rotation,
|
|
671
|
-
prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
|
-
let clearcoatContribution = clearcoatIbl * material.clearcoat;
|
|
675
|
-
#ifdef PROBE_BLEND_AVAILABLE
|
|
676
|
-
if (skylight.intensity < 0.0) {
|
|
677
|
-
ambient = ambient + clearcoatContribution;
|
|
678
|
-
} else {
|
|
679
|
-
ambient = ambient + clearcoatIbl * material.clearcoat * skyFactor;
|
|
680
|
-
}
|
|
681
|
-
#else
|
|
682
|
-
ambient = ambient + clearcoatContribution;
|
|
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);
|
|
683
877
|
#endif
|
|
684
|
-
reflectionFallback = reflectionFallback + clearcoatContribution;
|
|
685
|
-
}
|
|
686
878
|
#ifdef PROBE_BLEND_AVAILABLE
|
|
687
879
|
ambient = ambient * ao;
|
|
688
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.
|
|
689
885
|
if (skylight.intensity < 0.0) {
|
|
690
886
|
let environmentScale = max(-skylight.intensity - 1.0, 0.0) * ao;
|
|
691
887
|
ambient = ambient * environmentScale;
|
|
@@ -711,19 +907,16 @@ fn fs_main(in : VsOut) -> StandardPbrOutput {
|
|
|
711
907
|
let ssaoFactor = textureSample(ssaoBlurredTexture, ssaoBlurredSampler, ssaoUv).r;
|
|
712
908
|
// scope-amend-webgl2-ubo: intensity packed into cluster_uniform.near_far_log.w.
|
|
713
909
|
let ssaoIntensity = get_ssao_intensity();
|
|
714
|
-
let ssaoScale = mix(1.0, ssaoFactor * ao, ssaoIntensity);
|
|
715
910
|
ambient *= mix(1.0, ssaoFactor * ao, ssaoIntensity);
|
|
911
|
+
let ssaoScale = mix(1.0, ssaoFactor * ao, ssaoIntensity);
|
|
716
912
|
reflectionFallback *= ssaoScale;
|
|
717
913
|
#endif
|
|
718
914
|
var color = ambient;
|
|
719
915
|
#ifdef TRANSMISSION_AVAILABLE
|
|
720
916
|
let screenUv = in.ndc.xy * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
|
|
721
|
-
// Read the
|
|
722
|
-
//
|
|
723
|
-
//
|
|
724
|
-
// the fragment stage preserves that contract and keeps thickness in the
|
|
725
|
-
// glTF unit-scale mesh space before measuring the refracted segment in world
|
|
726
|
-
// 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.
|
|
727
920
|
let localToWorld0 = in.transmissionBasis0.xyz;
|
|
728
921
|
let localToWorld1 = vec3<f32>(
|
|
729
922
|
in.transmissionBasis0.w,
|
|
@@ -806,30 +999,158 @@ fn fs_main(in : VsOut) -> StandardPbrOutput {
|
|
|
806
999
|
color = color + finiteColor(transmittedBackdrop, vec3<f32>(0.0)) *
|
|
807
1000
|
transmittedEnergy * beerAttenuation;
|
|
808
1001
|
#endif
|
|
809
|
-
let directionalShadow = evalDirectionalShadowFactor(
|
|
810
|
-
|
|
1002
|
+
let directionalShadow = evalDirectionalShadowFactor(
|
|
1003
|
+
physicalNormal,
|
|
1004
|
+
in.worldPos,
|
|
1005
|
+
standardViewZ(in),
|
|
1006
|
+
);
|
|
1007
|
+
let directionalBase = evalDirectionalNoShadow(physicalNormal, v, diffuseAlbedo, metallic, a, f0);
|
|
811
1008
|
color = color + directionalShadow * directionalBase;
|
|
812
|
-
if (material.clearcoat != 0.0) {
|
|
813
|
-
let directionalClearcoat = evalDirectionalNoShadow(
|
|
814
|
-
n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
|
|
815
|
-
);
|
|
816
|
-
color = color + directionalShadow * material.clearcoat * directionalClearcoat;
|
|
817
|
-
}
|
|
818
1009
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
819
1010
|
// NDC from vertex shader (perspective-divided clip-space, interpolated).
|
|
820
1011
|
// view_z: NDC depth for cluster Z-slice lookup.
|
|
821
1012
|
color = color + evaluateStandardClusterLights(
|
|
822
|
-
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,
|
|
823
1028
|
);
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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,
|
|
1083
|
+
);
|
|
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,
|
|
827
1129
|
);
|
|
828
1130
|
}
|
|
829
|
-
|
|
830
|
-
let
|
|
831
|
-
|
|
832
|
-
|
|
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
|
|
833
1154
|
var output : StandardPbrOutput;
|
|
834
1155
|
output.color = vec4<f32>(color, alpha);
|
|
835
1156
|
#ifdef REFLECTION_FALLBACK_AVAILABLE
|
|
@@ -865,49 +1186,18 @@ struct GBufferOutput {
|
|
|
865
1186
|
/// pipeline's g-buffer render pass binds this entry point via the shader's
|
|
866
1187
|
/// multi-entry support (passKind='deferred' selects `fs_gbuffer`).
|
|
867
1188
|
@fragment
|
|
868
|
-
fn fs_gbuffer(in : VsOut) -> GBufferOutput {
|
|
869
|
-
let
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
);
|
|
874
|
-
let
|
|
875
|
-
let vertexColor = materialVertexColor(in);
|
|
876
|
-
alphaTest(material.baseColor.a * baseSample.a * vertexColor.a);
|
|
877
|
-
let alpha = materialAlpha(baseSample) * vertexColor.a;
|
|
878
|
-
let albedo = material.baseColor.rgb * baseSample.rgb * vertexColor.rgb;
|
|
879
|
-
|
|
880
|
-
let mrUv = transformedMaterialUv(
|
|
881
|
-
material.metallicRoughnessTextureCoordinatesTransform,
|
|
882
|
-
material.metallicRoughnessTextureCoordinatesMetadata,
|
|
883
|
-
in,
|
|
884
|
-
);
|
|
885
|
-
let mrSample = sampleMaterialTexture(metallicRoughnessTexture, metallicRoughnessSampler, mrUv, material.metallicRoughnessTextureCoordinatesMetadata.zw);
|
|
886
|
-
let metallic = material.metallic * pick_channel(mrSample, u32(material.metallicChannel));
|
|
887
|
-
let roughnessTex = pick_channel(mrSample, u32(material.roughnessChannel));
|
|
888
|
-
|
|
889
|
-
var a = max(material.roughness, 0.04);
|
|
890
|
-
a = a * roughnessTex;
|
|
891
|
-
|
|
892
|
-
let normalUv = transformedMaterialUv(material.normalTextureCoordinatesTransform, material.normalTextureCoordinatesMetadata, in);
|
|
893
|
-
let normSampleRg = sampleMaterialTexture(normalTexture, normalSampler, normalUv, material.normalTextureCoordinatesMetadata.zw).rg;
|
|
894
|
-
let normTangent = scaleTangentSpaceNormal(
|
|
895
|
-
decodeTangentSpaceNormalRg(normSampleRg), material.normalScale,
|
|
896
|
-
);
|
|
897
|
-
let n = applyTBN(in.worldNormal, in.worldTangent, normTangent);
|
|
898
|
-
|
|
899
|
-
let emissiveUv = transformedMaterialUv(material.emissiveTextureCoordinatesTransform, material.emissiveTextureCoordinatesMetadata, in);
|
|
900
|
-
let emissiveSample = sampleMaterialTexture(emissiveTexture, emissiveSampler, emissiveUv, material.emissiveTextureCoordinatesMetadata.zw).rgb;
|
|
901
|
-
let emissive = material.emissive * material.emissiveIntensity * emissiveSample;
|
|
902
|
-
|
|
903
|
-
let occlusionUv = transformedMaterialUv(material.occlusionTextureCoordinatesTransform, material.occlusionTextureCoordinatesMetadata, in);
|
|
904
|
-
let aoSample = sampleMaterialTexture(occlusionTexture, occlusionSampler, occlusionUv, material.occlusionTextureCoordinatesMetadata.zw);
|
|
905
|
-
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);
|
|
906
1196
|
|
|
907
1197
|
var out : GBufferOutput;
|
|
908
|
-
out.normal_roughness = vec4<f32>(n * 0.5 + 0.5,
|
|
1198
|
+
out.normal_roughness = vec4<f32>(n * 0.5 + 0.5, roughness);
|
|
909
1199
|
out.albedo_metallic = vec4<f32>(albedo, metallic);
|
|
910
|
-
out.emissive_ao = vec4<f32>(emissive,
|
|
1200
|
+
out.emissive_ao = vec4<f32>(surface.emissive, surface.occlusion);
|
|
911
1201
|
return out;
|
|
912
1202
|
}
|
|
913
1203
|
|
|
@@ -923,7 +1213,6 @@ struct TemporalVsOut {
|
|
|
923
1213
|
@location(7) uv7 : vec2<f32>,
|
|
924
1214
|
@location(8) @interpolate(perspective) currentClip : vec4<f32>,
|
|
925
1215
|
@location(9) @interpolate(perspective) previousClip : vec4<f32>,
|
|
926
|
-
@location(10) @interpolate(flat) reactive : f32,
|
|
927
1216
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
928
1217
|
@location(14) color : vec4<f32>,
|
|
929
1218
|
#endif
|
|
@@ -934,7 +1223,7 @@ fn vs_temporal(in : VsIn, @builtin(instance_index) idx : u32) -> TemporalVsOut {
|
|
|
934
1223
|
let currentWorld = meshes[0].worldFromLocal *
|
|
935
1224
|
instances[idx].localFromInstance * vec4<f32>(in.pos, 1.0);
|
|
936
1225
|
var previousWorld = currentWorld;
|
|
937
|
-
#
|
|
1226
|
+
#if STORAGE_BUFFER_AVAILABLE == true
|
|
938
1227
|
previousWorld = meshes[0].previousWorldFromLocal *
|
|
939
1228
|
instances[idx].previousLocalFromInstance * vec4<f32>(in.pos, 1.0);
|
|
940
1229
|
#endif
|
|
@@ -942,11 +1231,6 @@ fn vs_temporal(in : VsIn, @builtin(instance_index) idx : u32) -> TemporalVsOut {
|
|
|
942
1231
|
out.currentClip = view.temporalCurrentViewProj * currentWorld;
|
|
943
1232
|
out.clip = out.currentClip;
|
|
944
1233
|
out.previousClip = view.temporalPreviousViewProj * previousWorld;
|
|
945
|
-
#if STORAGE_BUFFER_AVAILABLE == true
|
|
946
|
-
out.reactive = meshes[0].temporal.x;
|
|
947
|
-
#else
|
|
948
|
-
out.reactive = 1.0;
|
|
949
|
-
#endif
|
|
950
1234
|
out.uv = in.uv;
|
|
951
1235
|
out.uv1 = in.uv1;
|
|
952
1236
|
out.uv2 = in.uv2;
|
|
@@ -972,7 +1256,7 @@ fn temporalVertexAlpha(in : TemporalVsOut) -> f32 {
|
|
|
972
1256
|
@fragment
|
|
973
1257
|
fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
974
1258
|
var reactive = 0.0;
|
|
975
|
-
#
|
|
1259
|
+
#if STORAGE_BUFFER_AVAILABLE == true
|
|
976
1260
|
reactive = meshes[0].temporal.x;
|
|
977
1261
|
#endif
|
|
978
1262
|
return projectPbrSceneTemporal(
|
|
@@ -985,7 +1269,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
985
1269
|
in.currentClip,
|
|
986
1270
|
in.previousClip,
|
|
987
1271
|
view.temporalProjection,
|
|
988
|
-
|
|
1272
|
+
reactive,
|
|
989
1273
|
in.uv, in.uv1, in.uv2, in.uv3,
|
|
990
1274
|
in.uv4, in.uv5, in.uv6, in.uv7,
|
|
991
1275
|
);
|