@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.
Files changed (83) hide show
  1. package/README.md +14 -5
  2. package/dist/ShaderRegistry.d.ts.map +1 -1
  3. package/dist/index.d.ts +4 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.mjs +33042 -55
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/ltc/provenance.d.ts +17 -0
  8. package/dist/ltc/provenance.d.ts.map +1 -0
  9. package/dist/ltc/tables.d.ts +22 -0
  10. package/dist/ltc/tables.d.ts.map +1 -0
  11. package/dist/material/artifact-types.d.ts +52 -0
  12. package/dist/material/artifact-types.d.ts.map +1 -1
  13. package/dist/material-schemas.d.ts +36 -0
  14. package/dist/material-schemas.d.ts.map +1 -1
  15. package/dist/register-default-sprite-lit.d.ts +3 -6
  16. package/dist/register-default-sprite-lit.d.ts.map +1 -1
  17. package/dist/types.d.ts +2 -1
  18. package/dist/types.d.ts.map +1 -1
  19. package/package.json +4 -4
  20. package/src/ShaderRegistry.ts +0 -17
  21. package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +9 -3
  22. package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +67 -122
  23. package/src/__tests__/deferred-lighting-ssao.test.ts +3 -3
  24. package/src/__tests__/direct-light-layout.unit.test.ts +40 -0
  25. package/src/__tests__/ibl-irradiance.unit.test.ts +16 -4
  26. package/src/__tests__/ibl-sampling.unit.test.ts +3 -4
  27. package/src/__tests__/lighting-directional.unit.test.ts +36 -0
  28. package/src/__tests__/lighting-point-spot-volume.unit.test.ts +8 -2
  29. package/src/__tests__/lighting-punctual.unit.test.ts +40 -5
  30. package/src/__tests__/lighting-spot-volume-attenuation.unit.test.ts +1 -1
  31. package/src/__tests__/ltc-provenance.unit.test.ts +33 -0
  32. package/src/__tests__/manifest.fixture.json +33 -0
  33. package/src/__tests__/material-builtins.unit.test.ts +21 -3
  34. package/src/__tests__/material-contract.unit.test.ts +188 -14
  35. package/src/__tests__/physical-clearcoat.unit.test.ts +65 -0
  36. package/src/__tests__/probe-lighting-composition.unit.test.ts +39 -0
  37. package/src/__tests__/rect-area-brdf.unit.test.ts +36 -0
  38. package/src/__tests__/reflection-probe-sampling.unit.test.ts +24 -2
  39. package/src/__tests__/shader-manifest-transmission.unit.test.ts +8 -10
  40. package/src/__tests__/shader.unit.test.ts +54 -85
  41. package/src/__tests__/spot-cookie-composition.unit.test.ts +58 -0
  42. package/src/__tests__/spot-modifier-composition.unit.test.ts +19 -0
  43. package/src/__tests__/sprite-variants.unit.test.ts +4 -1
  44. package/src/__tests__/standard-webgl2-varying-budget.unit.test.ts +17 -0
  45. package/src/__tests__/surface-v1-negative.unit.test.ts +37 -0
  46. package/src/__tests__/surface-v1.unit.test.ts +83 -0
  47. package/src/__tests__/transmission-thickness-scale.unit.test.ts +2 -6
  48. package/src/__tests__/vertex-color-variant.unit.test.ts +16 -5
  49. package/src/brdf.wgsl +115 -1
  50. package/src/common.wgsl +53 -79
  51. package/src/default-standard-pbr-skin.wgsl +687 -144
  52. package/src/default-standard-pbr.wgsl +600 -247
  53. package/src/default_standard_surface.wgsl +88 -0
  54. package/src/hdrp-cluster-forward.wgsl +225 -0
  55. package/src/ibl-irradiance.wgsl +47 -9
  56. package/src/ibl-prefilter.wgsl +13 -3
  57. package/src/ibl-sampling.wgsl +2 -2
  58. package/src/ibl-shared.wgsl +9 -2
  59. package/src/index.ts +23 -0
  60. package/src/lighting-directional.wgsl +9 -117
  61. package/src/lighting-probe.wgsl +51 -0
  62. package/src/lighting-punctual.wgsl +21 -11
  63. package/src/lighting-rect-area.wgsl +162 -0
  64. package/src/lighting-spot-modifiers.wgsl +100 -0
  65. package/src/lighting-spot-projector.wgsl +31 -0
  66. package/src/ltc/provenance.ts +19 -0
  67. package/src/ltc/tables.ts +2831 -0
  68. package/src/material/artifact-types.ts +137 -0
  69. package/src/material/physical/anisotropy.wgsl +26 -0
  70. package/src/material/physical/clearcoat.wgsl +24 -0
  71. package/src/material/physical/iridescence.wgsl +23 -0
  72. package/src/material/physical/sheen.wgsl +17 -0
  73. package/src/material-schemas.ts +77 -30
  74. package/src/register-default-sprite-lit.ts +3 -6
  75. package/src/register-default-standard-pbr-skin.ts +2 -2
  76. package/src/sprite-lit.wgsl +3 -3
  77. package/src/sprite.wgsl +1 -1
  78. package/src/standard-cluster.wgsl +80 -78
  79. package/src/surface_v1.wgsl +26 -0
  80. package/src/types.ts +2 -1
  81. package/src/volume/volume-inject.wgsl +34 -36
  82. package/src/volume/volume-integrate.wgsl +48 -38
  83. package/dist/.tsbuildinfo +0 -1
@@ -1,20 +1,56 @@
1
- #import forgeax_view::common::{View, Mesh, InstanceData, view, meshes, instances, sampleMaterialTexture}
1
+ #pragma material_slot surface
2
+ #import forgeax_material::slot::surface::{evaluate_surface}
3
+ #import forgeax_material::surface_v1::{SurfaceInput, SurfaceData}
4
+ #import forgeax_view::common::{View, Mesh, InstanceData, view, meshes, instances, shadowMap, shadowSampler, sampleMaterialTexture}
2
5
  #import forgeax_scene_temporal::{sceneViewZ}
6
+ #ifdef EXTENDED_LIGHTING_AVAILABLE
7
+ #import forgeax_view::common::{spotModifierSampler, iesProfileTexture, cookieTexture, cookieMatrices}
8
+ #endif
3
9
  #import forgeax_pbr::temporal::{projectPbrSceneTemporal}
4
10
  #import forgeax_pbr::brdf::{f_schlick, v_smith, d_ggx}
5
- #import forgeax_pbr::ibl_sampling::{sampleIblDiffuse, sampleIblSpecular}
11
+ #import forgeax_pbr::ibl_sampling::{sampleIblDiffuse, sampleIblSpecular, sampleReflectionProbeSpecular}
12
+ #import forgeax_pbr::lighting_probe::{evaluateProbeDiffuse}
6
13
  #import forgeax_pbr::tbn::{decodeTangentSpaceNormalRg, scaleTangentSpaceNormal, applyTBN}
14
+ #ifdef CLEARCOAT_AVAILABLE
15
+ #import forgeax_pbr::clearcoat::{evaluateClearcoatLayer}
16
+ #endif
17
+ #ifdef ANISOTROPY_AVAILABLE
18
+ #import forgeax_pbr::anisotropy::{evaluateAnisotropicNormal}
19
+ #endif
20
+ #ifdef SHEEN_AVAILABLE
21
+ #import forgeax_pbr::sheen::{evaluateSheenLayer}
22
+ #endif
23
+ #ifdef IRIDESCENCE_AVAILABLE
24
+ #import forgeax_pbr::iridescence::{evaluateIridescenceFresnel}
25
+ #endif
7
26
  #import forgeax_pbr::lighting_directional::{evalDirectionalNoShadow, evalDirectionalShadowFactor}
8
27
  #ifdef CLUSTER_FORWARD_AVAILABLE
9
- #import forgeax_standard::cluster::{evaluateStandardClusterLights}
28
+ #import forgeax_standard::cluster::{evaluateStandardClusterLights, get_ssao_intensity}
10
29
  #endif
11
30
 
12
31
  #define_import_path forgeax_material::pbr-skin
13
32
  #pragma variant_axis STORAGE_BUFFER_AVAILABLE
14
33
  #pragma variant_axis CLUSTER_FORWARD_AVAILABLE
15
34
  #pragma variant_axis VERTEX_COLOR_AVAILABLE
35
+ #pragma variant_axis PROBE_BLEND_AVAILABLE
36
+ #pragma variant_axis EXTENDED_LIGHTING_AVAILABLE
37
+ #pragma variant_axis TRANSMISSION_AVAILABLE
16
38
  #pragma variant_axis DIRECTIONAL_PCSS_AVAILABLE
17
39
 
40
+ #ifdef PROBE_BLEND_AVAILABLE
41
+ fn composeProbeDiffuse(
42
+ shPreblend : array<vec4<f32>, 9>,
43
+ localBlendFraction : f32,
44
+ normal : vec3<f32>,
45
+ skyIrradiance : vec3<f32>,
46
+ kD : vec3<f32>,
47
+ albedo : vec3<f32>,
48
+ metallic : f32,
49
+ ) -> vec3<f32> {
50
+ return evaluateProbeDiffuse(shPreblend, localBlendFraction, normal, skyIrradiance, kD, albedo, metallic);
51
+ }
52
+ #endif
53
+
18
54
  // @forgeax/engine-shader - default-standard-pbr-skin.wgsl
19
55
  // (feat-20260523-skin-skeleton-animation M3 / T-29).
20
56
  //
@@ -37,7 +73,6 @@
37
73
  // @group(1) @binding(4) metallicRoughnessTexture texture_2d<f32>
38
74
  // @group(1) @binding(5) normalSampler sampler
39
75
  // @group(1) @binding(6) normalTexture texture_2d<f32>
40
- // @group(1) @binding(7) specularTintSampler + @binding(8) specularTintTexture
41
76
  // @group(1) @binding(9..15) Skylight (irradiance / prefilter / brdfLut +
42
77
  // samplers) + skylight uniform (intensity)
43
78
  // @group(2) @binding(0) meshes storage (worldFromLocal mat4
@@ -67,86 +102,128 @@
67
102
  // 4 joints max; weighted sum of skinned positions from the palette buffer
68
103
  // indexed by the per-vertex skinIndex vector.
69
104
 
70
- struct Material {
71
- baseColor : vec4<f32>,
72
- metallic : f32,
73
- roughness : f32,
74
- // Channel selectors (D-8): 4 independent f32 entries split out of the
75
- // legacy `channelMap : vec4<u32>`. See default-standard-pbr.wgsl for
76
- // the full rationale; the skin shader shares the same merged UBO shape.
77
- metallicChannel : f32,
78
- roughnessChannel : f32,
79
- aoChannel : f32,
80
- extraChannel : f32,
81
- // vec3 align=16 inserts implicit padding so emissive lands at offset 48;
82
- // the authored schema rounds to 112 B (matches default-standard-pbr SSOT).
83
- emissive : vec3<f32>,
84
- emissiveIntensity : f32,
85
- occlusionStrength : f32,
86
- alphaCutoff : f32,
87
- clearcoat : f32,
88
- clearcoatRoughness : f32,
89
- specularTint : vec3<f32>,
90
- normalScale : f32,
91
- transmission : f32,
92
- ior : f32,
93
- thickness : f32,
94
- attenuationColor : vec3<f32>,
95
- attenuationDistance : f32,
96
- baseColorTextureCoordinatesTransform : vec4<f32>,
97
- baseColorTextureCoordinatesMetadata : vec4<f32>,
98
- metallicRoughnessTextureCoordinatesTransform : vec4<f32>,
99
- metallicRoughnessTextureCoordinatesMetadata : vec4<f32>,
100
- normalTextureCoordinatesTransform : vec4<f32>,
101
- normalTextureCoordinatesMetadata : vec4<f32>,
102
- specularTintTextureCoordinatesTransform : vec4<f32>,
103
- specularTintTextureCoordinatesMetadata : vec4<f32>,
104
- emissiveTextureCoordinatesTransform : vec4<f32>,
105
- emissiveTextureCoordinatesMetadata : vec4<f32>,
106
- occlusionTextureCoordinatesTransform : vec4<f32>,
107
- occlusionTextureCoordinatesMetadata : vec4<f32>,
108
- transmissionTextureCoordinatesTransform : vec4<f32>,
109
- transmissionTextureCoordinatesMetadata : vec4<f32>,
110
- thicknessTextureCoordinatesTransform : vec4<f32>,
111
- thicknessTextureCoordinatesMetadata : vec4<f32>,
112
- };
113
-
114
- @group(1) @binding(0) var<uniform> material : Material;
105
+ // The MaterialParameters struct and its binding-0 declaration are generated
106
+ // from the root ParamSchema during material composition. Keeping the ABI out
107
+ // of this template prevents a second handwritten interface from drifting from
108
+ // the runtime UBO writer.
115
109
  @group(1) @binding(1) var baseColorSampler : sampler;
116
110
  @group(1) @binding(2) var baseColorTexture : texture_2d<f32>;
117
111
  @group(1) @binding(3) var metallicRoughnessSampler : sampler;
118
112
  @group(1) @binding(4) var metallicRoughnessTexture : texture_2d<f32>;
119
113
  @group(1) @binding(5) var normalSampler : sampler;
120
114
  @group(1) @binding(6) var normalTexture : texture_2d<f32>;
121
- @group(1) @binding(7) var specularTintSampler : sampler;
122
- @group(1) @binding(8) var specularTintTexture : texture_2d<f32>;
123
115
  @group(1) @binding(9) var emissiveSampler : sampler;
124
116
  @group(1) @binding(10) var emissiveTexture : texture_2d<f32>;
125
117
  @group(1) @binding(11) var occlusionSampler : sampler;
126
118
  @group(1) @binding(12) var occlusionTexture : texture_2d<f32>;
119
+ #ifdef TRANSMISSION_AVAILABLE
127
120
  @group(1) @binding(13) var transmissionSampler : sampler;
128
121
  @group(1) @binding(14) var transmissionTexture : texture_2d<f32>;
129
122
  @group(1) @binding(15) var thicknessSampler : sampler;
130
123
  @group(1) @binding(16) var thicknessTexture : texture_2d<f32>;
124
+ @group(1) @binding(24) var transmissionBackdropSampler : sampler;
125
+ @group(1) @binding(25) var transmissionBackdropTexture : texture_2d<f32>;
126
+ #endif
127
+ #ifdef CLEARCOAT_TEXTURE_AVAILABLE
128
+ @group(1) @binding(26) var clearcoatSampler : sampler;
129
+ @group(1) @binding(27) var clearcoatTexture : texture_2d<f32>;
130
+ #endif
131
+ #ifdef CLEARCOAT_ROUGHNESS_TEXTURE_AVAILABLE
132
+ @group(1) @binding(28) var clearcoatRoughnessSampler : sampler;
133
+ @group(1) @binding(29) var clearcoatRoughnessTexture : texture_2d<f32>;
134
+ #endif
135
+ #ifdef CLEARCOAT_NORMAL_TEXTURE_AVAILABLE
136
+ @group(1) @binding(30) var clearcoatNormalSampler : sampler;
137
+ @group(1) @binding(31) var clearcoatNormalTexture : texture_2d<f32>;
138
+ #endif
139
+ #ifdef ANISOTROPY_TEXTURE_AVAILABLE
140
+ @group(1) @binding(32) var anisotropySampler : sampler;
141
+ @group(1) @binding(33) var anisotropyTexture : texture_2d<f32>;
142
+ #endif
143
+ #ifdef SHEEN_COLOR_TEXTURE_AVAILABLE
144
+ @group(1) @binding(34) var sheenColorSampler : sampler;
145
+ @group(1) @binding(35) var sheenColorTexture : texture_2d<f32>;
146
+ #endif
147
+ #ifdef SHEEN_ROUGHNESS_TEXTURE_AVAILABLE
148
+ @group(1) @binding(36) var sheenRoughnessSampler : sampler;
149
+ @group(1) @binding(37) var sheenRoughnessTexture : texture_2d<f32>;
150
+ #endif
151
+ #ifdef IRIDESCENCE_TEXTURE_AVAILABLE
152
+ @group(1) @binding(38) var iridescenceSampler : sampler;
153
+ @group(1) @binding(39) var iridescenceTexture : texture_2d<f32>;
154
+ #endif
155
+ #ifdef IRIDESCENCE_THICKNESS_TEXTURE_AVAILABLE
156
+ @group(1) @binding(40) var iridescenceThicknessSampler : sampler;
157
+ @group(1) @binding(41) var iridescenceThicknessTexture : texture_2d<f32>;
158
+ #endif
159
+ #ifdef SPECULAR_TEXTURE_AVAILABLE
160
+ @group(1) @binding(42) var specularTextureSampler : sampler;
161
+ @group(1) @binding(43) var specularTexture : texture_2d<f32>;
162
+ #endif
163
+ #ifdef SPECULAR_COLOR_TEXTURE_AVAILABLE
164
+ @group(1) @binding(44) var specularColorTextureSampler : sampler;
165
+ @group(1) @binding(45) var specularColorTexture : texture_2d<f32>;
166
+ #endif
131
167
 
132
168
  // Preserve filtering reflection for resources passed to the shared sampler.
133
169
  fn materialTextureFilteringWitness() {
134
170
  let base = baseColorTexture;
135
171
  let metallicRoughness = metallicRoughnessTexture;
136
172
  let normal = normalTexture;
137
- let specularTint = specularTintTexture;
138
173
  let emissive = emissiveTexture;
139
174
  let occlusion = occlusionTexture;
175
+ #ifdef CLEARCOAT_TEXTURE_AVAILABLE
176
+ let clearcoat = clearcoatTexture;
177
+ #endif
178
+ #ifdef CLEARCOAT_ROUGHNESS_TEXTURE_AVAILABLE
179
+ let clearcoatRoughness = clearcoatRoughnessTexture;
180
+ #endif
181
+ #ifdef CLEARCOAT_NORMAL_TEXTURE_AVAILABLE
182
+ let clearcoatNormal = clearcoatNormalTexture;
183
+ #endif
184
+ #ifdef TRANSMISSION_AVAILABLE
140
185
  let transmission = transmissionTexture;
141
186
  let thickness = thicknessTexture;
187
+ #endif
142
188
  let baseWitness = textureSample(base, baseColorSampler, vec2<f32>(0.0));
143
189
  let metallicRoughnessWitness = textureSample(metallicRoughness, metallicRoughnessSampler, vec2<f32>(0.0));
144
190
  let normalWitness = textureSample(normal, normalSampler, vec2<f32>(0.0));
145
- let specularTintWitness = textureSample(specularTint, specularTintSampler, vec2<f32>(0.0));
146
191
  let emissiveWitness = textureSample(emissive, emissiveSampler, vec2<f32>(0.0));
147
192
  let occlusionWitness = textureSample(occlusion, occlusionSampler, vec2<f32>(0.0));
193
+ #ifdef CLEARCOAT_TEXTURE_AVAILABLE
194
+ let clearcoatWitness = textureSample(clearcoat, clearcoatSampler, vec2<f32>(0.0));
195
+ #endif
196
+ #ifdef CLEARCOAT_ROUGHNESS_TEXTURE_AVAILABLE
197
+ let clearcoatRoughnessWitness = textureSample(clearcoatRoughness, clearcoatRoughnessSampler, vec2<f32>(0.0));
198
+ #endif
199
+ #ifdef CLEARCOAT_NORMAL_TEXTURE_AVAILABLE
200
+ let clearcoatNormalWitness = textureSample(clearcoatNormal, clearcoatNormalSampler, vec2<f32>(0.0));
201
+ #endif
202
+ #ifdef ANISOTROPY_TEXTURE_AVAILABLE
203
+ let anisotropyWitness = textureSample(anisotropyTexture, anisotropySampler, vec2<f32>(0.0));
204
+ #endif
205
+ #ifdef SHEEN_COLOR_TEXTURE_AVAILABLE
206
+ let sheenColorWitness = textureSample(sheenColorTexture, sheenColorSampler, vec2<f32>(0.0));
207
+ #endif
208
+ #ifdef SHEEN_ROUGHNESS_TEXTURE_AVAILABLE
209
+ let sheenRoughnessWitness = textureSample(sheenRoughnessTexture, sheenRoughnessSampler, vec2<f32>(0.0));
210
+ #endif
211
+ #ifdef IRIDESCENCE_TEXTURE_AVAILABLE
212
+ let iridescenceWitness = textureSample(iridescenceTexture, iridescenceSampler, vec2<f32>(0.0));
213
+ #endif
214
+ #ifdef IRIDESCENCE_THICKNESS_TEXTURE_AVAILABLE
215
+ let iridescenceThicknessWitness = textureSample(iridescenceThicknessTexture, iridescenceThicknessSampler, vec2<f32>(0.0));
216
+ #endif
217
+ #ifdef SPECULAR_TEXTURE_AVAILABLE
218
+ let specularWeightWitness = textureSample(specularTexture, specularTextureSampler, vec2<f32>(0.0));
219
+ #endif
220
+ #ifdef SPECULAR_COLOR_TEXTURE_AVAILABLE
221
+ let specularColorWitness = textureSample(specularColorTexture, specularColorTextureSampler, vec2<f32>(0.0));
222
+ #endif
223
+ #ifdef TRANSMISSION_AVAILABLE
148
224
  let transmissionWitness = textureSample(transmission, transmissionSampler, vec2<f32>(0.0));
149
225
  let thicknessWitness = textureSample(thickness, thicknessSampler, vec2<f32>(0.0));
226
+ #endif
150
227
  }
151
228
 
152
229
  struct SkylightUniforms {
@@ -171,6 +248,10 @@ struct SkylightUniforms {
171
248
  @group(1) @binding(22) var brdfLutSampler : sampler;
172
249
  @group(1) @binding(23) var<uniform> skylight : SkylightUniforms;
173
250
 
251
+ #ifdef PROBE_BLEND_AVAILABLE
252
+ @group(3) @binding(1) var<storage, read> probeBlendRecords : array<vec4<f32>>;
253
+ #endif
254
+
174
255
  #if STORAGE_BUFFER_AVAILABLE == true
175
256
  @group(2) @binding(1) var<storage, read> palette : array<mat4x4<f32>>;
176
257
  @group(2) @binding(2) var<storage, read> previousPalette : array<mat4x4<f32>>;
@@ -201,6 +282,13 @@ struct VsIn {
201
282
  };
202
283
  struct VsOut {
203
284
  @builtin(position) clip : vec4<f32>,
285
+ #ifdef TRANSMISSION_AVAILABLE
286
+ @location(4) @interpolate(flat) transmissionBasis0 : vec4<f32>,
287
+ #else
288
+ // WebGL2 exposes only inter-stage locations 0..14. Pack the object-space
289
+ // Surface position with its cluster/CSM depth in one varying.
290
+ @location(7) positionOSAndViewZ : vec4<f32>,
291
+ #endif
204
292
  @location(0) worldPos : vec3<f32>,
205
293
  @location(1) worldNormal : vec3<f32>,
206
294
  @location(2) uv : vec2<f32>,
@@ -212,28 +300,20 @@ struct VsOut {
212
300
  @location(9) uv3 : vec2<f32>,
213
301
  @location(10) uv4 : vec2<f32>,
214
302
  @location(11) uv5 : vec2<f32>,
215
- @location(12) uv6 : vec2<f32>,
216
- @location(13) uv7 : vec2<f32>,
303
+ // UV6/UV7 share one vec4 slot so the transmission basis tail can stay
304
+ // inside WebGL2's 14 user varying locations.
305
+ @location(12) uv6And7 : vec4<f32>,
217
306
  #ifdef VERTEX_COLOR_AVAILABLE
218
307
  @location(14) color : vec4<f32>,
219
308
  #endif
220
- // Vertex-produced Standard cluster coordinates. Fragment builtin position
221
- // is framebuffer-space and cannot recover the original clip-space NDC.
222
- @location(6) ndc : vec3<f32>,
309
+ @location(6) ndc : vec4<f32>,
310
+ #ifdef TRANSMISSION_AVAILABLE
223
311
  @location(7) viewZ : f32,
312
+ @location(13) @interpolate(flat) transmissionBasis1 : vec4<f32>,
313
+ #endif
224
314
  };
225
315
 
226
- fn pick_channel(rgba : vec4<f32>, channelIndex : u32) -> f32 {
227
- switch (channelIndex) {
228
- case 0u: { return rgba.r; }
229
- case 1u: { return rgba.g; }
230
- case 2u: { return rgba.b; }
231
- default: { return rgba.a; }
232
- }
233
- }
234
-
235
- @vertex
236
- fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
316
+ fn vs_main_impl(in : VsIn, meshIndex : u32, instanceIndex : u32) -> VsOut {
237
317
  // 4-bone weighted skinning (plan-strategy D-3 / D-3a).
238
318
  // The host pre-computes each joint matrix as worldFromJoint * inverseBindMatrix
239
319
  // (CPU-side pre-multiplication, per D-4) and writes them into the palette
@@ -282,10 +362,13 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
282
362
  // globals. The host-side BGL shape must remain compatible with non-skin
283
363
  // PBR pipeline layout (buildPbrSkinLayouts declares 2-entry mesh-array
284
364
  // slot + separate instances slot). Without these keep-alive references,
285
- // createRenderPipeline would fail on binding-count mismatch.
286
- _ = meshes[0].worldFromLocal;
287
- _ = instances[0].localFromInstance;
365
+ // createRenderPipeline would fail at binding-count validation.
366
+ _ = meshes[meshIndex].worldFromLocal;
367
+ _ = instances[instanceIndex].localFromInstance;
288
368
  out.clip = view.worldViewProj * skinnedLocal;
369
+ #ifndef TRANSMISSION_AVAILABLE
370
+ out.positionOSAndViewZ = vec4<f32>(in.pos, sceneViewZ(out.clip, view.temporalProjection));
371
+ #endif
289
372
  out.worldPos = skinnedLocal.xyz;
290
373
  out.worldNormal = normalize(skinNormal3x3 * in.normal);
291
374
  let worldTangentXyz = normalize(skinNormal3x3 * in.tangent.xyz);
@@ -296,22 +379,48 @@ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
296
379
  out.uv3 = in.uv3;
297
380
  out.uv4 = in.uv4;
298
381
  out.uv5 = in.uv5;
299
- out.uv6 = in.uv6;
300
- out.uv7 = in.uv7;
382
+ out.uv6And7 = vec4<f32>(in.uv6, in.uv7);
301
383
  #ifdef VERTEX_COLOR_AVAILABLE
302
384
  out.color = in.color;
303
385
  #endif
386
+ #ifdef TRANSMISSION_AVAILABLE
387
+ // Skin palette already contains the world-space transform. Forward its
388
+ // affine basis so transmission does not reread Mesh/Instance storage in
389
+ // the fragment stage.
390
+ out.transmissionBasis0 = vec4<f32>(skinMatrix[0].xyz, skinMatrix[1].x);
391
+ out.transmissionBasis1 = vec4<f32>(skinMatrix[1].y, skinMatrix[1].z, skinMatrix[2].x, skinMatrix[2].y);
392
+ #endif
393
+ let clipPos = out.clip;
394
+ out.ndc = vec4(clipPos.xy / clipPos.w, clipPos.z / clipPos.w, skinMatrix[2].z);
304
395
  // feat-20260613-csm-cascaded-shadow-maps M5 / w19: viewZ replaces the
305
396
  // prior light-space-position varying; evalDirectional picks the cascade
306
397
  // matrix per fragment from viewZ + worldPos.
307
- let clipPos = out.clip;
308
- out.ndc = vec3(clipPos.xy / clipPos.w, clipPos.z / clipPos.w);
309
398
  // Keep the cluster depth exactly aligned with the CPU binner for both
310
399
  // perspective and off-axis orthographic projections.
311
- out.viewZ = sceneViewZ(out.clip, view.temporalProjection);
400
+ #ifdef TRANSMISSION_AVAILABLE
401
+ out.viewZ = sceneViewZ(clipPos, view.temporalProjection);
402
+ #endif
312
403
  return out;
313
404
  }
314
405
 
406
+ fn standardViewZ(in : VsOut) -> f32 {
407
+ #ifdef TRANSMISSION_AVAILABLE
408
+ return in.viewZ;
409
+ #else
410
+ return in.positionOSAndViewZ.w;
411
+ #endif
412
+ }
413
+
414
+ @vertex
415
+ fn vs_main(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
416
+ return vs_main_impl(in, 0u, idx);
417
+ }
418
+
419
+ @vertex
420
+ fn vs_scene_index(in : VsIn, @builtin(instance_index) idx : u32) -> VsOut {
421
+ return vs_main_impl(in, idx, 0u);
422
+ }
423
+
315
424
  fn transformedMaterialUv(transform : vec4<f32>, metadata : vec4<f32>, in : VsOut) -> vec2<f32> {
316
425
  var source = in.uv;
317
426
  if (metadata.x >= 1.0) { source = in.uv1; }
@@ -319,8 +428,8 @@ fn transformedMaterialUv(transform : vec4<f32>, metadata : vec4<f32>, in : VsOut
319
428
  if (metadata.x >= 3.0) { source = in.uv3; }
320
429
  if (metadata.x >= 4.0) { source = in.uv4; }
321
430
  if (metadata.x >= 5.0) { source = in.uv5; }
322
- if (metadata.x >= 6.0) { source = in.uv6; }
323
- if (metadata.x >= 7.0) { source = in.uv7; }
431
+ if (metadata.x >= 6.0) { source = in.uv6And7.xy; }
432
+ if (metadata.x >= 7.0) { source = in.uv6And7.zw; }
324
433
  let scaled = source * transform.zw;
325
434
  let angle = metadata.y;
326
435
  let c = cos(angle);
@@ -336,83 +445,518 @@ fn materialVertexColor(in : VsOut) -> vec4<f32> {
336
445
  #endif
337
446
  }
338
447
 
339
- @fragment
340
- fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
341
- let baseUv = transformedMaterialUv(material.baseColorTextureCoordinatesTransform, material.baseColorTextureCoordinatesMetadata, in);
342
- let baseSample = sampleMaterialTexture(baseColorTexture, baseColorSampler, baseUv, material.baseColorTextureCoordinatesMetadata.zw);
343
- let vertexColor = materialVertexColor(in);
344
- let alpha = material.baseColor.a * baseSample.a * vertexColor.a;
345
- if (material.alphaCutoff > 0.0 && alpha <= material.alphaCutoff) {
346
- discard;
347
- }
348
- let albedo = material.baseColor.rgb * baseSample.rgb * vertexColor.rgb;
448
+ // Keep the cooked skinned artifact content-addressable per declared
449
+ // capability set, including axes whose imported helper/resource surface is
450
+ // otherwise identical after lowering.
451
+ fn standardSkinVariantIdentity() -> f32 {
452
+ var identity = 0.0;
453
+ #ifdef STORAGE_BUFFER_AVAILABLE
454
+ identity = identity + 1.0;
455
+ #endif
456
+ #ifdef CLUSTER_FORWARD_AVAILABLE
457
+ identity = identity + 2.0;
458
+ #endif
459
+ #ifdef VERTEX_COLOR_AVAILABLE
460
+ identity = identity + 4.0;
461
+ #endif
462
+ #ifdef PROBE_BLEND_AVAILABLE
463
+ identity = identity + 8.0;
464
+ #endif
465
+ #ifdef EXTENDED_LIGHTING_AVAILABLE
466
+ identity = identity + 16.0;
467
+ #endif
468
+ #ifdef TRANSMISSION_AVAILABLE
469
+ identity = identity + 32.0;
470
+ #endif
471
+ #ifdef DIRECTIONAL_PCSS_AVAILABLE
472
+ identity = identity + 64.0;
473
+ #endif
474
+ return identity;
475
+ }
349
476
 
350
- let mrUv = transformedMaterialUv(material.metallicRoughnessTextureCoordinatesTransform, material.metallicRoughnessTextureCoordinatesMetadata, in);
351
- let mrSample = sampleMaterialTexture(metallicRoughnessTexture, metallicRoughnessSampler, mrUv, material.metallicRoughnessTextureCoordinatesMetadata.zw);
352
- let metallic = material.metallic * pick_channel(mrSample, u32(material.metallicChannel));
353
- let roughnessTex = pick_channel(mrSample, u32(material.roughnessChannel));
477
+ fn evaluateStandardSurface(in : VsOut, frontFacing : bool) -> SurfaceData {
478
+ let viewDirectionWS = normalize(view.cameraPos - in.worldPos);
479
+ #ifdef TRANSMISSION_AVAILABLE
480
+ let positionOS = in.worldPos;
481
+ #else
482
+ let positionOS = in.positionOSAndViewZ.xyz;
483
+ #endif
484
+ return evaluate_surface(SurfaceInput(
485
+ positionOS,
486
+ in.worldPos,
487
+ in.worldNormal,
488
+ in.worldTangent,
489
+ viewDirectionWS,
490
+ in.uv,
491
+ in.uv1,
492
+ materialVertexColor(in),
493
+ frontFacing,
494
+ ));
495
+ }
354
496
 
355
- var a = max(material.roughness, 0.04);
356
- a = a * roughnessTex;
357
- a = a * a;
497
+ fn finiteScalar(value : f32, fallback : f32) -> f32 {
498
+ let bounded = clamp(value, -65504.0, 65504.0);
499
+ return select(fallback, bounded, value == value);
500
+ }
358
501
 
359
- let normalUv = transformedMaterialUv(material.normalTextureCoordinatesTransform, material.normalTextureCoordinatesMetadata, in);
360
- let normSampleRg = sampleMaterialTexture(normalTexture, normalSampler, normalUv, material.normalTextureCoordinatesMetadata.zw).rg;
361
- let normTangent = scaleTangentSpaceNormal(
362
- decodeTangentSpaceNormalRg(normSampleRg), material.normalScale,
502
+ fn finiteColor(value : vec3<f32>, fallback : vec3<f32>) -> vec3<f32> {
503
+ return vec3<f32>(
504
+ finiteScalar(value.x, fallback.x),
505
+ finiteScalar(value.y, fallback.y),
506
+ finiteScalar(value.z, fallback.z),
363
507
  );
364
- let n = applyTBN(in.worldNormal, in.worldTangent, normTangent);
508
+ }
509
+
510
+ fn alphaTestSurface(surface : SurfaceData) {
511
+ if (surface.alphaClipThreshold > 0.0 && surface.opacity <= surface.alphaClipThreshold) {
512
+ discard;
513
+ }
514
+ }
515
+
516
+ @fragment
517
+ fn fs_main(in : VsOut, @builtin(front_facing) frontFacing : bool) -> @location(0) vec4<f32> {
518
+ let _variantIdentity = standardSkinVariantIdentity();
519
+ let surface = evaluateStandardSurface(in, frontFacing);
520
+ alphaTestSurface(surface);
521
+ let alpha = surface.opacity;
522
+ let albedo = surface.baseColor;
523
+ let metallic = clamp(finiteScalar(surface.metallic, 0.0), 0.0, 1.0);
524
+ let iblRoughness = clamp(finiteScalar(surface.roughness, 0.5), 0.04, 1.0);
525
+ let a = iblRoughness * iblRoughness;
526
+ let n = normalize(surface.normalWS);
365
527
 
366
528
  let v = normalize(view.cameraPos - in.worldPos);
367
- let specularTint = material.specularTint * sampleMaterialTexture(
368
- specularTintTexture, specularTintSampler,
369
- transformedMaterialUv(material.specularTintTextureCoordinatesTransform, material.specularTintTextureCoordinatesMetadata, in),
370
- material.specularTintTextureCoordinatesMetadata.zw,
529
+ var specularColor = material.specularColor;
530
+ #ifdef SPECULAR_COLOR_TEXTURE_AVAILABLE
531
+ specularColor = specularColor * sampleMaterialTexture(
532
+ specularColorTexture,
533
+ specularColorTextureSampler,
534
+ transformedMaterialUv(
535
+ material.specularColorTextureCoordinatesTransform,
536
+ material.specularColorTextureCoordinatesMetadata,
537
+ in,
538
+ ),
539
+ material.specularColorTextureCoordinatesMetadata.zw,
371
540
  ).rgb;
372
- let f0 = mix(vec3<f32>(0.04) * specularTint, albedo, metallic);
373
- let coatRoughness = max(material.clearcoatRoughness, 0.04);
374
- let coatAlpha = coatRoughness * coatRoughness;
375
- let coatF = f_schlick(max(dot(n, v), 0.0), vec3<f32>(0.04)) * material.clearcoat;
376
-
377
- let kD = (vec3<f32>(1.0) - f_schlick(max(dot(n, v), 0.0), f0)) * (1.0 - metallic);
378
- let iblRoughness = max(material.roughness, 0.04) * roughnessTex;
379
- let irradiance = sampleIblDiffuse(n, skylight.rotation, irradianceMap, irradianceSampler);
380
- let specularIbl = sampleIblSpecular(
381
- n, v, iblRoughness, f0,
382
- skylight.rotation,
383
- prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
541
+ #endif
542
+ var specularWeight = clamp(finiteScalar(material.specular, 1.0), 0.0, 1.0);
543
+ #ifdef SPECULAR_TEXTURE_AVAILABLE
544
+ let specularWeightUv = transformedMaterialUv(
545
+ material.specularTextureCoordinatesTransform,
546
+ material.specularTextureCoordinatesMetadata,
547
+ in,
384
548
  );
385
- let skyColor = vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB);
386
- var ambient = (kD * irradiance * albedo + specularIbl) * (vec3<f32>(1.0) - coatF);
387
- if (material.clearcoat != 0.0) {
388
- let clearcoatIbl = sampleIblSpecular(
389
- n, v, coatRoughness, vec3<f32>(0.04),
549
+ specularWeight = specularWeight * sampleMaterialTexture(
550
+ specularTexture,
551
+ specularTextureSampler,
552
+ specularWeightUv,
553
+ material.specularTextureCoordinatesMetadata.zw,
554
+ ).a;
555
+ #endif
556
+ let safeIor = max(finiteScalar(material.ior, 1.5), 1.0);
557
+ let dielectricF0 = pow((safeIor - 1.0) / (safeIor + 1.0), 2.0);
558
+ var f0 = mix(vec3<f32>(dielectricF0) * specularColor * specularWeight, albedo, metallic);
559
+ var physicalNormal = n;
560
+ #ifdef ANISOTROPY_AVAILABLE
561
+ var anisotropyStrength = finiteScalar(material.anisotropyStrength, 0.0);
562
+ var anisotropyRotation = finiteScalar(material.anisotropyRotation, 0.0);
563
+ var anisotropyDirection = vec2<f32>(1.0, 0.0);
564
+ #ifdef ANISOTROPY_TEXTURE_AVAILABLE
565
+ let anisotropyUv = transformedMaterialUv(
566
+ material.anisotropyTextureCoordinatesTransform,
567
+ material.anisotropyTextureCoordinatesMetadata,
568
+ in,
569
+ );
570
+ let anisotropySample = sampleMaterialTexture(
571
+ anisotropyTexture,
572
+ anisotropySampler,
573
+ anisotropyUv,
574
+ material.anisotropyTextureCoordinatesMetadata.zw,
575
+ );
576
+ let encodedAnisotropyDirection = 2.0 * anisotropySample.rg - vec2<f32>(1.0, 1.0);
577
+ if (dot(encodedAnisotropyDirection, encodedAnisotropyDirection) >= 1e-6) {
578
+ anisotropyDirection = normalize(encodedAnisotropyDirection);
579
+ }
580
+ anisotropyStrength = anisotropyStrength * anisotropySample.b;
581
+ #endif
582
+ physicalNormal = evaluateAnisotropicNormal(
583
+ n,
584
+ in.worldTangent,
585
+ anisotropyStrength,
586
+ anisotropyRotation,
587
+ anisotropyDirection,
588
+ );
589
+ #endif
590
+ #ifdef IRIDESCENCE_AVAILABLE
591
+ var iridescenceStrength = finiteScalar(material.iridescence, 0.0);
592
+ var iridescenceThicknessFactor = 1.0;
593
+ #ifdef IRIDESCENCE_TEXTURE_AVAILABLE
594
+ let iridescenceUv = transformedMaterialUv(
595
+ material.iridescenceTextureCoordinatesTransform,
596
+ material.iridescenceTextureCoordinatesMetadata,
597
+ in,
598
+ );
599
+ let iridescenceSample = sampleMaterialTexture(
600
+ iridescenceTexture,
601
+ iridescenceSampler,
602
+ iridescenceUv,
603
+ material.iridescenceTextureCoordinatesMetadata.zw,
604
+ );
605
+ iridescenceStrength = iridescenceStrength * iridescenceSample.r;
606
+ #endif
607
+ #ifdef IRIDESCENCE_THICKNESS_TEXTURE_AVAILABLE
608
+ let iridescenceThicknessUv = transformedMaterialUv(
609
+ material.iridescenceThicknessTextureCoordinatesTransform,
610
+ material.iridescenceThicknessTextureCoordinatesMetadata,
611
+ in,
612
+ );
613
+ let iridescenceThicknessSample = sampleMaterialTexture(
614
+ iridescenceThicknessTexture,
615
+ iridescenceThicknessSampler,
616
+ iridescenceThicknessUv,
617
+ material.iridescenceThicknessTextureCoordinatesMetadata.zw,
618
+ );
619
+ iridescenceThicknessFactor = clamp(iridescenceThicknessSample.g, 0.0, 1.0);
620
+ #endif
621
+ let filmThickness = mix(
622
+ finiteScalar(material.iridescenceThicknessMinimum, 100.0),
623
+ finiteScalar(material.iridescenceThicknessMaximum, 400.0),
624
+ iridescenceThicknessFactor,
625
+ );
626
+ f0 = evaluateIridescenceFresnel(
627
+ f0,
628
+ iridescenceStrength,
629
+ finiteScalar(material.iridescenceIor, 1.3),
630
+ filmThickness,
631
+ );
632
+ #endif
633
+ var diffuseAlbedo = albedo;
634
+ #ifdef TRANSMISSION_AVAILABLE
635
+ let transmissionUv = transformedMaterialUv(
636
+ material.transmissionTextureCoordinatesTransform,
637
+ material.transmissionTextureCoordinatesMetadata,
638
+ in,
639
+ );
640
+ let thicknessUv = transformedMaterialUv(
641
+ material.thicknessTextureCoordinatesTransform,
642
+ material.thicknessTextureCoordinatesMetadata,
643
+ in,
644
+ );
645
+ let transmissionSample = sampleMaterialTexture(
646
+ transmissionTexture,
647
+ transmissionSampler,
648
+ transmissionUv,
649
+ material.transmissionTextureCoordinatesMetadata.zw,
650
+ ).r;
651
+ let thicknessSample = sampleMaterialTexture(
652
+ thicknessTexture,
653
+ thicknessSampler,
654
+ thicknessUv,
655
+ material.thicknessTextureCoordinatesMetadata.zw,
656
+ ).g;
657
+ let transmissionFactor = clamp(
658
+ finiteScalar(material.transmission, 0.0) * finiteScalar(transmissionSample, 1.0),
659
+ 0.0,
660
+ 1.0,
661
+ );
662
+ let viewDot = finiteScalar(dot(n, v), 0.0);
663
+ let refractionFromInside = viewDot < 0.0;
664
+ let refractionNormal = select(n, -n, refractionFromInside);
665
+ let refractionEta = select(1.0 / safeIor, safeIor, refractionFromInside);
666
+ let incident = -v;
667
+ let refracted = refract(incident, refractionNormal, refractionEta);
668
+ let refractedLengthSquared = dot(refracted, refracted);
669
+ let viewCos = clamp(abs(viewDot), 0.0, 1.0);
670
+ let fresnel = clamp(f_schlick(viewCos, vec3<f32>(dielectricF0)).x, 0.0, 1.0);
671
+ let transmittedEnergy = select(
672
+ 0.0,
673
+ transmissionFactor * (1.0 - metallic) * (1.0 - fresnel),
674
+ refractedLengthSquared > 1e-6,
675
+ );
676
+ diffuseAlbedo = albedo * (1.0 - transmittedEnergy);
677
+ #endif
678
+ let kD = (vec3<f32>(1.0) - f_schlick(max(dot(physicalNormal, v), 0.0), f0)) * (1.0 - metallic);
679
+ var coatRoughness = 0.04;
680
+ var coatAlpha = coatRoughness * coatRoughness;
681
+ var coatF = vec3<f32>(0.0);
682
+ #ifdef CLEARCOAT_AVAILABLE
683
+ coatRoughness = max(finiteScalar(material.clearcoatRoughness, 0.04), 0.04);
684
+ coatAlpha = coatRoughness * coatRoughness;
685
+ coatF = f_schlick(max(dot(physicalNormal, v), 0.0), vec3<f32>(0.04)) *
686
+ clamp(finiteScalar(material.clearcoat, 0.0), 0.0, 1.0);
687
+ #endif
688
+ var irradiance = vec3<f32>(0.0);
689
+ var specularIbl = vec3<f32>(0.0);
690
+ if (skylight.intensity < 0.0) {
691
+ specularIbl = sampleReflectionProbeSpecular(
692
+ physicalNormal, v, iblRoughness, f0, in.worldPos,
693
+ vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB),
694
+ skylight.rotation.xyz, vec4<f32>(0.0, 0.0, 0.0, 1.0),
695
+ prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
696
+ );
697
+ } else {
698
+ irradiance = sampleIblDiffuse(physicalNormal, skylight.rotation, irradianceMap, irradianceSampler);
699
+ specularIbl = sampleIblSpecular(
700
+ physicalNormal, v, iblRoughness, f0,
390
701
  skylight.rotation,
391
702
  prefilterMap, prefilterSampler, brdfLut, brdfLutSampler,
392
703
  );
393
- ambient = ambient + clearcoatIbl * material.clearcoat;
394
704
  }
395
- ambient = ambient * skyColor * skylight.intensity;
705
+ let ao = surface.occlusion;
706
+ let skyColor = vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB);
707
+ let skyFactor = skyColor * skylight.intensity;
708
+ var ambient = (kD * irradiance * diffuseAlbedo + specularIbl) *
709
+ (vec3<f32>(1.0) - coatF);
710
+ #ifdef PROBE_BLEND_AVAILABLE
711
+ let probeShPreblend = array<vec4<f32>, 9>(
712
+ probeBlendRecords[1], probeBlendRecords[2], probeBlendRecords[3],
713
+ probeBlendRecords[4], probeBlendRecords[5], probeBlendRecords[6],
714
+ probeBlendRecords[7], probeBlendRecords[8], probeBlendRecords[9],
715
+ );
716
+ let probeLocalBlendFraction = probeBlendRecords[0].z;
717
+ let probeDiffuseK = kD / max(vec3<f32>(1.0 - metallic), vec3<f32>(0.0001));
718
+ let probeDiffuse = composeProbeDiffuse(probeShPreblend,
719
+ probeLocalBlendFraction,
720
+ physicalNormal,
721
+ irradiance * skyFactor,
722
+ probeDiffuseK,
723
+ diffuseAlbedo,
724
+ metallic,
725
+ );
726
+ ambient = (probeDiffuse + specularIbl * skyFactor) *
727
+ (vec3<f32>(1.0) - coatF);
728
+ #endif
729
+ #ifdef PROBE_BLEND_AVAILABLE
730
+ ambient = ambient * ao;
731
+ #else
732
+ if (skylight.intensity < 0.0) {
733
+ let environmentScale = max(-skylight.intensity - 1.0, 0.0) * ao;
734
+ ambient = ambient * environmentScale;
735
+ } else {
736
+ let environmentScale = skyColor * skylight.intensity * ao;
737
+ ambient = ambient * environmentScale;
738
+ }
739
+ #endif
396
740
  var color = ambient;
397
- let directionalShadow = evalDirectionalShadowFactor(n, in.worldPos, in.viewZ);
398
- let directionalBase = evalDirectionalNoShadow(n, v, albedo, metallic, a, f0);
399
- color = color + directionalShadow * directionalBase;
400
- if (material.clearcoat != 0.0) {
401
- let directionalClearcoat = evalDirectionalNoShadow(
402
- n, v, vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04),
403
- );
404
- color = color + directionalShadow * material.clearcoat * directionalClearcoat;
741
+ #ifdef TRANSMISSION_AVAILABLE
742
+ let screenUv = in.ndc.xy * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
743
+ let localToWorld0 = in.transmissionBasis0.xyz;
744
+ let localToWorld1 = vec3<f32>(
745
+ in.transmissionBasis0.w,
746
+ in.transmissionBasis1.x,
747
+ in.transmissionBasis1.y,
748
+ );
749
+ let localToWorld2 = vec3<f32>(
750
+ in.transmissionBasis1.z,
751
+ in.transmissionBasis1.w,
752
+ in.ndc.w,
753
+ );
754
+ let inverseCofactor0 = cross(localToWorld1, localToWorld2);
755
+ let inverseCofactor1 = cross(localToWorld2, localToWorld0);
756
+ let inverseCofactor2 = cross(localToWorld0, localToWorld1);
757
+ let localToWorldDet = dot(localToWorld0, inverseCofactor0);
758
+ let safeLocalToWorldDet = select(1.0, localToWorldDet, abs(localToWorldDet) >= 1e-6);
759
+ let worldToLocal0 = vec3<f32>(
760
+ inverseCofactor0.x,
761
+ inverseCofactor1.x,
762
+ inverseCofactor2.x,
763
+ ) / safeLocalToWorldDet;
764
+ let worldToLocal1 = vec3<f32>(
765
+ inverseCofactor0.y,
766
+ inverseCofactor1.y,
767
+ inverseCofactor2.y,
768
+ ) / safeLocalToWorldDet;
769
+ let worldToLocal2 = vec3<f32>(
770
+ inverseCofactor0.z,
771
+ inverseCofactor1.z,
772
+ inverseCofactor2.z,
773
+ ) / safeLocalToWorldDet;
774
+ let refractedLocal = normalize(
775
+ worldToLocal0 * refracted.x +
776
+ worldToLocal1 * refracted.y +
777
+ worldToLocal2 * refracted.z,
778
+ );
779
+ let worldRefractedDirection =
780
+ localToWorld0 * refractedLocal.x +
781
+ localToWorld1 * refractedLocal.y +
782
+ localToWorld2 * refractedLocal.z;
783
+ let worldThickness = max(
784
+ finiteScalar(material.thickness, 0.0) * length(worldRefractedDirection) *
785
+ finiteScalar(thicknessSample, 1.0),
786
+ 0.0,
787
+ );
788
+ let refractedUv = screenUv + (refracted.xy - incident.xy) * worldThickness * 0.25;
789
+ let guardBand = 0.02;
790
+ let insideGuardBand = all(refractedUv >= vec2<f32>(guardBand)) &&
791
+ all(refractedUv <= vec2<f32>(1.0 - guardBand));
792
+ let backdropMipCount = textureNumLevels(transmissionBackdropTexture);
793
+ let backdropMaxLod = max(f32(backdropMipCount) - 1.0, 0.0);
794
+ let backdropLod = clamp(iblRoughness * iblRoughness * backdropMaxLod, 0.0, backdropMaxLod);
795
+ let unrefractedBackdrop = textureSampleLevel(
796
+ transmissionBackdropTexture,
797
+ transmissionBackdropSampler,
798
+ clamp(screenUv, vec2<f32>(0.0), vec2<f32>(1.0)),
799
+ backdropLod,
800
+ ).rgb;
801
+ var transmittedBackdrop = unrefractedBackdrop;
802
+ if (refractedLengthSquared > 1e-6 && insideGuardBand) {
803
+ transmittedBackdrop = textureSampleLevel(
804
+ transmissionBackdropTexture,
805
+ transmissionBackdropSampler,
806
+ clamp(refractedUv, vec2<f32>(0.0), vec2<f32>(1.0)),
807
+ backdropLod,
808
+ ).rgb;
405
809
  }
810
+ let safeAttenuationColor = clamp(
811
+ finiteColor(material.attenuationColor, vec3<f32>(1.0)),
812
+ vec3<f32>(0.0),
813
+ vec3<f32>(1.0),
814
+ );
815
+ let safeAttenuationDistance = max(finiteScalar(material.attenuationDistance, 0.0), 0.0);
816
+ let attenuationExponent = worldThickness / max(safeAttenuationDistance, 1e-6);
817
+ let beerAttenuation = select(
818
+ vec3<f32>(1.0),
819
+ pow(safeAttenuationColor, vec3<f32>(attenuationExponent)),
820
+ safeAttenuationDistance > 1e-6 && worldThickness > 0.0,
821
+ );
822
+ color = color + finiteColor(transmittedBackdrop, vec3<f32>(0.0)) *
823
+ transmittedEnergy * beerAttenuation;
824
+ #endif
825
+ color = color + surface.emissive;
826
+ let directionalShadow = evalDirectionalShadowFactor(
827
+ physicalNormal,
828
+ in.worldPos,
829
+ standardViewZ(in),
830
+ );
831
+ let directionalBase = evalDirectionalNoShadow(physicalNormal, v, diffuseAlbedo, metallic, a, f0);
832
+ color = color + directionalShadow * directionalBase;
406
833
  #ifdef CLUSTER_FORWARD_AVAILABLE
407
834
  color = color + evaluateStandardClusterLights(
408
- in.ndc, in.viewZ, in.worldPos, n, v, albedo, metallic, a, f0, false,
835
+ in.ndc.xyz, standardViewZ(in), in.worldPos, physicalNormal, v,
836
+ diffuseAlbedo, metallic, a, f0, false,
409
837
  );
410
- if (material.clearcoat != 0.0) {
411
- color = color + material.clearcoat * evaluateStandardClusterLights(
412
- in.ndc, in.viewZ, in.worldPos, n, v,
413
- vec3<f32>(0.0), 1.0, coatAlpha, vec3<f32>(0.04), false,
838
+ #endif // CLUSTER_FORWARD_AVAILABLE
839
+ #ifdef SHEEN_AVAILABLE
840
+ var sheenColor = material.sheenColor;
841
+ var sheenRoughnessFactor = 1.0;
842
+ #ifdef SHEEN_COLOR_TEXTURE_AVAILABLE
843
+ sheenColor = sheenColor * sampleMaterialTexture(
844
+ sheenColorTexture,
845
+ sheenColorSampler,
846
+ transformedMaterialUv(material.sheenColorTextureCoordinatesTransform, material.sheenColorTextureCoordinatesMetadata, in),
847
+ material.sheenColorTextureCoordinatesMetadata.zw,
848
+ ).rgb;
849
+ #endif
850
+ #ifdef SHEEN_ROUGHNESS_TEXTURE_AVAILABLE
851
+ sheenRoughnessFactor = sampleMaterialTexture(
852
+ sheenRoughnessTexture,
853
+ sheenRoughnessSampler,
854
+ transformedMaterialUv(material.sheenRoughnessTextureCoordinatesTransform, material.sheenRoughnessTextureCoordinatesMetadata, in),
855
+ material.sheenRoughnessTextureCoordinatesMetadata.zw,
856
+ ).a;
857
+ #endif
858
+ let sheenRoughness = clamp(
859
+ material.sheenRoughness * sheenRoughnessFactor,
860
+ 0.0,
861
+ 1.0,
862
+ );
863
+ color = evaluateSheenLayer(color, sheenColor, sheenRoughness, dot(n, v));
864
+ #endif
865
+ #ifdef CLEARCOAT_AVAILABLE
866
+ var clearcoatFactor = clamp(material.clearcoat, 0.0, 1.0);
867
+ #ifdef CLEARCOAT_TEXTURE_AVAILABLE
868
+ let clearcoatUv = transformedMaterialUv(material.clearcoatTextureCoordinatesTransform, material.clearcoatTextureCoordinatesMetadata, in);
869
+ clearcoatFactor = clamp(
870
+ material.clearcoat * sampleMaterialTexture(
871
+ clearcoatTexture,
872
+ clearcoatSampler,
873
+ clearcoatUv,
874
+ material.clearcoatTextureCoordinatesMetadata.zw,
875
+ ).r,
876
+ 0.0,
877
+ 1.0,
878
+ );
879
+ #endif
880
+ var clearcoatRoughnessValue = clamp(max(material.clearcoatRoughness, 0.04), 0.04, 1.0);
881
+ #ifdef CLEARCOAT_ROUGHNESS_TEXTURE_AVAILABLE
882
+ let clearcoatRoughnessUv = transformedMaterialUv(material.clearcoatRoughnessTextureCoordinatesTransform, material.clearcoatRoughnessTextureCoordinatesMetadata, in);
883
+ clearcoatRoughnessValue = clamp(
884
+ max(material.clearcoatRoughness, 0.04) * sampleMaterialTexture(
885
+ clearcoatRoughnessTexture,
886
+ clearcoatRoughnessSampler,
887
+ clearcoatRoughnessUv,
888
+ material.clearcoatRoughnessTextureCoordinatesMetadata.zw,
889
+ ).g,
890
+ 0.04,
891
+ 1.0,
892
+ );
893
+ #endif
894
+ var clearcoatNormalValue = in.worldNormal;
895
+ #ifdef CLEARCOAT_NORMAL_TEXTURE_AVAILABLE
896
+ let clearcoatNormalUv = transformedMaterialUv(material.clearcoatNormalTextureCoordinatesTransform, material.clearcoatNormalTextureCoordinatesMetadata, in);
897
+ clearcoatNormalValue = applyTBN(
898
+ in.worldNormal,
899
+ in.worldTangent,
900
+ scaleTangentSpaceNormal(
901
+ decodeTangentSpaceNormalRg(sampleMaterialTexture(
902
+ clearcoatNormalTexture,
903
+ clearcoatNormalSampler,
904
+ clearcoatNormalUv,
905
+ material.clearcoatNormalTextureCoordinatesMetadata.zw,
906
+ ).rg),
907
+ material.clearcoatNormalScale,
908
+ ),
909
+ );
910
+ #endif
911
+ var clearcoatIbl = vec3<f32>(0.0);
912
+ if (skylight.intensity < 0.0) {
913
+ clearcoatIbl = sampleReflectionProbeSpecular(
914
+ clearcoatNormalValue,
915
+ v,
916
+ clearcoatRoughnessValue,
917
+ vec3<f32>(0.04),
918
+ in.worldPos,
919
+ vec3<f32>(skylight.colorR, skylight.colorG, skylight.colorB),
920
+ skylight.rotation.xyz,
921
+ vec4<f32>(0.0, 0.0, 0.0, 1.0),
922
+ prefilterMap,
923
+ prefilterSampler,
924
+ brdfLut,
925
+ brdfLutSampler,
926
+ );
927
+ } else {
928
+ clearcoatIbl = sampleIblSpecular(
929
+ clearcoatNormalValue,
930
+ v,
931
+ clearcoatRoughnessValue,
932
+ vec3<f32>(0.04),
933
+ skylight.rotation,
934
+ prefilterMap,
935
+ prefilterSampler,
936
+ brdfLut,
937
+ brdfLutSampler,
414
938
  );
415
939
  }
940
+ let clearcoatAlpha = clearcoatRoughnessValue * clearcoatRoughnessValue;
941
+ let clearcoatDirect = directionalShadow * evalDirectionalNoShadow(
942
+ clearcoatNormalValue,
943
+ v,
944
+ vec3<f32>(0.0),
945
+ 1.0,
946
+ clearcoatAlpha,
947
+ vec3<f32>(0.04),
948
+ );
949
+ let directionalClearcoat = clearcoatDirect;
950
+ var clearcoatEnvironment = clearcoatIbl * skyColor * skylight.intensity;
951
+ if (skylight.intensity < 0.0) {
952
+ clearcoatEnvironment = clearcoatIbl;
953
+ }
954
+ color = evaluateClearcoatLayer(
955
+ color,
956
+ clearcoatEnvironment + directionalClearcoat,
957
+ dot(clearcoatNormalValue, v),
958
+ clearcoatFactor,
959
+ );
416
960
  #endif
417
961
  return vec4<f32>(color, alpha);
418
962
  }
@@ -429,7 +973,6 @@ struct TemporalVsOut {
429
973
  @location(7) uv7 : vec2<f32>,
430
974
  @location(8) @interpolate(perspective) currentClip : vec4<f32>,
431
975
  @location(9) @interpolate(perspective) previousClip : vec4<f32>,
432
- @location(10) @interpolate(flat) reactive : f32,
433
976
  #ifdef VERTEX_COLOR_AVAILABLE
434
977
  @location(14) color : vec4<f32>,
435
978
  #endif
@@ -451,11 +994,6 @@ fn vs_temporal(in : VsIn, @builtin(instance_index) idx : u32) -> TemporalVsOut {
451
994
  out.currentClip = view.temporalCurrentViewProj * currentWorld;
452
995
  out.clip = out.currentClip;
453
996
  out.previousClip = view.temporalPreviousViewProj * previousWorld;
454
- #if STORAGE_BUFFER_AVAILABLE == true
455
- out.reactive = meshes[0].temporal.x;
456
- #else
457
- out.reactive = 1.0;
458
- #endif
459
997
  out.uv = in.uv;
460
998
  out.uv1 = in.uv1;
461
999
  out.uv2 = in.uv2;
@@ -481,6 +1019,11 @@ fn temporalVertexAlpha(in : TemporalVsOut) -> f32 {
481
1019
 
482
1020
  @fragment
483
1021
  fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
1022
+ #if STORAGE_BUFFER_AVAILABLE == true
1023
+ let reactive = meshes[0].temporal.x;
1024
+ #else
1025
+ let reactive = 1.0;
1026
+ #endif
484
1027
  return projectPbrSceneTemporal(
485
1028
  material.baseColor.a * temporalVertexAlpha(in),
486
1029
  material.alphaCutoff,
@@ -491,7 +1034,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
491
1034
  in.currentClip,
492
1035
  in.previousClip,
493
1036
  view.temporalProjection,
494
- in.reactive,
1037
+ reactive,
495
1038
  in.uv, in.uv1, in.uv2, in.uv3,
496
1039
  in.uv4, in.uv5, in.uv6, in.uv7,
497
1040
  );