@luma.gl/shadertools 9.3.0-alpha.6 → 9.3.0-alpha.9

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 (152) hide show
  1. package/dist/dist.dev.js +2550 -331
  2. package/dist/dist.min.js +1803 -283
  3. package/dist/index.cjs +2496 -358
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +9 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/preprocessor/preprocessor.d.ts.map +1 -1
  10. package/dist/lib/preprocessor/preprocessor.js +4 -3
  11. package/dist/lib/preprocessor/preprocessor.js.map +1 -1
  12. package/dist/lib/shader-assembler.d.ts +10 -0
  13. package/dist/lib/shader-assembler.d.ts.map +1 -1
  14. package/dist/lib/shader-assembler.js +12 -2
  15. package/dist/lib/shader-assembler.js.map +1 -1
  16. package/dist/lib/shader-assembly/assemble-shaders.d.ts +23 -2
  17. package/dist/lib/shader-assembly/assemble-shaders.d.ts.map +1 -1
  18. package/dist/lib/shader-assembly/assemble-shaders.js +211 -11
  19. package/dist/lib/shader-assembly/assemble-shaders.js.map +1 -1
  20. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts +37 -0
  21. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts.map +1 -0
  22. package/dist/lib/shader-assembly/wgsl-binding-debug.js +140 -0
  23. package/dist/lib/shader-assembly/wgsl-binding-debug.js.map +1 -0
  24. package/dist/lib/shader-generator/glsl/generate-glsl.js +3 -0
  25. package/dist/lib/shader-generator/glsl/generate-glsl.js.map +1 -1
  26. package/dist/lib/shader-generator/wgsl/generate-wgsl.d.ts.map +1 -1
  27. package/dist/lib/shader-generator/wgsl/generate-wgsl.js +3 -0
  28. package/dist/lib/shader-generator/wgsl/generate-wgsl.js.map +1 -1
  29. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts +22 -0
  30. package/dist/lib/shader-module/shader-module-uniform-layout.d.ts.map +1 -0
  31. package/dist/lib/shader-module/shader-module-uniform-layout.js +113 -0
  32. package/dist/lib/shader-module/shader-module-uniform-layout.js.map +1 -0
  33. package/dist/lib/shader-module/shader-module.d.ts +11 -5
  34. package/dist/lib/shader-module/shader-module.d.ts.map +1 -1
  35. package/dist/lib/shader-module/shader-module.js.map +1 -1
  36. package/dist/lib/utils/uniform-types.d.ts +11 -7
  37. package/dist/lib/utils/uniform-types.d.ts.map +1 -1
  38. package/dist/modules/engine/picking/picking.d.ts +3 -0
  39. package/dist/modules/engine/picking/picking.d.ts.map +1 -1
  40. package/dist/modules/engine/picking/picking.js +3 -0
  41. package/dist/modules/engine/picking/picking.js.map +1 -1
  42. package/dist/modules/engine/skin/skin.d.ts +7 -6
  43. package/dist/modules/engine/skin/skin.d.ts.map +1 -1
  44. package/dist/modules/engine/skin/skin.js +3 -5
  45. package/dist/modules/engine/skin/skin.js.map +1 -1
  46. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts +1 -0
  47. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts.map +1 -1
  48. package/dist/modules/lighting/gouraud-material/gouraud-material.js +3 -0
  49. package/dist/modules/lighting/gouraud-material/gouraud-material.js.map +1 -1
  50. package/dist/modules/lighting/ibl/ibl.d.ts +26 -0
  51. package/dist/modules/lighting/ibl/ibl.d.ts.map +1 -0
  52. package/dist/modules/lighting/ibl/ibl.js +33 -0
  53. package/dist/modules/lighting/ibl/ibl.js.map +1 -0
  54. package/dist/modules/lighting/lambert-material/lambert-material.d.ts +10 -0
  55. package/dist/modules/lighting/lambert-material/lambert-material.d.ts.map +1 -0
  56. package/dist/modules/lighting/lambert-material/lambert-material.js +33 -0
  57. package/dist/modules/lighting/lambert-material/lambert-material.js.map +1 -0
  58. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts +3 -0
  59. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.d.ts.map +1 -0
  60. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.js +60 -0
  61. package/dist/modules/lighting/lambert-material/lambert-shaders-glsl.js.map +1 -0
  62. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.d.ts +2 -0
  63. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.d.ts.map +1 -0
  64. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.js +73 -0
  65. package/dist/modules/lighting/lambert-material/lambert-shaders-wgsl.js.map +1 -0
  66. package/dist/modules/lighting/lights/lighting-glsl.d.ts +1 -1
  67. package/dist/modules/lighting/lights/lighting-glsl.d.ts.map +1 -1
  68. package/dist/modules/lighting/lights/lighting-glsl.js +43 -55
  69. package/dist/modules/lighting/lights/lighting-glsl.js.map +1 -1
  70. package/dist/modules/lighting/lights/lighting-wgsl.d.ts +1 -1
  71. package/dist/modules/lighting/lights/lighting-wgsl.d.ts.map +1 -1
  72. package/dist/modules/lighting/lights/lighting-wgsl.js +43 -65
  73. package/dist/modules/lighting/lights/lighting-wgsl.js.map +1 -1
  74. package/dist/modules/lighting/lights/lighting.d.ts +104 -86
  75. package/dist/modules/lighting/lights/lighting.d.ts.map +1 -1
  76. package/dist/modules/lighting/lights/lighting.js +96 -83
  77. package/dist/modules/lighting/lights/lighting.js.map +1 -1
  78. package/dist/modules/lighting/no-material/dirlight.d.ts +7 -2
  79. package/dist/modules/lighting/no-material/dirlight.d.ts.map +1 -1
  80. package/dist/modules/lighting/no-material/dirlight.js +3 -1
  81. package/dist/modules/lighting/no-material/dirlight.js.map +1 -1
  82. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +1 -1
  83. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  84. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +524 -28
  85. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  86. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +2 -2
  87. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  88. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +706 -50
  89. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  90. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +110 -61
  91. package/dist/modules/lighting/pbr-material/pbr-material.d.ts.map +1 -1
  92. package/dist/modules/lighting/pbr-material/pbr-material.js +85 -9
  93. package/dist/modules/lighting/pbr-material/pbr-material.js.map +1 -1
  94. package/dist/modules/lighting/pbr-material/pbr-projection.d.ts.map +1 -1
  95. package/dist/modules/lighting/pbr-material/pbr-projection.js +2 -1
  96. package/dist/modules/lighting/pbr-material/pbr-projection.js.map +1 -1
  97. package/dist/modules/lighting/phong-material/phong-material.d.ts +1 -0
  98. package/dist/modules/lighting/phong-material/phong-material.d.ts.map +1 -1
  99. package/dist/modules/lighting/phong-material/phong-material.js +4 -0
  100. package/dist/modules/lighting/phong-material/phong-material.js.map +1 -1
  101. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts +2 -2
  102. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts.map +1 -1
  103. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js +15 -4
  104. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js.map +1 -1
  105. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.d.ts +1 -1
  106. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.d.ts.map +1 -1
  107. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.js +36 -5
  108. package/dist/modules/lighting/phong-material/phong-shaders-wgsl.js.map +1 -1
  109. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts +1 -1
  110. package/dist/modules/math/fp64/fp64-arithmetic-glsl.d.ts.map +1 -1
  111. package/dist/modules/math/fp64/fp64-arithmetic-glsl.js +41 -10
  112. package/dist/modules/math/fp64/fp64-arithmetic-glsl.js.map +1 -1
  113. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts +2 -0
  114. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.d.ts.map +1 -0
  115. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js +212 -0
  116. package/dist/modules/math/fp64/fp64-arithmetic-wgsl.js.map +1 -0
  117. package/dist/modules/math/fp64/fp64.d.ts +1 -0
  118. package/dist/modules/math/fp64/fp64.d.ts.map +1 -1
  119. package/dist/modules/math/fp64/fp64.js +8 -2
  120. package/dist/modules/math/fp64/fp64.js.map +1 -1
  121. package/package.json +3 -3
  122. package/src/index.ts +19 -2
  123. package/src/lib/preprocessor/preprocessor.ts +6 -3
  124. package/src/lib/shader-assembler.ts +17 -2
  125. package/src/lib/shader-assembly/assemble-shaders.ts +377 -12
  126. package/src/lib/shader-assembly/wgsl-binding-debug.ts +216 -0
  127. package/src/lib/shader-generator/glsl/generate-glsl.ts +7 -1
  128. package/src/lib/shader-generator/wgsl/generate-wgsl.ts +6 -0
  129. package/src/lib/shader-module/shader-module-uniform-layout.ts +195 -0
  130. package/src/lib/shader-module/shader-module.ts +16 -6
  131. package/src/lib/utils/uniform-types.ts +24 -9
  132. package/src/modules/engine/picking/picking.ts +3 -0
  133. package/src/modules/engine/skin/skin.ts +3 -5
  134. package/src/modules/lighting/gouraud-material/gouraud-material.ts +4 -0
  135. package/src/modules/lighting/ibl/ibl.ts +44 -0
  136. package/src/modules/lighting/lambert-material/lambert-material.ts +42 -0
  137. package/src/modules/lighting/lambert-material/lambert-shaders-glsl.ts +61 -0
  138. package/src/modules/lighting/lambert-material/lambert-shaders-wgsl.ts +73 -0
  139. package/src/modules/lighting/lights/lighting-glsl.ts +43 -55
  140. package/src/modules/lighting/lights/lighting-wgsl.ts +43 -65
  141. package/src/modules/lighting/lights/lighting.ts +186 -123
  142. package/src/modules/lighting/no-material/dirlight.ts +3 -1
  143. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +524 -28
  144. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +706 -50
  145. package/src/modules/lighting/pbr-material/pbr-material.ts +111 -18
  146. package/src/modules/lighting/pbr-material/pbr-projection.ts +2 -1
  147. package/src/modules/lighting/phong-material/phong-material.ts +5 -0
  148. package/src/modules/lighting/phong-material/phong-shaders-glsl.ts +15 -4
  149. package/src/modules/lighting/phong-material/phong-shaders-wgsl.ts +36 -5
  150. package/src/modules/math/fp64/fp64-arithmetic-glsl.ts +41 -10
  151. package/src/modules/math/fp64/fp64-arithmetic-wgsl.ts +212 -0
  152. package/src/modules/math/fp64/fp64.ts +9 -3
package/dist/index.cjs CHANGED
@@ -47,10 +47,15 @@ __export(dist_exports, {
47
47
  getShaderInfo: () => getShaderInfo,
48
48
  getShaderModuleDependencies: () => getShaderModuleDependencies,
49
49
  getShaderModuleSource: () => getShaderModuleSource,
50
+ getShaderModuleUniformBlockFields: () => getShaderModuleUniformBlockFields,
51
+ getShaderModuleUniformBlockName: () => getShaderModuleUniformBlockName,
52
+ getShaderModuleUniformLayoutValidationResult: () => getShaderModuleUniformLayoutValidationResult,
50
53
  getShaderModuleUniforms: () => getShaderModuleUniforms,
51
54
  gouraudMaterial: () => gouraudMaterial,
55
+ ibl: () => ibl,
52
56
  initializeShaderModule: () => initializeShaderModule,
53
57
  initializeShaderModules: () => initializeShaderModules,
58
+ lambertMaterial: () => lambertMaterial,
54
59
  lighting: () => lighting,
55
60
  pbrMaterial: () => pbrMaterial,
56
61
  phongMaterial: () => phongMaterial,
@@ -60,7 +65,8 @@ __export(dist_exports, {
60
65
  skin: () => skin,
61
66
  toHalfFloat: () => toHalfFloat,
62
67
  typeToChannelCount: () => typeToChannelCount,
63
- typeToChannelSuffix: () => typeToChannelSuffix
68
+ typeToChannelSuffix: () => typeToChannelSuffix,
69
+ validateShaderModuleUniformLayout: () => validateShaderModuleUniformLayout
64
70
  });
65
71
  module.exports = __toCommonJS(dist_exports);
66
72
 
@@ -379,6 +385,115 @@ function resolveModules(modules) {
379
385
  return getShaderDependencies(modules);
380
386
  }
381
387
 
388
+ // dist/lib/shader-module/shader-module-uniform-layout.js
389
+ var GLSL_UNIFORM_BLOCK_FIELD_REGEXP = /^(?:uniform\s+)?(?:(?:lowp|mediump|highp)\s+)?[A-Za-z0-9_]+(?:<[^>]+>)?\s+([A-Za-z0-9_]+)(?:\s*\[[^\]]+\])?\s*;/;
390
+ function getShaderModuleUniformBlockName(module2) {
391
+ return `${module2.name}Uniforms`;
392
+ }
393
+ function getShaderModuleUniformBlockFields(module2, stage) {
394
+ const shaderSource = stage === "wgsl" ? module2.source : stage === "vertex" ? module2.vs : module2.fs;
395
+ if (!shaderSource) {
396
+ return null;
397
+ }
398
+ const uniformBlockName = getShaderModuleUniformBlockName(module2);
399
+ return extractShaderUniformBlockFieldNames(shaderSource, stage === "wgsl" ? "wgsl" : "glsl", uniformBlockName);
400
+ }
401
+ function getShaderModuleUniformLayoutValidationResult(module2, stage) {
402
+ const expectedUniformNames = Object.keys(module2.uniformTypes || {});
403
+ if (!expectedUniformNames.length) {
404
+ return null;
405
+ }
406
+ const actualUniformNames = getShaderModuleUniformBlockFields(module2, stage);
407
+ if (!actualUniformNames) {
408
+ return null;
409
+ }
410
+ return {
411
+ moduleName: module2.name,
412
+ uniformBlockName: getShaderModuleUniformBlockName(module2),
413
+ stage,
414
+ expectedUniformNames,
415
+ actualUniformNames,
416
+ matches: areStringArraysEqual(expectedUniformNames, actualUniformNames)
417
+ };
418
+ }
419
+ function validateShaderModuleUniformLayout(module2, stage, options = {}) {
420
+ var _a, _b;
421
+ const validationResult = getShaderModuleUniformLayoutValidationResult(module2, stage);
422
+ if (!validationResult || validationResult.matches) {
423
+ return validationResult;
424
+ }
425
+ const message = formatShaderModuleUniformLayoutError(validationResult);
426
+ (_b = (_a = options.log) == null ? void 0 : _a.error) == null ? void 0 : _b.call(_a, message, validationResult)();
427
+ if (options.throwOnError !== false) {
428
+ assert(false, message);
429
+ }
430
+ return validationResult;
431
+ }
432
+ function extractShaderUniformBlockFieldNames(shaderSource, language, uniformBlockName) {
433
+ const sourceBody = language === "wgsl" ? extractWGSLStructBody(shaderSource, uniformBlockName) : extractGLSLUniformBlockBody(shaderSource, uniformBlockName);
434
+ if (!sourceBody) {
435
+ return null;
436
+ }
437
+ const fieldNames = [];
438
+ for (const sourceLine of sourceBody.split("\n")) {
439
+ const line = sourceLine.replace(/\/\/.*$/, "").trim();
440
+ if (!line || line.startsWith("#")) {
441
+ continue;
442
+ }
443
+ const fieldMatch = language === "wgsl" ? line.match(/^([A-Za-z0-9_]+)\s*:/) : line.match(GLSL_UNIFORM_BLOCK_FIELD_REGEXP);
444
+ if (fieldMatch) {
445
+ fieldNames.push(fieldMatch[1]);
446
+ }
447
+ }
448
+ return fieldNames;
449
+ }
450
+ function extractWGSLStructBody(shaderSource, uniformBlockName) {
451
+ const structMatch = new RegExp(`\\bstruct\\s+${uniformBlockName}\\b`, "m").exec(shaderSource);
452
+ if (!structMatch) {
453
+ return null;
454
+ }
455
+ const openBraceIndex = shaderSource.indexOf("{", structMatch.index);
456
+ if (openBraceIndex < 0) {
457
+ return null;
458
+ }
459
+ let braceDepth = 0;
460
+ for (let index = openBraceIndex; index < shaderSource.length; index++) {
461
+ const character = shaderSource[index];
462
+ if (character === "{") {
463
+ braceDepth++;
464
+ continue;
465
+ }
466
+ if (character !== "}") {
467
+ continue;
468
+ }
469
+ braceDepth--;
470
+ if (braceDepth === 0) {
471
+ return shaderSource.slice(openBraceIndex + 1, index);
472
+ }
473
+ }
474
+ return null;
475
+ }
476
+ function extractGLSLUniformBlockBody(shaderSource, uniformBlockName) {
477
+ const sourceMatch = shaderSource.match(new RegExp(`uniform\\s+${uniformBlockName}\\s*\\{([\\s\\S]*?)\\}\\s*[A-Za-z0-9_]+\\s*;`, "m"));
478
+ return (sourceMatch == null ? void 0 : sourceMatch[1]) || null;
479
+ }
480
+ function areStringArraysEqual(leftValues, rightValues) {
481
+ if (leftValues.length !== rightValues.length) {
482
+ return false;
483
+ }
484
+ for (let valueIndex = 0; valueIndex < leftValues.length; valueIndex++) {
485
+ if (leftValues[valueIndex] !== rightValues[valueIndex]) {
486
+ return false;
487
+ }
488
+ }
489
+ return true;
490
+ }
491
+ function formatShaderModuleUniformLayoutError(validationResult) {
492
+ return `${validationResult.moduleName}: ${validationResult.stage} shader uniform block ${validationResult.uniformBlockName} does not match module.uniformTypes.
493
+ Expected: ${validationResult.expectedUniformNames.join(", ")}
494
+ Actual: ${validationResult.actualUniformNames.join(", ")}`;
495
+ }
496
+
382
497
  // dist/lib/shader-assembly/platform-defines.js
383
498
  function getPlatformShaderDefines(platformInfo) {
384
499
  switch (platformInfo == null ? void 0 : platformInfo.gpu.toLowerCase()) {
@@ -564,11 +679,158 @@ function getShaderVersion(source4) {
564
679
  return version;
565
680
  }
566
681
 
682
+ // dist/lib/shader-assembly/wgsl-binding-debug.js
683
+ var WGSL_BINDING_DEBUG_REGEXES = [
684
+ /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g,
685
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g
686
+ ];
687
+ function getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments = []) {
688
+ var _a;
689
+ const assignmentMap = /* @__PURE__ */ new Map();
690
+ for (const bindingAssignment of bindingAssignments) {
691
+ assignmentMap.set(getBindingAssignmentKey(bindingAssignment.name, bindingAssignment.group, bindingAssignment.location), bindingAssignment.moduleName);
692
+ }
693
+ const rows = [];
694
+ for (const regex of WGSL_BINDING_DEBUG_REGEXES) {
695
+ regex.lastIndex = 0;
696
+ let match;
697
+ while (match = regex.exec(source4)) {
698
+ const isBindingFirst = regex === WGSL_BINDING_DEBUG_REGEXES[0];
699
+ const binding = Number(match[isBindingFirst ? 1 : 2]);
700
+ const group = Number(match[isBindingFirst ? 2 : 1]);
701
+ const accessDeclaration = (_a = match[3]) == null ? void 0 : _a.trim();
702
+ const name = match[4];
703
+ const resourceType = match[5].trim();
704
+ const moduleName = assignmentMap.get(getBindingAssignmentKey(name, group, binding));
705
+ rows.push(normalizeShaderBindingDebugRow({
706
+ name,
707
+ group,
708
+ binding,
709
+ owner: moduleName ? "module" : "application",
710
+ moduleName,
711
+ accessDeclaration,
712
+ resourceType
713
+ }));
714
+ }
715
+ }
716
+ return rows.sort((left, right) => {
717
+ if (left.group !== right.group) {
718
+ return left.group - right.group;
719
+ }
720
+ if (left.binding !== right.binding) {
721
+ return left.binding - right.binding;
722
+ }
723
+ return left.name.localeCompare(right.name);
724
+ });
725
+ }
726
+ function normalizeShaderBindingDebugRow(row) {
727
+ const baseRow = {
728
+ name: row.name,
729
+ group: row.group,
730
+ binding: row.binding,
731
+ owner: row.owner,
732
+ kind: "unknown",
733
+ moduleName: row.moduleName,
734
+ resourceType: row.resourceType
735
+ };
736
+ if (row.accessDeclaration) {
737
+ const access = row.accessDeclaration.split(",").map((value) => value.trim());
738
+ if (access[0] === "uniform") {
739
+ return { ...baseRow, kind: "uniform", access: "uniform" };
740
+ }
741
+ if (access[0] === "storage") {
742
+ const storageAccess = access[1] || "read_write";
743
+ return {
744
+ ...baseRow,
745
+ kind: storageAccess === "read" ? "read-only-storage" : "storage",
746
+ access: storageAccess
747
+ };
748
+ }
749
+ }
750
+ if (row.resourceType === "sampler" || row.resourceType === "sampler_comparison") {
751
+ return {
752
+ ...baseRow,
753
+ kind: "sampler",
754
+ samplerKind: row.resourceType === "sampler_comparison" ? "comparison" : "filtering"
755
+ };
756
+ }
757
+ if (row.resourceType.startsWith("texture_storage_")) {
758
+ return {
759
+ ...baseRow,
760
+ kind: "storage-texture",
761
+ access: getStorageTextureAccess(row.resourceType),
762
+ viewDimension: getTextureViewDimension(row.resourceType)
763
+ };
764
+ }
765
+ if (row.resourceType.startsWith("texture_")) {
766
+ return {
767
+ ...baseRow,
768
+ kind: "texture",
769
+ viewDimension: getTextureViewDimension(row.resourceType),
770
+ sampleType: getTextureSampleType(row.resourceType),
771
+ multisampled: row.resourceType.startsWith("texture_multisampled_")
772
+ };
773
+ }
774
+ return baseRow;
775
+ }
776
+ function getBindingAssignmentKey(name, group, binding) {
777
+ return `${group}:${binding}:${name}`;
778
+ }
779
+ function getTextureViewDimension(resourceType) {
780
+ if (resourceType.includes("cube_array")) {
781
+ return "cube-array";
782
+ }
783
+ if (resourceType.includes("2d_array")) {
784
+ return "2d-array";
785
+ }
786
+ if (resourceType.includes("cube")) {
787
+ return "cube";
788
+ }
789
+ if (resourceType.includes("3d")) {
790
+ return "3d";
791
+ }
792
+ if (resourceType.includes("2d")) {
793
+ return "2d";
794
+ }
795
+ if (resourceType.includes("1d")) {
796
+ return "1d";
797
+ }
798
+ return void 0;
799
+ }
800
+ function getTextureSampleType(resourceType) {
801
+ if (resourceType.startsWith("texture_depth_")) {
802
+ return "depth";
803
+ }
804
+ if (resourceType.includes("<i32>")) {
805
+ return "sint";
806
+ }
807
+ if (resourceType.includes("<u32>")) {
808
+ return "uint";
809
+ }
810
+ if (resourceType.includes("<f32>")) {
811
+ return "float";
812
+ }
813
+ return void 0;
814
+ }
815
+ function getStorageTextureAccess(resourceType) {
816
+ const match = /,\s*([A-Za-z_][A-Za-z0-9_]*)\s*>$/.exec(resourceType);
817
+ return match == null ? void 0 : match[1];
818
+ }
819
+
567
820
  // dist/lib/shader-assembly/assemble-shaders.js
568
821
  var INJECT_SHADER_DECLARATIONS = `
569
822
 
570
823
  ${DECLARATION_INJECT_MARKER}
571
824
  `;
825
+ var MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
826
+ /@binding\(\s*(auto|\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
827
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(auto|\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
828
+ ];
829
+ var WGSL_BINDING_DECLARATION_REGEXES = [
830
+ /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
831
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
832
+ ];
833
+ var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
572
834
  var FRAGMENT_SHADER_PROLOGUE = (
573
835
  /* glsl */
574
836
  `precision highp float;
@@ -576,14 +838,17 @@ var FRAGMENT_SHADER_PROLOGUE = (
576
838
  );
577
839
  function assembleWGSLShader(options) {
578
840
  const modules = getShaderModuleDependencies(options.modules || []);
841
+ const { source: source4, bindingAssignments } = assembleShaderWGSL(options.platformInfo, {
842
+ ...options,
843
+ source: options.source,
844
+ stage: "vertex",
845
+ modules
846
+ });
579
847
  return {
580
- source: assembleShaderWGSL(options.platformInfo, {
581
- ...options,
582
- source: options.source,
583
- stage: "vertex",
584
- modules
585
- }),
586
- getUniforms: assembleGetUniforms(modules)
848
+ source: source4,
849
+ getUniforms: assembleGetUniforms(modules),
850
+ bindingAssignments,
851
+ bindingTable: getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments)
587
852
  };
588
853
  }
589
854
  function assembleGLSLShaderPair(options) {
@@ -645,11 +910,20 @@ function assembleShaderWGSL(platformInfo, options) {
645
910
  }
646
911
  }
647
912
  const modulesToInject = modules;
913
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(coreSource);
914
+ const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(modulesToInject, options._bindingRegistry, usedBindingsByGroup);
915
+ const bindingAssignments = [];
648
916
  for (const module2 of modulesToInject) {
649
917
  if (log2) {
650
918
  checkShaderModuleDeprecations(module2, coreSource, log2);
651
919
  }
652
- const moduleSource = getShaderModuleSource(module2, "wgsl");
920
+ const relocation = relocateWGSLModuleBindings(getShaderModuleSource(module2, "wgsl", log2), module2, {
921
+ usedBindingsByGroup,
922
+ bindingRegistry: options._bindingRegistry,
923
+ reservedBindingKeysByGroup
924
+ });
925
+ bindingAssignments.push(...relocation.bindingAssignments);
926
+ const moduleSource = relocation.source;
653
927
  assembledSource += moduleSource;
654
928
  const injections = ((_a = module2.injections) == null ? void 0 : _a[stage]) || {};
655
929
  for (const key in injections) {
@@ -668,9 +942,11 @@ function assembleShaderWGSL(platformInfo, options) {
668
942
  assembledSource += INJECT_SHADER_DECLARATIONS;
669
943
  assembledSource = injectShader(assembledSource, stage, declInjections);
670
944
  assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
945
+ assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
671
946
  assembledSource += coreSource;
672
947
  assembledSource = injectShader(assembledSource, stage, mainInjections);
673
- return assembledSource;
948
+ assertNoUnresolvedAutoBindings(assembledSource);
949
+ return { source: assembledSource, bindingAssignments };
674
950
  }
675
951
  function assembleShaderGLSL(platformInfo, options) {
676
952
  var _a;
@@ -734,7 +1010,7 @@ ${getApplicationDefines(allDefines)}
734
1010
  if (log2) {
735
1011
  checkShaderModuleDeprecations(module2, coreSource, log2);
736
1012
  }
737
- const moduleSource = getShaderModuleSource(module2, stage);
1013
+ const moduleSource = getShaderModuleSource(module2, stage, log2);
738
1014
  assembledSource += moduleSource;
739
1015
  const injections = ((_a = module2.instance) == null ? void 0 : _a.normalizedInjections[stage]) || {};
740
1016
  for (const key in injections) {
@@ -783,7 +1059,7 @@ function getApplicationDefines(defines = {}) {
783
1059
  }
784
1060
  return sourceText;
785
1061
  }
786
- function getShaderModuleSource(module2, stage) {
1062
+ function getShaderModuleSource(module2, stage, log2) {
787
1063
  let moduleSource;
788
1064
  switch (stage) {
789
1065
  case "vertex":
@@ -801,6 +1077,7 @@ function getShaderModuleSource(module2, stage) {
801
1077
  if (!module2.name) {
802
1078
  throw new Error("Shader module must have a name");
803
1079
  }
1080
+ validateShaderModuleUniformLayout(module2, stage, { log: log2 });
804
1081
  const moduleName = module2.name.toUpperCase().replace(/[^0-9a-z]/gi, "_");
805
1082
  let source4 = `// ----- MODULE ${module2.name} ---------------
806
1083
 
@@ -813,13 +1090,177 @@ function getShaderModuleSource(module2, stage) {
813
1090
  `;
814
1091
  return source4;
815
1092
  }
1093
+ function getUsedBindingsByGroupFromApplicationWGSL(source4) {
1094
+ const usedBindingsByGroup = /* @__PURE__ */ new Map();
1095
+ for (const regex of WGSL_BINDING_DECLARATION_REGEXES) {
1096
+ regex.lastIndex = 0;
1097
+ let match;
1098
+ while (match = regex.exec(source4)) {
1099
+ const isBindingFirst = regex === WGSL_BINDING_DECLARATION_REGEXES[0];
1100
+ const location = Number(match[isBindingFirst ? 1 : 2]);
1101
+ const group = Number(match[isBindingFirst ? 2 : 1]);
1102
+ const name = match[4];
1103
+ validateApplicationWGSLBinding(group, location, name);
1104
+ registerUsedBindingLocation(usedBindingsByGroup, group, location, `application binding "${name}"`);
1105
+ }
1106
+ }
1107
+ return usedBindingsByGroup;
1108
+ }
1109
+ function relocateWGSLModuleBindings(moduleSource, module2, context) {
1110
+ const bindingAssignments = [];
1111
+ const relocationState = {
1112
+ sawSupportedBindingDeclaration: false,
1113
+ nextHintedBindingLocation: typeof module2.firstBindingSlot === "number" ? module2.firstBindingSlot : null
1114
+ };
1115
+ let relocatedSource = relocateWGSLModuleBindingsWithRegex(moduleSource, MODULE_WGSL_BINDING_DECLARATION_REGEXES[0], { isBindingFirst: true, module: module2, context, bindingAssignments, relocationState });
1116
+ relocatedSource = relocateWGSLModuleBindingsWithRegex(relocatedSource, MODULE_WGSL_BINDING_DECLARATION_REGEXES[1], { isBindingFirst: false, module: module2, context, bindingAssignments, relocationState });
1117
+ if (moduleSource.includes("@binding(auto)") && !relocationState.sawSupportedBindingDeclaration) {
1118
+ throw new Error(`Unsupported @binding(auto) declaration form in module "${module2.name}". Use "@group(N) @binding(auto) var ..." or "@binding(auto) @group(N) var ..." on a single line.`);
1119
+ }
1120
+ return { source: relocatedSource, bindingAssignments };
1121
+ }
1122
+ function relocateWGSLModuleBindingsWithRegex(source4, regex, params) {
1123
+ return source4.replace(regex, (...replaceArguments) => relocateWGSLModuleBindingMatch(replaceArguments, params));
1124
+ }
1125
+ function relocateWGSLModuleBindingMatch(replaceArguments, params) {
1126
+ var _a, _b;
1127
+ const { isBindingFirst, module: module2, context, bindingAssignments, relocationState } = params;
1128
+ relocationState.sawSupportedBindingDeclaration = true;
1129
+ const match = replaceArguments[0];
1130
+ const bindingToken = replaceArguments[isBindingFirst ? 1 : 2];
1131
+ const groupToken = replaceArguments[isBindingFirst ? 2 : 1];
1132
+ const name = replaceArguments[4];
1133
+ const group = Number(groupToken);
1134
+ if (bindingToken === "auto") {
1135
+ const registryKey = getBindingRegistryKey(group, module2.name, name);
1136
+ const registryLocation = (_a = context.bindingRegistry) == null ? void 0 : _a.get(registryKey);
1137
+ const location2 = registryLocation !== void 0 ? registryLocation : relocationState.nextHintedBindingLocation === null ? allocateAutoBindingLocation(group, context.usedBindingsByGroup) : allocateAutoBindingLocation(group, context.usedBindingsByGroup, relocationState.nextHintedBindingLocation);
1138
+ validateModuleWGSLBinding(module2.name, group, location2, name);
1139
+ if (registryLocation !== void 0 && claimReservedBindingLocation(context.reservedBindingKeysByGroup, group, location2, registryKey)) {
1140
+ bindingAssignments.push({ moduleName: module2.name, name, group, location: location2 });
1141
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location2})`);
1142
+ }
1143
+ registerUsedBindingLocation(context.usedBindingsByGroup, group, location2, `module "${module2.name}" binding "${name}"`);
1144
+ (_b = context.bindingRegistry) == null ? void 0 : _b.set(registryKey, location2);
1145
+ bindingAssignments.push({ moduleName: module2.name, name, group, location: location2 });
1146
+ if (relocationState.nextHintedBindingLocation !== null && registryLocation === void 0) {
1147
+ relocationState.nextHintedBindingLocation = location2 + 1;
1148
+ }
1149
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location2})`);
1150
+ }
1151
+ const location = Number(bindingToken);
1152
+ validateModuleWGSLBinding(module2.name, group, location, name);
1153
+ registerUsedBindingLocation(context.usedBindingsByGroup, group, location, `module "${module2.name}" binding "${name}"`);
1154
+ bindingAssignments.push({ moduleName: module2.name, name, group, location });
1155
+ return match;
1156
+ }
1157
+ function reserveRegisteredModuleBindings(modules, bindingRegistry, usedBindingsByGroup) {
1158
+ const reservedBindingKeysByGroup = /* @__PURE__ */ new Map();
1159
+ if (!bindingRegistry) {
1160
+ return reservedBindingKeysByGroup;
1161
+ }
1162
+ for (const module2 of modules) {
1163
+ for (const binding of getModuleWGSLBindingDeclarations(module2)) {
1164
+ const registryKey = getBindingRegistryKey(binding.group, module2.name, binding.name);
1165
+ const location = bindingRegistry.get(registryKey);
1166
+ if (location !== void 0) {
1167
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(binding.group) || /* @__PURE__ */ new Map();
1168
+ const existingReservation = reservedBindingKeys.get(location);
1169
+ if (existingReservation && existingReservation !== registryKey) {
1170
+ throw new Error(`Duplicate WGSL binding reservation for modules "${existingReservation}" and "${registryKey}": group ${binding.group}, binding ${location}.`);
1171
+ }
1172
+ registerUsedBindingLocation(usedBindingsByGroup, binding.group, location, `registered module binding "${registryKey}"`);
1173
+ reservedBindingKeys.set(location, registryKey);
1174
+ reservedBindingKeysByGroup.set(binding.group, reservedBindingKeys);
1175
+ }
1176
+ }
1177
+ }
1178
+ return reservedBindingKeysByGroup;
1179
+ }
1180
+ function claimReservedBindingLocation(reservedBindingKeysByGroup, group, location, registryKey) {
1181
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(group);
1182
+ if (!reservedBindingKeys) {
1183
+ return false;
1184
+ }
1185
+ const reservedKey = reservedBindingKeys.get(location);
1186
+ if (!reservedKey) {
1187
+ return false;
1188
+ }
1189
+ if (reservedKey !== registryKey) {
1190
+ throw new Error(`Registered module binding "${registryKey}" collided with "${reservedKey}": group ${group}, binding ${location}.`);
1191
+ }
1192
+ return true;
1193
+ }
1194
+ function getModuleWGSLBindingDeclarations(module2) {
1195
+ const declarations = [];
1196
+ const moduleSource = module2.source || "";
1197
+ for (const regex of MODULE_WGSL_BINDING_DECLARATION_REGEXES) {
1198
+ regex.lastIndex = 0;
1199
+ let match;
1200
+ while (match = regex.exec(moduleSource)) {
1201
+ const isBindingFirst = regex === MODULE_WGSL_BINDING_DECLARATION_REGEXES[0];
1202
+ declarations.push({
1203
+ name: match[4],
1204
+ group: Number(match[isBindingFirst ? 2 : 1])
1205
+ });
1206
+ }
1207
+ }
1208
+ return declarations;
1209
+ }
1210
+ function validateApplicationWGSLBinding(group, location, name) {
1211
+ if (group === 0 && location >= RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
1212
+ throw new Error(`Application binding "${name}" in group 0 uses reserved binding ${location}. Application-owned explicit group-0 bindings must stay below ${RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT}.`);
1213
+ }
1214
+ }
1215
+ function validateModuleWGSLBinding(moduleName, group, location, name) {
1216
+ if (group === 0 && location < RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
1217
+ throw new Error(`Module "${moduleName}" binding "${name}" in group 0 uses reserved application binding ${location}. Module-owned explicit group-0 bindings must be ${RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT} or higher.`);
1218
+ }
1219
+ }
1220
+ function registerUsedBindingLocation(usedBindingsByGroup, group, location, label) {
1221
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1222
+ if (usedBindings.has(location)) {
1223
+ throw new Error(`Duplicate WGSL binding assignment for ${label}: group ${group}, binding ${location}.`);
1224
+ }
1225
+ usedBindings.add(location);
1226
+ usedBindingsByGroup.set(group, usedBindings);
1227
+ }
1228
+ function allocateAutoBindingLocation(group, usedBindingsByGroup, preferredBindingLocation) {
1229
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1230
+ let nextBinding = preferredBindingLocation ?? (group === 0 ? RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT : usedBindings.size > 0 ? Math.max(...usedBindings) + 1 : 0);
1231
+ while (usedBindings.has(nextBinding)) {
1232
+ nextBinding++;
1233
+ }
1234
+ return nextBinding;
1235
+ }
1236
+ function assertNoUnresolvedAutoBindings(source4) {
1237
+ if (/@binding\(\s*auto\s*\)/.test(source4)) {
1238
+ throw new Error("Unresolved @binding(auto) remained in assembled WGSL source.");
1239
+ }
1240
+ }
1241
+ function formatWGSLBindingAssignmentComments(bindingAssignments) {
1242
+ if (bindingAssignments.length === 0) {
1243
+ return "";
1244
+ }
1245
+ let source4 = "// ----- MODULE WGSL BINDING ASSIGNMENTS ---------------\n";
1246
+ for (const bindingAssignment of bindingAssignments) {
1247
+ source4 += `// ${bindingAssignment.moduleName}.${bindingAssignment.name} -> @group(${bindingAssignment.group}) @binding(${bindingAssignment.location})
1248
+ `;
1249
+ }
1250
+ source4 += "\n";
1251
+ return source4;
1252
+ }
1253
+ function getBindingRegistryKey(group, moduleName, bindingName) {
1254
+ return `${group}:${moduleName}:${bindingName}`;
1255
+ }
816
1256
 
817
1257
  // dist/lib/preprocessor/preprocessor.js
818
- var IFDEF_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*$/;
819
- var IFNDEF_REGEXP = /^\s*\#\s*ifndef\s*([a-zA-Z_]+)\s*(?:\/\/.*)?$/;
1258
+ var DEFINE_NAME_PATTERN = "([a-zA-Z_][a-zA-Z0-9_]*)";
1259
+ var IFDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*$`);
1260
+ var IFNDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifndef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
820
1261
  var ELSE_REGEXP = /^\s*\#\s*else\s*(?:\/\/.*)?$/;
821
1262
  var ENDIF_REGEXP = /^\s*\#\s*endif\s*$/;
822
- var IFDEF_WITH_COMMENT_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*(?:\/\/.*)?$/;
1263
+ var IFDEF_WITH_COMMENT_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
823
1264
  var ENDIF_WITH_COMMENT_REGEXP = /^\s*\#\s*endif\s*(?:\/\/.*)?$/;
824
1265
  function preprocess(source4, options) {
825
1266
  var _a, _b;
@@ -866,6 +1307,8 @@ var _ShaderAssembler = class {
866
1307
  _hookFunctions = [];
867
1308
  /** Shader modules */
868
1309
  _defaultModules = [];
1310
+ /** Stable per-run WGSL auto-binding assignments keyed by group/module/binding. */
1311
+ _wgslBindingRegistry = /* @__PURE__ */ new Map();
869
1312
  /**
870
1313
  * A default shader assembler instance - the natural place to register default modules and hooks
871
1314
  * @returns
@@ -909,10 +1352,11 @@ var _ShaderAssembler = class {
909
1352
  assembleWGSLShader(props) {
910
1353
  const modules = this._getModuleList(props.modules);
911
1354
  const hookFunctions = this._hookFunctions;
912
- const { source: source4, getUniforms: getUniforms4 } = assembleWGSLShader({
1355
+ const { source: source4, getUniforms: getUniforms4, bindingAssignments } = assembleWGSLShader({
913
1356
  ...props,
914
1357
  // @ts-expect-error
915
1358
  source: props.source,
1359
+ _bindingRegistry: this._wgslBindingRegistry,
916
1360
  modules,
917
1361
  hookFunctions
918
1362
  });
@@ -924,7 +1368,13 @@ var _ShaderAssembler = class {
924
1368
  ...props.defines
925
1369
  };
926
1370
  const preprocessedSource = props.platformInfo.shaderLanguage === "wgsl" ? preprocess(source4, { defines }) : source4;
927
- return { source: preprocessedSource, getUniforms: getUniforms4, modules };
1371
+ return {
1372
+ source: preprocessedSource,
1373
+ getUniforms: getUniforms4,
1374
+ modules,
1375
+ bindingAssignments,
1376
+ bindingTable: getShaderBindingDebugRowsFromWGSL(preprocessedSource, bindingAssignments)
1377
+ };
928
1378
  }
929
1379
  /**
930
1380
  * Assemble a pair of shaders into a single shader program
@@ -1089,6 +1539,9 @@ function generateGLSLUniformDeclarations(module2, options) {
1089
1539
  case "uniforms":
1090
1540
  }
1091
1541
  for (const [uniformName, uniformFormat] of Object.entries(module2.uniformTypes || {})) {
1542
+ if (typeof uniformFormat !== "string") {
1543
+ throw new Error(`Composite uniform types are not supported by GLSL shader generation: ${module2.name}.${uniformName}`);
1544
+ }
1092
1545
  const glslUniformType = getGLSLUniformType(uniformFormat);
1093
1546
  switch (options.uniforms) {
1094
1547
  case "scoped-interface-blocks":
@@ -1149,6 +1602,9 @@ function generateWGSLUniformDeclarations(module2, options) {
1149
1602
  const wgsl = [];
1150
1603
  wgsl.push(`struct ${capitalize(module2.name)} {`);
1151
1604
  for (const [uniformName, uniformFormat] of Object.entries((module2 == null ? void 0 : module2.uniformTypes) || {})) {
1605
+ if (typeof uniformFormat !== "string") {
1606
+ throw new Error(`Composite uniform types are not supported by WGSL shader generation: ${module2.name}.${uniformName}`);
1607
+ }
1152
1608
  const wgslUniformType = uniformFormat;
1153
1609
  wgsl.push(` ${uniformName} : ${wgslUniformType};`);
1154
1610
  }
@@ -1463,6 +1919,7 @@ var fp64arithmeticShader = (
1463
1919
  `
1464
1920
  uniform fp64arithmeticUniforms {
1465
1921
  uniform float ONE;
1922
+ uniform float SPLIT;
1466
1923
  } fp64;
1467
1924
 
1468
1925
  /*
@@ -1472,6 +1929,12 @@ The purpose of this workaround is to prevent shader compilers from
1472
1929
  optimizing away necessary arithmetic operations by swapping their sequences
1473
1930
  or transform the equation to some 'equivalent' form.
1474
1931
 
1932
+ These helpers implement Dekker/Veltkamp-style error tracking. If the compiler
1933
+ folds constants or reassociates the arithmetic, the high/low split can stop
1934
+ tracking the rounding error correctly. That failure mode tends to look fine in
1935
+ simple coordinate setup, but then breaks down inside iterative arithmetic such
1936
+ as fp64 Mandelbrot loops.
1937
+
1475
1938
  The method is to multiply an artifical variable, ONE, which will be known to
1476
1939
  the compiler to be 1 only at runtime. The whole expression is then represented
1477
1940
  as a polynomial with respective to ONE. In the coefficients of all terms, only one a
@@ -1480,17 +1943,23 @@ and one b should appear
1480
1943
  err = (a + b) * ONE^6 - a * ONE^5 - (a + b) * ONE^4 + a * ONE^3 - b - (a + b) * ONE^2 + a * ONE
1481
1944
  */
1482
1945
 
1483
- // Divide float number to high and low floats to extend fraction bits
1484
- vec2 split(float a) {
1485
- const float SPLIT = 4097.0;
1486
- float t = a * SPLIT;
1946
+ float prevent_fp64_optimization(float value) {
1487
1947
  #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
1488
- float a_hi = t * fp64.ONE - (t - a);
1489
- float a_lo = a * fp64.ONE - a_hi;
1948
+ return value + fp64.ONE * 0.0;
1490
1949
  #else
1491
- float a_hi = t - (t - a);
1492
- float a_lo = a - a_hi;
1950
+ return value;
1493
1951
  #endif
1952
+ }
1953
+
1954
+ // Divide float number to high and low floats to extend fraction bits
1955
+ vec2 split(float a) {
1956
+ // Keep SPLIT as a runtime uniform so the compiler cannot fold the Dekker
1957
+ // split into a constant expression and reassociate the recovery steps.
1958
+ float split = prevent_fp64_optimization(fp64.SPLIT);
1959
+ float t = prevent_fp64_optimization(a * split);
1960
+ float temp = t - a;
1961
+ float a_hi = t - temp;
1962
+ float a_lo = a - a_hi;
1494
1963
  return vec2(a_hi, a_lo);
1495
1964
  }
1496
1965
 
@@ -1554,8 +2023,26 @@ vec2 twoProd(float a, float b) {
1554
2023
  float prod = a * b;
1555
2024
  vec2 a_fp64 = split(a);
1556
2025
  vec2 b_fp64 = split(b);
1557
- float err = ((a_fp64.x * b_fp64.x - prod) + a_fp64.x * b_fp64.y +
1558
- a_fp64.y * b_fp64.x) + a_fp64.y * b_fp64.y;
2026
+ // twoProd is especially sensitive because mul_fp64 and div_fp64 both depend
2027
+ // on the split terms and cross terms staying in the original evaluation
2028
+ // order. If the compiler folds or reassociates them, the low part tends to
2029
+ // collapse to zero or NaN on some drivers.
2030
+ float highProduct = prevent_fp64_optimization(a_fp64.x * b_fp64.x);
2031
+ float crossProduct1 = prevent_fp64_optimization(a_fp64.x * b_fp64.y);
2032
+ float crossProduct2 = prevent_fp64_optimization(a_fp64.y * b_fp64.x);
2033
+ float lowProduct = prevent_fp64_optimization(a_fp64.y * b_fp64.y);
2034
+ #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
2035
+ float err1 = (highProduct - prod) * fp64.ONE;
2036
+ float err2 = crossProduct1 * fp64.ONE * fp64.ONE;
2037
+ float err3 = crossProduct2 * fp64.ONE * fp64.ONE * fp64.ONE;
2038
+ float err4 = lowProduct * fp64.ONE * fp64.ONE * fp64.ONE * fp64.ONE;
2039
+ #else
2040
+ float err1 = highProduct - prod;
2041
+ float err2 = crossProduct1;
2042
+ float err3 = crossProduct2;
2043
+ float err4 = lowProduct;
2044
+ #endif
2045
+ float err = ((err1 + err2) + err3) + err4;
1559
2046
  return vec2(prod, err);
1560
2047
  }
1561
2048
 
@@ -1631,6 +2118,218 @@ vec2 sqrt_fp64(vec2 a) {
1631
2118
  `
1632
2119
  );
1633
2120
 
2121
+ // dist/modules/math/fp64/fp64-arithmetic-wgsl.js
2122
+ var fp64arithmeticWGSL = (
2123
+ /* wgsl */
2124
+ `struct Fp64ArithmeticUniforms {
2125
+ ONE: f32,
2126
+ SPLIT: f32,
2127
+ };
2128
+
2129
+ @group(0) @binding(auto) var<uniform> fp64arithmetic : Fp64ArithmeticUniforms;
2130
+
2131
+ fn fp64_nan(seed: f32) -> f32 {
2132
+ let nanBits = 0x7fc00000u | select(0u, 1u, seed < 0.0);
2133
+ return bitcast<f32>(nanBits);
2134
+ }
2135
+
2136
+ fn fp64_runtime_zero() -> f32 {
2137
+ return fp64arithmetic.ONE * 0.0;
2138
+ }
2139
+
2140
+ fn prevent_fp64_optimization(value: f32) -> f32 {
2141
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2142
+ return value + fp64_runtime_zero();
2143
+ #else
2144
+ return value;
2145
+ #endif
2146
+ }
2147
+
2148
+ fn split(a: f32) -> vec2f {
2149
+ let splitValue = prevent_fp64_optimization(fp64arithmetic.SPLIT + fp64_runtime_zero());
2150
+ let t = prevent_fp64_optimization(a * splitValue);
2151
+ let temp = prevent_fp64_optimization(t - a);
2152
+ let aHi = prevent_fp64_optimization(t - temp);
2153
+ let aLo = prevent_fp64_optimization(a - aHi);
2154
+ return vec2f(aHi, aLo);
2155
+ }
2156
+
2157
+ fn split2(a: vec2f) -> vec2f {
2158
+ var b = split(a.x);
2159
+ b.y = b.y + a.y;
2160
+ return b;
2161
+ }
2162
+
2163
+ fn quickTwoSum(a: f32, b: f32) -> vec2f {
2164
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2165
+ let sum = prevent_fp64_optimization((a + b) * fp64arithmetic.ONE);
2166
+ let err = prevent_fp64_optimization(b - (sum - a) * fp64arithmetic.ONE);
2167
+ #else
2168
+ let sum = prevent_fp64_optimization(a + b);
2169
+ let err = prevent_fp64_optimization(b - (sum - a));
2170
+ #endif
2171
+ return vec2f(sum, err);
2172
+ }
2173
+
2174
+ fn twoSum(a: f32, b: f32) -> vec2f {
2175
+ let s = prevent_fp64_optimization(a + b);
2176
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2177
+ let v = prevent_fp64_optimization((s * fp64arithmetic.ONE - a) * fp64arithmetic.ONE);
2178
+ let err =
2179
+ prevent_fp64_optimization((a - (s - v) * fp64arithmetic.ONE) *
2180
+ fp64arithmetic.ONE *
2181
+ fp64arithmetic.ONE *
2182
+ fp64arithmetic.ONE) +
2183
+ prevent_fp64_optimization(b - v);
2184
+ #else
2185
+ let v = prevent_fp64_optimization(s - a);
2186
+ let err = prevent_fp64_optimization(a - (s - v)) + prevent_fp64_optimization(b - v);
2187
+ #endif
2188
+ return vec2f(s, err);
2189
+ }
2190
+
2191
+ fn twoSub(a: f32, b: f32) -> vec2f {
2192
+ let s = prevent_fp64_optimization(a - b);
2193
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2194
+ let v = prevent_fp64_optimization((s * fp64arithmetic.ONE - a) * fp64arithmetic.ONE);
2195
+ let err =
2196
+ prevent_fp64_optimization((a - (s - v) * fp64arithmetic.ONE) *
2197
+ fp64arithmetic.ONE *
2198
+ fp64arithmetic.ONE *
2199
+ fp64arithmetic.ONE) -
2200
+ prevent_fp64_optimization(b + v);
2201
+ #else
2202
+ let v = prevent_fp64_optimization(s - a);
2203
+ let err = prevent_fp64_optimization(a - (s - v)) - prevent_fp64_optimization(b + v);
2204
+ #endif
2205
+ return vec2f(s, err);
2206
+ }
2207
+
2208
+ fn twoSqr(a: f32) -> vec2f {
2209
+ let prod = prevent_fp64_optimization(a * a);
2210
+ let aFp64 = split(a);
2211
+ let highProduct = prevent_fp64_optimization(aFp64.x * aFp64.x);
2212
+ let crossProduct = prevent_fp64_optimization(2.0 * aFp64.x * aFp64.y);
2213
+ let lowProduct = prevent_fp64_optimization(aFp64.y * aFp64.y);
2214
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2215
+ let err =
2216
+ (prevent_fp64_optimization(highProduct - prod) * fp64arithmetic.ONE +
2217
+ crossProduct * fp64arithmetic.ONE * fp64arithmetic.ONE) +
2218
+ lowProduct * fp64arithmetic.ONE * fp64arithmetic.ONE * fp64arithmetic.ONE;
2219
+ #else
2220
+ let err = ((prevent_fp64_optimization(highProduct - prod) + crossProduct) + lowProduct);
2221
+ #endif
2222
+ return vec2f(prod, err);
2223
+ }
2224
+
2225
+ fn twoProd(a: f32, b: f32) -> vec2f {
2226
+ let prod = prevent_fp64_optimization(a * b);
2227
+ let aFp64 = split(a);
2228
+ let bFp64 = split(b);
2229
+ let highProduct = prevent_fp64_optimization(aFp64.x * bFp64.x);
2230
+ let crossProduct1 = prevent_fp64_optimization(aFp64.x * bFp64.y);
2231
+ let crossProduct2 = prevent_fp64_optimization(aFp64.y * bFp64.x);
2232
+ let lowProduct = prevent_fp64_optimization(aFp64.y * bFp64.y);
2233
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2234
+ let err1 = (highProduct - prod) * fp64arithmetic.ONE;
2235
+ let err2 = crossProduct1 * fp64arithmetic.ONE * fp64arithmetic.ONE;
2236
+ let err3 = crossProduct2 * fp64arithmetic.ONE * fp64arithmetic.ONE * fp64arithmetic.ONE;
2237
+ let err4 =
2238
+ lowProduct *
2239
+ fp64arithmetic.ONE *
2240
+ fp64arithmetic.ONE *
2241
+ fp64arithmetic.ONE *
2242
+ fp64arithmetic.ONE;
2243
+ #else
2244
+ let err1 = highProduct - prod;
2245
+ let err2 = crossProduct1;
2246
+ let err3 = crossProduct2;
2247
+ let err4 = lowProduct;
2248
+ #endif
2249
+ let err12InputA = prevent_fp64_optimization(err1);
2250
+ let err12InputB = prevent_fp64_optimization(err2);
2251
+ let err12 = prevent_fp64_optimization(err12InputA + err12InputB);
2252
+ let err123InputA = prevent_fp64_optimization(err12);
2253
+ let err123InputB = prevent_fp64_optimization(err3);
2254
+ let err123 = prevent_fp64_optimization(err123InputA + err123InputB);
2255
+ let err1234InputA = prevent_fp64_optimization(err123);
2256
+ let err1234InputB = prevent_fp64_optimization(err4);
2257
+ let err = prevent_fp64_optimization(err1234InputA + err1234InputB);
2258
+ return vec2f(prod, err);
2259
+ }
2260
+
2261
+ fn sum_fp64(a: vec2f, b: vec2f) -> vec2f {
2262
+ var s = twoSum(a.x, b.x);
2263
+ let t = twoSum(a.y, b.y);
2264
+ s.y = prevent_fp64_optimization(s.y + t.x);
2265
+ s = quickTwoSum(s.x, s.y);
2266
+ s.y = prevent_fp64_optimization(s.y + t.y);
2267
+ s = quickTwoSum(s.x, s.y);
2268
+ return s;
2269
+ }
2270
+
2271
+ fn sub_fp64(a: vec2f, b: vec2f) -> vec2f {
2272
+ var s = twoSub(a.x, b.x);
2273
+ let t = twoSub(a.y, b.y);
2274
+ s.y = prevent_fp64_optimization(s.y + t.x);
2275
+ s = quickTwoSum(s.x, s.y);
2276
+ s.y = prevent_fp64_optimization(s.y + t.y);
2277
+ s = quickTwoSum(s.x, s.y);
2278
+ return s;
2279
+ }
2280
+
2281
+ fn mul_fp64(a: vec2f, b: vec2f) -> vec2f {
2282
+ var prod = twoProd(a.x, b.x);
2283
+ let crossProduct1 = prevent_fp64_optimization(a.x * b.y);
2284
+ prod.y = prevent_fp64_optimization(prod.y + crossProduct1);
2285
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
2286
+ prod = split2(prod);
2287
+ #endif
2288
+ prod = quickTwoSum(prod.x, prod.y);
2289
+ let crossProduct2 = prevent_fp64_optimization(a.y * b.x);
2290
+ prod.y = prevent_fp64_optimization(prod.y + crossProduct2);
2291
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
2292
+ prod = split2(prod);
2293
+ #endif
2294
+ prod = quickTwoSum(prod.x, prod.y);
2295
+ return prod;
2296
+ }
2297
+
2298
+ fn div_fp64(a: vec2f, b: vec2f) -> vec2f {
2299
+ let xn = prevent_fp64_optimization(1.0 / b.x);
2300
+ let yn = mul_fp64(a, vec2f(xn, fp64_runtime_zero()));
2301
+ let diff = prevent_fp64_optimization(sub_fp64(a, mul_fp64(b, yn)).x);
2302
+ let prod = twoProd(xn, diff);
2303
+ return sum_fp64(yn, prod);
2304
+ }
2305
+
2306
+ fn sqrt_fp64(a: vec2f) -> vec2f {
2307
+ if (a.x == 0.0 && a.y == 0.0) {
2308
+ return vec2f(0.0, 0.0);
2309
+ }
2310
+ if (a.x < 0.0) {
2311
+ let nanValue = fp64_nan(a.x);
2312
+ return vec2f(nanValue, nanValue);
2313
+ }
2314
+
2315
+ let x = prevent_fp64_optimization(1.0 / sqrt(a.x));
2316
+ let yn = prevent_fp64_optimization(a.x * x);
2317
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2318
+ let ynSqr = twoSqr(yn) * fp64arithmetic.ONE;
2319
+ #else
2320
+ let ynSqr = twoSqr(yn);
2321
+ #endif
2322
+ let diff = prevent_fp64_optimization(sub_fp64(a, ynSqr).x);
2323
+ let prod = twoProd(prevent_fp64_optimization(x * 0.5), diff);
2324
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
2325
+ return sum_fp64(split(yn), prod);
2326
+ #else
2327
+ return sum_fp64(vec2f(yn, 0.0), prod);
2328
+ #endif
2329
+ }
2330
+ `
2331
+ );
2332
+
1634
2333
  // dist/modules/math/fp64/fp64-functions-glsl.js
1635
2334
  var fp64functionShader = (
1636
2335
  /* glsl */
@@ -2309,13 +3008,18 @@ void mat4_vec4_mul_fp64(vec2 b[16], vec2 a[4], out vec2 out_val[4]) {
2309
3008
  // dist/modules/math/fp64/fp64.js
2310
3009
  var defaultUniforms = {
2311
3010
  // Used in LUMA_FP64_CODE_ELIMINATION_WORKAROUND
2312
- ONE: 1
3011
+ ONE: 1,
3012
+ // Runtime split factor for Dekker splitting. Keeping this as a uniform helps
3013
+ // prevent aggressive constant folding in shader compilers.
3014
+ SPLIT: 4097
2313
3015
  };
2314
3016
  var fp64arithmetic = {
2315
3017
  name: "fp64arithmetic",
3018
+ source: fp64arithmeticWGSL,
3019
+ fs: fp64arithmeticShader,
2316
3020
  vs: fp64arithmeticShader,
2317
3021
  defaultUniforms,
2318
- uniformTypes: { ONE: "f32" },
3022
+ uniformTypes: { ONE: "f32", SPLIT: "f32" },
2319
3023
  // Additional Functions
2320
3024
  fp64ify,
2321
3025
  fp64LowPart,
@@ -2529,7 +3233,7 @@ struct skinUniforms {
2529
3233
  jointMatrix: array<mat4x4<f32>, ${SKIN_MAX_JOINTS}>,
2530
3234
  };
2531
3235
 
2532
- @binding(19) @group(0) var<uniform> skin: skinUniforms;
3236
+ @group(0) @binding(auto) var<uniform> skin: skinUniforms;
2533
3237
 
2534
3238
  fn getSkinMatrix(weights: vec4f, joints: vec4u) -> mat4x4<f32> {
2535
3239
  return (weights.x * skin.jointMatrix[joints.x])
@@ -2563,6 +3267,7 @@ var skin = {
2563
3267
  props: {},
2564
3268
  uniforms: {},
2565
3269
  name: "skin",
3270
+ bindingLayout: [{ name: "skin", group: 0 }],
2566
3271
  dependencies: [],
2567
3272
  source: source2,
2568
3273
  vs: vs2,
@@ -2606,10 +3311,7 @@ var skin = {
2606
3311
  };
2607
3312
  },
2608
3313
  uniformTypes: {
2609
- jointMatrix: "mat4x4<f32>"
2610
- },
2611
- uniformSizes: {
2612
- jointMatrix: SKIN_MAX_JOINTS
3314
+ jointMatrix: ["mat4x4<f32>", SKIN_MAX_JOINTS]
2613
3315
  }
2614
3316
  };
2615
3317
 
@@ -2632,77 +3334,51 @@ struct PointLight {
2632
3334
  vec3 attenuation; // 2nd order x:Constant-y:Linear-z:Exponential
2633
3335
  };
2634
3336
 
3337
+ struct SpotLight {
3338
+ vec3 color;
3339
+ vec3 position;
3340
+ vec3 direction;
3341
+ vec3 attenuation;
3342
+ vec2 coneCos;
3343
+ };
3344
+
2635
3345
  struct DirectionalLight {
2636
3346
  vec3 color;
2637
3347
  vec3 direction;
2638
3348
  };
2639
3349
 
3350
+ struct UniformLight {
3351
+ vec3 color;
3352
+ vec3 position;
3353
+ vec3 direction;
3354
+ vec3 attenuation;
3355
+ vec2 coneCos;
3356
+ };
3357
+
2640
3358
  uniform lightingUniforms {
2641
3359
  int enabled;
2642
- int lightType;
2643
-
2644
3360
  int directionalLightCount;
2645
3361
  int pointLightCount;
2646
-
3362
+ int spotLightCount;
2647
3363
  vec3 ambientColor;
2648
-
2649
- vec3 lightColor0;
2650
- vec3 lightPosition0;
2651
- vec3 lightDirection0;
2652
- vec3 lightAttenuation0;
2653
-
2654
- vec3 lightColor1;
2655
- vec3 lightPosition1;
2656
- vec3 lightDirection1;
2657
- vec3 lightAttenuation1;
2658
-
2659
- vec3 lightColor2;
2660
- vec3 lightPosition2;
2661
- vec3 lightDirection2;
2662
- vec3 lightAttenuation2;
2663
-
2664
- vec3 lightColor3;
2665
- vec3 lightPosition3;
2666
- vec3 lightDirection3;
2667
- vec3 lightAttenuation3;
2668
-
2669
- vec3 lightColor4;
2670
- vec3 lightPosition4;
2671
- vec3 lightDirection4;
2672
- vec3 lightAttenuation4;
3364
+ UniformLight lights[5];
2673
3365
  } lighting;
2674
3366
 
2675
3367
  PointLight lighting_getPointLight(int index) {
2676
- switch (index) {
2677
- case 0:
2678
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
2679
- case 1:
2680
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
2681
- case 2:
2682
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
2683
- case 3:
2684
- return PointLight(lighting.lightColor3, lighting.lightPosition3, lighting.lightAttenuation3);
2685
- case 4:
2686
- default:
2687
- return PointLight(lighting.lightColor4, lighting.lightPosition4, lighting.lightAttenuation4);
2688
- }
3368
+ UniformLight light = lighting.lights[index];
3369
+ return PointLight(light.color, light.position, light.attenuation);
3370
+ }
3371
+
3372
+ SpotLight lighting_getSpotLight(int index) {
3373
+ UniformLight light = lighting.lights[lighting.pointLightCount + index];
3374
+ return SpotLight(light.color, light.position, light.direction, light.attenuation, light.coneCos);
2689
3375
  }
2690
3376
 
2691
3377
  DirectionalLight lighting_getDirectionalLight(int index) {
2692
- switch (index) {
2693
- case 0:
2694
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
2695
- case 1:
2696
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
2697
- case 2:
2698
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
2699
- case 3:
2700
- return DirectionalLight(lighting.lightColor3, lighting.lightDirection3);
2701
- case 4:
2702
- default:
2703
- return DirectionalLight(lighting.lightColor4, lighting.lightDirection4);
2704
- }
2705
- }
3378
+ UniformLight light =
3379
+ lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
3380
+ return DirectionalLight(light.color, light.direction);
3381
+ }
2706
3382
 
2707
3383
  float getPointLightAttenuation(PointLight pointLight, float distance) {
2708
3384
  return pointLight.attenuation.x
@@ -2710,6 +3386,20 @@ float getPointLightAttenuation(PointLight pointLight, float distance) {
2710
3386
  + pointLight.attenuation.z * distance * distance;
2711
3387
  }
2712
3388
 
3389
+ float getSpotLightAttenuation(SpotLight spotLight, vec3 positionWorldspace) {
3390
+ vec3 light_direction = normalize(positionWorldspace - spotLight.position);
3391
+ float coneFactor = smoothstep(
3392
+ spotLight.coneCos.y,
3393
+ spotLight.coneCos.x,
3394
+ dot(normalize(spotLight.direction), light_direction)
3395
+ );
3396
+ float distanceAttenuation = getPointLightAttenuation(
3397
+ PointLight(spotLight.color, spotLight.position, spotLight.attenuation),
3398
+ distance(spotLight.position, positionWorldspace)
3399
+ );
3400
+ return distanceAttenuation / max(coneFactor, 0.0001);
3401
+ }
3402
+
2713
3403
  // #endif
2714
3404
  `
2715
3405
  );
@@ -2730,105 +3420,85 @@ struct PointLight {
2730
3420
  attenuation: vec3<f32>, // 2nd order x:Constant-y:Linear-z:Exponential
2731
3421
  };
2732
3422
 
3423
+ struct SpotLight {
3424
+ color: vec3<f32>,
3425
+ position: vec3<f32>,
3426
+ direction: vec3<f32>,
3427
+ attenuation: vec3<f32>,
3428
+ coneCos: vec2<f32>,
3429
+ };
3430
+
2733
3431
  struct DirectionalLight {
2734
3432
  color: vec3<f32>,
2735
3433
  direction: vec3<f32>,
2736
3434
  };
2737
3435
 
3436
+ struct UniformLight {
3437
+ color: vec3<f32>,
3438
+ position: vec3<f32>,
3439
+ direction: vec3<f32>,
3440
+ attenuation: vec3<f32>,
3441
+ coneCos: vec2<f32>,
3442
+ };
3443
+
2738
3444
  struct lightingUniforms {
2739
3445
  enabled: i32,
2740
- lightType: i32,
2741
-
2742
3446
  directionalLightCount: i32,
2743
3447
  pointLightCount: i32,
2744
-
3448
+ spotLightCount: i32,
2745
3449
  ambientColor: vec3<f32>,
2746
-
2747
- lightColor0: vec3<f32>,
2748
- lightPosition0: vec3<f32>,
2749
- lightDirection0: vec3<f32>,
2750
- lightAttenuation0: vec3<f32>,
2751
-
2752
- lightColor1: vec3<f32>,
2753
- lightPosition1: vec3<f32>,
2754
- lightDirection1: vec3<f32>,
2755
- lightAttenuation1: vec3<f32>,
2756
-
2757
- lightColor2: vec3<f32>,
2758
- lightPosition2: vec3<f32>,
2759
- lightDirection2: vec3<f32>,
2760
- lightAttenuation2: vec3<f32>,
2761
-
2762
- lightColor3: vec3<f32>,
2763
- lightPosition3: vec3<f32>,
2764
- lightDirection3: vec3<f32>,
2765
- lightAttenuation3: vec3<f32>,
2766
-
2767
- lightColor4: vec3<f32>,
2768
- lightPosition4: vec3<f32>,
2769
- lightDirection4: vec3<f32>,
2770
- lightAttenuation4: vec3<f32>,
3450
+ lights: array<UniformLight, 5>,
2771
3451
  };
2772
3452
 
2773
- // Binding 0:1 is reserved for lighting (Note: could go into separate bind group as it is stable across draw calls)
2774
- @binding(1) @group(0) var<uniform> lighting : lightingUniforms;
3453
+ @group(2) @binding(auto) var<uniform> lighting : lightingUniforms;
2775
3454
 
2776
3455
  fn lighting_getPointLight(index: i32) -> PointLight {
2777
- switch (index) {
2778
- case 0: {
2779
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
2780
- }
2781
- case 1: {
2782
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
2783
- }
2784
- case 2: {
2785
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
2786
- }
2787
- case 3: {
2788
- return PointLight(lighting.lightColor3, lighting.lightPosition3, lighting.lightAttenuation3);
2789
- }
2790
- case 4, default: {
2791
- return PointLight(lighting.lightColor4, lighting.lightPosition4, lighting.lightAttenuation4);
2792
- }
2793
- }
3456
+ let light = lighting.lights[index];
3457
+ return PointLight(light.color, light.position, light.attenuation);
3458
+ }
3459
+
3460
+ fn lighting_getSpotLight(index: i32) -> SpotLight {
3461
+ let light = lighting.lights[lighting.pointLightCount + index];
3462
+ return SpotLight(light.color, light.position, light.direction, light.attenuation, light.coneCos);
2794
3463
  }
2795
3464
 
2796
3465
  fn lighting_getDirectionalLight(index: i32) -> DirectionalLight {
2797
- switch (index) {
2798
- case 0: {
2799
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
2800
- }
2801
- case 1: {
2802
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
2803
- }
2804
- case 2: {
2805
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
2806
- }
2807
- case 3: {
2808
- return DirectionalLight(lighting.lightColor3, lighting.lightDirection3);
2809
- }
2810
- case 4, default: {
2811
- return DirectionalLight(lighting.lightColor4, lighting.lightDirection4);
2812
- }
2813
- }
2814
- }
3466
+ let light = lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
3467
+ return DirectionalLight(light.color, light.direction);
3468
+ }
2815
3469
 
2816
3470
  fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
2817
3471
  return pointLight.attenuation.x
2818
3472
  + pointLight.attenuation.y * distance
2819
3473
  + pointLight.attenuation.z * distance * distance;
2820
3474
  }
3475
+
3476
+ fn getSpotLightAttenuation(spotLight: SpotLight, positionWorldspace: vec3<f32>) -> f32 {
3477
+ let lightDirection = normalize(positionWorldspace - spotLight.position);
3478
+ let coneFactor = smoothstep(
3479
+ spotLight.coneCos.y,
3480
+ spotLight.coneCos.x,
3481
+ dot(normalize(spotLight.direction), lightDirection)
3482
+ );
3483
+ let distanceAttenuation = getPointLightAttenuation(
3484
+ PointLight(spotLight.color, spotLight.position, spotLight.attenuation),
3485
+ distance(spotLight.position, positionWorldspace)
3486
+ );
3487
+ return distanceAttenuation / max(coneFactor, 0.0001);
3488
+ }
2821
3489
  `
2822
3490
  );
2823
3491
 
2824
3492
  // dist/modules/lighting/lights/lighting.js
2825
3493
  var MAX_LIGHTS = 5;
2826
3494
  var COLOR_FACTOR = 255;
2827
- var LIGHT_TYPE;
2828
- (function(LIGHT_TYPE2) {
2829
- LIGHT_TYPE2[LIGHT_TYPE2["POINT"] = 0] = "POINT";
2830
- LIGHT_TYPE2[LIGHT_TYPE2["DIRECTIONAL"] = 1] = "DIRECTIONAL";
2831
- })(LIGHT_TYPE || (LIGHT_TYPE = {}));
3495
+ var LIGHT_UNIFORM_TYPE = {
3496
+ color: "vec3<f32>",
3497
+ position: "vec3<f32>",
3498
+ direction: "vec3<f32>",
3499
+ attenuation: "vec3<f32>",
3500
+ coneCos: "vec2<f32>"
3501
+ };
2832
3502
  var lighting = {
2833
3503
  props: {},
2834
3504
  uniforms: {},
@@ -2838,128 +3508,105 @@ var lighting = {
2838
3508
  },
2839
3509
  uniformTypes: {
2840
3510
  enabled: "i32",
2841
- lightType: "i32",
2842
3511
  directionalLightCount: "i32",
2843
3512
  pointLightCount: "i32",
3513
+ spotLightCount: "i32",
2844
3514
  ambientColor: "vec3<f32>",
2845
- // TODO define as arrays once we have appropriate uniformTypes
2846
- lightColor0: "vec3<f32>",
2847
- lightPosition0: "vec3<f32>",
2848
- // TODO - could combine direction and attenuation
2849
- lightDirection0: "vec3<f32>",
2850
- lightAttenuation0: "vec3<f32>",
2851
- lightColor1: "vec3<f32>",
2852
- lightPosition1: "vec3<f32>",
2853
- lightDirection1: "vec3<f32>",
2854
- lightAttenuation1: "vec3<f32>",
2855
- lightColor2: "vec3<f32>",
2856
- lightPosition2: "vec3<f32>",
2857
- lightDirection2: "vec3<f32>",
2858
- lightAttenuation2: "vec3<f32>",
2859
- lightColor3: "vec3<f32>",
2860
- lightPosition3: "vec3<f32>",
2861
- lightDirection3: "vec3<f32>",
2862
- lightAttenuation3: "vec3<f32>",
2863
- lightColor4: "vec3<f32>",
2864
- lightPosition4: "vec3<f32>",
2865
- lightDirection4: "vec3<f32>",
2866
- lightAttenuation4: "vec3<f32>"
2867
- },
2868
- defaultUniforms: {
2869
- enabled: 1,
2870
- lightType: LIGHT_TYPE.POINT,
2871
- directionalLightCount: 0,
2872
- pointLightCount: 0,
2873
- ambientColor: [0.1, 0.1, 0.1],
2874
- lightColor0: [1, 1, 1],
2875
- lightPosition0: [1, 1, 2],
2876
- // TODO - could combine direction and attenuation
2877
- lightDirection0: [1, 1, 1],
2878
- lightAttenuation0: [1, 0, 0],
2879
- lightColor1: [1, 1, 1],
2880
- lightPosition1: [1, 1, 2],
2881
- lightDirection1: [1, 1, 1],
2882
- lightAttenuation1: [1, 0, 0],
2883
- lightColor2: [1, 1, 1],
2884
- lightPosition2: [1, 1, 2],
2885
- lightDirection2: [1, 1, 1],
2886
- lightAttenuation2: [1, 0, 0],
2887
- lightColor3: [1, 1, 1],
2888
- lightPosition3: [1, 1, 2],
2889
- lightDirection3: [1, 1, 1],
2890
- lightAttenuation3: [1, 0, 0],
2891
- lightColor4: [1, 1, 1],
2892
- lightPosition4: [1, 1, 2],
2893
- lightDirection4: [1, 1, 1],
2894
- lightAttenuation4: [1, 0, 0]
3515
+ lights: [LIGHT_UNIFORM_TYPE, MAX_LIGHTS]
2895
3516
  },
3517
+ defaultUniforms: createDefaultLightingUniforms(),
3518
+ bindingLayout: [{ name: "lighting", group: 2 }],
3519
+ firstBindingSlot: 0,
2896
3520
  source: lightingUniformsWGSL,
2897
3521
  vs: lightingUniformsGLSL,
2898
3522
  fs: lightingUniformsGLSL,
2899
3523
  getUniforms: getUniforms2
2900
3524
  };
2901
- function getUniforms2(props, prevUniforms = {}) {
3525
+ function getUniforms2(props, _prevUniforms = {}) {
2902
3526
  props = props ? { ...props } : props;
2903
3527
  if (!props) {
2904
- return { ...lighting.defaultUniforms };
3528
+ return createDefaultLightingUniforms();
2905
3529
  }
2906
3530
  if (props.lights) {
2907
3531
  props = { ...props, ...extractLightTypes(props.lights), lights: void 0 };
2908
3532
  }
2909
- const { ambientLight, pointLights, directionalLights } = props || {};
2910
- const hasLights = ambientLight || pointLights && pointLights.length > 0 || directionalLights && directionalLights.length > 0;
3533
+ const { ambientLight, pointLights, spotLights, directionalLights } = props || {};
3534
+ const hasLights = ambientLight || pointLights && pointLights.length > 0 || spotLights && spotLights.length > 0 || directionalLights && directionalLights.length > 0;
2911
3535
  if (!hasLights) {
2912
- return { ...lighting.defaultUniforms, enabled: 0 };
3536
+ return {
3537
+ ...createDefaultLightingUniforms(),
3538
+ enabled: 0
3539
+ };
2913
3540
  }
2914
3541
  const uniforms = {
2915
- ...lighting.defaultUniforms,
2916
- ...prevUniforms,
2917
- ...getLightSourceUniforms({ ambientLight, pointLights, directionalLights })
3542
+ ...createDefaultLightingUniforms(),
3543
+ ...getLightSourceUniforms({ ambientLight, pointLights, spotLights, directionalLights })
2918
3544
  };
2919
3545
  if (props.enabled !== void 0) {
2920
3546
  uniforms.enabled = props.enabled ? 1 : 0;
2921
3547
  }
2922
3548
  return uniforms;
2923
3549
  }
2924
- function getLightSourceUniforms({ ambientLight, pointLights = [], directionalLights = [] }) {
2925
- const lightSourceUniforms = {};
2926
- lightSourceUniforms.ambientColor = convertColor(ambientLight);
3550
+ function getLightSourceUniforms({ ambientLight, pointLights = [], spotLights = [], directionalLights = [] }) {
3551
+ const lights = createDefaultLightUniforms();
2927
3552
  let currentLight = 0;
2928
3553
  let pointLightCount = 0;
3554
+ let spotLightCount = 0;
2929
3555
  let directionalLightCount = 0;
2930
3556
  for (const pointLight of pointLights) {
2931
3557
  if (currentLight >= MAX_LIGHTS) {
2932
3558
  break;
2933
3559
  }
2934
- lightSourceUniforms.lightType = LIGHT_TYPE.POINT;
2935
- const i = currentLight;
2936
- lightSourceUniforms[`lightColor${i}`] = convertColor(pointLight);
2937
- lightSourceUniforms[`lightPosition${i}`] = pointLight.position;
2938
- lightSourceUniforms[`lightAttenuation${i}`] = pointLight.attenuation || [1, 0, 0];
3560
+ lights[currentLight] = {
3561
+ ...lights[currentLight],
3562
+ color: convertColor(pointLight),
3563
+ position: pointLight.position,
3564
+ attenuation: pointLight.attenuation || [1, 0, 0]
3565
+ };
2939
3566
  currentLight++;
2940
3567
  pointLightCount++;
2941
3568
  }
3569
+ for (const spotLight of spotLights) {
3570
+ if (currentLight >= MAX_LIGHTS) {
3571
+ break;
3572
+ }
3573
+ lights[currentLight] = {
3574
+ ...lights[currentLight],
3575
+ color: convertColor(spotLight),
3576
+ position: spotLight.position,
3577
+ direction: spotLight.direction,
3578
+ attenuation: spotLight.attenuation || [1, 0, 0],
3579
+ coneCos: getSpotConeCos(spotLight)
3580
+ };
3581
+ currentLight++;
3582
+ spotLightCount++;
3583
+ }
2942
3584
  for (const directionalLight of directionalLights) {
2943
3585
  if (currentLight >= MAX_LIGHTS) {
2944
3586
  break;
2945
3587
  }
2946
- lightSourceUniforms.lightType = LIGHT_TYPE.DIRECTIONAL;
2947
- const i = currentLight;
2948
- lightSourceUniforms[`lightColor${i}`] = convertColor(directionalLight);
2949
- lightSourceUniforms[`lightDirection${i}`] = directionalLight.direction;
3588
+ lights[currentLight] = {
3589
+ ...lights[currentLight],
3590
+ color: convertColor(directionalLight),
3591
+ direction: directionalLight.direction
3592
+ };
2950
3593
  currentLight++;
2951
3594
  directionalLightCount++;
2952
3595
  }
2953
- if (pointLights.length + directionalLights.length > MAX_LIGHTS) {
3596
+ if (pointLights.length + spotLights.length + directionalLights.length > MAX_LIGHTS) {
2954
3597
  import_core3.log.warn(`MAX_LIGHTS exceeded, truncating to ${MAX_LIGHTS}`)();
2955
3598
  }
2956
- lightSourceUniforms.directionalLightCount = directionalLightCount;
2957
- lightSourceUniforms.pointLightCount = pointLightCount;
2958
- return lightSourceUniforms;
3599
+ return {
3600
+ ambientColor: convertColor(ambientLight),
3601
+ directionalLightCount,
3602
+ pointLightCount,
3603
+ spotLightCount,
3604
+ lights
3605
+ };
2959
3606
  }
2960
3607
  function extractLightTypes(lights) {
2961
- var _a, _b;
2962
- const lightSources = { pointLights: [], directionalLights: [] };
3608
+ var _a, _b, _c;
3609
+ const lightSources = { pointLights: [], spotLights: [], directionalLights: [] };
2963
3610
  for (const light of lights || []) {
2964
3611
  switch (light.type) {
2965
3612
  case "ambient":
@@ -2971,6 +3618,9 @@ function extractLightTypes(lights) {
2971
3618
  case "point":
2972
3619
  (_b = lightSources.pointLights) == null ? void 0 : _b.push(light);
2973
3620
  break;
3621
+ case "spot":
3622
+ (_c = lightSources.spotLights) == null ? void 0 : _c.push(light);
3623
+ break;
2974
3624
  default:
2975
3625
  }
2976
3626
  }
@@ -2980,6 +3630,68 @@ function convertColor(colorDef = {}) {
2980
3630
  const { color = [0, 0, 0], intensity = 1 } = colorDef;
2981
3631
  return color.map((component) => component * intensity / COLOR_FACTOR);
2982
3632
  }
3633
+ function createDefaultLightingUniforms() {
3634
+ return {
3635
+ enabled: 1,
3636
+ directionalLightCount: 0,
3637
+ pointLightCount: 0,
3638
+ spotLightCount: 0,
3639
+ ambientColor: [0.1, 0.1, 0.1],
3640
+ lights: createDefaultLightUniforms()
3641
+ };
3642
+ }
3643
+ function createDefaultLightUniforms() {
3644
+ return Array.from({ length: MAX_LIGHTS }, () => createDefaultLightUniform());
3645
+ }
3646
+ function createDefaultLightUniform() {
3647
+ return {
3648
+ color: [1, 1, 1],
3649
+ position: [1, 1, 2],
3650
+ direction: [1, 1, 1],
3651
+ attenuation: [1, 0, 0],
3652
+ coneCos: [1, 0]
3653
+ };
3654
+ }
3655
+ function getSpotConeCos(spotLight) {
3656
+ const innerConeAngle = spotLight.innerConeAngle ?? 0;
3657
+ const outerConeAngle = spotLight.outerConeAngle ?? Math.PI / 4;
3658
+ return [Math.cos(innerConeAngle), Math.cos(outerConeAngle)];
3659
+ }
3660
+
3661
+ // dist/modules/lighting/ibl/ibl.js
3662
+ var iblWGSL = (
3663
+ /* wgsl */
3664
+ `#ifdef USE_IBL
3665
+ @group(2) @binding(auto) var pbr_diffuseEnvSampler: texture_cube<f32>;
3666
+ @group(2) @binding(auto) var pbr_diffuseEnvSamplerSampler: sampler;
3667
+ @group(2) @binding(auto) var pbr_specularEnvSampler: texture_cube<f32>;
3668
+ @group(2) @binding(auto) var pbr_specularEnvSamplerSampler: sampler;
3669
+ @group(2) @binding(auto) var pbr_brdfLUT: texture_2d<f32>;
3670
+ @group(2) @binding(auto) var pbr_brdfLUTSampler: sampler;
3671
+ #endif
3672
+ `
3673
+ );
3674
+ var iblGLSL = (
3675
+ /* glsl */
3676
+ `#ifdef USE_IBL
3677
+ uniform samplerCube pbr_diffuseEnvSampler;
3678
+ uniform samplerCube pbr_specularEnvSampler;
3679
+ uniform sampler2D pbr_brdfLUT;
3680
+ #endif
3681
+ `
3682
+ );
3683
+ var ibl = {
3684
+ name: "ibl",
3685
+ firstBindingSlot: 32,
3686
+ bindingLayout: [
3687
+ { name: "pbr_diffuseEnvSampler", group: 2 },
3688
+ { name: "pbr_specularEnvSampler", group: 2 },
3689
+ { name: "pbr_brdfLUT", group: 2 }
3690
+ ],
3691
+ source: iblWGSL,
3692
+ vs: iblGLSL,
3693
+ fs: iblGLSL
3694
+ };
2983
3695
 
2984
3696
  // dist/modules/lighting/no-material/dirlight.js
2985
3697
  var SOURCE_WGSL = (
@@ -2995,7 +3707,7 @@ struct DirlightInputs {
2995
3707
  normal: DirlightNormal,
2996
3708
  };
2997
3709
 
2998
- @binding(1) @group(0) var<uniform> dirlight : dirlightUniforms;
3710
+ @group(2) @binding(auto) var<uniform> dirlight : dirlightUniforms;
2999
3711
 
3000
3712
  // For vertex
3001
3713
  fn dirlight_setNormal(normal: vec3<f32>) -> DirlightNormal {
@@ -3040,6 +3752,8 @@ var dirlight = {
3040
3752
  props: {},
3041
3753
  uniforms: {},
3042
3754
  name: "dirlight",
3755
+ bindingLayout: [{ name: "dirlight", group: 2 }],
3756
+ firstBindingSlot: 16,
3043
3757
  dependencies: [],
3044
3758
  source: SOURCE_WGSL,
3045
3759
  vs: VS_GLSL,
@@ -3066,10 +3780,173 @@ function getUniforms3(opts = dirlight.defaultUniforms) {
3066
3780
  return uniforms;
3067
3781
  }
3068
3782
 
3783
+ // dist/modules/lighting/lambert-material/lambert-shaders-wgsl.js
3784
+ var LAMBERT_WGSL = (
3785
+ /* wgsl */
3786
+ `struct lambertMaterialUniforms {
3787
+ unlit: u32,
3788
+ ambient: f32,
3789
+ diffuse: f32,
3790
+ };
3791
+
3792
+ @group(3) @binding(auto) var<uniform> lambertMaterial : lambertMaterialUniforms;
3793
+
3794
+ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
3795
+ let lambertian: f32 = max(dot(light_direction, normal_worldspace), 0.0);
3796
+ return lambertian * lambertMaterial.diffuse * surfaceColor * color;
3797
+ }
3798
+
3799
+ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
3800
+ var lightColor: vec3<f32> = surfaceColor;
3801
+
3802
+ if (lambertMaterial.unlit != 0u) {
3803
+ return surfaceColor;
3804
+ }
3805
+
3806
+ if (lighting.enabled == 0) {
3807
+ return lightColor;
3808
+ }
3809
+
3810
+ lightColor = lambertMaterial.ambient * surfaceColor * lighting.ambientColor;
3811
+
3812
+ for (var i: i32 = 0; i < lighting.pointLightCount; i++) {
3813
+ let pointLight: PointLight = lighting_getPointLight(i);
3814
+ let light_position_worldspace: vec3<f32> = pointLight.position;
3815
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
3816
+ let light_attenuation = getPointLightAttenuation(
3817
+ pointLight,
3818
+ distance(light_position_worldspace, position_worldspace)
3819
+ );
3820
+ lightColor += lighting_getLightColor(
3821
+ surfaceColor,
3822
+ light_direction,
3823
+ normal_worldspace,
3824
+ pointLight.color / light_attenuation
3825
+ );
3826
+ }
3827
+
3828
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
3829
+ let spotLight: SpotLight = lighting_getSpotLight(i);
3830
+ let light_position_worldspace: vec3<f32> = spotLight.position;
3831
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
3832
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
3833
+ lightColor += lighting_getLightColor(
3834
+ surfaceColor,
3835
+ light_direction,
3836
+ normal_worldspace,
3837
+ spotLight.color / light_attenuation
3838
+ );
3839
+ }
3840
+
3841
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
3842
+ let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
3843
+ lightColor += lighting_getLightColor(
3844
+ surfaceColor,
3845
+ -directionalLight.direction,
3846
+ normal_worldspace,
3847
+ directionalLight.color
3848
+ );
3849
+ }
3850
+
3851
+ return lightColor;
3852
+ }
3853
+ `
3854
+ );
3855
+
3856
+ // dist/modules/lighting/lambert-material/lambert-shaders-glsl.js
3857
+ var LAMBERT_VS = (
3858
+ /* glsl */
3859
+ `uniform lambertMaterialUniforms {
3860
+ uniform bool unlit;
3861
+ uniform float ambient;
3862
+ uniform float diffuse;
3863
+ } material;
3864
+ `
3865
+ );
3866
+ var LAMBERT_FS = (
3867
+ /* glsl */
3868
+ `uniform lambertMaterialUniforms {
3869
+ uniform bool unlit;
3870
+ uniform float ambient;
3871
+ uniform float diffuse;
3872
+ } material;
3873
+
3874
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 normal_worldspace, vec3 color) {
3875
+ float lambertian = max(dot(light_direction, normal_worldspace), 0.0);
3876
+ return lambertian * material.diffuse * surfaceColor * color;
3877
+ }
3878
+
3879
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
3880
+ vec3 lightColor = surfaceColor;
3881
+
3882
+ if (material.unlit) {
3883
+ return surfaceColor;
3884
+ }
3885
+
3886
+ if (lighting.enabled == 0) {
3887
+ return lightColor;
3888
+ }
3889
+
3890
+ lightColor = material.ambient * surfaceColor * lighting.ambientColor;
3891
+
3892
+ for (int i = 0; i < lighting.pointLightCount; i++) {
3893
+ PointLight pointLight = lighting_getPointLight(i);
3894
+ vec3 light_position_worldspace = pointLight.position;
3895
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
3896
+ float light_attenuation = getPointLightAttenuation(pointLight, distance(light_position_worldspace, position_worldspace));
3897
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, normal_worldspace, pointLight.color / light_attenuation);
3898
+ }
3899
+
3900
+ for (int i = 0; i < lighting.spotLightCount; i++) {
3901
+ SpotLight spotLight = lighting_getSpotLight(i);
3902
+ vec3 light_position_worldspace = spotLight.position;
3903
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
3904
+ float light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
3905
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, normal_worldspace, spotLight.color / light_attenuation);
3906
+ }
3907
+
3908
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
3909
+ DirectionalLight directionalLight = lighting_getDirectionalLight(i);
3910
+ lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, normal_worldspace, directionalLight.color);
3911
+ }
3912
+
3913
+ return lightColor;
3914
+ }
3915
+ `
3916
+ );
3917
+
3918
+ // dist/modules/lighting/lambert-material/lambert-material.js
3919
+ var lambertMaterial = {
3920
+ name: "lambertMaterial",
3921
+ firstBindingSlot: 0,
3922
+ bindingLayout: [{ name: "lambertMaterial", group: 3 }],
3923
+ dependencies: [lighting],
3924
+ source: LAMBERT_WGSL,
3925
+ vs: LAMBERT_VS,
3926
+ fs: LAMBERT_FS,
3927
+ defines: {
3928
+ LIGHTING_FRAGMENT: true
3929
+ },
3930
+ uniformTypes: {
3931
+ unlit: "i32",
3932
+ ambient: "f32",
3933
+ diffuse: "f32"
3934
+ },
3935
+ defaultUniforms: {
3936
+ unlit: false,
3937
+ ambient: 0.35,
3938
+ diffuse: 0.6
3939
+ },
3940
+ getUniforms(props) {
3941
+ return { ...lambertMaterial.defaultUniforms, ...props };
3942
+ }
3943
+ };
3944
+
3069
3945
  // dist/modules/lighting/phong-material/phong-shaders-glsl.js
3070
3946
  var PHONG_VS = (
3071
3947
  /* glsl */
3072
3948
  `uniform phongMaterialUniforms {
3949
+ uniform bool unlit;
3073
3950
  uniform float ambient;
3074
3951
  uniform float diffuse;
3075
3952
  uniform float shininess;
@@ -3079,9 +3956,8 @@ var PHONG_VS = (
3079
3956
  );
3080
3957
  var PHONG_FS = (
3081
3958
  /* glsl */
3082
- `#define MAX_LIGHTS 3
3083
-
3084
- uniform phongMaterialUniforms {
3959
+ `uniform phongMaterialUniforms {
3960
+ uniform bool unlit;
3085
3961
  uniform float ambient;
3086
3962
  uniform float diffuse;
3087
3963
  uniform float shininess;
@@ -3103,6 +3979,10 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 view_d
3103
3979
  vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
3104
3980
  vec3 lightColor = surfaceColor;
3105
3981
 
3982
+ if (material.unlit) {
3983
+ return surfaceColor;
3984
+ }
3985
+
3106
3986
  if (lighting.enabled == 0) {
3107
3987
  return lightColor;
3108
3988
  }
@@ -3118,8 +3998,15 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
3118
3998
  lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color / light_attenuation);
3119
3999
  }
3120
4000
 
3121
- int totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
3122
- for (int i = lighting.pointLightCount; i < totalLights; i++) {
4001
+ for (int i = 0; i < lighting.spotLightCount; i++) {
4002
+ SpotLight spotLight = lighting_getSpotLight(i);
4003
+ vec3 light_position_worldspace = spotLight.position;
4004
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
4005
+ float light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
4006
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, spotLight.color / light_attenuation);
4007
+ }
4008
+
4009
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
3123
4010
  DirectionalLight directionalLight = lighting_getDirectionalLight(i);
3124
4011
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
3125
4012
  }
@@ -3133,13 +4020,14 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
3133
4020
  var PHONG_WGSL = (
3134
4021
  /* wgsl */
3135
4022
  `struct phongMaterialUniforms {
4023
+ unlit: u32,
3136
4024
  ambient: f32,
3137
4025
  diffuse: f32,
3138
4026
  shininess: f32,
3139
4027
  specularColor: vec3<f32>,
3140
4028
  };
3141
4029
 
3142
- @binding(2) @group(0) var<uniform> phongMaterial : phongMaterialUniforms;
4030
+ @group(3) @binding(auto) var<uniform> phongMaterial : phongMaterialUniforms;
3143
4031
 
3144
4032
  fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, view_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
3145
4033
  let halfway_direction: vec3<f32> = normalize(light_direction + view_direction);
@@ -3156,6 +4044,10 @@ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, v
3156
4044
  fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
3157
4045
  var lightColor: vec3<f32> = surfaceColor;
3158
4046
 
4047
+ if (phongMaterial.unlit != 0u) {
4048
+ return surfaceColor;
4049
+ }
4050
+
3159
4051
  if (lighting.enabled == 0) {
3160
4052
  return lightColor;
3161
4053
  }
@@ -3180,8 +4072,21 @@ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, p
3180
4072
  );
3181
4073
  }
3182
4074
 
3183
- let totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
3184
- for (var i: i32 = lighting.pointLightCount; i < totalLights; i++) {
4075
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
4076
+ let spotLight: SpotLight = lighting_getSpotLight(i);
4077
+ let light_position_worldspace: vec3<f32> = spotLight.position;
4078
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
4079
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
4080
+ lightColor += lighting_getLightColor(
4081
+ surfaceColor,
4082
+ light_direction,
4083
+ view_direction,
4084
+ normal_worldspace,
4085
+ spotLight.color / light_attenuation
4086
+ );
4087
+ }
4088
+
4089
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
3185
4090
  let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
3186
4091
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
3187
4092
  }
@@ -3213,8 +4118,21 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
3213
4118
  );
3214
4119
  }
3215
4120
 
3216
- let totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
3217
- for (var i: i32 = lighting.pointLightCount; i < totalLights; i++) {
4121
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
4122
+ let spotLight: SpotLight = lighting_getSpotLight(i);
4123
+ let light_position_worldspace: vec3<f32> = spotLight.position;
4124
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
4125
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
4126
+ lightColor += lighting_getLightColor(
4127
+ surfaceColor,
4128
+ light_direction,
4129
+ view_direction,
4130
+ normal_worldspace,
4131
+ spotLight.color / light_attenuation
4132
+ );
4133
+ }
4134
+
4135
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
3218
4136
  let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
3219
4137
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
3220
4138
  }
@@ -3228,6 +4146,7 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
3228
4146
  var gouraudMaterial = {
3229
4147
  props: {},
3230
4148
  name: "gouraudMaterial",
4149
+ bindingLayout: [{ name: "gouraudMaterial", group: 3 }],
3231
4150
  // Note these are switched between phong and gouraud
3232
4151
  vs: PHONG_FS.replace("phongMaterial", "gouraudMaterial"),
3233
4152
  fs: PHONG_VS.replace("phongMaterial", "gouraudMaterial"),
@@ -3237,12 +4156,14 @@ var gouraudMaterial = {
3237
4156
  },
3238
4157
  dependencies: [lighting],
3239
4158
  uniformTypes: {
4159
+ unlit: "i32",
3240
4160
  ambient: "f32",
3241
4161
  diffuse: "f32",
3242
4162
  shininess: "f32",
3243
4163
  specularColor: "vec3<f32>"
3244
4164
  },
3245
4165
  defaultUniforms: {
4166
+ unlit: false,
3246
4167
  ambient: 0.35,
3247
4168
  diffuse: 0.6,
3248
4169
  shininess: 32,
@@ -3260,6 +4181,8 @@ var gouraudMaterial = {
3260
4181
  // dist/modules/lighting/phong-material/phong-material.js
3261
4182
  var phongMaterial = {
3262
4183
  name: "phongMaterial",
4184
+ firstBindingSlot: 0,
4185
+ bindingLayout: [{ name: "phongMaterial", group: 3 }],
3263
4186
  dependencies: [lighting],
3264
4187
  // Note these are switched between phong and gouraud
3265
4188
  source: PHONG_WGSL,
@@ -3269,12 +4192,14 @@ var phongMaterial = {
3269
4192
  LIGHTING_FRAGMENT: true
3270
4193
  },
3271
4194
  uniformTypes: {
4195
+ unlit: "i32",
3272
4196
  ambient: "f32",
3273
4197
  diffuse: "f32",
3274
4198
  shininess: "f32",
3275
4199
  specularColor: "vec3<f32>"
3276
4200
  },
3277
4201
  defaultUniforms: {
4202
+ unlit: false,
3278
4203
  ambient: 0.35,
3279
4204
  diffuse: 0.6,
3280
4205
  shininess: 32,
@@ -3371,10 +4296,12 @@ uniform pbrMaterialUniforms {
3371
4296
  float clearcoatFactor;
3372
4297
  float clearcoatRoughnessFactor;
3373
4298
  bool clearcoatMapEnabled;
4299
+ bool clearcoatRoughnessMapEnabled;
3374
4300
 
3375
4301
  vec3 sheenColorFactor;
3376
4302
  float sheenRoughnessFactor;
3377
4303
  bool sheenColorMapEnabled;
4304
+ bool sheenRoughnessMapEnabled;
3378
4305
 
3379
4306
  float iridescenceFactor;
3380
4307
  float iridescenceIor;
@@ -3424,26 +4351,33 @@ uniform sampler2D pbr_specularIntensitySampler;
3424
4351
  #ifdef HAS_TRANSMISSIONMAP
3425
4352
  uniform sampler2D pbr_transmissionSampler;
3426
4353
  #endif
4354
+ #ifdef HAS_THICKNESSMAP
4355
+ uniform sampler2D pbr_thicknessSampler;
4356
+ #endif
3427
4357
  #ifdef HAS_CLEARCOATMAP
3428
4358
  uniform sampler2D pbr_clearcoatSampler;
4359
+ #endif
4360
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
3429
4361
  uniform sampler2D pbr_clearcoatRoughnessSampler;
3430
4362
  #endif
4363
+ #ifdef HAS_CLEARCOATNORMALMAP
4364
+ uniform sampler2D pbr_clearcoatNormalSampler;
4365
+ #endif
3431
4366
  #ifdef HAS_SHEENCOLORMAP
3432
4367
  uniform sampler2D pbr_sheenColorSampler;
4368
+ #endif
4369
+ #ifdef HAS_SHEENROUGHNESSMAP
3433
4370
  uniform sampler2D pbr_sheenRoughnessSampler;
3434
4371
  #endif
3435
4372
  #ifdef HAS_IRIDESCENCEMAP
3436
4373
  uniform sampler2D pbr_iridescenceSampler;
3437
4374
  #endif
4375
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
4376
+ uniform sampler2D pbr_iridescenceThicknessSampler;
4377
+ #endif
3438
4378
  #ifdef HAS_ANISOTROPYMAP
3439
4379
  uniform sampler2D pbr_anisotropySampler;
3440
4380
  #endif
3441
- #ifdef USE_IBL
3442
- uniform samplerCube pbr_diffuseEnvSampler;
3443
- uniform samplerCube pbr_specularEnvSampler;
3444
- uniform sampler2D pbr_brdfLUT;
3445
- #endif
3446
-
3447
4381
  // Inputs from vertex shader
3448
4382
 
3449
4383
  in vec3 pbr_vPosition;
@@ -3480,6 +4414,8 @@ struct PBRInfo {
3480
4414
  const float M_PI = 3.141592653589793;
3481
4415
  const float c_MinRoughness = 0.04;
3482
4416
 
4417
+ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor);
4418
+
3483
4419
  vec4 SRGBtoLINEAR(vec4 srgbIn)
3484
4420
  {
3485
4421
  #ifdef MANUAL_SRGB
@@ -3495,11 +4431,9 @@ vec4 SRGBtoLINEAR(vec4 srgbIn)
3495
4431
  #endif //MANUAL_SRGB
3496
4432
  }
3497
4433
 
3498
- // Find the normal for this fragment, pulling either from a predefined normal map
3499
- // or from the interpolated mesh normal and tangent attributes.
3500
- vec3 getNormal()
4434
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
4435
+ mat3 getTBN()
3501
4436
  {
3502
- // Retrieve the tangent space matrix
3503
4437
  #ifndef HAS_TANGENTS
3504
4438
  vec3 pos_dx = dFdx(pbr_vPosition);
3505
4439
  vec3 pos_dy = dFdy(pbr_vPosition);
@@ -3520,9 +4454,21 @@ vec3 getNormal()
3520
4454
  mat3 tbn = pbr_vTBN;
3521
4455
  #endif
3522
4456
 
4457
+ return tbn;
4458
+ }
4459
+
4460
+ // Find the normal for this fragment, pulling either from a predefined normal map
4461
+ // or from the interpolated mesh normal and tangent attributes.
4462
+ vec3 getMappedNormal(sampler2D normalSampler, mat3 tbn, float normalScale)
4463
+ {
4464
+ vec3 n = texture(normalSampler, pbr_vUV).rgb;
4465
+ return normalize(tbn * ((2.0 * n - 1.0) * vec3(normalScale, normalScale, 1.0)));
4466
+ }
4467
+
4468
+ vec3 getNormal(mat3 tbn)
4469
+ {
3523
4470
  #ifdef HAS_NORMALMAP
3524
- vec3 n = texture(pbr_normalSampler, pbr_vUV).rgb;
3525
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
4471
+ vec3 n = getMappedNormal(pbr_normalSampler, tbn, pbrMaterial.normalScale);
3526
4472
  #else
3527
4473
  // The tbn matrix is linearly interpolated, so we need to re-normalize
3528
4474
  vec3 n = normalize(tbn[2].xyz);
@@ -3531,6 +4477,15 @@ vec3 getNormal()
3531
4477
  return n;
3532
4478
  }
3533
4479
 
4480
+ vec3 getClearcoatNormal(mat3 tbn, vec3 baseNormal)
4481
+ {
4482
+ #ifdef HAS_CLEARCOATNORMALMAP
4483
+ return getMappedNormal(pbr_clearcoatNormalSampler, tbn, 1.0);
4484
+ #else
4485
+ return baseNormal;
4486
+ #endif
4487
+ }
4488
+
3534
4489
  // Calculation of the lighting contribution from an optional Image Based Light source.
3535
4490
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
3536
4491
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -3606,35 +4561,203 @@ float microfacetDistribution(PBRInfo pbrInfo)
3606
4561
  return roughnessSq / (M_PI * f * f);
3607
4562
  }
3608
4563
 
3609
- void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
3610
- pbrInfo.NdotL = 1.0;
3611
- pbrInfo.NdotH = 0.0;
3612
- pbrInfo.LdotH = 0.0;
3613
- pbrInfo.VdotH = 1.0;
4564
+ float maxComponent(vec3 value)
4565
+ {
4566
+ return max(max(value.r, value.g), value.b);
3614
4567
  }
3615
4568
 
3616
- void PBRInfo_setDirectionalLight(inout PBRInfo pbrInfo, vec3 lightDirection) {
3617
- vec3 n = pbrInfo.n;
3618
- vec3 v = pbrInfo.v;
3619
- vec3 l = normalize(lightDirection); // Vector from surface point to light
3620
- vec3 h = normalize(l+v); // Half vector between both l and v
4569
+ float getDielectricF0(float ior)
4570
+ {
4571
+ float clampedIor = max(ior, 1.0);
4572
+ float ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
4573
+ return ratio * ratio;
4574
+ }
3621
4575
 
3622
- pbrInfo.NdotL = clamp(dot(n, l), 0.001, 1.0);
3623
- pbrInfo.NdotH = clamp(dot(n, h), 0.0, 1.0);
3624
- pbrInfo.LdotH = clamp(dot(l, h), 0.0, 1.0);
3625
- pbrInfo.VdotH = clamp(dot(v, h), 0.0, 1.0);
4576
+ vec2 normalizeDirection(vec2 direction)
4577
+ {
4578
+ float directionLength = length(direction);
4579
+ return directionLength > 0.0001 ? direction / directionLength : vec2(1.0, 0.0);
3626
4580
  }
3627
4581
 
3628
- void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) {
3629
- vec3 light_direction = normalize(pointLight.position - pbr_vPosition);
3630
- PBRInfo_setDirectionalLight(pbrInfo, light_direction);
4582
+ vec2 rotateDirection(vec2 direction, float rotation)
4583
+ {
4584
+ float s = sin(rotation);
4585
+ float c = cos(rotation);
4586
+ return vec2(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
3631
4587
  }
3632
4588
 
3633
- vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
3634
- // Calculate the shading terms for the microfacet specular shading model
3635
- vec3 F = specularReflection(pbrInfo);
3636
- float G = geometricOcclusion(pbrInfo);
3637
- float D = microfacetDistribution(pbrInfo);
4589
+ vec3 getIridescenceTint(float iridescence, float thickness, float NdotV)
4590
+ {
4591
+ if (iridescence <= 0.0) {
4592
+ return vec3(1.0);
4593
+ }
4594
+
4595
+ float phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
4596
+ vec3 thinFilmTint =
4597
+ 0.5 + 0.5 * cos(vec3(phase, phase + 2.0943951, phase + 4.1887902));
4598
+ return mix(vec3(1.0), thinFilmTint, iridescence);
4599
+ }
4600
+
4601
+ vec3 getVolumeAttenuation(float thickness)
4602
+ {
4603
+ if (thickness <= 0.0) {
4604
+ return vec3(1.0);
4605
+ }
4606
+
4607
+ vec3 attenuationCoefficient =
4608
+ -log(max(pbrMaterial.attenuationColor, vec3(0.0001))) /
4609
+ max(pbrMaterial.attenuationDistance, 0.0001);
4610
+ return exp(-attenuationCoefficient * thickness);
4611
+ }
4612
+
4613
+ PBRInfo createClearcoatPBRInfo(PBRInfo basePBRInfo, vec3 clearcoatNormal, float clearcoatRoughness)
4614
+ {
4615
+ float perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
4616
+ float alphaRoughness = perceptualRoughness * perceptualRoughness;
4617
+ float NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
4618
+
4619
+ return PBRInfo(
4620
+ basePBRInfo.NdotL,
4621
+ NdotV,
4622
+ basePBRInfo.NdotH,
4623
+ basePBRInfo.LdotH,
4624
+ basePBRInfo.VdotH,
4625
+ perceptualRoughness,
4626
+ 0.0,
4627
+ vec3(0.04),
4628
+ vec3(1.0),
4629
+ alphaRoughness,
4630
+ vec3(0.0),
4631
+ vec3(0.04),
4632
+ clearcoatNormal,
4633
+ basePBRInfo.v
4634
+ );
4635
+ }
4636
+
4637
+ vec3 calculateClearcoatContribution(
4638
+ PBRInfo pbrInfo,
4639
+ vec3 lightColor,
4640
+ vec3 clearcoatNormal,
4641
+ float clearcoatFactor,
4642
+ float clearcoatRoughness
4643
+ ) {
4644
+ if (clearcoatFactor <= 0.0) {
4645
+ return vec3(0.0);
4646
+ }
4647
+
4648
+ PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
4649
+ return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
4650
+ }
4651
+
4652
+ #ifdef USE_IBL
4653
+ vec3 calculateClearcoatIBLContribution(
4654
+ PBRInfo pbrInfo,
4655
+ vec3 clearcoatNormal,
4656
+ vec3 reflection,
4657
+ float clearcoatFactor,
4658
+ float clearcoatRoughness
4659
+ ) {
4660
+ if (clearcoatFactor <= 0.0) {
4661
+ return vec3(0.0);
4662
+ }
4663
+
4664
+ PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
4665
+ return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
4666
+ }
4667
+ #endif
4668
+
4669
+ vec3 calculateSheenContribution(
4670
+ PBRInfo pbrInfo,
4671
+ vec3 lightColor,
4672
+ vec3 sheenColor,
4673
+ float sheenRoughness
4674
+ ) {
4675
+ if (maxComponent(sheenColor) <= 0.0) {
4676
+ return vec3(0.0);
4677
+ }
4678
+
4679
+ float sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
4680
+ float sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
4681
+ return pbrInfo.NdotL *
4682
+ lightColor *
4683
+ sheenColor *
4684
+ (0.25 + 0.75 * sheenFresnel) *
4685
+ sheenVisibility *
4686
+ (1.0 - pbrInfo.metalness);
4687
+ }
4688
+
4689
+ float calculateAnisotropyBoost(
4690
+ PBRInfo pbrInfo,
4691
+ vec3 anisotropyTangent,
4692
+ float anisotropyStrength
4693
+ ) {
4694
+ if (anisotropyStrength <= 0.0) {
4695
+ return 1.0;
4696
+ }
4697
+
4698
+ vec3 anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
4699
+ float bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
4700
+ return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
4701
+ }
4702
+
4703
+ vec3 calculateMaterialLightColor(
4704
+ PBRInfo pbrInfo,
4705
+ vec3 lightColor,
4706
+ vec3 clearcoatNormal,
4707
+ float clearcoatFactor,
4708
+ float clearcoatRoughness,
4709
+ vec3 sheenColor,
4710
+ float sheenRoughness,
4711
+ vec3 anisotropyTangent,
4712
+ float anisotropyStrength
4713
+ ) {
4714
+ float anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
4715
+ vec3 color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
4716
+ color += calculateClearcoatContribution(
4717
+ pbrInfo,
4718
+ lightColor,
4719
+ clearcoatNormal,
4720
+ clearcoatFactor,
4721
+ clearcoatRoughness
4722
+ );
4723
+ color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
4724
+ return color;
4725
+ }
4726
+
4727
+ void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
4728
+ pbrInfo.NdotL = 1.0;
4729
+ pbrInfo.NdotH = 0.0;
4730
+ pbrInfo.LdotH = 0.0;
4731
+ pbrInfo.VdotH = 1.0;
4732
+ }
4733
+
4734
+ void PBRInfo_setDirectionalLight(inout PBRInfo pbrInfo, vec3 lightDirection) {
4735
+ vec3 n = pbrInfo.n;
4736
+ vec3 v = pbrInfo.v;
4737
+ vec3 l = normalize(lightDirection); // Vector from surface point to light
4738
+ vec3 h = normalize(l+v); // Half vector between both l and v
4739
+
4740
+ pbrInfo.NdotL = clamp(dot(n, l), 0.001, 1.0);
4741
+ pbrInfo.NdotH = clamp(dot(n, h), 0.0, 1.0);
4742
+ pbrInfo.LdotH = clamp(dot(l, h), 0.0, 1.0);
4743
+ pbrInfo.VdotH = clamp(dot(v, h), 0.0, 1.0);
4744
+ }
4745
+
4746
+ void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) {
4747
+ vec3 light_direction = normalize(pointLight.position - pbr_vPosition);
4748
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
4749
+ }
4750
+
4751
+ void PBRInfo_setSpotLight(inout PBRInfo pbrInfo, SpotLight spotLight) {
4752
+ vec3 light_direction = normalize(spotLight.position - pbr_vPosition);
4753
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
4754
+ }
4755
+
4756
+ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
4757
+ // Calculate the shading terms for the microfacet specular shading model
4758
+ vec3 F = specularReflection(pbrInfo);
4759
+ float G = geometricOcclusion(pbrInfo);
4760
+ float D = microfacetDistribution(pbrInfo);
3638
4761
 
3639
4762
  // Calculation of analytical lighting contribution
3640
4763
  vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInfo);
@@ -3660,6 +4783,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
3660
4783
 
3661
4784
  vec3 color = vec3(0, 0, 0);
3662
4785
 
4786
+ float transmission = 0.0;
4787
+
3663
4788
  if(pbrMaterial.unlit){
3664
4789
  color.rgb = baseColor.rgb;
3665
4790
  }
@@ -3678,14 +4803,252 @@ vec4 pbr_filterColor(vec4 colorUnused)
3678
4803
  #endif
3679
4804
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
3680
4805
  metallic = clamp(metallic, 0.0, 1.0);
4806
+ mat3 tbn = getTBN();
4807
+ vec3 n = getNormal(tbn); // normal at surface point
4808
+ vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
4809
+ float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
4810
+ #ifdef USE_MATERIAL_EXTENSIONS
4811
+ bool useExtendedPBR =
4812
+ pbrMaterial.specularColorMapEnabled ||
4813
+ pbrMaterial.specularIntensityMapEnabled ||
4814
+ abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
4815
+ maxComponent(abs(pbrMaterial.specularColorFactor - vec3(1.0))) > 0.0001 ||
4816
+ abs(pbrMaterial.ior - 1.5) > 0.0001 ||
4817
+ pbrMaterial.transmissionMapEnabled ||
4818
+ pbrMaterial.transmissionFactor > 0.0001 ||
4819
+ pbrMaterial.clearcoatMapEnabled ||
4820
+ pbrMaterial.clearcoatRoughnessMapEnabled ||
4821
+ pbrMaterial.clearcoatFactor > 0.0001 ||
4822
+ pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
4823
+ pbrMaterial.sheenColorMapEnabled ||
4824
+ pbrMaterial.sheenRoughnessMapEnabled ||
4825
+ maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
4826
+ pbrMaterial.sheenRoughnessFactor > 0.0001 ||
4827
+ pbrMaterial.iridescenceMapEnabled ||
4828
+ pbrMaterial.iridescenceFactor > 0.0001 ||
4829
+ abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
4830
+ abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
4831
+ abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
4832
+ pbrMaterial.anisotropyMapEnabled ||
4833
+ pbrMaterial.anisotropyStrength > 0.0001 ||
4834
+ abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
4835
+ length(pbrMaterial.anisotropyDirection - vec2(1.0, 0.0)) > 0.0001;
4836
+ #else
4837
+ bool useExtendedPBR = false;
4838
+ #endif
4839
+
4840
+ if (!useExtendedPBR) {
4841
+ // Keep the baseline metallic-roughness implementation byte-for-byte equivalent in behavior.
4842
+ float alphaRoughness = perceptualRoughness * perceptualRoughness;
4843
+
4844
+ vec3 f0 = vec3(0.04);
4845
+ vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
4846
+ diffuseColor *= 1.0 - metallic;
4847
+ vec3 specularColor = mix(f0, baseColor.rgb, metallic);
4848
+
4849
+ float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
4850
+ float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
4851
+ vec3 specularEnvironmentR0 = specularColor.rgb;
4852
+ vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
4853
+ vec3 reflection = -normalize(reflect(v, n));
4854
+
4855
+ PBRInfo pbrInfo = PBRInfo(
4856
+ 0.0, // NdotL
4857
+ NdotV,
4858
+ 0.0, // NdotH
4859
+ 0.0, // LdotH
4860
+ 0.0, // VdotH
4861
+ perceptualRoughness,
4862
+ metallic,
4863
+ specularEnvironmentR0,
4864
+ specularEnvironmentR90,
4865
+ alphaRoughness,
4866
+ diffuseColor,
4867
+ specularColor,
4868
+ n,
4869
+ v
4870
+ );
4871
+
4872
+ #ifdef USE_LIGHTS
4873
+ PBRInfo_setAmbientLight(pbrInfo);
4874
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor);
4875
+
4876
+ for(int i = 0; i < lighting.directionalLightCount; i++) {
4877
+ if (i < lighting.directionalLightCount) {
4878
+ PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
4879
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
4880
+ }
4881
+ }
4882
+
4883
+ for(int i = 0; i < lighting.pointLightCount; i++) {
4884
+ if (i < lighting.pointLightCount) {
4885
+ PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
4886
+ float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
4887
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
4888
+ }
4889
+ }
4890
+
4891
+ for(int i = 0; i < lighting.spotLightCount; i++) {
4892
+ if (i < lighting.spotLightCount) {
4893
+ PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
4894
+ float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
4895
+ color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
4896
+ }
4897
+ }
4898
+ #endif
4899
+
4900
+ #ifdef USE_IBL
4901
+ if (pbrMaterial.IBLenabled) {
4902
+ color += getIBLContribution(pbrInfo, n, reflection);
4903
+ }
4904
+ #endif
4905
+
4906
+ #ifdef HAS_OCCLUSIONMAP
4907
+ if (pbrMaterial.occlusionMapEnabled) {
4908
+ float ao = texture(pbr_occlusionSampler, pbr_vUV).r;
4909
+ color = mix(color, color * ao, pbrMaterial.occlusionStrength);
4910
+ }
4911
+ #endif
4912
+
4913
+ vec3 emissive = pbrMaterial.emissiveFactor;
4914
+ #ifdef HAS_EMISSIVEMAP
4915
+ if (pbrMaterial.emissiveMapEnabled) {
4916
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
4917
+ }
4918
+ #endif
4919
+ color += emissive * pbrMaterial.emissiveStrength;
4920
+
4921
+ #ifdef PBR_DEBUG
4922
+ color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
4923
+ color = mix(color, vec3(metallic), pbrMaterial.scaleDiffBaseMR.z);
4924
+ color = mix(color, vec3(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
4925
+ #endif
4926
+
4927
+ return vec4(pow(color, vec3(1.0 / 2.2)), baseColor.a);
4928
+ }
4929
+
4930
+ float specularIntensity = pbrMaterial.specularIntensityFactor;
4931
+ #ifdef HAS_SPECULARINTENSITYMAP
4932
+ if (pbrMaterial.specularIntensityMapEnabled) {
4933
+ specularIntensity *= texture(pbr_specularIntensitySampler, pbr_vUV).a;
4934
+ }
4935
+ #endif
4936
+
4937
+ vec3 specularFactor = pbrMaterial.specularColorFactor;
4938
+ #ifdef HAS_SPECULARCOLORMAP
4939
+ if (pbrMaterial.specularColorMapEnabled) {
4940
+ specularFactor *= SRGBtoLINEAR(texture(pbr_specularColorSampler, pbr_vUV)).rgb;
4941
+ }
4942
+ #endif
4943
+
4944
+ transmission = pbrMaterial.transmissionFactor;
4945
+ #ifdef HAS_TRANSMISSIONMAP
4946
+ if (pbrMaterial.transmissionMapEnabled) {
4947
+ transmission *= texture(pbr_transmissionSampler, pbr_vUV).r;
4948
+ }
4949
+ #endif
4950
+ transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
4951
+ float thickness = max(pbrMaterial.thicknessFactor, 0.0);
4952
+ #ifdef HAS_THICKNESSMAP
4953
+ thickness *= texture(pbr_thicknessSampler, pbr_vUV).g;
4954
+ #endif
4955
+
4956
+ float clearcoatFactor = pbrMaterial.clearcoatFactor;
4957
+ float clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
4958
+ #ifdef HAS_CLEARCOATMAP
4959
+ if (pbrMaterial.clearcoatMapEnabled) {
4960
+ clearcoatFactor *= texture(pbr_clearcoatSampler, pbr_vUV).r;
4961
+ }
4962
+ #endif
4963
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
4964
+ if (pbrMaterial.clearcoatRoughnessMapEnabled) {
4965
+ clearcoatRoughness *= texture(pbr_clearcoatRoughnessSampler, pbr_vUV).g;
4966
+ }
4967
+ #endif
4968
+ clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
4969
+ clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
4970
+ vec3 clearcoatNormal = getClearcoatNormal(tbn, n);
4971
+
4972
+ vec3 sheenColor = pbrMaterial.sheenColorFactor;
4973
+ float sheenRoughness = pbrMaterial.sheenRoughnessFactor;
4974
+ #ifdef HAS_SHEENCOLORMAP
4975
+ if (pbrMaterial.sheenColorMapEnabled) {
4976
+ sheenColor *= SRGBtoLINEAR(texture(pbr_sheenColorSampler, pbr_vUV)).rgb;
4977
+ }
4978
+ #endif
4979
+ #ifdef HAS_SHEENROUGHNESSMAP
4980
+ if (pbrMaterial.sheenRoughnessMapEnabled) {
4981
+ sheenRoughness *= texture(pbr_sheenRoughnessSampler, pbr_vUV).a;
4982
+ }
4983
+ #endif
4984
+ sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
4985
+
4986
+ float iridescence = pbrMaterial.iridescenceFactor;
4987
+ #ifdef HAS_IRIDESCENCEMAP
4988
+ if (pbrMaterial.iridescenceMapEnabled) {
4989
+ iridescence *= texture(pbr_iridescenceSampler, pbr_vUV).r;
4990
+ }
4991
+ #endif
4992
+ iridescence = clamp(iridescence, 0.0, 1.0);
4993
+ float iridescenceThickness = mix(
4994
+ pbrMaterial.iridescenceThicknessRange.x,
4995
+ pbrMaterial.iridescenceThicknessRange.y,
4996
+ 0.5
4997
+ );
4998
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
4999
+ iridescenceThickness = mix(
5000
+ pbrMaterial.iridescenceThicknessRange.x,
5001
+ pbrMaterial.iridescenceThicknessRange.y,
5002
+ texture(pbr_iridescenceThicknessSampler, pbr_vUV).g
5003
+ );
5004
+ #endif
5005
+
5006
+ float anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
5007
+ vec2 anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
5008
+ #ifdef HAS_ANISOTROPYMAP
5009
+ if (pbrMaterial.anisotropyMapEnabled) {
5010
+ vec3 anisotropySample = texture(pbr_anisotropySampler, pbr_vUV).rgb;
5011
+ anisotropyStrength *= anisotropySample.b;
5012
+ vec2 mappedDirection = anisotropySample.rg * 2.0 - 1.0;
5013
+ if (length(mappedDirection) > 0.0001) {
5014
+ anisotropyDirection = normalize(mappedDirection);
5015
+ }
5016
+ }
5017
+ #endif
5018
+ anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
5019
+ vec3 anisotropyTangent = normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
5020
+ if (length(anisotropyTangent) < 0.0001) {
5021
+ anisotropyTangent = normalize(tbn[0]);
5022
+ }
5023
+ float anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
5024
+ perceptualRoughness = mix(
5025
+ perceptualRoughness,
5026
+ clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
5027
+ anisotropyStrength
5028
+ );
5029
+
3681
5030
  // Roughness is authored as perceptual roughness; as is convention,
3682
5031
  // convert to material roughness by squaring the perceptual roughness [2].
3683
5032
  float alphaRoughness = perceptualRoughness * perceptualRoughness;
3684
5033
 
3685
- vec3 f0 = vec3(0.04);
3686
- vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
3687
- diffuseColor *= 1.0 - metallic;
3688
- vec3 specularColor = mix(f0, baseColor.rgb, metallic);
5034
+ float dielectricF0 = getDielectricF0(pbrMaterial.ior);
5035
+ vec3 dielectricSpecularF0 = min(
5036
+ vec3(dielectricF0) * specularFactor * specularIntensity,
5037
+ vec3(1.0)
5038
+ );
5039
+ vec3 iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
5040
+ dielectricSpecularF0 = mix(
5041
+ dielectricSpecularF0,
5042
+ dielectricSpecularF0 * iridescenceTint,
5043
+ iridescence
5044
+ );
5045
+ vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - dielectricSpecularF0);
5046
+ diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
5047
+ vec3 specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
5048
+
5049
+ float baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
5050
+ diffuseColor *= baseLayerEnergy;
5051
+ specularColor *= baseLayerEnergy;
3689
5052
 
3690
5053
  // Compute reflectance.
3691
5054
  float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -3697,11 +5060,6 @@ vec4 pbr_filterColor(vec4 colorUnused)
3697
5060
  float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
3698
5061
  vec3 specularEnvironmentR0 = specularColor.rgb;
3699
5062
  vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
3700
-
3701
- vec3 n = getNormal(); // normal at surface point
3702
- vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
3703
-
3704
- float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
3705
5063
  vec3 reflection = -normalize(reflect(v, n));
3706
5064
 
3707
5065
  PBRInfo pbrInfo = PBRInfo(
@@ -3725,13 +5083,33 @@ vec4 pbr_filterColor(vec4 colorUnused)
3725
5083
  #ifdef USE_LIGHTS
3726
5084
  // Apply ambient light
3727
5085
  PBRInfo_setAmbientLight(pbrInfo);
3728
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
5086
+ color += calculateMaterialLightColor(
5087
+ pbrInfo,
5088
+ lighting.ambientColor,
5089
+ clearcoatNormal,
5090
+ clearcoatFactor,
5091
+ clearcoatRoughness,
5092
+ sheenColor,
5093
+ sheenRoughness,
5094
+ anisotropyTangent,
5095
+ anisotropyStrength
5096
+ );
3729
5097
 
3730
5098
  // Apply directional light
3731
5099
  for(int i = 0; i < lighting.directionalLightCount; i++) {
3732
5100
  if (i < lighting.directionalLightCount) {
3733
5101
  PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
3734
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
5102
+ color += calculateMaterialLightColor(
5103
+ pbrInfo,
5104
+ lighting_getDirectionalLight(i).color,
5105
+ clearcoatNormal,
5106
+ clearcoatFactor,
5107
+ clearcoatRoughness,
5108
+ sheenColor,
5109
+ sheenRoughness,
5110
+ anisotropyTangent,
5111
+ anisotropyStrength
5112
+ );
3735
5113
  }
3736
5114
  }
3737
5115
 
@@ -3740,7 +5118,35 @@ vec4 pbr_filterColor(vec4 colorUnused)
3740
5118
  if (i < lighting.pointLightCount) {
3741
5119
  PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
3742
5120
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
3743
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
5121
+ color += calculateMaterialLightColor(
5122
+ pbrInfo,
5123
+ lighting_getPointLight(i).color / attenuation,
5124
+ clearcoatNormal,
5125
+ clearcoatFactor,
5126
+ clearcoatRoughness,
5127
+ sheenColor,
5128
+ sheenRoughness,
5129
+ anisotropyTangent,
5130
+ anisotropyStrength
5131
+ );
5132
+ }
5133
+ }
5134
+
5135
+ for(int i = 0; i < lighting.spotLightCount; i++) {
5136
+ if (i < lighting.spotLightCount) {
5137
+ PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
5138
+ float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
5139
+ color += calculateMaterialLightColor(
5140
+ pbrInfo,
5141
+ lighting_getSpotLight(i).color / attenuation,
5142
+ clearcoatNormal,
5143
+ clearcoatFactor,
5144
+ clearcoatRoughness,
5145
+ sheenColor,
5146
+ sheenRoughness,
5147
+ anisotropyTangent,
5148
+ anisotropyStrength
5149
+ );
3744
5150
  }
3745
5151
  }
3746
5152
  #endif
@@ -3748,7 +5154,16 @@ vec4 pbr_filterColor(vec4 colorUnused)
3748
5154
  // Calculate lighting contribution from image based lighting source (IBL)
3749
5155
  #ifdef USE_IBL
3750
5156
  if (pbrMaterial.IBLenabled) {
3751
- color += getIBLContribution(pbrInfo, n, reflection);
5157
+ color += getIBLContribution(pbrInfo, n, reflection) *
5158
+ calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
5159
+ color += calculateClearcoatIBLContribution(
5160
+ pbrInfo,
5161
+ clearcoatNormal,
5162
+ -normalize(reflect(v, clearcoatNormal)),
5163
+ clearcoatFactor,
5164
+ clearcoatRoughness
5165
+ );
5166
+ color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
3752
5167
  }
3753
5168
  #endif
3754
5169
 
@@ -3760,12 +5175,17 @@ vec4 pbr_filterColor(vec4 colorUnused)
3760
5175
  }
3761
5176
  #endif
3762
5177
 
5178
+ vec3 emissive = pbrMaterial.emissiveFactor;
3763
5179
  #ifdef HAS_EMISSIVEMAP
3764
5180
  if (pbrMaterial.emissiveMapEnabled) {
3765
- vec3 emissive = SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb * pbrMaterial.emissiveFactor;
3766
- color += emissive;
5181
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
3767
5182
  }
3768
5183
  #endif
5184
+ color += emissive * pbrMaterial.emissiveStrength;
5185
+
5186
+ if (transmission > 0.0) {
5187
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
5188
+ }
3769
5189
 
3770
5190
  // This section uses mix to override final color for reference app visualization
3771
5191
  // of various parameters in the lighting equation.
@@ -3785,7 +5205,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
3785
5205
 
3786
5206
  }
3787
5207
 
3788
- return vec4(pow(color,vec3(1.0/2.2)), baseColor.a);
5208
+ float alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
5209
+ return vec4(pow(color,vec3(1.0/2.2)), alpha);
3789
5210
  }
3790
5211
  `
3791
5212
  );
@@ -3851,6 +5272,42 @@ struct pbrMaterialUniforms {
3851
5272
 
3852
5273
  alphaCutoffEnabled: i32,
3853
5274
  alphaCutoff: f32, // #ifdef ALPHA_CUTOFF
5275
+
5276
+ specularColorFactor: vec3f,
5277
+ specularIntensityFactor: f32,
5278
+ specularColorMapEnabled: i32,
5279
+ specularIntensityMapEnabled: i32,
5280
+
5281
+ ior: f32,
5282
+
5283
+ transmissionFactor: f32,
5284
+ transmissionMapEnabled: i32,
5285
+
5286
+ thicknessFactor: f32,
5287
+ attenuationDistance: f32,
5288
+ attenuationColor: vec3f,
5289
+
5290
+ clearcoatFactor: f32,
5291
+ clearcoatRoughnessFactor: f32,
5292
+ clearcoatMapEnabled: i32,
5293
+ clearcoatRoughnessMapEnabled: i32,
5294
+
5295
+ sheenColorFactor: vec3f,
5296
+ sheenRoughnessFactor: f32,
5297
+ sheenColorMapEnabled: i32,
5298
+ sheenRoughnessMapEnabled: i32,
5299
+
5300
+ iridescenceFactor: f32,
5301
+ iridescenceIor: f32,
5302
+ iridescenceThicknessRange: vec2f,
5303
+ iridescenceMapEnabled: i32,
5304
+
5305
+ anisotropyStrength: f32,
5306
+ anisotropyRotation: f32,
5307
+ anisotropyDirection: vec2f,
5308
+ anisotropyMapEnabled: i32,
5309
+
5310
+ emissiveStrength: f32,
3854
5311
 
3855
5312
  // IBL
3856
5313
  IBLenabled: i32,
@@ -3863,38 +5320,77 @@ struct pbrMaterialUniforms {
3863
5320
  // #endif
3864
5321
  }
3865
5322
 
3866
- @binding(2) @group(0) var<uniform> pbrMaterial : pbrMaterialUniforms;
5323
+ @group(3) @binding(auto) var<uniform> pbrMaterial : pbrMaterialUniforms;
3867
5324
 
3868
5325
  // Samplers
3869
5326
  #ifdef HAS_BASECOLORMAP
3870
- @binding(3) @group(0) var pbr_baseColorSampler: texture_2d<f32>;
3871
- @binding(4) @group(0) var pbr_baseColorSamplerSampler: sampler;
5327
+ @group(3) @binding(auto) var pbr_baseColorSampler: texture_2d<f32>;
5328
+ @group(3) @binding(auto) var pbr_baseColorSamplerSampler: sampler;
3872
5329
  #endif
3873
5330
  #ifdef HAS_NORMALMAP
3874
- @binding(5) @group(0) var pbr_normalSampler: texture_2d<f32>;
3875
- @binding(6) @group(0) var pbr_normalSamplerSampler: sampler;
5331
+ @group(3) @binding(auto) var pbr_normalSampler: texture_2d<f32>;
5332
+ @group(3) @binding(auto) var pbr_normalSamplerSampler: sampler;
3876
5333
  #endif
3877
5334
  #ifdef HAS_EMISSIVEMAP
3878
- @binding(7) @group(0) var pbr_emissiveSampler: texture_2d<f32>;
3879
- @binding(8) @group(0) var pbr_emissiveSamplerSampler: sampler;
5335
+ @group(3) @binding(auto) var pbr_emissiveSampler: texture_2d<f32>;
5336
+ @group(3) @binding(auto) var pbr_emissiveSamplerSampler: sampler;
3880
5337
  #endif
3881
5338
  #ifdef HAS_METALROUGHNESSMAP
3882
- @binding(9) @group(0) var pbr_metallicRoughnessSampler: texture_2d<f32>;
3883
- @binding(10) @group(0) var pbr_metallicRoughnessSamplerSampler: sampler;
5339
+ @group(3) @binding(auto) var pbr_metallicRoughnessSampler: texture_2d<f32>;
5340
+ @group(3) @binding(auto) var pbr_metallicRoughnessSamplerSampler: sampler;
3884
5341
  #endif
3885
5342
  #ifdef HAS_OCCLUSIONMAP
3886
- @binding(11) @group(0) var pbr_occlusionSampler: texture_2d<f32>;
3887
- @binding(12) @group(0) var pbr_occlusionSamplerSampler: sampler;
5343
+ @group(3) @binding(auto) var pbr_occlusionSampler: texture_2d<f32>;
5344
+ @group(3) @binding(auto) var pbr_occlusionSamplerSampler: sampler;
3888
5345
  #endif
3889
- #ifdef USE_IBL
3890
- @binding(13) @group(0) var pbr_diffuseEnvSampler: texture_cube<f32>;
3891
- @binding(14) @group(0) var pbr_diffuseEnvSamplerSampler: sampler;
3892
- @binding(15) @group(0) var pbr_specularEnvSampler: texture_cube<f32>;
3893
- @binding(16) @group(0) var pbr_specularEnvSamplerSampler: sampler;
3894
- @binding(17) @group(0) var pbr_BrdfLUT: texture_2d<f32>;
3895
- @binding(18) @group(0) var pbr_BrdfLUTSampler: sampler;
5346
+ #ifdef HAS_SPECULARCOLORMAP
5347
+ @group(3) @binding(auto) var pbr_specularColorSampler: texture_2d<f32>;
5348
+ @group(3) @binding(auto) var pbr_specularColorSamplerSampler: sampler;
5349
+ #endif
5350
+ #ifdef HAS_SPECULARINTENSITYMAP
5351
+ @group(3) @binding(auto) var pbr_specularIntensitySampler: texture_2d<f32>;
5352
+ @group(3) @binding(auto) var pbr_specularIntensitySamplerSampler: sampler;
5353
+ #endif
5354
+ #ifdef HAS_TRANSMISSIONMAP
5355
+ @group(3) @binding(auto) var pbr_transmissionSampler: texture_2d<f32>;
5356
+ @group(3) @binding(auto) var pbr_transmissionSamplerSampler: sampler;
5357
+ #endif
5358
+ #ifdef HAS_THICKNESSMAP
5359
+ @group(3) @binding(auto) var pbr_thicknessSampler: texture_2d<f32>;
5360
+ @group(3) @binding(auto) var pbr_thicknessSamplerSampler: sampler;
5361
+ #endif
5362
+ #ifdef HAS_CLEARCOATMAP
5363
+ @group(3) @binding(auto) var pbr_clearcoatSampler: texture_2d<f32>;
5364
+ @group(3) @binding(auto) var pbr_clearcoatSamplerSampler: sampler;
5365
+ #endif
5366
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
5367
+ @group(3) @binding(auto) var pbr_clearcoatRoughnessSampler: texture_2d<f32>;
5368
+ @group(3) @binding(auto) var pbr_clearcoatRoughnessSamplerSampler: sampler;
5369
+ #endif
5370
+ #ifdef HAS_CLEARCOATNORMALMAP
5371
+ @group(3) @binding(auto) var pbr_clearcoatNormalSampler: texture_2d<f32>;
5372
+ @group(3) @binding(auto) var pbr_clearcoatNormalSamplerSampler: sampler;
5373
+ #endif
5374
+ #ifdef HAS_SHEENCOLORMAP
5375
+ @group(3) @binding(auto) var pbr_sheenColorSampler: texture_2d<f32>;
5376
+ @group(3) @binding(auto) var pbr_sheenColorSamplerSampler: sampler;
5377
+ #endif
5378
+ #ifdef HAS_SHEENROUGHNESSMAP
5379
+ @group(3) @binding(auto) var pbr_sheenRoughnessSampler: texture_2d<f32>;
5380
+ @group(3) @binding(auto) var pbr_sheenRoughnessSamplerSampler: sampler;
5381
+ #endif
5382
+ #ifdef HAS_IRIDESCENCEMAP
5383
+ @group(3) @binding(auto) var pbr_iridescenceSampler: texture_2d<f32>;
5384
+ @group(3) @binding(auto) var pbr_iridescenceSamplerSampler: sampler;
5385
+ #endif
5386
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
5387
+ @group(3) @binding(auto) var pbr_iridescenceThicknessSampler: texture_2d<f32>;
5388
+ @group(3) @binding(auto) var pbr_iridescenceThicknessSamplerSampler: sampler;
5389
+ #endif
5390
+ #ifdef HAS_ANISOTROPYMAP
5391
+ @group(3) @binding(auto) var pbr_anisotropySampler: texture_2d<f32>;
5392
+ @group(3) @binding(auto) var pbr_anisotropySamplerSampler: sampler;
3896
5393
  #endif
3897
-
3898
5394
  // Encapsulate the various inputs used by the various functions in the shading equation
3899
5395
  // We store values in this struct to simplify the integration of alternative implementations
3900
5396
  // of the shading terms, outlined in the Readme.MD Appendix.
@@ -3935,11 +5431,9 @@ fn SRGBtoLINEAR(srgbIn: vec4f ) -> vec4f
3935
5431
  return vec4f(linOut, srgbIn.w);
3936
5432
  }
3937
5433
 
3938
- // Find the normal for this fragment, pulling either from a predefined normal map
3939
- // or from the interpolated mesh normal and tangent attributes.
3940
- fn getNormal() -> vec3f
5434
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
5435
+ fn getTBN() -> mat3x3f
3941
5436
  {
3942
- // Retrieve the tangent space matrix
3943
5437
  let pos_dx: vec3f = dpdx(fragmentInputs.pbr_vPosition);
3944
5438
  let pos_dy: vec3f = dpdy(fragmentInputs.pbr_vPosition);
3945
5439
  let tex_dx: vec3f = dpdx(vec3f(fragmentInputs.pbr_vUV, 0.0));
@@ -3957,16 +5451,52 @@ fn getNormal() -> vec3f
3957
5451
  tbn = fragmentInputs.pbr_vTBN;
3958
5452
  #endif
3959
5453
 
5454
+ return tbn;
5455
+ }
5456
+
5457
+ // Find the normal for this fragment, pulling either from a predefined normal map
5458
+ // or from the interpolated mesh normal and tangent attributes.
5459
+ fn getMappedNormal(
5460
+ normalSampler: texture_2d<f32>,
5461
+ normalSamplerBinding: sampler,
5462
+ tbn: mat3x3f,
5463
+ normalScale: f32
5464
+ ) -> vec3f
5465
+ {
5466
+ let n = textureSample(normalSampler, normalSamplerBinding, fragmentInputs.pbr_vUV).rgb;
5467
+ return normalize(tbn * ((2.0 * n - 1.0) * vec3f(normalScale, normalScale, 1.0)));
5468
+ }
5469
+
5470
+ fn getNormal(tbn: mat3x3f) -> vec3f
5471
+ {
3960
5472
  // The tbn matrix is linearly interpolated, so we need to re-normalize
3961
5473
  var n: vec3f = normalize(tbn[2].xyz);
3962
5474
  #ifdef HAS_NORMALMAP
3963
- n = textureSample(pbr_normalSampler, pbr_normalSamplerSampler, fragmentInputs.pbr_vUV).rgb;
3964
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3f(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
5475
+ n = getMappedNormal(
5476
+ pbr_normalSampler,
5477
+ pbr_normalSamplerSampler,
5478
+ tbn,
5479
+ pbrMaterial.normalScale
5480
+ );
3965
5481
  #endif
3966
5482
 
3967
5483
  return n;
3968
5484
  }
3969
5485
 
5486
+ fn getClearcoatNormal(tbn: mat3x3f, baseNormal: vec3f) -> vec3f
5487
+ {
5488
+ #ifdef HAS_CLEARCOATNORMALMAP
5489
+ return getMappedNormal(
5490
+ pbr_clearcoatNormalSampler,
5491
+ pbr_clearcoatNormalSamplerSampler,
5492
+ tbn,
5493
+ 1.0
5494
+ );
5495
+ #else
5496
+ return baseNormal;
5497
+ #endif
5498
+ }
5499
+
3970
5500
  // Calculation of the lighting contribution from an optional Image Based Light source.
3971
5501
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
3972
5502
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -3977,17 +5507,25 @@ fn getIBLContribution(pbrInfo: PBRInfo, n: vec3f, reflection: vec3f) -> vec3f
3977
5507
  let lod: f32 = pbrInfo.perceptualRoughness * mipCount;
3978
5508
  // retrieve a scale and bias to F0. See [1], Figure 3
3979
5509
  let brdf = SRGBtoLINEAR(
3980
- textureSample(
3981
- pbr_BrdfLUT,
3982
- pbr_BrdfLUTSampler,
3983
- vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness)
5510
+ textureSampleLevel(
5511
+ pbr_brdfLUT,
5512
+ pbr_brdfLUTSampler,
5513
+ vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness),
5514
+ 0.0
3984
5515
  )
3985
5516
  ).rgb;
3986
5517
  let diffuseLight =
3987
- SRGBtoLINEAR(textureSample(pbr_diffuseEnvSampler, pbr_diffuseEnvSamplerSampler, n)).rgb;
3988
- let specularLightDefault =
3989
- SRGBtoLINEAR(textureSample(pbr_specularEnvSampler, pbr_specularEnvSamplerSampler, reflection)).rgb;
3990
- var specularLight = specularLightDefault;
5518
+ SRGBtoLINEAR(
5519
+ textureSampleLevel(pbr_diffuseEnvSampler, pbr_diffuseEnvSamplerSampler, n, 0.0)
5520
+ ).rgb;
5521
+ var specularLight = SRGBtoLINEAR(
5522
+ textureSampleLevel(
5523
+ pbr_specularEnvSampler,
5524
+ pbr_specularEnvSamplerSampler,
5525
+ reflection,
5526
+ 0.0
5527
+ )
5528
+ ).rgb;
3991
5529
  #ifdef USE_TEX_LOD
3992
5530
  specularLight = SRGBtoLINEAR(
3993
5531
  textureSampleLevel(
@@ -4048,6 +5586,172 @@ fn microfacetDistribution(pbrInfo: PBRInfo) -> f32 {
4048
5586
  return roughnessSq / (M_PI * f * f);
4049
5587
  }
4050
5588
 
5589
+ fn maxComponent(value: vec3f) -> f32 {
5590
+ return max(max(value.r, value.g), value.b);
5591
+ }
5592
+
5593
+ fn getDielectricF0(ior: f32) -> f32 {
5594
+ let clampedIor = max(ior, 1.0);
5595
+ let ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
5596
+ return ratio * ratio;
5597
+ }
5598
+
5599
+ fn normalizeDirection(direction: vec2f) -> vec2f {
5600
+ let directionLength = length(direction);
5601
+ if (directionLength > 0.0001) {
5602
+ return direction / directionLength;
5603
+ }
5604
+
5605
+ return vec2f(1.0, 0.0);
5606
+ }
5607
+
5608
+ fn rotateDirection(direction: vec2f, rotation: f32) -> vec2f {
5609
+ let s = sin(rotation);
5610
+ let c = cos(rotation);
5611
+ return vec2f(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
5612
+ }
5613
+
5614
+ fn getIridescenceTint(iridescence: f32, thickness: f32, NdotV: f32) -> vec3f {
5615
+ if (iridescence <= 0.0) {
5616
+ return vec3f(1.0);
5617
+ }
5618
+
5619
+ let phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
5620
+ let thinFilmTint =
5621
+ 0.5 +
5622
+ 0.5 *
5623
+ cos(vec3f(phase, phase + 2.0943951, phase + 4.1887902));
5624
+ return mix(vec3f(1.0), thinFilmTint, iridescence);
5625
+ }
5626
+
5627
+ fn getVolumeAttenuation(thickness: f32) -> vec3f {
5628
+ if (thickness <= 0.0) {
5629
+ return vec3f(1.0);
5630
+ }
5631
+
5632
+ let attenuationCoefficient =
5633
+ -log(max(pbrMaterial.attenuationColor, vec3f(0.0001))) /
5634
+ max(pbrMaterial.attenuationDistance, 0.0001);
5635
+ return exp(-attenuationCoefficient * thickness);
5636
+ }
5637
+
5638
+ fn createClearcoatPBRInfo(
5639
+ basePBRInfo: PBRInfo,
5640
+ clearcoatNormal: vec3f,
5641
+ clearcoatRoughness: f32
5642
+ ) -> PBRInfo {
5643
+ let perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
5644
+ let alphaRoughness = perceptualRoughness * perceptualRoughness;
5645
+ let NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
5646
+
5647
+ return PBRInfo(
5648
+ basePBRInfo.NdotL,
5649
+ NdotV,
5650
+ basePBRInfo.NdotH,
5651
+ basePBRInfo.LdotH,
5652
+ basePBRInfo.VdotH,
5653
+ perceptualRoughness,
5654
+ 0.0,
5655
+ vec3f(0.04),
5656
+ vec3f(1.0),
5657
+ alphaRoughness,
5658
+ vec3f(0.0),
5659
+ vec3f(0.04),
5660
+ clearcoatNormal,
5661
+ basePBRInfo.v
5662
+ );
5663
+ }
5664
+
5665
+ fn calculateClearcoatContribution(
5666
+ pbrInfo: PBRInfo,
5667
+ lightColor: vec3f,
5668
+ clearcoatNormal: vec3f,
5669
+ clearcoatFactor: f32,
5670
+ clearcoatRoughness: f32
5671
+ ) -> vec3f {
5672
+ if (clearcoatFactor <= 0.0) {
5673
+ return vec3f(0.0);
5674
+ }
5675
+
5676
+ let clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
5677
+ return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
5678
+ }
5679
+
5680
+ #ifdef USE_IBL
5681
+ fn calculateClearcoatIBLContribution(
5682
+ pbrInfo: PBRInfo,
5683
+ clearcoatNormal: vec3f,
5684
+ reflection: vec3f,
5685
+ clearcoatFactor: f32,
5686
+ clearcoatRoughness: f32
5687
+ ) -> vec3f {
5688
+ if (clearcoatFactor <= 0.0) {
5689
+ return vec3f(0.0);
5690
+ }
5691
+
5692
+ let clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
5693
+ return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
5694
+ }
5695
+ #endif
5696
+
5697
+ fn calculateSheenContribution(
5698
+ pbrInfo: PBRInfo,
5699
+ lightColor: vec3f,
5700
+ sheenColor: vec3f,
5701
+ sheenRoughness: f32
5702
+ ) -> vec3f {
5703
+ if (maxComponent(sheenColor) <= 0.0) {
5704
+ return vec3f(0.0);
5705
+ }
5706
+
5707
+ let sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
5708
+ let sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
5709
+ return pbrInfo.NdotL *
5710
+ lightColor *
5711
+ sheenColor *
5712
+ (0.25 + 0.75 * sheenFresnel) *
5713
+ sheenVisibility *
5714
+ (1.0 - pbrInfo.metalness);
5715
+ }
5716
+
5717
+ fn calculateAnisotropyBoost(
5718
+ pbrInfo: PBRInfo,
5719
+ anisotropyTangent: vec3f,
5720
+ anisotropyStrength: f32
5721
+ ) -> f32 {
5722
+ if (anisotropyStrength <= 0.0) {
5723
+ return 1.0;
5724
+ }
5725
+
5726
+ let anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
5727
+ let bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
5728
+ return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
5729
+ }
5730
+
5731
+ fn calculateMaterialLightColor(
5732
+ pbrInfo: PBRInfo,
5733
+ lightColor: vec3f,
5734
+ clearcoatNormal: vec3f,
5735
+ clearcoatFactor: f32,
5736
+ clearcoatRoughness: f32,
5737
+ sheenColor: vec3f,
5738
+ sheenRoughness: f32,
5739
+ anisotropyTangent: vec3f,
5740
+ anisotropyStrength: f32
5741
+ ) -> vec3f {
5742
+ let anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
5743
+ var color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
5744
+ color += calculateClearcoatContribution(
5745
+ pbrInfo,
5746
+ lightColor,
5747
+ clearcoatNormal,
5748
+ clearcoatFactor,
5749
+ clearcoatRoughness
5750
+ );
5751
+ color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
5752
+ return color;
5753
+ }
5754
+
4051
5755
  fn PBRInfo_setAmbientLight(pbrInfo: ptr<function, PBRInfo>) {
4052
5756
  (*pbrInfo).NdotL = 1.0;
4053
5757
  (*pbrInfo).NdotH = 0.0;
@@ -4072,6 +5776,11 @@ fn PBRInfo_setPointLight(pbrInfo: ptr<function, PBRInfo>, pointLight: PointLight
4072
5776
  PBRInfo_setDirectionalLight(pbrInfo, light_direction);
4073
5777
  }
4074
5778
 
5779
+ fn PBRInfo_setSpotLight(pbrInfo: ptr<function, PBRInfo>, spotLight: SpotLight) {
5780
+ let light_direction = normalize(spotLight.position - fragmentInputs.pbr_vPosition);
5781
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
5782
+ }
5783
+
4075
5784
  fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
4076
5785
  // Calculate the shading terms for the microfacet specular shading model
4077
5786
  let F = specularReflection(pbrInfo);
@@ -4101,6 +5810,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4101
5810
  #endif
4102
5811
 
4103
5812
  var color = vec3<f32>(0.0, 0.0, 0.0);
5813
+ var transmission = 0.0;
4104
5814
 
4105
5815
  if (pbrMaterial.unlit != 0u) {
4106
5816
  color = baseColor.rgb;
@@ -4123,14 +5833,308 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4123
5833
  #endif
4124
5834
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
4125
5835
  metallic = clamp(metallic, 0.0, 1.0);
5836
+ let tbn = getTBN();
5837
+ let n = getNormal(tbn); // normal at surface point
5838
+ let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
5839
+ let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
5840
+ var useExtendedPBR = false;
5841
+ #ifdef USE_MATERIAL_EXTENSIONS
5842
+ useExtendedPBR =
5843
+ pbrMaterial.specularColorMapEnabled != 0 ||
5844
+ pbrMaterial.specularIntensityMapEnabled != 0 ||
5845
+ abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
5846
+ maxComponent(abs(pbrMaterial.specularColorFactor - vec3f(1.0))) > 0.0001 ||
5847
+ abs(pbrMaterial.ior - 1.5) > 0.0001 ||
5848
+ pbrMaterial.transmissionMapEnabled != 0 ||
5849
+ pbrMaterial.transmissionFactor > 0.0001 ||
5850
+ pbrMaterial.clearcoatMapEnabled != 0 ||
5851
+ pbrMaterial.clearcoatRoughnessMapEnabled != 0 ||
5852
+ pbrMaterial.clearcoatFactor > 0.0001 ||
5853
+ pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
5854
+ pbrMaterial.sheenColorMapEnabled != 0 ||
5855
+ pbrMaterial.sheenRoughnessMapEnabled != 0 ||
5856
+ maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
5857
+ pbrMaterial.sheenRoughnessFactor > 0.0001 ||
5858
+ pbrMaterial.iridescenceMapEnabled != 0 ||
5859
+ pbrMaterial.iridescenceFactor > 0.0001 ||
5860
+ abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
5861
+ abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
5862
+ abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
5863
+ pbrMaterial.anisotropyMapEnabled != 0 ||
5864
+ pbrMaterial.anisotropyStrength > 0.0001 ||
5865
+ abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
5866
+ length(pbrMaterial.anisotropyDirection - vec2f(1.0, 0.0)) > 0.0001;
5867
+ #endif
5868
+
5869
+ if (!useExtendedPBR) {
5870
+ let alphaRoughness = perceptualRoughness * perceptualRoughness;
5871
+
5872
+ let f0 = vec3<f32>(0.04);
5873
+ var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
5874
+ diffuseColor *= 1.0 - metallic;
5875
+ let specularColor = mix(f0, baseColor.rgb, metallic);
5876
+
5877
+ let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
5878
+ let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
5879
+ let specularEnvironmentR0 = specularColor;
5880
+ let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
5881
+ let reflection = -normalize(reflect(v, n));
5882
+
5883
+ var pbrInfo = PBRInfo(
5884
+ 0.0, // NdotL
5885
+ NdotV,
5886
+ 0.0, // NdotH
5887
+ 0.0, // LdotH
5888
+ 0.0, // VdotH
5889
+ perceptualRoughness,
5890
+ metallic,
5891
+ specularEnvironmentR0,
5892
+ specularEnvironmentR90,
5893
+ alphaRoughness,
5894
+ diffuseColor,
5895
+ specularColor,
5896
+ n,
5897
+ v
5898
+ );
5899
+
5900
+ #ifdef USE_LIGHTS
5901
+ PBRInfo_setAmbientLight(&pbrInfo);
5902
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor);
5903
+
5904
+ for (var i = 0; i < lighting.directionalLightCount; i++) {
5905
+ if (i < lighting.directionalLightCount) {
5906
+ PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
5907
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
5908
+ }
5909
+ }
5910
+
5911
+ for (var i = 0; i < lighting.pointLightCount; i++) {
5912
+ if (i < lighting.pointLightCount) {
5913
+ PBRInfo_setPointLight(&pbrInfo, lighting_getPointLight(i));
5914
+ let attenuation = getPointLightAttenuation(
5915
+ lighting_getPointLight(i),
5916
+ distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
5917
+ );
5918
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
5919
+ }
5920
+ }
5921
+
5922
+ for (var i = 0; i < lighting.spotLightCount; i++) {
5923
+ if (i < lighting.spotLightCount) {
5924
+ PBRInfo_setSpotLight(&pbrInfo, lighting_getSpotLight(i));
5925
+ let attenuation = getSpotLightAttenuation(
5926
+ lighting_getSpotLight(i),
5927
+ fragmentInputs.pbr_vPosition
5928
+ );
5929
+ color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
5930
+ }
5931
+ }
5932
+ #endif
5933
+
5934
+ #ifdef USE_IBL
5935
+ if (pbrMaterial.IBLenabled != 0) {
5936
+ color += getIBLContribution(pbrInfo, n, reflection);
5937
+ }
5938
+ #endif
5939
+
5940
+ #ifdef HAS_OCCLUSIONMAP
5941
+ if (pbrMaterial.occlusionMapEnabled != 0) {
5942
+ let ao =
5943
+ textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, fragmentInputs.pbr_vUV).r;
5944
+ color = mix(color, color * ao, pbrMaterial.occlusionStrength);
5945
+ }
5946
+ #endif
5947
+
5948
+ var emissive = pbrMaterial.emissiveFactor;
5949
+ #ifdef HAS_EMISSIVEMAP
5950
+ if (pbrMaterial.emissiveMapEnabled != 0u) {
5951
+ emissive *= SRGBtoLINEAR(
5952
+ textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
5953
+ ).rgb;
5954
+ }
5955
+ #endif
5956
+ color += emissive * pbrMaterial.emissiveStrength;
5957
+
5958
+ #ifdef PBR_DEBUG
5959
+ color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
5960
+ color = mix(color, vec3<f32>(metallic), pbrMaterial.scaleDiffBaseMR.z);
5961
+ color = mix(color, vec3<f32>(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
5962
+ #endif
5963
+
5964
+ return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), baseColor.a);
5965
+ }
5966
+
5967
+ var specularIntensity = pbrMaterial.specularIntensityFactor;
5968
+ #ifdef HAS_SPECULARINTENSITYMAP
5969
+ if (pbrMaterial.specularIntensityMapEnabled != 0) {
5970
+ specularIntensity *= textureSample(
5971
+ pbr_specularIntensitySampler,
5972
+ pbr_specularIntensitySamplerSampler,
5973
+ fragmentInputs.pbr_vUV
5974
+ ).a;
5975
+ }
5976
+ #endif
5977
+
5978
+ var specularFactor = pbrMaterial.specularColorFactor;
5979
+ #ifdef HAS_SPECULARCOLORMAP
5980
+ if (pbrMaterial.specularColorMapEnabled != 0) {
5981
+ specularFactor *= SRGBtoLINEAR(
5982
+ textureSample(
5983
+ pbr_specularColorSampler,
5984
+ pbr_specularColorSamplerSampler,
5985
+ fragmentInputs.pbr_vUV
5986
+ )
5987
+ ).rgb;
5988
+ }
5989
+ #endif
5990
+
5991
+ transmission = pbrMaterial.transmissionFactor;
5992
+ #ifdef HAS_TRANSMISSIONMAP
5993
+ if (pbrMaterial.transmissionMapEnabled != 0) {
5994
+ transmission *= textureSample(
5995
+ pbr_transmissionSampler,
5996
+ pbr_transmissionSamplerSampler,
5997
+ fragmentInputs.pbr_vUV
5998
+ ).r;
5999
+ }
6000
+ #endif
6001
+ transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
6002
+ var thickness = max(pbrMaterial.thicknessFactor, 0.0);
6003
+ #ifdef HAS_THICKNESSMAP
6004
+ thickness *= textureSample(
6005
+ pbr_thicknessSampler,
6006
+ pbr_thicknessSamplerSampler,
6007
+ fragmentInputs.pbr_vUV
6008
+ ).g;
6009
+ #endif
6010
+
6011
+ var clearcoatFactor = pbrMaterial.clearcoatFactor;
6012
+ var clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
6013
+ #ifdef HAS_CLEARCOATMAP
6014
+ if (pbrMaterial.clearcoatMapEnabled != 0) {
6015
+ clearcoatFactor *= textureSample(
6016
+ pbr_clearcoatSampler,
6017
+ pbr_clearcoatSamplerSampler,
6018
+ fragmentInputs.pbr_vUV
6019
+ ).r;
6020
+ }
6021
+ #endif
6022
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
6023
+ if (pbrMaterial.clearcoatRoughnessMapEnabled != 0) {
6024
+ clearcoatRoughness *= textureSample(
6025
+ pbr_clearcoatRoughnessSampler,
6026
+ pbr_clearcoatRoughnessSamplerSampler,
6027
+ fragmentInputs.pbr_vUV
6028
+ ).g;
6029
+ }
6030
+ #endif
6031
+ clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
6032
+ clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
6033
+ let clearcoatNormal = getClearcoatNormal(tbn, n);
6034
+
6035
+ var sheenColor = pbrMaterial.sheenColorFactor;
6036
+ var sheenRoughness = pbrMaterial.sheenRoughnessFactor;
6037
+ #ifdef HAS_SHEENCOLORMAP
6038
+ if (pbrMaterial.sheenColorMapEnabled != 0) {
6039
+ sheenColor *= SRGBtoLINEAR(
6040
+ textureSample(
6041
+ pbr_sheenColorSampler,
6042
+ pbr_sheenColorSamplerSampler,
6043
+ fragmentInputs.pbr_vUV
6044
+ )
6045
+ ).rgb;
6046
+ }
6047
+ #endif
6048
+ #ifdef HAS_SHEENROUGHNESSMAP
6049
+ if (pbrMaterial.sheenRoughnessMapEnabled != 0) {
6050
+ sheenRoughness *= textureSample(
6051
+ pbr_sheenRoughnessSampler,
6052
+ pbr_sheenRoughnessSamplerSampler,
6053
+ fragmentInputs.pbr_vUV
6054
+ ).a;
6055
+ }
6056
+ #endif
6057
+ sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
6058
+
6059
+ var iridescence = pbrMaterial.iridescenceFactor;
6060
+ #ifdef HAS_IRIDESCENCEMAP
6061
+ if (pbrMaterial.iridescenceMapEnabled != 0) {
6062
+ iridescence *= textureSample(
6063
+ pbr_iridescenceSampler,
6064
+ pbr_iridescenceSamplerSampler,
6065
+ fragmentInputs.pbr_vUV
6066
+ ).r;
6067
+ }
6068
+ #endif
6069
+ iridescence = clamp(iridescence, 0.0, 1.0);
6070
+ var iridescenceThickness = mix(
6071
+ pbrMaterial.iridescenceThicknessRange.x,
6072
+ pbrMaterial.iridescenceThicknessRange.y,
6073
+ 0.5
6074
+ );
6075
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
6076
+ iridescenceThickness = mix(
6077
+ pbrMaterial.iridescenceThicknessRange.x,
6078
+ pbrMaterial.iridescenceThicknessRange.y,
6079
+ textureSample(
6080
+ pbr_iridescenceThicknessSampler,
6081
+ pbr_iridescenceThicknessSamplerSampler,
6082
+ fragmentInputs.pbr_vUV
6083
+ ).g
6084
+ );
6085
+ #endif
6086
+
6087
+ var anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
6088
+ var anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
6089
+ #ifdef HAS_ANISOTROPYMAP
6090
+ if (pbrMaterial.anisotropyMapEnabled != 0) {
6091
+ let anisotropySample = textureSample(
6092
+ pbr_anisotropySampler,
6093
+ pbr_anisotropySamplerSampler,
6094
+ fragmentInputs.pbr_vUV
6095
+ ).rgb;
6096
+ anisotropyStrength *= anisotropySample.b;
6097
+ let mappedDirection = anisotropySample.rg * 2.0 - 1.0;
6098
+ if (length(mappedDirection) > 0.0001) {
6099
+ anisotropyDirection = normalize(mappedDirection);
6100
+ }
6101
+ }
6102
+ #endif
6103
+ anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
6104
+ var anisotropyTangent =
6105
+ normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
6106
+ if (length(anisotropyTangent) < 0.0001) {
6107
+ anisotropyTangent = normalize(tbn[0]);
6108
+ }
6109
+ let anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
6110
+ perceptualRoughness = mix(
6111
+ perceptualRoughness,
6112
+ clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
6113
+ anisotropyStrength
6114
+ );
6115
+
4126
6116
  // Roughness is authored as perceptual roughness; as is convention,
4127
6117
  // convert to material roughness by squaring the perceptual roughness [2].
4128
6118
  let alphaRoughness = perceptualRoughness * perceptualRoughness;
4129
6119
 
4130
- let f0 = vec3<f32>(0.04);
4131
- var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
4132
- diffuseColor *= 1.0 - metallic;
4133
- let specularColor = mix(f0, baseColor.rgb, metallic);
6120
+ let dielectricF0 = getDielectricF0(pbrMaterial.ior);
6121
+ var dielectricSpecularF0 = min(
6122
+ vec3f(dielectricF0) * specularFactor * specularIntensity,
6123
+ vec3f(1.0)
6124
+ );
6125
+ let iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
6126
+ dielectricSpecularF0 = mix(
6127
+ dielectricSpecularF0,
6128
+ dielectricSpecularF0 * iridescenceTint,
6129
+ iridescence
6130
+ );
6131
+ var diffuseColor = baseColor.rgb * (vec3f(1.0) - dielectricSpecularF0);
6132
+ diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
6133
+ var specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
6134
+
6135
+ let baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
6136
+ diffuseColor *= baseLayerEnergy;
6137
+ specularColor *= baseLayerEnergy;
4134
6138
 
4135
6139
  // Compute reflectance.
4136
6140
  let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -4142,11 +6146,6 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4142
6146
  let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
4143
6147
  let specularEnvironmentR0 = specularColor;
4144
6148
  let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
4145
-
4146
- let n = getNormal(); // normal at surface point
4147
- let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
4148
-
4149
- let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
4150
6149
  let reflection = -normalize(reflect(v, n));
4151
6150
 
4152
6151
  var pbrInfo = PBRInfo(
@@ -4169,13 +6168,33 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4169
6168
  #ifdef USE_LIGHTS
4170
6169
  // Apply ambient light
4171
6170
  PBRInfo_setAmbientLight(&pbrInfo);
4172
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
6171
+ color += calculateMaterialLightColor(
6172
+ pbrInfo,
6173
+ lighting.ambientColor,
6174
+ clearcoatNormal,
6175
+ clearcoatFactor,
6176
+ clearcoatRoughness,
6177
+ sheenColor,
6178
+ sheenRoughness,
6179
+ anisotropyTangent,
6180
+ anisotropyStrength
6181
+ );
4173
6182
 
4174
6183
  // Apply directional light
4175
6184
  for (var i = 0; i < lighting.directionalLightCount; i++) {
4176
6185
  if (i < lighting.directionalLightCount) {
4177
6186
  PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
4178
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
6187
+ color += calculateMaterialLightColor(
6188
+ pbrInfo,
6189
+ lighting_getDirectionalLight(i).color,
6190
+ clearcoatNormal,
6191
+ clearcoatFactor,
6192
+ clearcoatRoughness,
6193
+ sheenColor,
6194
+ sheenRoughness,
6195
+ anisotropyTangent,
6196
+ anisotropyStrength
6197
+ );
4179
6198
  }
4180
6199
  }
4181
6200
 
@@ -4187,7 +6206,35 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4187
6206
  lighting_getPointLight(i),
4188
6207
  distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
4189
6208
  );
4190
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
6209
+ color += calculateMaterialLightColor(
6210
+ pbrInfo,
6211
+ lighting_getPointLight(i).color / attenuation,
6212
+ clearcoatNormal,
6213
+ clearcoatFactor,
6214
+ clearcoatRoughness,
6215
+ sheenColor,
6216
+ sheenRoughness,
6217
+ anisotropyTangent,
6218
+ anisotropyStrength
6219
+ );
6220
+ }
6221
+ }
6222
+
6223
+ for (var i = 0; i < lighting.spotLightCount; i++) {
6224
+ if (i < lighting.spotLightCount) {
6225
+ PBRInfo_setSpotLight(&pbrInfo, lighting_getSpotLight(i));
6226
+ let attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), fragmentInputs.pbr_vPosition);
6227
+ color += calculateMaterialLightColor(
6228
+ pbrInfo,
6229
+ lighting_getSpotLight(i).color / attenuation,
6230
+ clearcoatNormal,
6231
+ clearcoatFactor,
6232
+ clearcoatRoughness,
6233
+ sheenColor,
6234
+ sheenRoughness,
6235
+ anisotropyTangent,
6236
+ anisotropyStrength
6237
+ );
4191
6238
  }
4192
6239
  }
4193
6240
  #endif
@@ -4195,7 +6242,16 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4195
6242
  // Calculate lighting contribution from image based lighting source (IBL)
4196
6243
  #ifdef USE_IBL
4197
6244
  if (pbrMaterial.IBLenabled != 0) {
4198
- color += getIBLContribution(pbrInfo, n, reflection);
6245
+ color += getIBLContribution(pbrInfo, n, reflection) *
6246
+ calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
6247
+ color += calculateClearcoatIBLContribution(
6248
+ pbrInfo,
6249
+ clearcoatNormal,
6250
+ -normalize(reflect(v, clearcoatNormal)),
6251
+ clearcoatFactor,
6252
+ clearcoatRoughness
6253
+ );
6254
+ color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
4199
6255
  }
4200
6256
  #endif
4201
6257
 
@@ -4208,14 +6264,19 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4208
6264
  }
4209
6265
  #endif
4210
6266
 
6267
+ var emissive = pbrMaterial.emissiveFactor;
4211
6268
  #ifdef HAS_EMISSIVEMAP
4212
6269
  if (pbrMaterial.emissiveMapEnabled != 0u) {
4213
- let emissive = SRGBtoLINEAR(
6270
+ emissive *= SRGBtoLINEAR(
4214
6271
  textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
4215
- ).rgb * pbrMaterial.emissiveFactor;
4216
- color += emissive;
6272
+ ).rgb;
4217
6273
  }
4218
6274
  #endif
6275
+ color += emissive * pbrMaterial.emissiveStrength;
6276
+
6277
+ if (transmission > 0.0) {
6278
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
6279
+ }
4219
6280
 
4220
6281
  // This section uses mix to override final color for reference app visualization
4221
6282
  // of various parameters in the lighting equation.
@@ -4234,7 +6295,8 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
4234
6295
  #endif
4235
6296
  }
4236
6297
 
4237
- return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), baseColor.a);
6298
+ let alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
6299
+ return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), alpha);
4238
6300
  }
4239
6301
  `
4240
6302
  );
@@ -4259,11 +6321,12 @@ var wgslUniformBlock = (
4259
6321
  camera: vec3<f32>
4260
6322
  };
4261
6323
 
4262
- @binding(0) @group(0) var<uniform> pbrProjection: pbrProjectionUniforms;
6324
+ @group(0) @binding(auto) var<uniform> pbrProjection: pbrProjectionUniforms;
4263
6325
  `
4264
6326
  );
4265
6327
  var pbrProjection = {
4266
6328
  name: "pbrProjection",
6329
+ bindingLayout: [{ name: "pbrProjection", group: 0 }],
4267
6330
  source: wgslUniformBlock,
4268
6331
  vs: uniformBlock,
4269
6332
  fs: uniformBlock,
@@ -4281,8 +6344,75 @@ var pbrProjection = {
4281
6344
  var pbrMaterial = {
4282
6345
  props: {},
4283
6346
  uniforms: {},
6347
+ defaultUniforms: {
6348
+ unlit: false,
6349
+ baseColorMapEnabled: false,
6350
+ baseColorFactor: [1, 1, 1, 1],
6351
+ normalMapEnabled: false,
6352
+ normalScale: 1,
6353
+ emissiveMapEnabled: false,
6354
+ emissiveFactor: [0, 0, 0],
6355
+ metallicRoughnessValues: [1, 1],
6356
+ metallicRoughnessMapEnabled: false,
6357
+ occlusionMapEnabled: false,
6358
+ occlusionStrength: 1,
6359
+ alphaCutoffEnabled: false,
6360
+ alphaCutoff: 0.5,
6361
+ IBLenabled: false,
6362
+ scaleIBLAmbient: [1, 1],
6363
+ scaleDiffBaseMR: [0, 0, 0, 0],
6364
+ scaleFGDSpec: [0, 0, 0, 0],
6365
+ specularColorFactor: [1, 1, 1],
6366
+ specularIntensityFactor: 1,
6367
+ specularColorMapEnabled: false,
6368
+ specularIntensityMapEnabled: false,
6369
+ ior: 1.5,
6370
+ transmissionFactor: 0,
6371
+ transmissionMapEnabled: false,
6372
+ thicknessFactor: 0,
6373
+ attenuationDistance: 1e9,
6374
+ attenuationColor: [1, 1, 1],
6375
+ clearcoatFactor: 0,
6376
+ clearcoatRoughnessFactor: 0,
6377
+ clearcoatMapEnabled: false,
6378
+ clearcoatRoughnessMapEnabled: false,
6379
+ sheenColorFactor: [0, 0, 0],
6380
+ sheenRoughnessFactor: 0,
6381
+ sheenColorMapEnabled: false,
6382
+ sheenRoughnessMapEnabled: false,
6383
+ iridescenceFactor: 0,
6384
+ iridescenceIor: 1.3,
6385
+ iridescenceThicknessRange: [100, 400],
6386
+ iridescenceMapEnabled: false,
6387
+ anisotropyStrength: 0,
6388
+ anisotropyRotation: 0,
6389
+ anisotropyDirection: [1, 0],
6390
+ anisotropyMapEnabled: false,
6391
+ emissiveStrength: 1
6392
+ },
4284
6393
  name: "pbrMaterial",
4285
- dependencies: [lighting, pbrProjection],
6394
+ firstBindingSlot: 0,
6395
+ bindingLayout: [
6396
+ { name: "pbrMaterial", group: 3 },
6397
+ { name: "pbr_baseColorSampler", group: 3 },
6398
+ { name: "pbr_normalSampler", group: 3 },
6399
+ { name: "pbr_emissiveSampler", group: 3 },
6400
+ { name: "pbr_metallicRoughnessSampler", group: 3 },
6401
+ { name: "pbr_occlusionSampler", group: 3 },
6402
+ { name: "pbr_specularColorSampler", group: 3 },
6403
+ { name: "pbr_specularIntensitySampler", group: 3 },
6404
+ { name: "pbr_transmissionSampler", group: 3 },
6405
+ { name: "pbr_thicknessSampler", group: 3 },
6406
+ { name: "pbr_clearcoatSampler", group: 3 },
6407
+ { name: "pbr_clearcoatRoughnessSampler", group: 3 },
6408
+ { name: "pbr_clearcoatNormalSampler", group: 3 },
6409
+ { name: "pbr_sheenColorSampler", group: 3 },
6410
+ { name: "pbr_sheenRoughnessSampler", group: 3 },
6411
+ { name: "pbr_iridescenceSampler", group: 3 },
6412
+ { name: "pbr_iridescenceThicknessSampler", group: 3 },
6413
+ { name: "pbr_anisotropySampler", group: 3 }
6414
+ ],
6415
+ dependencies: [lighting, ibl, pbrProjection],
4286
6416
  source: source3,
4287
6417
  vs: vs3,
4288
6418
  fs: fs4,
@@ -4296,10 +6426,16 @@ var pbrMaterial = {
4296
6426
  HAS_SPECULARCOLORMAP: false,
4297
6427
  HAS_SPECULARINTENSITYMAP: false,
4298
6428
  HAS_TRANSMISSIONMAP: false,
6429
+ HAS_THICKNESSMAP: false,
4299
6430
  HAS_CLEARCOATMAP: false,
6431
+ HAS_CLEARCOATROUGHNESSMAP: false,
6432
+ HAS_CLEARCOATNORMALMAP: false,
4300
6433
  HAS_SHEENCOLORMAP: false,
6434
+ HAS_SHEENROUGHNESSMAP: false,
4301
6435
  HAS_IRIDESCENCEMAP: false,
6436
+ HAS_IRIDESCENCETHICKNESSMAP: false,
4302
6437
  HAS_ANISOTROPYMAP: false,
6438
+ USE_MATERIAL_EXTENSIONS: false,
4303
6439
  ALPHA_CUTOFF: false,
4304
6440
  USE_IBL: false,
4305
6441
  PBR_DEBUG: false
@@ -4325,14 +6461,6 @@ var pbrMaterial = {
4325
6461
  alphaCutoffEnabled: "i32",
4326
6462
  alphaCutoff: "f32",
4327
6463
  // #ifdef ALPHA_CUTOFF
4328
- // IBL
4329
- IBLenabled: "i32",
4330
- scaleIBLAmbient: "vec2<f32>",
4331
- // #ifdef USE_IBL
4332
- // debugging flags used for shader output of intermediate PBR variables
4333
- // #ifdef PBR_DEBUG
4334
- scaleDiffBaseMR: "vec4<f32>",
4335
- scaleFGDSpec: "vec4<f32>",
4336
6464
  specularColorFactor: "vec3<f32>",
4337
6465
  specularIntensityFactor: "f32",
4338
6466
  specularColorMapEnabled: "i32",
@@ -4346,9 +6474,11 @@ var pbrMaterial = {
4346
6474
  clearcoatFactor: "f32",
4347
6475
  clearcoatRoughnessFactor: "f32",
4348
6476
  clearcoatMapEnabled: "i32",
6477
+ clearcoatRoughnessMapEnabled: "i32",
4349
6478
  sheenColorFactor: "vec3<f32>",
4350
6479
  sheenRoughnessFactor: "f32",
4351
6480
  sheenColorMapEnabled: "i32",
6481
+ sheenRoughnessMapEnabled: "i32",
4352
6482
  iridescenceFactor: "f32",
4353
6483
  iridescenceIor: "f32",
4354
6484
  iridescenceThicknessRange: "vec2<f32>",
@@ -4357,7 +6487,15 @@ var pbrMaterial = {
4357
6487
  anisotropyRotation: "f32",
4358
6488
  anisotropyDirection: "vec2<f32>",
4359
6489
  anisotropyMapEnabled: "i32",
4360
- emissiveStrength: "f32"
6490
+ emissiveStrength: "f32",
6491
+ // IBL
6492
+ IBLenabled: "i32",
6493
+ scaleIBLAmbient: "vec2<f32>",
6494
+ // #ifdef USE_IBL
6495
+ // debugging flags used for shader output of intermediate PBR variables
6496
+ // #ifdef PBR_DEBUG
6497
+ scaleDiffBaseMR: "vec4<f32>",
6498
+ scaleFGDSpec: "vec4<f32>"
4361
6499
  }
4362
6500
  };
4363
6501
  //# sourceMappingURL=index.cjs.map