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

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 -330
  2. package/dist/dist.min.js +1803 -283
  3. package/dist/index.cjs +2495 -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 +112 -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 +194 -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/dist.dev.js CHANGED
@@ -75,10 +75,15 @@ var __exports__ = (() => {
75
75
  getShaderInfo: () => getShaderInfo,
76
76
  getShaderModuleDependencies: () => getShaderModuleDependencies,
77
77
  getShaderModuleSource: () => getShaderModuleSource,
78
+ getShaderModuleUniformBlockFields: () => getShaderModuleUniformBlockFields,
79
+ getShaderModuleUniformBlockName: () => getShaderModuleUniformBlockName,
80
+ getShaderModuleUniformLayoutValidationResult: () => getShaderModuleUniformLayoutValidationResult,
78
81
  getShaderModuleUniforms: () => getShaderModuleUniforms,
79
82
  gouraudMaterial: () => gouraudMaterial,
83
+ ibl: () => ibl,
80
84
  initializeShaderModule: () => initializeShaderModule,
81
85
  initializeShaderModules: () => initializeShaderModules,
86
+ lambertMaterial: () => lambertMaterial,
82
87
  lighting: () => lighting,
83
88
  pbrMaterial: () => pbrMaterial,
84
89
  phongMaterial: () => phongMaterial,
@@ -88,7 +93,8 @@ var __exports__ = (() => {
88
93
  skin: () => skin,
89
94
  toHalfFloat: () => toHalfFloat,
90
95
  typeToChannelCount: () => typeToChannelCount,
91
- typeToChannelSuffix: () => typeToChannelSuffix
96
+ typeToChannelSuffix: () => typeToChannelSuffix,
97
+ validateShaderModuleUniformLayout: () => validateShaderModuleUniformLayout
92
98
  });
93
99
  __reExport(bundle_exports, __toESM(require_core(), 1));
94
100
 
@@ -409,6 +415,123 @@ ${inject[key]}` : inject[key];
409
415
  return getShaderDependencies(modules);
410
416
  }
411
417
 
418
+ // src/lib/shader-module/shader-module-uniform-layout.ts
419
+ function getShaderModuleUniformBlockName(module) {
420
+ return `${module.name}Uniforms`;
421
+ }
422
+ function getShaderModuleUniformBlockFields(module, stage) {
423
+ const shaderSource = stage === "wgsl" ? module.source : stage === "vertex" ? module.vs : module.fs;
424
+ if (!shaderSource) {
425
+ return null;
426
+ }
427
+ const uniformBlockName = getShaderModuleUniformBlockName(module);
428
+ return extractShaderUniformBlockFieldNames(
429
+ shaderSource,
430
+ stage === "wgsl" ? "wgsl" : "glsl",
431
+ uniformBlockName
432
+ );
433
+ }
434
+ function getShaderModuleUniformLayoutValidationResult(module, stage) {
435
+ const expectedUniformNames = Object.keys(module.uniformTypes || {});
436
+ if (!expectedUniformNames.length) {
437
+ return null;
438
+ }
439
+ const actualUniformNames = getShaderModuleUniformBlockFields(module, stage);
440
+ if (!actualUniformNames) {
441
+ return null;
442
+ }
443
+ return {
444
+ moduleName: module.name,
445
+ uniformBlockName: getShaderModuleUniformBlockName(module),
446
+ stage,
447
+ expectedUniformNames,
448
+ actualUniformNames,
449
+ matches: areStringArraysEqual(expectedUniformNames, actualUniformNames)
450
+ };
451
+ }
452
+ function validateShaderModuleUniformLayout(module, stage, options = {}) {
453
+ const validationResult = getShaderModuleUniformLayoutValidationResult(module, stage);
454
+ if (!validationResult || validationResult.matches) {
455
+ return validationResult;
456
+ }
457
+ const message = formatShaderModuleUniformLayoutError(validationResult);
458
+ options.log?.error?.(message, validationResult)();
459
+ if (options.throwOnError !== false) {
460
+ assert(false, message);
461
+ }
462
+ return validationResult;
463
+ }
464
+ function extractShaderUniformBlockFieldNames(shaderSource, language, uniformBlockName) {
465
+ const sourceBody = language === "wgsl" ? extractWGSLStructBody(shaderSource, uniformBlockName) : extractGLSLUniformBlockBody(shaderSource, uniformBlockName);
466
+ if (!sourceBody) {
467
+ return null;
468
+ }
469
+ const fieldNames = [];
470
+ for (const sourceLine of sourceBody.split("\n")) {
471
+ const line = sourceLine.replace(/\/\/.*$/, "").trim();
472
+ if (!line || line.startsWith("#")) {
473
+ continue;
474
+ }
475
+ const fieldMatch = language === "wgsl" ? line.match(/^([A-Za-z0-9_]+)\s*:/) : line.match(
476
+ /^(?:uniform\s+)?[A-Za-z0-9_]+(?:<[^>]+>)?\s+([A-Za-z0-9_]+)(?:\s*\[[^\]]+\])?\s*;/
477
+ );
478
+ if (fieldMatch) {
479
+ fieldNames.push(fieldMatch[1]);
480
+ }
481
+ }
482
+ return fieldNames;
483
+ }
484
+ function extractWGSLStructBody(shaderSource, uniformBlockName) {
485
+ const structMatch = new RegExp(`\\bstruct\\s+${uniformBlockName}\\b`, "m").exec(shaderSource);
486
+ if (!structMatch) {
487
+ return null;
488
+ }
489
+ const openBraceIndex = shaderSource.indexOf("{", structMatch.index);
490
+ if (openBraceIndex < 0) {
491
+ return null;
492
+ }
493
+ let braceDepth = 0;
494
+ for (let index = openBraceIndex; index < shaderSource.length; index++) {
495
+ const character = shaderSource[index];
496
+ if (character === "{") {
497
+ braceDepth++;
498
+ continue;
499
+ }
500
+ if (character !== "}") {
501
+ continue;
502
+ }
503
+ braceDepth--;
504
+ if (braceDepth === 0) {
505
+ return shaderSource.slice(openBraceIndex + 1, index);
506
+ }
507
+ }
508
+ return null;
509
+ }
510
+ function extractGLSLUniformBlockBody(shaderSource, uniformBlockName) {
511
+ const sourceMatch = shaderSource.match(
512
+ new RegExp(`uniform\\s+${uniformBlockName}\\s*\\{([\\s\\S]*?)\\}\\s*[A-Za-z0-9_]+\\s*;`, "m")
513
+ );
514
+ return sourceMatch?.[1] || null;
515
+ }
516
+ function areStringArraysEqual(leftValues, rightValues) {
517
+ if (leftValues.length !== rightValues.length) {
518
+ return false;
519
+ }
520
+ for (let valueIndex = 0; valueIndex < leftValues.length; valueIndex++) {
521
+ if (leftValues[valueIndex] !== rightValues[valueIndex]) {
522
+ return false;
523
+ }
524
+ }
525
+ return true;
526
+ }
527
+ function formatShaderModuleUniformLayoutError(validationResult) {
528
+ return `${validationResult.moduleName}: ${validationResult.stage} shader uniform block ${validationResult.uniformBlockName} does not match module.uniformTypes.
529
+ Expected: ${validationResult.expectedUniformNames.join(
530
+ ", "
531
+ )}
532
+ Actual: ${validationResult.actualUniformNames.join(", ")}`;
533
+ }
534
+
412
535
  // src/lib/shader-assembly/platform-defines.ts
413
536
  function getPlatformShaderDefines(platformInfo) {
414
537
  switch (platformInfo?.gpu.toLowerCase()) {
@@ -593,11 +716,166 @@ ${inject[key]}` : inject[key];
593
716
  return version;
594
717
  }
595
718
 
719
+ // src/lib/shader-assembly/wgsl-binding-debug.ts
720
+ var WGSL_BINDING_DEBUG_REGEXES = [
721
+ /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g,
722
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g
723
+ ];
724
+ function getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments = []) {
725
+ const assignmentMap = /* @__PURE__ */ new Map();
726
+ for (const bindingAssignment of bindingAssignments) {
727
+ assignmentMap.set(
728
+ getBindingAssignmentKey(
729
+ bindingAssignment.name,
730
+ bindingAssignment.group,
731
+ bindingAssignment.location
732
+ ),
733
+ bindingAssignment.moduleName
734
+ );
735
+ }
736
+ const rows = [];
737
+ for (const regex of WGSL_BINDING_DEBUG_REGEXES) {
738
+ regex.lastIndex = 0;
739
+ let match;
740
+ while (match = regex.exec(source4)) {
741
+ const isBindingFirst = regex === WGSL_BINDING_DEBUG_REGEXES[0];
742
+ const binding = Number(match[isBindingFirst ? 1 : 2]);
743
+ const group = Number(match[isBindingFirst ? 2 : 1]);
744
+ const accessDeclaration = match[3]?.trim();
745
+ const name = match[4];
746
+ const resourceType = match[5].trim();
747
+ const moduleName = assignmentMap.get(getBindingAssignmentKey(name, group, binding));
748
+ rows.push(
749
+ normalizeShaderBindingDebugRow({
750
+ name,
751
+ group,
752
+ binding,
753
+ owner: moduleName ? "module" : "application",
754
+ moduleName,
755
+ accessDeclaration,
756
+ resourceType
757
+ })
758
+ );
759
+ }
760
+ }
761
+ return rows.sort((left, right) => {
762
+ if (left.group !== right.group) {
763
+ return left.group - right.group;
764
+ }
765
+ if (left.binding !== right.binding) {
766
+ return left.binding - right.binding;
767
+ }
768
+ return left.name.localeCompare(right.name);
769
+ });
770
+ }
771
+ function normalizeShaderBindingDebugRow(row) {
772
+ const baseRow = {
773
+ name: row.name,
774
+ group: row.group,
775
+ binding: row.binding,
776
+ owner: row.owner,
777
+ kind: "unknown",
778
+ moduleName: row.moduleName,
779
+ resourceType: row.resourceType
780
+ };
781
+ if (row.accessDeclaration) {
782
+ const access = row.accessDeclaration.split(",").map((value) => value.trim());
783
+ if (access[0] === "uniform") {
784
+ return { ...baseRow, kind: "uniform", access: "uniform" };
785
+ }
786
+ if (access[0] === "storage") {
787
+ const storageAccess = access[1] || "read_write";
788
+ return {
789
+ ...baseRow,
790
+ kind: storageAccess === "read" ? "read-only-storage" : "storage",
791
+ access: storageAccess
792
+ };
793
+ }
794
+ }
795
+ if (row.resourceType === "sampler" || row.resourceType === "sampler_comparison") {
796
+ return {
797
+ ...baseRow,
798
+ kind: "sampler",
799
+ samplerKind: row.resourceType === "sampler_comparison" ? "comparison" : "filtering"
800
+ };
801
+ }
802
+ if (row.resourceType.startsWith("texture_storage_")) {
803
+ return {
804
+ ...baseRow,
805
+ kind: "storage-texture",
806
+ access: getStorageTextureAccess(row.resourceType),
807
+ viewDimension: getTextureViewDimension(row.resourceType)
808
+ };
809
+ }
810
+ if (row.resourceType.startsWith("texture_")) {
811
+ return {
812
+ ...baseRow,
813
+ kind: "texture",
814
+ viewDimension: getTextureViewDimension(row.resourceType),
815
+ sampleType: getTextureSampleType(row.resourceType),
816
+ multisampled: row.resourceType.startsWith("texture_multisampled_")
817
+ };
818
+ }
819
+ return baseRow;
820
+ }
821
+ function getBindingAssignmentKey(name, group, binding) {
822
+ return `${group}:${binding}:${name}`;
823
+ }
824
+ function getTextureViewDimension(resourceType) {
825
+ if (resourceType.includes("cube_array")) {
826
+ return "cube-array";
827
+ }
828
+ if (resourceType.includes("2d_array")) {
829
+ return "2d-array";
830
+ }
831
+ if (resourceType.includes("cube")) {
832
+ return "cube";
833
+ }
834
+ if (resourceType.includes("3d")) {
835
+ return "3d";
836
+ }
837
+ if (resourceType.includes("2d")) {
838
+ return "2d";
839
+ }
840
+ if (resourceType.includes("1d")) {
841
+ return "1d";
842
+ }
843
+ return void 0;
844
+ }
845
+ function getTextureSampleType(resourceType) {
846
+ if (resourceType.startsWith("texture_depth_")) {
847
+ return "depth";
848
+ }
849
+ if (resourceType.includes("<i32>")) {
850
+ return "sint";
851
+ }
852
+ if (resourceType.includes("<u32>")) {
853
+ return "uint";
854
+ }
855
+ if (resourceType.includes("<f32>")) {
856
+ return "float";
857
+ }
858
+ return void 0;
859
+ }
860
+ function getStorageTextureAccess(resourceType) {
861
+ const match = /,\s*([A-Za-z_][A-Za-z0-9_]*)\s*>$/.exec(resourceType);
862
+ return match?.[1];
863
+ }
864
+
596
865
  // src/lib/shader-assembly/assemble-shaders.ts
597
866
  var INJECT_SHADER_DECLARATIONS = `
598
867
 
599
868
  ${DECLARATION_INJECT_MARKER}
600
869
  `;
870
+ var MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
871
+ /@binding\(\s*(auto|\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
872
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(auto|\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
873
+ ];
874
+ var WGSL_BINDING_DECLARATION_REGEXES = [
875
+ /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
876
+ /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
877
+ ];
878
+ var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
601
879
  var FRAGMENT_SHADER_PROLOGUE = (
602
880
  /* glsl */
603
881
  `precision highp float;
@@ -605,14 +883,17 @@ ${DECLARATION_INJECT_MARKER}
605
883
  );
606
884
  function assembleWGSLShader(options) {
607
885
  const modules = getShaderModuleDependencies(options.modules || []);
886
+ const { source: source4, bindingAssignments } = assembleShaderWGSL(options.platformInfo, {
887
+ ...options,
888
+ source: options.source,
889
+ stage: "vertex",
890
+ modules
891
+ });
608
892
  return {
609
- source: assembleShaderWGSL(options.platformInfo, {
610
- ...options,
611
- source: options.source,
612
- stage: "vertex",
613
- modules
614
- }),
615
- getUniforms: assembleGetUniforms(modules)
893
+ source: source4,
894
+ getUniforms: assembleGetUniforms(modules),
895
+ bindingAssignments,
896
+ bindingTable: getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments)
616
897
  };
617
898
  }
618
899
  function assembleGLSLShaderPair(options) {
@@ -673,11 +954,28 @@ ${DECLARATION_INJECT_MARKER}
673
954
  }
674
955
  }
675
956
  const modulesToInject = modules;
957
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(coreSource);
958
+ const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(
959
+ modulesToInject,
960
+ options._bindingRegistry,
961
+ usedBindingsByGroup
962
+ );
963
+ const bindingAssignments = [];
676
964
  for (const module of modulesToInject) {
677
965
  if (log2) {
678
966
  checkShaderModuleDeprecations(module, coreSource, log2);
679
967
  }
680
- const moduleSource = getShaderModuleSource(module, "wgsl");
968
+ const relocation = relocateWGSLModuleBindings(
969
+ getShaderModuleSource(module, "wgsl", log2),
970
+ module,
971
+ {
972
+ usedBindingsByGroup,
973
+ bindingRegistry: options._bindingRegistry,
974
+ reservedBindingKeysByGroup
975
+ }
976
+ );
977
+ bindingAssignments.push(...relocation.bindingAssignments);
978
+ const moduleSource = relocation.source;
681
979
  assembledSource += moduleSource;
682
980
  const injections = module.injections?.[stage] || {};
683
981
  for (const key in injections) {
@@ -696,9 +994,11 @@ ${DECLARATION_INJECT_MARKER}
696
994
  assembledSource += INJECT_SHADER_DECLARATIONS;
697
995
  assembledSource = injectShader(assembledSource, stage, declInjections);
698
996
  assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
997
+ assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
699
998
  assembledSource += coreSource;
700
999
  assembledSource = injectShader(assembledSource, stage, mainInjections);
701
- return assembledSource;
1000
+ assertNoUnresolvedAutoBindings(assembledSource);
1001
+ return { source: assembledSource, bindingAssignments };
702
1002
  }
703
1003
  function assembleShaderGLSL(platformInfo, options) {
704
1004
  const {
@@ -771,7 +1071,7 @@ ${getApplicationDefines(allDefines)}
771
1071
  if (log2) {
772
1072
  checkShaderModuleDeprecations(module, coreSource, log2);
773
1073
  }
774
- const moduleSource = getShaderModuleSource(module, stage);
1074
+ const moduleSource = getShaderModuleSource(module, stage, log2);
775
1075
  assembledSource += moduleSource;
776
1076
  const injections = module.instance?.normalizedInjections[stage] || {};
777
1077
  for (const key in injections) {
@@ -819,7 +1119,7 @@ ${getApplicationDefines(allDefines)}
819
1119
  }
820
1120
  return sourceText;
821
1121
  }
822
- function getShaderModuleSource(module, stage) {
1122
+ function getShaderModuleSource(module, stage, log2) {
823
1123
  let moduleSource;
824
1124
  switch (stage) {
825
1125
  case "vertex":
@@ -837,6 +1137,7 @@ ${getApplicationDefines(allDefines)}
837
1137
  if (!module.name) {
838
1138
  throw new Error("Shader module must have a name");
839
1139
  }
1140
+ validateShaderModuleUniformLayout(module, stage, { log: log2 });
840
1141
  const moduleName = module.name.toUpperCase().replace(/[^0-9a-z]/gi, "_");
841
1142
  let source4 = `// ----- MODULE ${module.name} ---------------
842
1143
 
@@ -849,13 +1150,225 @@ ${getApplicationDefines(allDefines)}
849
1150
  `;
850
1151
  return source4;
851
1152
  }
1153
+ function getUsedBindingsByGroupFromApplicationWGSL(source4) {
1154
+ const usedBindingsByGroup = /* @__PURE__ */ new Map();
1155
+ for (const regex of WGSL_BINDING_DECLARATION_REGEXES) {
1156
+ regex.lastIndex = 0;
1157
+ let match;
1158
+ while (match = regex.exec(source4)) {
1159
+ const isBindingFirst = regex === WGSL_BINDING_DECLARATION_REGEXES[0];
1160
+ const location = Number(match[isBindingFirst ? 1 : 2]);
1161
+ const group = Number(match[isBindingFirst ? 2 : 1]);
1162
+ const name = match[4];
1163
+ validateApplicationWGSLBinding(group, location, name);
1164
+ registerUsedBindingLocation(
1165
+ usedBindingsByGroup,
1166
+ group,
1167
+ location,
1168
+ `application binding "${name}"`
1169
+ );
1170
+ }
1171
+ }
1172
+ return usedBindingsByGroup;
1173
+ }
1174
+ function relocateWGSLModuleBindings(moduleSource, module, context) {
1175
+ const bindingAssignments = [];
1176
+ const relocationState = {
1177
+ sawSupportedBindingDeclaration: false,
1178
+ nextHintedBindingLocation: typeof module.firstBindingSlot === "number" ? module.firstBindingSlot : null
1179
+ };
1180
+ let relocatedSource = relocateWGSLModuleBindingsWithRegex(
1181
+ moduleSource,
1182
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES[0],
1183
+ { isBindingFirst: true, module, context, bindingAssignments, relocationState }
1184
+ );
1185
+ relocatedSource = relocateWGSLModuleBindingsWithRegex(
1186
+ relocatedSource,
1187
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES[1],
1188
+ { isBindingFirst: false, module, context, bindingAssignments, relocationState }
1189
+ );
1190
+ if (moduleSource.includes("@binding(auto)") && !relocationState.sawSupportedBindingDeclaration) {
1191
+ throw new Error(
1192
+ `Unsupported @binding(auto) declaration form in module "${module.name}". Use "@group(N) @binding(auto) var ..." or "@binding(auto) @group(N) var ..." on a single line.`
1193
+ );
1194
+ }
1195
+ return { source: relocatedSource, bindingAssignments };
1196
+ }
1197
+ function relocateWGSLModuleBindingsWithRegex(source4, regex, params) {
1198
+ return source4.replace(
1199
+ regex,
1200
+ (...replaceArguments) => relocateWGSLModuleBindingMatch(replaceArguments, params)
1201
+ );
1202
+ }
1203
+ function relocateWGSLModuleBindingMatch(replaceArguments, params) {
1204
+ const { isBindingFirst, module, context, bindingAssignments, relocationState } = params;
1205
+ relocationState.sawSupportedBindingDeclaration = true;
1206
+ const match = replaceArguments[0];
1207
+ const bindingToken = replaceArguments[isBindingFirst ? 1 : 2];
1208
+ const groupToken = replaceArguments[isBindingFirst ? 2 : 1];
1209
+ const name = replaceArguments[4];
1210
+ const group = Number(groupToken);
1211
+ if (bindingToken === "auto") {
1212
+ const registryKey = getBindingRegistryKey(group, module.name, name);
1213
+ const registryLocation = context.bindingRegistry?.get(registryKey);
1214
+ const location2 = registryLocation !== void 0 ? registryLocation : relocationState.nextHintedBindingLocation === null ? allocateAutoBindingLocation(group, context.usedBindingsByGroup) : allocateAutoBindingLocation(
1215
+ group,
1216
+ context.usedBindingsByGroup,
1217
+ relocationState.nextHintedBindingLocation
1218
+ );
1219
+ validateModuleWGSLBinding(module.name, group, location2, name);
1220
+ if (registryLocation !== void 0 && claimReservedBindingLocation(context.reservedBindingKeysByGroup, group, location2, registryKey)) {
1221
+ bindingAssignments.push({ moduleName: module.name, name, group, location: location2 });
1222
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location2})`);
1223
+ }
1224
+ registerUsedBindingLocation(
1225
+ context.usedBindingsByGroup,
1226
+ group,
1227
+ location2,
1228
+ `module "${module.name}" binding "${name}"`
1229
+ );
1230
+ context.bindingRegistry?.set(registryKey, location2);
1231
+ bindingAssignments.push({ moduleName: module.name, name, group, location: location2 });
1232
+ if (relocationState.nextHintedBindingLocation !== null && registryLocation === void 0) {
1233
+ relocationState.nextHintedBindingLocation = location2 + 1;
1234
+ }
1235
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location2})`);
1236
+ }
1237
+ const location = Number(bindingToken);
1238
+ validateModuleWGSLBinding(module.name, group, location, name);
1239
+ registerUsedBindingLocation(
1240
+ context.usedBindingsByGroup,
1241
+ group,
1242
+ location,
1243
+ `module "${module.name}" binding "${name}"`
1244
+ );
1245
+ bindingAssignments.push({ moduleName: module.name, name, group, location });
1246
+ return match;
1247
+ }
1248
+ function reserveRegisteredModuleBindings(modules, bindingRegistry, usedBindingsByGroup) {
1249
+ const reservedBindingKeysByGroup = /* @__PURE__ */ new Map();
1250
+ if (!bindingRegistry) {
1251
+ return reservedBindingKeysByGroup;
1252
+ }
1253
+ for (const module of modules) {
1254
+ for (const binding of getModuleWGSLBindingDeclarations(module)) {
1255
+ const registryKey = getBindingRegistryKey(binding.group, module.name, binding.name);
1256
+ const location = bindingRegistry.get(registryKey);
1257
+ if (location !== void 0) {
1258
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(binding.group) || /* @__PURE__ */ new Map();
1259
+ const existingReservation = reservedBindingKeys.get(location);
1260
+ if (existingReservation && existingReservation !== registryKey) {
1261
+ throw new Error(
1262
+ `Duplicate WGSL binding reservation for modules "${existingReservation}" and "${registryKey}": group ${binding.group}, binding ${location}.`
1263
+ );
1264
+ }
1265
+ registerUsedBindingLocation(
1266
+ usedBindingsByGroup,
1267
+ binding.group,
1268
+ location,
1269
+ `registered module binding "${registryKey}"`
1270
+ );
1271
+ reservedBindingKeys.set(location, registryKey);
1272
+ reservedBindingKeysByGroup.set(binding.group, reservedBindingKeys);
1273
+ }
1274
+ }
1275
+ }
1276
+ return reservedBindingKeysByGroup;
1277
+ }
1278
+ function claimReservedBindingLocation(reservedBindingKeysByGroup, group, location, registryKey) {
1279
+ const reservedBindingKeys = reservedBindingKeysByGroup.get(group);
1280
+ if (!reservedBindingKeys) {
1281
+ return false;
1282
+ }
1283
+ const reservedKey = reservedBindingKeys.get(location);
1284
+ if (!reservedKey) {
1285
+ return false;
1286
+ }
1287
+ if (reservedKey !== registryKey) {
1288
+ throw new Error(
1289
+ `Registered module binding "${registryKey}" collided with "${reservedKey}": group ${group}, binding ${location}.`
1290
+ );
1291
+ }
1292
+ return true;
1293
+ }
1294
+ function getModuleWGSLBindingDeclarations(module) {
1295
+ const declarations = [];
1296
+ const moduleSource = module.source || "";
1297
+ for (const regex of MODULE_WGSL_BINDING_DECLARATION_REGEXES) {
1298
+ regex.lastIndex = 0;
1299
+ let match;
1300
+ while (match = regex.exec(moduleSource)) {
1301
+ const isBindingFirst = regex === MODULE_WGSL_BINDING_DECLARATION_REGEXES[0];
1302
+ declarations.push({
1303
+ name: match[4],
1304
+ group: Number(match[isBindingFirst ? 2 : 1])
1305
+ });
1306
+ }
1307
+ }
1308
+ return declarations;
1309
+ }
1310
+ function validateApplicationWGSLBinding(group, location, name) {
1311
+ if (group === 0 && location >= RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
1312
+ throw new Error(
1313
+ `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}.`
1314
+ );
1315
+ }
1316
+ }
1317
+ function validateModuleWGSLBinding(moduleName, group, location, name) {
1318
+ if (group === 0 && location < RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT) {
1319
+ throw new Error(
1320
+ `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.`
1321
+ );
1322
+ }
1323
+ }
1324
+ function registerUsedBindingLocation(usedBindingsByGroup, group, location, label) {
1325
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1326
+ if (usedBindings.has(location)) {
1327
+ throw new Error(
1328
+ `Duplicate WGSL binding assignment for ${label}: group ${group}, binding ${location}.`
1329
+ );
1330
+ }
1331
+ usedBindings.add(location);
1332
+ usedBindingsByGroup.set(group, usedBindings);
1333
+ }
1334
+ function allocateAutoBindingLocation(group, usedBindingsByGroup, preferredBindingLocation) {
1335
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1336
+ let nextBinding = preferredBindingLocation ?? (group === 0 ? RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT : usedBindings.size > 0 ? Math.max(...usedBindings) + 1 : 0);
1337
+ while (usedBindings.has(nextBinding)) {
1338
+ nextBinding++;
1339
+ }
1340
+ return nextBinding;
1341
+ }
1342
+ function assertNoUnresolvedAutoBindings(source4) {
1343
+ if (/@binding\(\s*auto\s*\)/.test(source4)) {
1344
+ throw new Error("Unresolved @binding(auto) remained in assembled WGSL source.");
1345
+ }
1346
+ }
1347
+ function formatWGSLBindingAssignmentComments(bindingAssignments) {
1348
+ if (bindingAssignments.length === 0) {
1349
+ return "";
1350
+ }
1351
+ let source4 = "// ----- MODULE WGSL BINDING ASSIGNMENTS ---------------\n";
1352
+ for (const bindingAssignment of bindingAssignments) {
1353
+ source4 += `// ${bindingAssignment.moduleName}.${bindingAssignment.name} -> @group(${bindingAssignment.group}) @binding(${bindingAssignment.location})
1354
+ `;
1355
+ }
1356
+ source4 += "\n";
1357
+ return source4;
1358
+ }
1359
+ function getBindingRegistryKey(group, moduleName, bindingName) {
1360
+ return `${group}:${moduleName}:${bindingName}`;
1361
+ }
852
1362
 
853
1363
  // src/lib/preprocessor/preprocessor.ts
854
- var IFDEF_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*$/;
855
- var IFNDEF_REGEXP = /^\s*\#\s*ifndef\s*([a-zA-Z_]+)\s*(?:\/\/.*)?$/;
1364
+ var DEFINE_NAME_PATTERN = "([a-zA-Z_][a-zA-Z0-9_]*)";
1365
+ var IFDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*$`);
1366
+ var IFNDEF_REGEXP = new RegExp(`^\\s*\\#\\s*ifndef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`);
856
1367
  var ELSE_REGEXP = /^\s*\#\s*else\s*(?:\/\/.*)?$/;
857
1368
  var ENDIF_REGEXP = /^\s*\#\s*endif\s*$/;
858
- var IFDEF_WITH_COMMENT_REGEXP = /^\s*\#\s*ifdef\s*([a-zA-Z_]+)\s*(?:\/\/.*)?$/;
1369
+ var IFDEF_WITH_COMMENT_REGEXP = new RegExp(
1370
+ `^\\s*\\#\\s*ifdef\\s*${DEFINE_NAME_PATTERN}\\s*(?:\\/\\/.*)?$`
1371
+ );
859
1372
  var ENDIF_WITH_COMMENT_REGEXP = /^\s*\#\s*endif\s*(?:\/\/.*)?$/;
860
1373
  function preprocess(source4, options) {
861
1374
  const lines = source4.split("\n");
@@ -901,6 +1414,8 @@ ${getApplicationDefines(allDefines)}
901
1414
  _hookFunctions = [];
902
1415
  /** Shader modules */
903
1416
  _defaultModules = [];
1417
+ /** Stable per-run WGSL auto-binding assignments keyed by group/module/binding. */
1418
+ _wgslBindingRegistry = /* @__PURE__ */ new Map();
904
1419
  /**
905
1420
  * A default shader assembler instance - the natural place to register default modules and hooks
906
1421
  * @returns
@@ -946,10 +1461,11 @@ ${getApplicationDefines(allDefines)}
946
1461
  assembleWGSLShader(props) {
947
1462
  const modules = this._getModuleList(props.modules);
948
1463
  const hookFunctions = this._hookFunctions;
949
- const { source: source4, getUniforms: getUniforms4 } = assembleWGSLShader({
1464
+ const { source: source4, getUniforms: getUniforms4, bindingAssignments } = assembleWGSLShader({
950
1465
  ...props,
951
1466
  // @ts-expect-error
952
1467
  source: props.source,
1468
+ _bindingRegistry: this._wgslBindingRegistry,
953
1469
  modules,
954
1470
  hookFunctions
955
1471
  });
@@ -961,7 +1477,13 @@ ${getApplicationDefines(allDefines)}
961
1477
  ...props.defines
962
1478
  };
963
1479
  const preprocessedSource = props.platformInfo.shaderLanguage === "wgsl" ? preprocess(source4, { defines }) : source4;
964
- return { source: preprocessedSource, getUniforms: getUniforms4, modules };
1480
+ return {
1481
+ source: preprocessedSource,
1482
+ getUniforms: getUniforms4,
1483
+ modules,
1484
+ bindingAssignments,
1485
+ bindingTable: getShaderBindingDebugRowsFromWGSL(preprocessedSource, bindingAssignments)
1486
+ };
965
1487
  }
966
1488
  /**
967
1489
  * Assemble a pair of shaders into a single shader program
@@ -1126,6 +1648,11 @@ void main() {
1126
1648
  case "uniforms":
1127
1649
  }
1128
1650
  for (const [uniformName, uniformFormat] of Object.entries(module.uniformTypes || {})) {
1651
+ if (typeof uniformFormat !== "string") {
1652
+ throw new Error(
1653
+ `Composite uniform types are not supported by GLSL shader generation: ${module.name}.${uniformName}`
1654
+ );
1655
+ }
1129
1656
  const glslUniformType = getGLSLUniformType(uniformFormat);
1130
1657
  switch (options.uniforms) {
1131
1658
  case "scoped-interface-blocks":
@@ -1186,6 +1713,11 @@ void main() {
1186
1713
  const wgsl = [];
1187
1714
  wgsl.push(`struct ${capitalize(module.name)} {`);
1188
1715
  for (const [uniformName, uniformFormat] of Object.entries(module?.uniformTypes || {})) {
1716
+ if (typeof uniformFormat !== "string") {
1717
+ throw new Error(
1718
+ `Composite uniform types are not supported by WGSL shader generation: ${module.name}.${uniformName}`
1719
+ );
1720
+ }
1189
1721
  const wgslUniformType = uniformFormat;
1190
1722
  wgsl.push(` ${uniformName} : ${wgslUniformType};`);
1191
1723
  }
@@ -3190,6 +3722,7 @@ float tan_fp32(float a) {
3190
3722
  `
3191
3723
  uniform fp64arithmeticUniforms {
3192
3724
  uniform float ONE;
3725
+ uniform float SPLIT;
3193
3726
  } fp64;
3194
3727
 
3195
3728
  /*
@@ -3199,6 +3732,12 @@ The purpose of this workaround is to prevent shader compilers from
3199
3732
  optimizing away necessary arithmetic operations by swapping their sequences
3200
3733
  or transform the equation to some 'equivalent' form.
3201
3734
 
3735
+ These helpers implement Dekker/Veltkamp-style error tracking. If the compiler
3736
+ folds constants or reassociates the arithmetic, the high/low split can stop
3737
+ tracking the rounding error correctly. That failure mode tends to look fine in
3738
+ simple coordinate setup, but then breaks down inside iterative arithmetic such
3739
+ as fp64 Mandelbrot loops.
3740
+
3202
3741
  The method is to multiply an artifical variable, ONE, which will be known to
3203
3742
  the compiler to be 1 only at runtime. The whole expression is then represented
3204
3743
  as a polynomial with respective to ONE. In the coefficients of all terms, only one a
@@ -3207,17 +3746,23 @@ and one b should appear
3207
3746
  err = (a + b) * ONE^6 - a * ONE^5 - (a + b) * ONE^4 + a * ONE^3 - b - (a + b) * ONE^2 + a * ONE
3208
3747
  */
3209
3748
 
3210
- // Divide float number to high and low floats to extend fraction bits
3211
- vec2 split(float a) {
3212
- const float SPLIT = 4097.0;
3213
- float t = a * SPLIT;
3749
+ float prevent_fp64_optimization(float value) {
3214
3750
  #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
3215
- float a_hi = t * fp64.ONE - (t - a);
3216
- float a_lo = a * fp64.ONE - a_hi;
3751
+ return value + fp64.ONE * 0.0;
3217
3752
  #else
3218
- float a_hi = t - (t - a);
3219
- float a_lo = a - a_hi;
3753
+ return value;
3220
3754
  #endif
3755
+ }
3756
+
3757
+ // Divide float number to high and low floats to extend fraction bits
3758
+ vec2 split(float a) {
3759
+ // Keep SPLIT as a runtime uniform so the compiler cannot fold the Dekker
3760
+ // split into a constant expression and reassociate the recovery steps.
3761
+ float split = prevent_fp64_optimization(fp64.SPLIT);
3762
+ float t = prevent_fp64_optimization(a * split);
3763
+ float temp = t - a;
3764
+ float a_hi = t - temp;
3765
+ float a_lo = a - a_hi;
3221
3766
  return vec2(a_hi, a_lo);
3222
3767
  }
3223
3768
 
@@ -3281,8 +3826,26 @@ vec2 twoProd(float a, float b) {
3281
3826
  float prod = a * b;
3282
3827
  vec2 a_fp64 = split(a);
3283
3828
  vec2 b_fp64 = split(b);
3284
- float err = ((a_fp64.x * b_fp64.x - prod) + a_fp64.x * b_fp64.y +
3285
- a_fp64.y * b_fp64.x) + a_fp64.y * b_fp64.y;
3829
+ // twoProd is especially sensitive because mul_fp64 and div_fp64 both depend
3830
+ // on the split terms and cross terms staying in the original evaluation
3831
+ // order. If the compiler folds or reassociates them, the low part tends to
3832
+ // collapse to zero or NaN on some drivers.
3833
+ float highProduct = prevent_fp64_optimization(a_fp64.x * b_fp64.x);
3834
+ float crossProduct1 = prevent_fp64_optimization(a_fp64.x * b_fp64.y);
3835
+ float crossProduct2 = prevent_fp64_optimization(a_fp64.y * b_fp64.x);
3836
+ float lowProduct = prevent_fp64_optimization(a_fp64.y * b_fp64.y);
3837
+ #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND)
3838
+ float err1 = (highProduct - prod) * fp64.ONE;
3839
+ float err2 = crossProduct1 * fp64.ONE * fp64.ONE;
3840
+ float err3 = crossProduct2 * fp64.ONE * fp64.ONE * fp64.ONE;
3841
+ float err4 = lowProduct * fp64.ONE * fp64.ONE * fp64.ONE * fp64.ONE;
3842
+ #else
3843
+ float err1 = highProduct - prod;
3844
+ float err2 = crossProduct1;
3845
+ float err3 = crossProduct2;
3846
+ float err4 = lowProduct;
3847
+ #endif
3848
+ float err = ((err1 + err2) + err3) + err4;
3286
3849
  return vec2(prod, err);
3287
3850
  }
3288
3851
 
@@ -3358,6 +3921,218 @@ vec2 sqrt_fp64(vec2 a) {
3358
3921
  `
3359
3922
  );
3360
3923
 
3924
+ // src/modules/math/fp64/fp64-arithmetic-wgsl.ts
3925
+ var fp64arithmeticWGSL = (
3926
+ /* wgsl */
3927
+ `struct Fp64ArithmeticUniforms {
3928
+ ONE: f32,
3929
+ SPLIT: f32,
3930
+ };
3931
+
3932
+ @group(0) @binding(auto) var<uniform> fp64arithmetic : Fp64ArithmeticUniforms;
3933
+
3934
+ fn fp64_nan(seed: f32) -> f32 {
3935
+ let nanBits = 0x7fc00000u | select(0u, 1u, seed < 0.0);
3936
+ return bitcast<f32>(nanBits);
3937
+ }
3938
+
3939
+ fn fp64_runtime_zero() -> f32 {
3940
+ return fp64arithmetic.ONE * 0.0;
3941
+ }
3942
+
3943
+ fn prevent_fp64_optimization(value: f32) -> f32 {
3944
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
3945
+ return value + fp64_runtime_zero();
3946
+ #else
3947
+ return value;
3948
+ #endif
3949
+ }
3950
+
3951
+ fn split(a: f32) -> vec2f {
3952
+ let splitValue = prevent_fp64_optimization(fp64arithmetic.SPLIT + fp64_runtime_zero());
3953
+ let t = prevent_fp64_optimization(a * splitValue);
3954
+ let temp = prevent_fp64_optimization(t - a);
3955
+ let aHi = prevent_fp64_optimization(t - temp);
3956
+ let aLo = prevent_fp64_optimization(a - aHi);
3957
+ return vec2f(aHi, aLo);
3958
+ }
3959
+
3960
+ fn split2(a: vec2f) -> vec2f {
3961
+ var b = split(a.x);
3962
+ b.y = b.y + a.y;
3963
+ return b;
3964
+ }
3965
+
3966
+ fn quickTwoSum(a: f32, b: f32) -> vec2f {
3967
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
3968
+ let sum = prevent_fp64_optimization((a + b) * fp64arithmetic.ONE);
3969
+ let err = prevent_fp64_optimization(b - (sum - a) * fp64arithmetic.ONE);
3970
+ #else
3971
+ let sum = prevent_fp64_optimization(a + b);
3972
+ let err = prevent_fp64_optimization(b - (sum - a));
3973
+ #endif
3974
+ return vec2f(sum, err);
3975
+ }
3976
+
3977
+ fn twoSum(a: f32, b: f32) -> vec2f {
3978
+ let s = prevent_fp64_optimization(a + b);
3979
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
3980
+ let v = prevent_fp64_optimization((s * fp64arithmetic.ONE - a) * fp64arithmetic.ONE);
3981
+ let err =
3982
+ prevent_fp64_optimization((a - (s - v) * fp64arithmetic.ONE) *
3983
+ fp64arithmetic.ONE *
3984
+ fp64arithmetic.ONE *
3985
+ fp64arithmetic.ONE) +
3986
+ prevent_fp64_optimization(b - v);
3987
+ #else
3988
+ let v = prevent_fp64_optimization(s - a);
3989
+ let err = prevent_fp64_optimization(a - (s - v)) + prevent_fp64_optimization(b - v);
3990
+ #endif
3991
+ return vec2f(s, err);
3992
+ }
3993
+
3994
+ fn twoSub(a: f32, b: f32) -> vec2f {
3995
+ let s = prevent_fp64_optimization(a - b);
3996
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
3997
+ let v = prevent_fp64_optimization((s * fp64arithmetic.ONE - a) * fp64arithmetic.ONE);
3998
+ let err =
3999
+ prevent_fp64_optimization((a - (s - v) * fp64arithmetic.ONE) *
4000
+ fp64arithmetic.ONE *
4001
+ fp64arithmetic.ONE *
4002
+ fp64arithmetic.ONE) -
4003
+ prevent_fp64_optimization(b + v);
4004
+ #else
4005
+ let v = prevent_fp64_optimization(s - a);
4006
+ let err = prevent_fp64_optimization(a - (s - v)) - prevent_fp64_optimization(b + v);
4007
+ #endif
4008
+ return vec2f(s, err);
4009
+ }
4010
+
4011
+ fn twoSqr(a: f32) -> vec2f {
4012
+ let prod = prevent_fp64_optimization(a * a);
4013
+ let aFp64 = split(a);
4014
+ let highProduct = prevent_fp64_optimization(aFp64.x * aFp64.x);
4015
+ let crossProduct = prevent_fp64_optimization(2.0 * aFp64.x * aFp64.y);
4016
+ let lowProduct = prevent_fp64_optimization(aFp64.y * aFp64.y);
4017
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
4018
+ let err =
4019
+ (prevent_fp64_optimization(highProduct - prod) * fp64arithmetic.ONE +
4020
+ crossProduct * fp64arithmetic.ONE * fp64arithmetic.ONE) +
4021
+ lowProduct * fp64arithmetic.ONE * fp64arithmetic.ONE * fp64arithmetic.ONE;
4022
+ #else
4023
+ let err = ((prevent_fp64_optimization(highProduct - prod) + crossProduct) + lowProduct);
4024
+ #endif
4025
+ return vec2f(prod, err);
4026
+ }
4027
+
4028
+ fn twoProd(a: f32, b: f32) -> vec2f {
4029
+ let prod = prevent_fp64_optimization(a * b);
4030
+ let aFp64 = split(a);
4031
+ let bFp64 = split(b);
4032
+ let highProduct = prevent_fp64_optimization(aFp64.x * bFp64.x);
4033
+ let crossProduct1 = prevent_fp64_optimization(aFp64.x * bFp64.y);
4034
+ let crossProduct2 = prevent_fp64_optimization(aFp64.y * bFp64.x);
4035
+ let lowProduct = prevent_fp64_optimization(aFp64.y * bFp64.y);
4036
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
4037
+ let err1 = (highProduct - prod) * fp64arithmetic.ONE;
4038
+ let err2 = crossProduct1 * fp64arithmetic.ONE * fp64arithmetic.ONE;
4039
+ let err3 = crossProduct2 * fp64arithmetic.ONE * fp64arithmetic.ONE * fp64arithmetic.ONE;
4040
+ let err4 =
4041
+ lowProduct *
4042
+ fp64arithmetic.ONE *
4043
+ fp64arithmetic.ONE *
4044
+ fp64arithmetic.ONE *
4045
+ fp64arithmetic.ONE;
4046
+ #else
4047
+ let err1 = highProduct - prod;
4048
+ let err2 = crossProduct1;
4049
+ let err3 = crossProduct2;
4050
+ let err4 = lowProduct;
4051
+ #endif
4052
+ let err12InputA = prevent_fp64_optimization(err1);
4053
+ let err12InputB = prevent_fp64_optimization(err2);
4054
+ let err12 = prevent_fp64_optimization(err12InputA + err12InputB);
4055
+ let err123InputA = prevent_fp64_optimization(err12);
4056
+ let err123InputB = prevent_fp64_optimization(err3);
4057
+ let err123 = prevent_fp64_optimization(err123InputA + err123InputB);
4058
+ let err1234InputA = prevent_fp64_optimization(err123);
4059
+ let err1234InputB = prevent_fp64_optimization(err4);
4060
+ let err = prevent_fp64_optimization(err1234InputA + err1234InputB);
4061
+ return vec2f(prod, err);
4062
+ }
4063
+
4064
+ fn sum_fp64(a: vec2f, b: vec2f) -> vec2f {
4065
+ var s = twoSum(a.x, b.x);
4066
+ let t = twoSum(a.y, b.y);
4067
+ s.y = prevent_fp64_optimization(s.y + t.x);
4068
+ s = quickTwoSum(s.x, s.y);
4069
+ s.y = prevent_fp64_optimization(s.y + t.y);
4070
+ s = quickTwoSum(s.x, s.y);
4071
+ return s;
4072
+ }
4073
+
4074
+ fn sub_fp64(a: vec2f, b: vec2f) -> vec2f {
4075
+ var s = twoSub(a.x, b.x);
4076
+ let t = twoSub(a.y, b.y);
4077
+ s.y = prevent_fp64_optimization(s.y + t.x);
4078
+ s = quickTwoSum(s.x, s.y);
4079
+ s.y = prevent_fp64_optimization(s.y + t.y);
4080
+ s = quickTwoSum(s.x, s.y);
4081
+ return s;
4082
+ }
4083
+
4084
+ fn mul_fp64(a: vec2f, b: vec2f) -> vec2f {
4085
+ var prod = twoProd(a.x, b.x);
4086
+ let crossProduct1 = prevent_fp64_optimization(a.x * b.y);
4087
+ prod.y = prevent_fp64_optimization(prod.y + crossProduct1);
4088
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
4089
+ prod = split2(prod);
4090
+ #endif
4091
+ prod = quickTwoSum(prod.x, prod.y);
4092
+ let crossProduct2 = prevent_fp64_optimization(a.y * b.x);
4093
+ prod.y = prevent_fp64_optimization(prod.y + crossProduct2);
4094
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
4095
+ prod = split2(prod);
4096
+ #endif
4097
+ prod = quickTwoSum(prod.x, prod.y);
4098
+ return prod;
4099
+ }
4100
+
4101
+ fn div_fp64(a: vec2f, b: vec2f) -> vec2f {
4102
+ let xn = prevent_fp64_optimization(1.0 / b.x);
4103
+ let yn = mul_fp64(a, vec2f(xn, fp64_runtime_zero()));
4104
+ let diff = prevent_fp64_optimization(sub_fp64(a, mul_fp64(b, yn)).x);
4105
+ let prod = twoProd(xn, diff);
4106
+ return sum_fp64(yn, prod);
4107
+ }
4108
+
4109
+ fn sqrt_fp64(a: vec2f) -> vec2f {
4110
+ if (a.x == 0.0 && a.y == 0.0) {
4111
+ return vec2f(0.0, 0.0);
4112
+ }
4113
+ if (a.x < 0.0) {
4114
+ let nanValue = fp64_nan(a.x);
4115
+ return vec2f(nanValue, nanValue);
4116
+ }
4117
+
4118
+ let x = prevent_fp64_optimization(1.0 / sqrt(a.x));
4119
+ let yn = prevent_fp64_optimization(a.x * x);
4120
+ #ifdef LUMA_FP64_CODE_ELIMINATION_WORKAROUND
4121
+ let ynSqr = twoSqr(yn) * fp64arithmetic.ONE;
4122
+ #else
4123
+ let ynSqr = twoSqr(yn);
4124
+ #endif
4125
+ let diff = prevent_fp64_optimization(sub_fp64(a, ynSqr).x);
4126
+ let prod = twoProd(prevent_fp64_optimization(x * 0.5), diff);
4127
+ #ifdef LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND
4128
+ return sum_fp64(split(yn), prod);
4129
+ #else
4130
+ return sum_fp64(vec2f(yn, 0.0), prod);
4131
+ #endif
4132
+ }
4133
+ `
4134
+ );
4135
+
3361
4136
  // src/modules/math/fp64/fp64-functions-glsl.ts
3362
4137
  var fp64functionShader = (
3363
4138
  /* glsl */
@@ -4036,13 +4811,18 @@ void mat4_vec4_mul_fp64(vec2 b[16], vec2 a[4], out vec2 out_val[4]) {
4036
4811
  // src/modules/math/fp64/fp64.ts
4037
4812
  var defaultUniforms = {
4038
4813
  // Used in LUMA_FP64_CODE_ELIMINATION_WORKAROUND
4039
- ONE: 1
4814
+ ONE: 1,
4815
+ // Runtime split factor for Dekker splitting. Keeping this as a uniform helps
4816
+ // prevent aggressive constant folding in shader compilers.
4817
+ SPLIT: 4097
4040
4818
  };
4041
4819
  var fp64arithmetic = {
4042
4820
  name: "fp64arithmetic",
4821
+ source: fp64arithmeticWGSL,
4822
+ fs: fp64arithmeticShader,
4043
4823
  vs: fp64arithmeticShader,
4044
4824
  defaultUniforms,
4045
- uniformTypes: { ONE: "f32" },
4825
+ uniformTypes: { ONE: "f32", SPLIT: "f32" },
4046
4826
  // Additional Functions
4047
4827
  fp64ify,
4048
4828
  fp64LowPart,
@@ -4255,7 +5035,7 @@ struct skinUniforms {
4255
5035
  jointMatrix: array<mat4x4<f32>, ${SKIN_MAX_JOINTS}>,
4256
5036
  };
4257
5037
 
4258
- @binding(19) @group(0) var<uniform> skin: skinUniforms;
5038
+ @group(0) @binding(auto) var<uniform> skin: skinUniforms;
4259
5039
 
4260
5040
  fn getSkinMatrix(weights: vec4f, joints: vec4u) -> mat4x4<f32> {
4261
5041
  return (weights.x * skin.jointMatrix[joints.x])
@@ -4289,6 +5069,7 @@ mat4 getSkinMatrix(vec4 weights, uvec4 joints) {
4289
5069
  props: {},
4290
5070
  uniforms: {},
4291
5071
  name: "skin",
5072
+ bindingLayout: [{ name: "skin", group: 0 }],
4292
5073
  dependencies: [],
4293
5074
  source: source2,
4294
5075
  vs: vs2,
@@ -4331,10 +5112,7 @@ mat4 getSkinMatrix(vec4 weights, uvec4 joints) {
4331
5112
  };
4332
5113
  },
4333
5114
  uniformTypes: {
4334
- jointMatrix: "mat4x4<f32>"
4335
- },
4336
- uniformSizes: {
4337
- jointMatrix: SKIN_MAX_JOINTS
5115
+ jointMatrix: ["mat4x4<f32>", SKIN_MAX_JOINTS]
4338
5116
  }
4339
5117
  };
4340
5118
 
@@ -4357,77 +5135,51 @@ struct PointLight {
4357
5135
  vec3 attenuation; // 2nd order x:Constant-y:Linear-z:Exponential
4358
5136
  };
4359
5137
 
5138
+ struct SpotLight {
5139
+ vec3 color;
5140
+ vec3 position;
5141
+ vec3 direction;
5142
+ vec3 attenuation;
5143
+ vec2 coneCos;
5144
+ };
5145
+
4360
5146
  struct DirectionalLight {
4361
5147
  vec3 color;
4362
5148
  vec3 direction;
4363
5149
  };
4364
5150
 
5151
+ struct UniformLight {
5152
+ vec3 color;
5153
+ vec3 position;
5154
+ vec3 direction;
5155
+ vec3 attenuation;
5156
+ vec2 coneCos;
5157
+ };
5158
+
4365
5159
  uniform lightingUniforms {
4366
5160
  int enabled;
4367
- int lightType;
4368
-
4369
5161
  int directionalLightCount;
4370
5162
  int pointLightCount;
4371
-
5163
+ int spotLightCount;
4372
5164
  vec3 ambientColor;
4373
-
4374
- vec3 lightColor0;
4375
- vec3 lightPosition0;
4376
- vec3 lightDirection0;
4377
- vec3 lightAttenuation0;
4378
-
4379
- vec3 lightColor1;
4380
- vec3 lightPosition1;
4381
- vec3 lightDirection1;
4382
- vec3 lightAttenuation1;
4383
-
4384
- vec3 lightColor2;
4385
- vec3 lightPosition2;
4386
- vec3 lightDirection2;
4387
- vec3 lightAttenuation2;
4388
-
4389
- vec3 lightColor3;
4390
- vec3 lightPosition3;
4391
- vec3 lightDirection3;
4392
- vec3 lightAttenuation3;
4393
-
4394
- vec3 lightColor4;
4395
- vec3 lightPosition4;
4396
- vec3 lightDirection4;
4397
- vec3 lightAttenuation4;
5165
+ UniformLight lights[5];
4398
5166
  } lighting;
4399
5167
 
4400
5168
  PointLight lighting_getPointLight(int index) {
4401
- switch (index) {
4402
- case 0:
4403
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
4404
- case 1:
4405
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
4406
- case 2:
4407
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
4408
- case 3:
4409
- return PointLight(lighting.lightColor3, lighting.lightPosition3, lighting.lightAttenuation3);
4410
- case 4:
4411
- default:
4412
- return PointLight(lighting.lightColor4, lighting.lightPosition4, lighting.lightAttenuation4);
4413
- }
5169
+ UniformLight light = lighting.lights[index];
5170
+ return PointLight(light.color, light.position, light.attenuation);
5171
+ }
5172
+
5173
+ SpotLight lighting_getSpotLight(int index) {
5174
+ UniformLight light = lighting.lights[lighting.pointLightCount + index];
5175
+ return SpotLight(light.color, light.position, light.direction, light.attenuation, light.coneCos);
4414
5176
  }
4415
5177
 
4416
5178
  DirectionalLight lighting_getDirectionalLight(int index) {
4417
- switch (index) {
4418
- case 0:
4419
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
4420
- case 1:
4421
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
4422
- case 2:
4423
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
4424
- case 3:
4425
- return DirectionalLight(lighting.lightColor3, lighting.lightDirection3);
4426
- case 4:
4427
- default:
4428
- return DirectionalLight(lighting.lightColor4, lighting.lightDirection4);
4429
- }
4430
- }
5179
+ UniformLight light =
5180
+ lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
5181
+ return DirectionalLight(light.color, light.direction);
5182
+ }
4431
5183
 
4432
5184
  float getPointLightAttenuation(PointLight pointLight, float distance) {
4433
5185
  return pointLight.attenuation.x
@@ -4435,6 +5187,20 @@ float getPointLightAttenuation(PointLight pointLight, float distance) {
4435
5187
  + pointLight.attenuation.z * distance * distance;
4436
5188
  }
4437
5189
 
5190
+ float getSpotLightAttenuation(SpotLight spotLight, vec3 positionWorldspace) {
5191
+ vec3 light_direction = normalize(positionWorldspace - spotLight.position);
5192
+ float coneFactor = smoothstep(
5193
+ spotLight.coneCos.y,
5194
+ spotLight.coneCos.x,
5195
+ dot(normalize(spotLight.direction), light_direction)
5196
+ );
5197
+ float distanceAttenuation = getPointLightAttenuation(
5198
+ PointLight(spotLight.color, spotLight.position, spotLight.attenuation),
5199
+ distance(spotLight.position, positionWorldspace)
5200
+ );
5201
+ return distanceAttenuation / max(coneFactor, 0.0001);
5202
+ }
5203
+
4438
5204
  // #endif
4439
5205
  `
4440
5206
  );
@@ -4455,100 +5221,85 @@ struct PointLight {
4455
5221
  attenuation: vec3<f32>, // 2nd order x:Constant-y:Linear-z:Exponential
4456
5222
  };
4457
5223
 
5224
+ struct SpotLight {
5225
+ color: vec3<f32>,
5226
+ position: vec3<f32>,
5227
+ direction: vec3<f32>,
5228
+ attenuation: vec3<f32>,
5229
+ coneCos: vec2<f32>,
5230
+ };
5231
+
4458
5232
  struct DirectionalLight {
4459
5233
  color: vec3<f32>,
4460
5234
  direction: vec3<f32>,
4461
5235
  };
4462
5236
 
5237
+ struct UniformLight {
5238
+ color: vec3<f32>,
5239
+ position: vec3<f32>,
5240
+ direction: vec3<f32>,
5241
+ attenuation: vec3<f32>,
5242
+ coneCos: vec2<f32>,
5243
+ };
5244
+
4463
5245
  struct lightingUniforms {
4464
5246
  enabled: i32,
4465
- lightType: i32,
4466
-
4467
5247
  directionalLightCount: i32,
4468
5248
  pointLightCount: i32,
4469
-
5249
+ spotLightCount: i32,
4470
5250
  ambientColor: vec3<f32>,
4471
-
4472
- lightColor0: vec3<f32>,
4473
- lightPosition0: vec3<f32>,
4474
- lightDirection0: vec3<f32>,
4475
- lightAttenuation0: vec3<f32>,
4476
-
4477
- lightColor1: vec3<f32>,
4478
- lightPosition1: vec3<f32>,
4479
- lightDirection1: vec3<f32>,
4480
- lightAttenuation1: vec3<f32>,
4481
-
4482
- lightColor2: vec3<f32>,
4483
- lightPosition2: vec3<f32>,
4484
- lightDirection2: vec3<f32>,
4485
- lightAttenuation2: vec3<f32>,
4486
-
4487
- lightColor3: vec3<f32>,
4488
- lightPosition3: vec3<f32>,
4489
- lightDirection3: vec3<f32>,
4490
- lightAttenuation3: vec3<f32>,
4491
-
4492
- lightColor4: vec3<f32>,
4493
- lightPosition4: vec3<f32>,
4494
- lightDirection4: vec3<f32>,
4495
- lightAttenuation4: vec3<f32>,
5251
+ lights: array<UniformLight, 5>,
4496
5252
  };
4497
5253
 
4498
- // Binding 0:1 is reserved for lighting (Note: could go into separate bind group as it is stable across draw calls)
4499
- @binding(1) @group(0) var<uniform> lighting : lightingUniforms;
5254
+ @group(2) @binding(auto) var<uniform> lighting : lightingUniforms;
4500
5255
 
4501
5256
  fn lighting_getPointLight(index: i32) -> PointLight {
4502
- switch (index) {
4503
- case 0: {
4504
- return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
4505
- }
4506
- case 1: {
4507
- return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
4508
- }
4509
- case 2: {
4510
- return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
4511
- }
4512
- case 3: {
4513
- return PointLight(lighting.lightColor3, lighting.lightPosition3, lighting.lightAttenuation3);
4514
- }
4515
- case 4, default: {
4516
- return PointLight(lighting.lightColor4, lighting.lightPosition4, lighting.lightAttenuation4);
4517
- }
4518
- }
5257
+ let light = lighting.lights[index];
5258
+ return PointLight(light.color, light.position, light.attenuation);
5259
+ }
5260
+
5261
+ fn lighting_getSpotLight(index: i32) -> SpotLight {
5262
+ let light = lighting.lights[lighting.pointLightCount + index];
5263
+ return SpotLight(light.color, light.position, light.direction, light.attenuation, light.coneCos);
4519
5264
  }
4520
5265
 
4521
5266
  fn lighting_getDirectionalLight(index: i32) -> DirectionalLight {
4522
- switch (index) {
4523
- case 0: {
4524
- return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
4525
- }
4526
- case 1: {
4527
- return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
4528
- }
4529
- case 2: {
4530
- return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
4531
- }
4532
- case 3: {
4533
- return DirectionalLight(lighting.lightColor3, lighting.lightDirection3);
4534
- }
4535
- case 4, default: {
4536
- return DirectionalLight(lighting.lightColor4, lighting.lightDirection4);
4537
- }
4538
- }
4539
- }
5267
+ let light = lighting.lights[lighting.pointLightCount + lighting.spotLightCount + index];
5268
+ return DirectionalLight(light.color, light.direction);
5269
+ }
4540
5270
 
4541
5271
  fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4542
5272
  return pointLight.attenuation.x
4543
5273
  + pointLight.attenuation.y * distance
4544
5274
  + pointLight.attenuation.z * distance * distance;
4545
5275
  }
5276
+
5277
+ fn getSpotLightAttenuation(spotLight: SpotLight, positionWorldspace: vec3<f32>) -> f32 {
5278
+ let lightDirection = normalize(positionWorldspace - spotLight.position);
5279
+ let coneFactor = smoothstep(
5280
+ spotLight.coneCos.y,
5281
+ spotLight.coneCos.x,
5282
+ dot(normalize(spotLight.direction), lightDirection)
5283
+ );
5284
+ let distanceAttenuation = getPointLightAttenuation(
5285
+ PointLight(spotLight.color, spotLight.position, spotLight.attenuation),
5286
+ distance(spotLight.position, positionWorldspace)
5287
+ );
5288
+ return distanceAttenuation / max(coneFactor, 0.0001);
5289
+ }
4546
5290
  `
4547
5291
  );
4548
5292
 
4549
5293
  // src/modules/lighting/lights/lighting.ts
4550
5294
  var MAX_LIGHTS = 5;
4551
5295
  var COLOR_FACTOR = 255;
5296
+ var LIGHT_UNIFORM_TYPE = {
5297
+ color: "vec3<f32>",
5298
+ position: "vec3<f32>",
5299
+ direction: "vec3<f32>",
5300
+ attenuation: "vec3<f32>",
5301
+ coneCos: "vec2<f32>"
5302
+ };
4552
5303
  var lighting = {
4553
5304
  props: {},
4554
5305
  uniforms: {},
@@ -4558,83 +5309,39 @@ fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4558
5309
  },
4559
5310
  uniformTypes: {
4560
5311
  enabled: "i32",
4561
- lightType: "i32",
4562
5312
  directionalLightCount: "i32",
4563
5313
  pointLightCount: "i32",
5314
+ spotLightCount: "i32",
4564
5315
  ambientColor: "vec3<f32>",
4565
- // TODO define as arrays once we have appropriate uniformTypes
4566
- lightColor0: "vec3<f32>",
4567
- lightPosition0: "vec3<f32>",
4568
- // TODO - could combine direction and attenuation
4569
- lightDirection0: "vec3<f32>",
4570
- lightAttenuation0: "vec3<f32>",
4571
- lightColor1: "vec3<f32>",
4572
- lightPosition1: "vec3<f32>",
4573
- lightDirection1: "vec3<f32>",
4574
- lightAttenuation1: "vec3<f32>",
4575
- lightColor2: "vec3<f32>",
4576
- lightPosition2: "vec3<f32>",
4577
- lightDirection2: "vec3<f32>",
4578
- lightAttenuation2: "vec3<f32>",
4579
- lightColor3: "vec3<f32>",
4580
- lightPosition3: "vec3<f32>",
4581
- lightDirection3: "vec3<f32>",
4582
- lightAttenuation3: "vec3<f32>",
4583
- lightColor4: "vec3<f32>",
4584
- lightPosition4: "vec3<f32>",
4585
- lightDirection4: "vec3<f32>",
4586
- lightAttenuation4: "vec3<f32>"
4587
- },
4588
- defaultUniforms: {
4589
- enabled: 1,
4590
- lightType: 0 /* POINT */,
4591
- directionalLightCount: 0,
4592
- pointLightCount: 0,
4593
- ambientColor: [0.1, 0.1, 0.1],
4594
- lightColor0: [1, 1, 1],
4595
- lightPosition0: [1, 1, 2],
4596
- // TODO - could combine direction and attenuation
4597
- lightDirection0: [1, 1, 1],
4598
- lightAttenuation0: [1, 0, 0],
4599
- lightColor1: [1, 1, 1],
4600
- lightPosition1: [1, 1, 2],
4601
- lightDirection1: [1, 1, 1],
4602
- lightAttenuation1: [1, 0, 0],
4603
- lightColor2: [1, 1, 1],
4604
- lightPosition2: [1, 1, 2],
4605
- lightDirection2: [1, 1, 1],
4606
- lightAttenuation2: [1, 0, 0],
4607
- lightColor3: [1, 1, 1],
4608
- lightPosition3: [1, 1, 2],
4609
- lightDirection3: [1, 1, 1],
4610
- lightAttenuation3: [1, 0, 0],
4611
- lightColor4: [1, 1, 1],
4612
- lightPosition4: [1, 1, 2],
4613
- lightDirection4: [1, 1, 1],
4614
- lightAttenuation4: [1, 0, 0]
5316
+ lights: [LIGHT_UNIFORM_TYPE, MAX_LIGHTS]
4615
5317
  },
5318
+ defaultUniforms: createDefaultLightingUniforms(),
5319
+ bindingLayout: [{ name: "lighting", group: 2 }],
5320
+ firstBindingSlot: 0,
4616
5321
  source: lightingUniformsWGSL,
4617
5322
  vs: lightingUniformsGLSL,
4618
5323
  fs: lightingUniformsGLSL,
4619
5324
  getUniforms: getUniforms2
4620
5325
  };
4621
- function getUniforms2(props, prevUniforms = {}) {
5326
+ function getUniforms2(props, _prevUniforms = {}) {
4622
5327
  props = props ? { ...props } : props;
4623
5328
  if (!props) {
4624
- return { ...lighting.defaultUniforms };
5329
+ return createDefaultLightingUniforms();
4625
5330
  }
4626
5331
  if (props.lights) {
4627
5332
  props = { ...props, ...extractLightTypes(props.lights), lights: void 0 };
4628
5333
  }
4629
- const { ambientLight, pointLights, directionalLights } = props || {};
4630
- const hasLights = ambientLight || pointLights && pointLights.length > 0 || directionalLights && directionalLights.length > 0;
5334
+ const { ambientLight, pointLights, spotLights, directionalLights } = props || {};
5335
+ const hasLights = ambientLight || pointLights && pointLights.length > 0 || spotLights && spotLights.length > 0 || directionalLights && directionalLights.length > 0;
4631
5336
  if (!hasLights) {
4632
- return { ...lighting.defaultUniforms, enabled: 0 };
5337
+ return {
5338
+ ...createDefaultLightingUniforms(),
5339
+ enabled: 0
5340
+ };
4633
5341
  }
4634
5342
  const uniforms = {
4635
- ...lighting.defaultUniforms,
4636
- ...prevUniforms,
4637
- ...getLightSourceUniforms({ ambientLight, pointLights, directionalLights })
5343
+ ...createDefaultLightingUniforms(),
5344
+ ...getLightSourceUniforms({ ambientLight, pointLights, spotLights, directionalLights })
4638
5345
  };
4639
5346
  if (props.enabled !== void 0) {
4640
5347
  uniforms.enabled = props.enabled ? 1 : 0;
@@ -4644,45 +5351,67 @@ fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4644
5351
  function getLightSourceUniforms({
4645
5352
  ambientLight,
4646
5353
  pointLights = [],
5354
+ spotLights = [],
4647
5355
  directionalLights = []
4648
5356
  }) {
4649
- const lightSourceUniforms = {};
4650
- lightSourceUniforms.ambientColor = convertColor(ambientLight);
5357
+ const lights = createDefaultLightUniforms();
4651
5358
  let currentLight = 0;
4652
5359
  let pointLightCount = 0;
5360
+ let spotLightCount = 0;
4653
5361
  let directionalLightCount = 0;
4654
5362
  for (const pointLight of pointLights) {
4655
5363
  if (currentLight >= MAX_LIGHTS) {
4656
5364
  break;
4657
5365
  }
4658
- lightSourceUniforms.lightType = 0 /* POINT */;
4659
- const i = currentLight;
4660
- lightSourceUniforms[`lightColor${i}`] = convertColor(pointLight);
4661
- lightSourceUniforms[`lightPosition${i}`] = pointLight.position;
4662
- lightSourceUniforms[`lightAttenuation${i}`] = pointLight.attenuation || [1, 0, 0];
5366
+ lights[currentLight] = {
5367
+ ...lights[currentLight],
5368
+ color: convertColor(pointLight),
5369
+ position: pointLight.position,
5370
+ attenuation: pointLight.attenuation || [1, 0, 0]
5371
+ };
4663
5372
  currentLight++;
4664
5373
  pointLightCount++;
4665
5374
  }
5375
+ for (const spotLight of spotLights) {
5376
+ if (currentLight >= MAX_LIGHTS) {
5377
+ break;
5378
+ }
5379
+ lights[currentLight] = {
5380
+ ...lights[currentLight],
5381
+ color: convertColor(spotLight),
5382
+ position: spotLight.position,
5383
+ direction: spotLight.direction,
5384
+ attenuation: spotLight.attenuation || [1, 0, 0],
5385
+ coneCos: getSpotConeCos(spotLight)
5386
+ };
5387
+ currentLight++;
5388
+ spotLightCount++;
5389
+ }
4666
5390
  for (const directionalLight of directionalLights) {
4667
5391
  if (currentLight >= MAX_LIGHTS) {
4668
5392
  break;
4669
5393
  }
4670
- lightSourceUniforms.lightType = 1 /* DIRECTIONAL */;
4671
- const i = currentLight;
4672
- lightSourceUniforms[`lightColor${i}`] = convertColor(directionalLight);
4673
- lightSourceUniforms[`lightDirection${i}`] = directionalLight.direction;
5394
+ lights[currentLight] = {
5395
+ ...lights[currentLight],
5396
+ color: convertColor(directionalLight),
5397
+ direction: directionalLight.direction
5398
+ };
4674
5399
  currentLight++;
4675
5400
  directionalLightCount++;
4676
5401
  }
4677
- if (pointLights.length + directionalLights.length > MAX_LIGHTS) {
5402
+ if (pointLights.length + spotLights.length + directionalLights.length > MAX_LIGHTS) {
4678
5403
  import_core3.log.warn(`MAX_LIGHTS exceeded, truncating to ${MAX_LIGHTS}`)();
4679
5404
  }
4680
- lightSourceUniforms.directionalLightCount = directionalLightCount;
4681
- lightSourceUniforms.pointLightCount = pointLightCount;
4682
- return lightSourceUniforms;
5405
+ return {
5406
+ ambientColor: convertColor(ambientLight),
5407
+ directionalLightCount,
5408
+ pointLightCount,
5409
+ spotLightCount,
5410
+ lights
5411
+ };
4683
5412
  }
4684
5413
  function extractLightTypes(lights) {
4685
- const lightSources = { pointLights: [], directionalLights: [] };
5414
+ const lightSources = { pointLights: [], spotLights: [], directionalLights: [] };
4686
5415
  for (const light of lights || []) {
4687
5416
  switch (light.type) {
4688
5417
  case "ambient":
@@ -4694,6 +5423,9 @@ fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4694
5423
  case "point":
4695
5424
  lightSources.pointLights?.push(light);
4696
5425
  break;
5426
+ case "spot":
5427
+ lightSources.spotLights?.push(light);
5428
+ break;
4697
5429
  default:
4698
5430
  }
4699
5431
  }
@@ -4703,6 +5435,68 @@ fn getPointLightAttenuation(pointLight: PointLight, distance: f32) -> f32 {
4703
5435
  const { color = [0, 0, 0], intensity = 1 } = colorDef;
4704
5436
  return color.map((component) => component * intensity / COLOR_FACTOR);
4705
5437
  }
5438
+ function createDefaultLightingUniforms() {
5439
+ return {
5440
+ enabled: 1,
5441
+ directionalLightCount: 0,
5442
+ pointLightCount: 0,
5443
+ spotLightCount: 0,
5444
+ ambientColor: [0.1, 0.1, 0.1],
5445
+ lights: createDefaultLightUniforms()
5446
+ };
5447
+ }
5448
+ function createDefaultLightUniforms() {
5449
+ return Array.from({ length: MAX_LIGHTS }, () => createDefaultLightUniform());
5450
+ }
5451
+ function createDefaultLightUniform() {
5452
+ return {
5453
+ color: [1, 1, 1],
5454
+ position: [1, 1, 2],
5455
+ direction: [1, 1, 1],
5456
+ attenuation: [1, 0, 0],
5457
+ coneCos: [1, 0]
5458
+ };
5459
+ }
5460
+ function getSpotConeCos(spotLight) {
5461
+ const innerConeAngle = spotLight.innerConeAngle ?? 0;
5462
+ const outerConeAngle = spotLight.outerConeAngle ?? Math.PI / 4;
5463
+ return [Math.cos(innerConeAngle), Math.cos(outerConeAngle)];
5464
+ }
5465
+
5466
+ // src/modules/lighting/ibl/ibl.ts
5467
+ var iblWGSL = (
5468
+ /* wgsl */
5469
+ `#ifdef USE_IBL
5470
+ @group(2) @binding(auto) var pbr_diffuseEnvSampler: texture_cube<f32>;
5471
+ @group(2) @binding(auto) var pbr_diffuseEnvSamplerSampler: sampler;
5472
+ @group(2) @binding(auto) var pbr_specularEnvSampler: texture_cube<f32>;
5473
+ @group(2) @binding(auto) var pbr_specularEnvSamplerSampler: sampler;
5474
+ @group(2) @binding(auto) var pbr_brdfLUT: texture_2d<f32>;
5475
+ @group(2) @binding(auto) var pbr_brdfLUTSampler: sampler;
5476
+ #endif
5477
+ `
5478
+ );
5479
+ var iblGLSL = (
5480
+ /* glsl */
5481
+ `#ifdef USE_IBL
5482
+ uniform samplerCube pbr_diffuseEnvSampler;
5483
+ uniform samplerCube pbr_specularEnvSampler;
5484
+ uniform sampler2D pbr_brdfLUT;
5485
+ #endif
5486
+ `
5487
+ );
5488
+ var ibl = {
5489
+ name: "ibl",
5490
+ firstBindingSlot: 32,
5491
+ bindingLayout: [
5492
+ { name: "pbr_diffuseEnvSampler", group: 2 },
5493
+ { name: "pbr_specularEnvSampler", group: 2 },
5494
+ { name: "pbr_brdfLUT", group: 2 }
5495
+ ],
5496
+ source: iblWGSL,
5497
+ vs: iblGLSL,
5498
+ fs: iblGLSL
5499
+ };
4706
5500
 
4707
5501
  // src/modules/lighting/no-material/dirlight.ts
4708
5502
  var SOURCE_WGSL = (
@@ -4718,7 +5512,7 @@ struct DirlightInputs {
4718
5512
  normal: DirlightNormal,
4719
5513
  };
4720
5514
 
4721
- @binding(1) @group(0) var<uniform> dirlight : dirlightUniforms;
5515
+ @group(2) @binding(auto) var<uniform> dirlight : dirlightUniforms;
4722
5516
 
4723
5517
  // For vertex
4724
5518
  fn dirlight_setNormal(normal: vec3<f32>) -> DirlightNormal {
@@ -4763,6 +5557,8 @@ vec4 dirlight_filterColor(vec4 color) {
4763
5557
  props: {},
4764
5558
  uniforms: {},
4765
5559
  name: "dirlight",
5560
+ bindingLayout: [{ name: "dirlight", group: 2 }],
5561
+ firstBindingSlot: 16,
4766
5562
  dependencies: [],
4767
5563
  source: SOURCE_WGSL,
4768
5564
  vs: VS_GLSL,
@@ -4789,10 +5585,173 @@ vec4 dirlight_filterColor(vec4 color) {
4789
5585
  return uniforms;
4790
5586
  }
4791
5587
 
5588
+ // src/modules/lighting/lambert-material/lambert-shaders-wgsl.ts
5589
+ var LAMBERT_WGSL = (
5590
+ /* wgsl */
5591
+ `struct lambertMaterialUniforms {
5592
+ unlit: u32,
5593
+ ambient: f32,
5594
+ diffuse: f32,
5595
+ };
5596
+
5597
+ @group(3) @binding(auto) var<uniform> lambertMaterial : lambertMaterialUniforms;
5598
+
5599
+ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
5600
+ let lambertian: f32 = max(dot(light_direction, normal_worldspace), 0.0);
5601
+ return lambertian * lambertMaterial.diffuse * surfaceColor * color;
5602
+ }
5603
+
5604
+ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
5605
+ var lightColor: vec3<f32> = surfaceColor;
5606
+
5607
+ if (lambertMaterial.unlit != 0u) {
5608
+ return surfaceColor;
5609
+ }
5610
+
5611
+ if (lighting.enabled == 0) {
5612
+ return lightColor;
5613
+ }
5614
+
5615
+ lightColor = lambertMaterial.ambient * surfaceColor * lighting.ambientColor;
5616
+
5617
+ for (var i: i32 = 0; i < lighting.pointLightCount; i++) {
5618
+ let pointLight: PointLight = lighting_getPointLight(i);
5619
+ let light_position_worldspace: vec3<f32> = pointLight.position;
5620
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
5621
+ let light_attenuation = getPointLightAttenuation(
5622
+ pointLight,
5623
+ distance(light_position_worldspace, position_worldspace)
5624
+ );
5625
+ lightColor += lighting_getLightColor(
5626
+ surfaceColor,
5627
+ light_direction,
5628
+ normal_worldspace,
5629
+ pointLight.color / light_attenuation
5630
+ );
5631
+ }
5632
+
5633
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
5634
+ let spotLight: SpotLight = lighting_getSpotLight(i);
5635
+ let light_position_worldspace: vec3<f32> = spotLight.position;
5636
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
5637
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5638
+ lightColor += lighting_getLightColor(
5639
+ surfaceColor,
5640
+ light_direction,
5641
+ normal_worldspace,
5642
+ spotLight.color / light_attenuation
5643
+ );
5644
+ }
5645
+
5646
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
5647
+ let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
5648
+ lightColor += lighting_getLightColor(
5649
+ surfaceColor,
5650
+ -directionalLight.direction,
5651
+ normal_worldspace,
5652
+ directionalLight.color
5653
+ );
5654
+ }
5655
+
5656
+ return lightColor;
5657
+ }
5658
+ `
5659
+ );
5660
+
5661
+ // src/modules/lighting/lambert-material/lambert-shaders-glsl.ts
5662
+ var LAMBERT_VS = (
5663
+ /* glsl */
5664
+ `uniform lambertMaterialUniforms {
5665
+ uniform bool unlit;
5666
+ uniform float ambient;
5667
+ uniform float diffuse;
5668
+ } material;
5669
+ `
5670
+ );
5671
+ var LAMBERT_FS = (
5672
+ /* glsl */
5673
+ `uniform lambertMaterialUniforms {
5674
+ uniform bool unlit;
5675
+ uniform float ambient;
5676
+ uniform float diffuse;
5677
+ } material;
5678
+
5679
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 normal_worldspace, vec3 color) {
5680
+ float lambertian = max(dot(light_direction, normal_worldspace), 0.0);
5681
+ return lambertian * material.diffuse * surfaceColor * color;
5682
+ }
5683
+
5684
+ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
5685
+ vec3 lightColor = surfaceColor;
5686
+
5687
+ if (material.unlit) {
5688
+ return surfaceColor;
5689
+ }
5690
+
5691
+ if (lighting.enabled == 0) {
5692
+ return lightColor;
5693
+ }
5694
+
5695
+ lightColor = material.ambient * surfaceColor * lighting.ambientColor;
5696
+
5697
+ for (int i = 0; i < lighting.pointLightCount; i++) {
5698
+ PointLight pointLight = lighting_getPointLight(i);
5699
+ vec3 light_position_worldspace = pointLight.position;
5700
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
5701
+ float light_attenuation = getPointLightAttenuation(pointLight, distance(light_position_worldspace, position_worldspace));
5702
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, normal_worldspace, pointLight.color / light_attenuation);
5703
+ }
5704
+
5705
+ for (int i = 0; i < lighting.spotLightCount; i++) {
5706
+ SpotLight spotLight = lighting_getSpotLight(i);
5707
+ vec3 light_position_worldspace = spotLight.position;
5708
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
5709
+ float light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5710
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, normal_worldspace, spotLight.color / light_attenuation);
5711
+ }
5712
+
5713
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
5714
+ DirectionalLight directionalLight = lighting_getDirectionalLight(i);
5715
+ lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, normal_worldspace, directionalLight.color);
5716
+ }
5717
+
5718
+ return lightColor;
5719
+ }
5720
+ `
5721
+ );
5722
+
5723
+ // src/modules/lighting/lambert-material/lambert-material.ts
5724
+ var lambertMaterial = {
5725
+ name: "lambertMaterial",
5726
+ firstBindingSlot: 0,
5727
+ bindingLayout: [{ name: "lambertMaterial", group: 3 }],
5728
+ dependencies: [lighting],
5729
+ source: LAMBERT_WGSL,
5730
+ vs: LAMBERT_VS,
5731
+ fs: LAMBERT_FS,
5732
+ defines: {
5733
+ LIGHTING_FRAGMENT: true
5734
+ },
5735
+ uniformTypes: {
5736
+ unlit: "i32",
5737
+ ambient: "f32",
5738
+ diffuse: "f32"
5739
+ },
5740
+ defaultUniforms: {
5741
+ unlit: false,
5742
+ ambient: 0.35,
5743
+ diffuse: 0.6
5744
+ },
5745
+ getUniforms(props) {
5746
+ return { ...lambertMaterial.defaultUniforms, ...props };
5747
+ }
5748
+ };
5749
+
4792
5750
  // src/modules/lighting/phong-material/phong-shaders-glsl.ts
4793
5751
  var PHONG_VS = (
4794
5752
  /* glsl */
4795
5753
  `uniform phongMaterialUniforms {
5754
+ uniform bool unlit;
4796
5755
  uniform float ambient;
4797
5756
  uniform float diffuse;
4798
5757
  uniform float shininess;
@@ -4802,9 +5761,8 @@ vec4 dirlight_filterColor(vec4 color) {
4802
5761
  );
4803
5762
  var PHONG_FS = (
4804
5763
  /* glsl */
4805
- `#define MAX_LIGHTS 3
4806
-
4807
- uniform phongMaterialUniforms {
5764
+ `uniform phongMaterialUniforms {
5765
+ uniform bool unlit;
4808
5766
  uniform float ambient;
4809
5767
  uniform float diffuse;
4810
5768
  uniform float shininess;
@@ -4826,6 +5784,10 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 view_d
4826
5784
  vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
4827
5785
  vec3 lightColor = surfaceColor;
4828
5786
 
5787
+ if (material.unlit) {
5788
+ return surfaceColor;
5789
+ }
5790
+
4829
5791
  if (lighting.enabled == 0) {
4830
5792
  return lightColor;
4831
5793
  }
@@ -4841,8 +5803,15 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
4841
5803
  lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color / light_attenuation);
4842
5804
  }
4843
5805
 
4844
- int totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
4845
- for (int i = lighting.pointLightCount; i < totalLights; i++) {
5806
+ for (int i = 0; i < lighting.spotLightCount; i++) {
5807
+ SpotLight spotLight = lighting_getSpotLight(i);
5808
+ vec3 light_position_worldspace = spotLight.position;
5809
+ vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
5810
+ float light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5811
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, spotLight.color / light_attenuation);
5812
+ }
5813
+
5814
+ for (int i = 0; i < lighting.directionalLightCount; i++) {
4846
5815
  DirectionalLight directionalLight = lighting_getDirectionalLight(i);
4847
5816
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
4848
5817
  }
@@ -4856,13 +5825,14 @@ vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 positio
4856
5825
  var PHONG_WGSL = (
4857
5826
  /* wgsl */
4858
5827
  `struct phongMaterialUniforms {
5828
+ unlit: u32,
4859
5829
  ambient: f32,
4860
5830
  diffuse: f32,
4861
5831
  shininess: f32,
4862
5832
  specularColor: vec3<f32>,
4863
5833
  };
4864
5834
 
4865
- @binding(2) @group(0) var<uniform> phongMaterial : phongMaterialUniforms;
5835
+ @group(3) @binding(auto) var<uniform> phongMaterial : phongMaterialUniforms;
4866
5836
 
4867
5837
  fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, view_direction: vec3<f32>, normal_worldspace: vec3<f32>, color: vec3<f32>) -> vec3<f32> {
4868
5838
  let halfway_direction: vec3<f32> = normalize(light_direction + view_direction);
@@ -4879,6 +5849,10 @@ fn lighting_getLightColor(surfaceColor: vec3<f32>, light_direction: vec3<f32>, v
4879
5849
  fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, position_worldspace: vec3<f32>, normal_worldspace: vec3<f32>) -> vec3<f32> {
4880
5850
  var lightColor: vec3<f32> = surfaceColor;
4881
5851
 
5852
+ if (phongMaterial.unlit != 0u) {
5853
+ return surfaceColor;
5854
+ }
5855
+
4882
5856
  if (lighting.enabled == 0) {
4883
5857
  return lightColor;
4884
5858
  }
@@ -4903,8 +5877,21 @@ fn lighting_getLightColor2(surfaceColor: vec3<f32>, cameraPosition: vec3<f32>, p
4903
5877
  );
4904
5878
  }
4905
5879
 
4906
- let totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
4907
- for (var i: i32 = lighting.pointLightCount; i < totalLights; i++) {
5880
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
5881
+ let spotLight: SpotLight = lighting_getSpotLight(i);
5882
+ let light_position_worldspace: vec3<f32> = spotLight.position;
5883
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
5884
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5885
+ lightColor += lighting_getLightColor(
5886
+ surfaceColor,
5887
+ light_direction,
5888
+ view_direction,
5889
+ normal_worldspace,
5890
+ spotLight.color / light_attenuation
5891
+ );
5892
+ }
5893
+
5894
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
4908
5895
  let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
4909
5896
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
4910
5897
  }
@@ -4936,8 +5923,21 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4936
5923
  );
4937
5924
  }
4938
5925
 
4939
- let totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
4940
- for (var i: i32 = lighting.pointLightCount; i < totalLights; i++) {
5926
+ for (var i: i32 = 0; i < lighting.spotLightCount; i++) {
5927
+ let spotLight: SpotLight = lighting_getSpotLight(i);
5928
+ let light_position_worldspace: vec3<f32> = spotLight.position;
5929
+ let light_direction: vec3<f32> = normalize(light_position_worldspace - position_worldspace);
5930
+ let light_attenuation = getSpotLightAttenuation(spotLight, position_worldspace);
5931
+ lightColor += lighting_getLightColor(
5932
+ surfaceColor,
5933
+ light_direction,
5934
+ view_direction,
5935
+ normal_worldspace,
5936
+ spotLight.color / light_attenuation
5937
+ );
5938
+ }
5939
+
5940
+ for (var i: i32 = 0; i < lighting.directionalLightCount; i++) {
4941
5941
  let directionalLight: DirectionalLight = lighting_getDirectionalLight(i);
4942
5942
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
4943
5943
  }
@@ -4951,6 +5951,7 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4951
5951
  var gouraudMaterial = {
4952
5952
  props: {},
4953
5953
  name: "gouraudMaterial",
5954
+ bindingLayout: [{ name: "gouraudMaterial", group: 3 }],
4954
5955
  // Note these are switched between phong and gouraud
4955
5956
  vs: PHONG_FS.replace("phongMaterial", "gouraudMaterial"),
4956
5957
  fs: PHONG_VS.replace("phongMaterial", "gouraudMaterial"),
@@ -4960,12 +5961,14 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4960
5961
  },
4961
5962
  dependencies: [lighting],
4962
5963
  uniformTypes: {
5964
+ unlit: "i32",
4963
5965
  ambient: "f32",
4964
5966
  diffuse: "f32",
4965
5967
  shininess: "f32",
4966
5968
  specularColor: "vec3<f32>"
4967
5969
  },
4968
5970
  defaultUniforms: {
5971
+ unlit: false,
4969
5972
  ambient: 0.35,
4970
5973
  diffuse: 0.6,
4971
5974
  shininess: 32,
@@ -4983,6 +5986,8 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4983
5986
  // src/modules/lighting/phong-material/phong-material.ts
4984
5987
  var phongMaterial = {
4985
5988
  name: "phongMaterial",
5989
+ firstBindingSlot: 0,
5990
+ bindingLayout: [{ name: "phongMaterial", group: 3 }],
4986
5991
  dependencies: [lighting],
4987
5992
  // Note these are switched between phong and gouraud
4988
5993
  source: PHONG_WGSL,
@@ -4992,12 +5997,14 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
4992
5997
  LIGHTING_FRAGMENT: true
4993
5998
  },
4994
5999
  uniformTypes: {
6000
+ unlit: "i32",
4995
6001
  ambient: "f32",
4996
6002
  diffuse: "f32",
4997
6003
  shininess: "f32",
4998
6004
  specularColor: "vec3<f32>"
4999
6005
  },
5000
6006
  defaultUniforms: {
6007
+ unlit: false,
5001
6008
  ambient: 0.35,
5002
6009
  diffuse: 0.6,
5003
6010
  shininess: 32,
@@ -5094,10 +6101,12 @@ uniform pbrMaterialUniforms {
5094
6101
  float clearcoatFactor;
5095
6102
  float clearcoatRoughnessFactor;
5096
6103
  bool clearcoatMapEnabled;
6104
+ bool clearcoatRoughnessMapEnabled;
5097
6105
 
5098
6106
  vec3 sheenColorFactor;
5099
6107
  float sheenRoughnessFactor;
5100
6108
  bool sheenColorMapEnabled;
6109
+ bool sheenRoughnessMapEnabled;
5101
6110
 
5102
6111
  float iridescenceFactor;
5103
6112
  float iridescenceIor;
@@ -5147,26 +6156,33 @@ uniform sampler2D pbr_specularIntensitySampler;
5147
6156
  #ifdef HAS_TRANSMISSIONMAP
5148
6157
  uniform sampler2D pbr_transmissionSampler;
5149
6158
  #endif
6159
+ #ifdef HAS_THICKNESSMAP
6160
+ uniform sampler2D pbr_thicknessSampler;
6161
+ #endif
5150
6162
  #ifdef HAS_CLEARCOATMAP
5151
6163
  uniform sampler2D pbr_clearcoatSampler;
6164
+ #endif
6165
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
5152
6166
  uniform sampler2D pbr_clearcoatRoughnessSampler;
5153
6167
  #endif
6168
+ #ifdef HAS_CLEARCOATNORMALMAP
6169
+ uniform sampler2D pbr_clearcoatNormalSampler;
6170
+ #endif
5154
6171
  #ifdef HAS_SHEENCOLORMAP
5155
6172
  uniform sampler2D pbr_sheenColorSampler;
6173
+ #endif
6174
+ #ifdef HAS_SHEENROUGHNESSMAP
5156
6175
  uniform sampler2D pbr_sheenRoughnessSampler;
5157
6176
  #endif
5158
6177
  #ifdef HAS_IRIDESCENCEMAP
5159
6178
  uniform sampler2D pbr_iridescenceSampler;
5160
6179
  #endif
6180
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
6181
+ uniform sampler2D pbr_iridescenceThicknessSampler;
6182
+ #endif
5161
6183
  #ifdef HAS_ANISOTROPYMAP
5162
6184
  uniform sampler2D pbr_anisotropySampler;
5163
6185
  #endif
5164
- #ifdef USE_IBL
5165
- uniform samplerCube pbr_diffuseEnvSampler;
5166
- uniform samplerCube pbr_specularEnvSampler;
5167
- uniform sampler2D pbr_brdfLUT;
5168
- #endif
5169
-
5170
6186
  // Inputs from vertex shader
5171
6187
 
5172
6188
  in vec3 pbr_vPosition;
@@ -5203,6 +6219,8 @@ struct PBRInfo {
5203
6219
  const float M_PI = 3.141592653589793;
5204
6220
  const float c_MinRoughness = 0.04;
5205
6221
 
6222
+ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor);
6223
+
5206
6224
  vec4 SRGBtoLINEAR(vec4 srgbIn)
5207
6225
  {
5208
6226
  #ifdef MANUAL_SRGB
@@ -5218,11 +6236,9 @@ vec4 SRGBtoLINEAR(vec4 srgbIn)
5218
6236
  #endif //MANUAL_SRGB
5219
6237
  }
5220
6238
 
5221
- // Find the normal for this fragment, pulling either from a predefined normal map
5222
- // or from the interpolated mesh normal and tangent attributes.
5223
- vec3 getNormal()
6239
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
6240
+ mat3 getTBN()
5224
6241
  {
5225
- // Retrieve the tangent space matrix
5226
6242
  #ifndef HAS_TANGENTS
5227
6243
  vec3 pos_dx = dFdx(pbr_vPosition);
5228
6244
  vec3 pos_dy = dFdy(pbr_vPosition);
@@ -5243,9 +6259,21 @@ vec3 getNormal()
5243
6259
  mat3 tbn = pbr_vTBN;
5244
6260
  #endif
5245
6261
 
6262
+ return tbn;
6263
+ }
6264
+
6265
+ // Find the normal for this fragment, pulling either from a predefined normal map
6266
+ // or from the interpolated mesh normal and tangent attributes.
6267
+ vec3 getMappedNormal(sampler2D normalSampler, mat3 tbn, float normalScale)
6268
+ {
6269
+ vec3 n = texture(normalSampler, pbr_vUV).rgb;
6270
+ return normalize(tbn * ((2.0 * n - 1.0) * vec3(normalScale, normalScale, 1.0)));
6271
+ }
6272
+
6273
+ vec3 getNormal(mat3 tbn)
6274
+ {
5246
6275
  #ifdef HAS_NORMALMAP
5247
- vec3 n = texture(pbr_normalSampler, pbr_vUV).rgb;
5248
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
6276
+ vec3 n = getMappedNormal(pbr_normalSampler, tbn, pbrMaterial.normalScale);
5249
6277
  #else
5250
6278
  // The tbn matrix is linearly interpolated, so we need to re-normalize
5251
6279
  vec3 n = normalize(tbn[2].xyz);
@@ -5254,6 +6282,15 @@ vec3 getNormal()
5254
6282
  return n;
5255
6283
  }
5256
6284
 
6285
+ vec3 getClearcoatNormal(mat3 tbn, vec3 baseNormal)
6286
+ {
6287
+ #ifdef HAS_CLEARCOATNORMALMAP
6288
+ return getMappedNormal(pbr_clearcoatNormalSampler, tbn, 1.0);
6289
+ #else
6290
+ return baseNormal;
6291
+ #endif
6292
+ }
6293
+
5257
6294
  // Calculation of the lighting contribution from an optional Image Based Light source.
5258
6295
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
5259
6296
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -5329,7 +6366,170 @@ float microfacetDistribution(PBRInfo pbrInfo)
5329
6366
  return roughnessSq / (M_PI * f * f);
5330
6367
  }
5331
6368
 
5332
- void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
6369
+ float maxComponent(vec3 value)
6370
+ {
6371
+ return max(max(value.r, value.g), value.b);
6372
+ }
6373
+
6374
+ float getDielectricF0(float ior)
6375
+ {
6376
+ float clampedIor = max(ior, 1.0);
6377
+ float ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
6378
+ return ratio * ratio;
6379
+ }
6380
+
6381
+ vec2 normalizeDirection(vec2 direction)
6382
+ {
6383
+ float directionLength = length(direction);
6384
+ return directionLength > 0.0001 ? direction / directionLength : vec2(1.0, 0.0);
6385
+ }
6386
+
6387
+ vec2 rotateDirection(vec2 direction, float rotation)
6388
+ {
6389
+ float s = sin(rotation);
6390
+ float c = cos(rotation);
6391
+ return vec2(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
6392
+ }
6393
+
6394
+ vec3 getIridescenceTint(float iridescence, float thickness, float NdotV)
6395
+ {
6396
+ if (iridescence <= 0.0) {
6397
+ return vec3(1.0);
6398
+ }
6399
+
6400
+ float phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
6401
+ vec3 thinFilmTint =
6402
+ 0.5 + 0.5 * cos(vec3(phase, phase + 2.0943951, phase + 4.1887902));
6403
+ return mix(vec3(1.0), thinFilmTint, iridescence);
6404
+ }
6405
+
6406
+ vec3 getVolumeAttenuation(float thickness)
6407
+ {
6408
+ if (thickness <= 0.0) {
6409
+ return vec3(1.0);
6410
+ }
6411
+
6412
+ vec3 attenuationCoefficient =
6413
+ -log(max(pbrMaterial.attenuationColor, vec3(0.0001))) /
6414
+ max(pbrMaterial.attenuationDistance, 0.0001);
6415
+ return exp(-attenuationCoefficient * thickness);
6416
+ }
6417
+
6418
+ PBRInfo createClearcoatPBRInfo(PBRInfo basePBRInfo, vec3 clearcoatNormal, float clearcoatRoughness)
6419
+ {
6420
+ float perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
6421
+ float alphaRoughness = perceptualRoughness * perceptualRoughness;
6422
+ float NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
6423
+
6424
+ return PBRInfo(
6425
+ basePBRInfo.NdotL,
6426
+ NdotV,
6427
+ basePBRInfo.NdotH,
6428
+ basePBRInfo.LdotH,
6429
+ basePBRInfo.VdotH,
6430
+ perceptualRoughness,
6431
+ 0.0,
6432
+ vec3(0.04),
6433
+ vec3(1.0),
6434
+ alphaRoughness,
6435
+ vec3(0.0),
6436
+ vec3(0.04),
6437
+ clearcoatNormal,
6438
+ basePBRInfo.v
6439
+ );
6440
+ }
6441
+
6442
+ vec3 calculateClearcoatContribution(
6443
+ PBRInfo pbrInfo,
6444
+ vec3 lightColor,
6445
+ vec3 clearcoatNormal,
6446
+ float clearcoatFactor,
6447
+ float clearcoatRoughness
6448
+ ) {
6449
+ if (clearcoatFactor <= 0.0) {
6450
+ return vec3(0.0);
6451
+ }
6452
+
6453
+ PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
6454
+ return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
6455
+ }
6456
+
6457
+ #ifdef USE_IBL
6458
+ vec3 calculateClearcoatIBLContribution(
6459
+ PBRInfo pbrInfo,
6460
+ vec3 clearcoatNormal,
6461
+ vec3 reflection,
6462
+ float clearcoatFactor,
6463
+ float clearcoatRoughness
6464
+ ) {
6465
+ if (clearcoatFactor <= 0.0) {
6466
+ return vec3(0.0);
6467
+ }
6468
+
6469
+ PBRInfo clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
6470
+ return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
6471
+ }
6472
+ #endif
6473
+
6474
+ vec3 calculateSheenContribution(
6475
+ PBRInfo pbrInfo,
6476
+ vec3 lightColor,
6477
+ vec3 sheenColor,
6478
+ float sheenRoughness
6479
+ ) {
6480
+ if (maxComponent(sheenColor) <= 0.0) {
6481
+ return vec3(0.0);
6482
+ }
6483
+
6484
+ float sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
6485
+ float sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
6486
+ return pbrInfo.NdotL *
6487
+ lightColor *
6488
+ sheenColor *
6489
+ (0.25 + 0.75 * sheenFresnel) *
6490
+ sheenVisibility *
6491
+ (1.0 - pbrInfo.metalness);
6492
+ }
6493
+
6494
+ float calculateAnisotropyBoost(
6495
+ PBRInfo pbrInfo,
6496
+ vec3 anisotropyTangent,
6497
+ float anisotropyStrength
6498
+ ) {
6499
+ if (anisotropyStrength <= 0.0) {
6500
+ return 1.0;
6501
+ }
6502
+
6503
+ vec3 anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
6504
+ float bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
6505
+ return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
6506
+ }
6507
+
6508
+ vec3 calculateMaterialLightColor(
6509
+ PBRInfo pbrInfo,
6510
+ vec3 lightColor,
6511
+ vec3 clearcoatNormal,
6512
+ float clearcoatFactor,
6513
+ float clearcoatRoughness,
6514
+ vec3 sheenColor,
6515
+ float sheenRoughness,
6516
+ vec3 anisotropyTangent,
6517
+ float anisotropyStrength
6518
+ ) {
6519
+ float anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
6520
+ vec3 color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
6521
+ color += calculateClearcoatContribution(
6522
+ pbrInfo,
6523
+ lightColor,
6524
+ clearcoatNormal,
6525
+ clearcoatFactor,
6526
+ clearcoatRoughness
6527
+ );
6528
+ color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
6529
+ return color;
6530
+ }
6531
+
6532
+ void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) {
5333
6533
  pbrInfo.NdotL = 1.0;
5334
6534
  pbrInfo.NdotH = 0.0;
5335
6535
  pbrInfo.LdotH = 0.0;
@@ -5353,6 +6553,11 @@ void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) {
5353
6553
  PBRInfo_setDirectionalLight(pbrInfo, light_direction);
5354
6554
  }
5355
6555
 
6556
+ void PBRInfo_setSpotLight(inout PBRInfo pbrInfo, SpotLight spotLight) {
6557
+ vec3 light_direction = normalize(spotLight.position - pbr_vPosition);
6558
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
6559
+ }
6560
+
5356
6561
  vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
5357
6562
  // Calculate the shading terms for the microfacet specular shading model
5358
6563
  vec3 F = specularReflection(pbrInfo);
@@ -5383,6 +6588,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
5383
6588
 
5384
6589
  vec3 color = vec3(0, 0, 0);
5385
6590
 
6591
+ float transmission = 0.0;
6592
+
5386
6593
  if(pbrMaterial.unlit){
5387
6594
  color.rgb = baseColor.rgb;
5388
6595
  }
@@ -5401,14 +6608,252 @@ vec4 pbr_filterColor(vec4 colorUnused)
5401
6608
  #endif
5402
6609
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
5403
6610
  metallic = clamp(metallic, 0.0, 1.0);
6611
+ mat3 tbn = getTBN();
6612
+ vec3 n = getNormal(tbn); // normal at surface point
6613
+ vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
6614
+ float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
6615
+ #ifdef USE_MATERIAL_EXTENSIONS
6616
+ bool useExtendedPBR =
6617
+ pbrMaterial.specularColorMapEnabled ||
6618
+ pbrMaterial.specularIntensityMapEnabled ||
6619
+ abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
6620
+ maxComponent(abs(pbrMaterial.specularColorFactor - vec3(1.0))) > 0.0001 ||
6621
+ abs(pbrMaterial.ior - 1.5) > 0.0001 ||
6622
+ pbrMaterial.transmissionMapEnabled ||
6623
+ pbrMaterial.transmissionFactor > 0.0001 ||
6624
+ pbrMaterial.clearcoatMapEnabled ||
6625
+ pbrMaterial.clearcoatRoughnessMapEnabled ||
6626
+ pbrMaterial.clearcoatFactor > 0.0001 ||
6627
+ pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
6628
+ pbrMaterial.sheenColorMapEnabled ||
6629
+ pbrMaterial.sheenRoughnessMapEnabled ||
6630
+ maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
6631
+ pbrMaterial.sheenRoughnessFactor > 0.0001 ||
6632
+ pbrMaterial.iridescenceMapEnabled ||
6633
+ pbrMaterial.iridescenceFactor > 0.0001 ||
6634
+ abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
6635
+ abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
6636
+ abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
6637
+ pbrMaterial.anisotropyMapEnabled ||
6638
+ pbrMaterial.anisotropyStrength > 0.0001 ||
6639
+ abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
6640
+ length(pbrMaterial.anisotropyDirection - vec2(1.0, 0.0)) > 0.0001;
6641
+ #else
6642
+ bool useExtendedPBR = false;
6643
+ #endif
6644
+
6645
+ if (!useExtendedPBR) {
6646
+ // Keep the baseline metallic-roughness implementation byte-for-byte equivalent in behavior.
6647
+ float alphaRoughness = perceptualRoughness * perceptualRoughness;
6648
+
6649
+ vec3 f0 = vec3(0.04);
6650
+ vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
6651
+ diffuseColor *= 1.0 - metallic;
6652
+ vec3 specularColor = mix(f0, baseColor.rgb, metallic);
6653
+
6654
+ float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
6655
+ float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
6656
+ vec3 specularEnvironmentR0 = specularColor.rgb;
6657
+ vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
6658
+ vec3 reflection = -normalize(reflect(v, n));
6659
+
6660
+ PBRInfo pbrInfo = PBRInfo(
6661
+ 0.0, // NdotL
6662
+ NdotV,
6663
+ 0.0, // NdotH
6664
+ 0.0, // LdotH
6665
+ 0.0, // VdotH
6666
+ perceptualRoughness,
6667
+ metallic,
6668
+ specularEnvironmentR0,
6669
+ specularEnvironmentR90,
6670
+ alphaRoughness,
6671
+ diffuseColor,
6672
+ specularColor,
6673
+ n,
6674
+ v
6675
+ );
6676
+
6677
+ #ifdef USE_LIGHTS
6678
+ PBRInfo_setAmbientLight(pbrInfo);
6679
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor);
6680
+
6681
+ for(int i = 0; i < lighting.directionalLightCount; i++) {
6682
+ if (i < lighting.directionalLightCount) {
6683
+ PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
6684
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
6685
+ }
6686
+ }
6687
+
6688
+ for(int i = 0; i < lighting.pointLightCount; i++) {
6689
+ if (i < lighting.pointLightCount) {
6690
+ PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
6691
+ float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
6692
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
6693
+ }
6694
+ }
6695
+
6696
+ for(int i = 0; i < lighting.spotLightCount; i++) {
6697
+ if (i < lighting.spotLightCount) {
6698
+ PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
6699
+ float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
6700
+ color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
6701
+ }
6702
+ }
6703
+ #endif
6704
+
6705
+ #ifdef USE_IBL
6706
+ if (pbrMaterial.IBLenabled) {
6707
+ color += getIBLContribution(pbrInfo, n, reflection);
6708
+ }
6709
+ #endif
6710
+
6711
+ #ifdef HAS_OCCLUSIONMAP
6712
+ if (pbrMaterial.occlusionMapEnabled) {
6713
+ float ao = texture(pbr_occlusionSampler, pbr_vUV).r;
6714
+ color = mix(color, color * ao, pbrMaterial.occlusionStrength);
6715
+ }
6716
+ #endif
6717
+
6718
+ vec3 emissive = pbrMaterial.emissiveFactor;
6719
+ #ifdef HAS_EMISSIVEMAP
6720
+ if (pbrMaterial.emissiveMapEnabled) {
6721
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
6722
+ }
6723
+ #endif
6724
+ color += emissive * pbrMaterial.emissiveStrength;
6725
+
6726
+ #ifdef PBR_DEBUG
6727
+ color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
6728
+ color = mix(color, vec3(metallic), pbrMaterial.scaleDiffBaseMR.z);
6729
+ color = mix(color, vec3(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
6730
+ #endif
6731
+
6732
+ return vec4(pow(color, vec3(1.0 / 2.2)), baseColor.a);
6733
+ }
6734
+
6735
+ float specularIntensity = pbrMaterial.specularIntensityFactor;
6736
+ #ifdef HAS_SPECULARINTENSITYMAP
6737
+ if (pbrMaterial.specularIntensityMapEnabled) {
6738
+ specularIntensity *= texture(pbr_specularIntensitySampler, pbr_vUV).a;
6739
+ }
6740
+ #endif
6741
+
6742
+ vec3 specularFactor = pbrMaterial.specularColorFactor;
6743
+ #ifdef HAS_SPECULARCOLORMAP
6744
+ if (pbrMaterial.specularColorMapEnabled) {
6745
+ specularFactor *= SRGBtoLINEAR(texture(pbr_specularColorSampler, pbr_vUV)).rgb;
6746
+ }
6747
+ #endif
6748
+
6749
+ transmission = pbrMaterial.transmissionFactor;
6750
+ #ifdef HAS_TRANSMISSIONMAP
6751
+ if (pbrMaterial.transmissionMapEnabled) {
6752
+ transmission *= texture(pbr_transmissionSampler, pbr_vUV).r;
6753
+ }
6754
+ #endif
6755
+ transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
6756
+ float thickness = max(pbrMaterial.thicknessFactor, 0.0);
6757
+ #ifdef HAS_THICKNESSMAP
6758
+ thickness *= texture(pbr_thicknessSampler, pbr_vUV).g;
6759
+ #endif
6760
+
6761
+ float clearcoatFactor = pbrMaterial.clearcoatFactor;
6762
+ float clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
6763
+ #ifdef HAS_CLEARCOATMAP
6764
+ if (pbrMaterial.clearcoatMapEnabled) {
6765
+ clearcoatFactor *= texture(pbr_clearcoatSampler, pbr_vUV).r;
6766
+ }
6767
+ #endif
6768
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
6769
+ if (pbrMaterial.clearcoatRoughnessMapEnabled) {
6770
+ clearcoatRoughness *= texture(pbr_clearcoatRoughnessSampler, pbr_vUV).g;
6771
+ }
6772
+ #endif
6773
+ clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
6774
+ clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
6775
+ vec3 clearcoatNormal = getClearcoatNormal(tbn, n);
6776
+
6777
+ vec3 sheenColor = pbrMaterial.sheenColorFactor;
6778
+ float sheenRoughness = pbrMaterial.sheenRoughnessFactor;
6779
+ #ifdef HAS_SHEENCOLORMAP
6780
+ if (pbrMaterial.sheenColorMapEnabled) {
6781
+ sheenColor *= SRGBtoLINEAR(texture(pbr_sheenColorSampler, pbr_vUV)).rgb;
6782
+ }
6783
+ #endif
6784
+ #ifdef HAS_SHEENROUGHNESSMAP
6785
+ if (pbrMaterial.sheenRoughnessMapEnabled) {
6786
+ sheenRoughness *= texture(pbr_sheenRoughnessSampler, pbr_vUV).a;
6787
+ }
6788
+ #endif
6789
+ sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
6790
+
6791
+ float iridescence = pbrMaterial.iridescenceFactor;
6792
+ #ifdef HAS_IRIDESCENCEMAP
6793
+ if (pbrMaterial.iridescenceMapEnabled) {
6794
+ iridescence *= texture(pbr_iridescenceSampler, pbr_vUV).r;
6795
+ }
6796
+ #endif
6797
+ iridescence = clamp(iridescence, 0.0, 1.0);
6798
+ float iridescenceThickness = mix(
6799
+ pbrMaterial.iridescenceThicknessRange.x,
6800
+ pbrMaterial.iridescenceThicknessRange.y,
6801
+ 0.5
6802
+ );
6803
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
6804
+ iridescenceThickness = mix(
6805
+ pbrMaterial.iridescenceThicknessRange.x,
6806
+ pbrMaterial.iridescenceThicknessRange.y,
6807
+ texture(pbr_iridescenceThicknessSampler, pbr_vUV).g
6808
+ );
6809
+ #endif
6810
+
6811
+ float anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
6812
+ vec2 anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
6813
+ #ifdef HAS_ANISOTROPYMAP
6814
+ if (pbrMaterial.anisotropyMapEnabled) {
6815
+ vec3 anisotropySample = texture(pbr_anisotropySampler, pbr_vUV).rgb;
6816
+ anisotropyStrength *= anisotropySample.b;
6817
+ vec2 mappedDirection = anisotropySample.rg * 2.0 - 1.0;
6818
+ if (length(mappedDirection) > 0.0001) {
6819
+ anisotropyDirection = normalize(mappedDirection);
6820
+ }
6821
+ }
6822
+ #endif
6823
+ anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
6824
+ vec3 anisotropyTangent = normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
6825
+ if (length(anisotropyTangent) < 0.0001) {
6826
+ anisotropyTangent = normalize(tbn[0]);
6827
+ }
6828
+ float anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
6829
+ perceptualRoughness = mix(
6830
+ perceptualRoughness,
6831
+ clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
6832
+ anisotropyStrength
6833
+ );
6834
+
5404
6835
  // Roughness is authored as perceptual roughness; as is convention,
5405
6836
  // convert to material roughness by squaring the perceptual roughness [2].
5406
6837
  float alphaRoughness = perceptualRoughness * perceptualRoughness;
5407
6838
 
5408
- vec3 f0 = vec3(0.04);
5409
- vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
5410
- diffuseColor *= 1.0 - metallic;
5411
- vec3 specularColor = mix(f0, baseColor.rgb, metallic);
6839
+ float dielectricF0 = getDielectricF0(pbrMaterial.ior);
6840
+ vec3 dielectricSpecularF0 = min(
6841
+ vec3(dielectricF0) * specularFactor * specularIntensity,
6842
+ vec3(1.0)
6843
+ );
6844
+ vec3 iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
6845
+ dielectricSpecularF0 = mix(
6846
+ dielectricSpecularF0,
6847
+ dielectricSpecularF0 * iridescenceTint,
6848
+ iridescence
6849
+ );
6850
+ vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - dielectricSpecularF0);
6851
+ diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
6852
+ vec3 specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
6853
+
6854
+ float baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
6855
+ diffuseColor *= baseLayerEnergy;
6856
+ specularColor *= baseLayerEnergy;
5412
6857
 
5413
6858
  // Compute reflectance.
5414
6859
  float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -5420,11 +6865,6 @@ vec4 pbr_filterColor(vec4 colorUnused)
5420
6865
  float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
5421
6866
  vec3 specularEnvironmentR0 = specularColor.rgb;
5422
6867
  vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
5423
-
5424
- vec3 n = getNormal(); // normal at surface point
5425
- vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
5426
-
5427
- float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
5428
6868
  vec3 reflection = -normalize(reflect(v, n));
5429
6869
 
5430
6870
  PBRInfo pbrInfo = PBRInfo(
@@ -5448,13 +6888,33 @@ vec4 pbr_filterColor(vec4 colorUnused)
5448
6888
  #ifdef USE_LIGHTS
5449
6889
  // Apply ambient light
5450
6890
  PBRInfo_setAmbientLight(pbrInfo);
5451
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
6891
+ color += calculateMaterialLightColor(
6892
+ pbrInfo,
6893
+ lighting.ambientColor,
6894
+ clearcoatNormal,
6895
+ clearcoatFactor,
6896
+ clearcoatRoughness,
6897
+ sheenColor,
6898
+ sheenRoughness,
6899
+ anisotropyTangent,
6900
+ anisotropyStrength
6901
+ );
5452
6902
 
5453
6903
  // Apply directional light
5454
6904
  for(int i = 0; i < lighting.directionalLightCount; i++) {
5455
6905
  if (i < lighting.directionalLightCount) {
5456
6906
  PBRInfo_setDirectionalLight(pbrInfo, lighting_getDirectionalLight(i).direction);
5457
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
6907
+ color += calculateMaterialLightColor(
6908
+ pbrInfo,
6909
+ lighting_getDirectionalLight(i).color,
6910
+ clearcoatNormal,
6911
+ clearcoatFactor,
6912
+ clearcoatRoughness,
6913
+ sheenColor,
6914
+ sheenRoughness,
6915
+ anisotropyTangent,
6916
+ anisotropyStrength
6917
+ );
5458
6918
  }
5459
6919
  }
5460
6920
 
@@ -5463,7 +6923,35 @@ vec4 pbr_filterColor(vec4 colorUnused)
5463
6923
  if (i < lighting.pointLightCount) {
5464
6924
  PBRInfo_setPointLight(pbrInfo, lighting_getPointLight(i));
5465
6925
  float attenuation = getPointLightAttenuation(lighting_getPointLight(i), distance(lighting_getPointLight(i).position, pbr_vPosition));
5466
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
6926
+ color += calculateMaterialLightColor(
6927
+ pbrInfo,
6928
+ lighting_getPointLight(i).color / attenuation,
6929
+ clearcoatNormal,
6930
+ clearcoatFactor,
6931
+ clearcoatRoughness,
6932
+ sheenColor,
6933
+ sheenRoughness,
6934
+ anisotropyTangent,
6935
+ anisotropyStrength
6936
+ );
6937
+ }
6938
+ }
6939
+
6940
+ for(int i = 0; i < lighting.spotLightCount; i++) {
6941
+ if (i < lighting.spotLightCount) {
6942
+ PBRInfo_setSpotLight(pbrInfo, lighting_getSpotLight(i));
6943
+ float attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), pbr_vPosition);
6944
+ color += calculateMaterialLightColor(
6945
+ pbrInfo,
6946
+ lighting_getSpotLight(i).color / attenuation,
6947
+ clearcoatNormal,
6948
+ clearcoatFactor,
6949
+ clearcoatRoughness,
6950
+ sheenColor,
6951
+ sheenRoughness,
6952
+ anisotropyTangent,
6953
+ anisotropyStrength
6954
+ );
5467
6955
  }
5468
6956
  }
5469
6957
  #endif
@@ -5471,7 +6959,16 @@ vec4 pbr_filterColor(vec4 colorUnused)
5471
6959
  // Calculate lighting contribution from image based lighting source (IBL)
5472
6960
  #ifdef USE_IBL
5473
6961
  if (pbrMaterial.IBLenabled) {
5474
- color += getIBLContribution(pbrInfo, n, reflection);
6962
+ color += getIBLContribution(pbrInfo, n, reflection) *
6963
+ calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
6964
+ color += calculateClearcoatIBLContribution(
6965
+ pbrInfo,
6966
+ clearcoatNormal,
6967
+ -normalize(reflect(v, clearcoatNormal)),
6968
+ clearcoatFactor,
6969
+ clearcoatRoughness
6970
+ );
6971
+ color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
5475
6972
  }
5476
6973
  #endif
5477
6974
 
@@ -5483,12 +6980,17 @@ vec4 pbr_filterColor(vec4 colorUnused)
5483
6980
  }
5484
6981
  #endif
5485
6982
 
6983
+ vec3 emissive = pbrMaterial.emissiveFactor;
5486
6984
  #ifdef HAS_EMISSIVEMAP
5487
6985
  if (pbrMaterial.emissiveMapEnabled) {
5488
- vec3 emissive = SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb * pbrMaterial.emissiveFactor;
5489
- color += emissive;
6986
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
5490
6987
  }
5491
6988
  #endif
6989
+ color += emissive * pbrMaterial.emissiveStrength;
6990
+
6991
+ if (transmission > 0.0) {
6992
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
6993
+ }
5492
6994
 
5493
6995
  // This section uses mix to override final color for reference app visualization
5494
6996
  // of various parameters in the lighting equation.
@@ -5508,7 +7010,8 @@ vec4 pbr_filterColor(vec4 colorUnused)
5508
7010
 
5509
7011
  }
5510
7012
 
5511
- return vec4(pow(color,vec3(1.0/2.2)), baseColor.a);
7013
+ float alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
7014
+ return vec4(pow(color,vec3(1.0/2.2)), alpha);
5512
7015
  }
5513
7016
  `
5514
7017
  );
@@ -5574,6 +7077,42 @@ struct pbrMaterialUniforms {
5574
7077
 
5575
7078
  alphaCutoffEnabled: i32,
5576
7079
  alphaCutoff: f32, // #ifdef ALPHA_CUTOFF
7080
+
7081
+ specularColorFactor: vec3f,
7082
+ specularIntensityFactor: f32,
7083
+ specularColorMapEnabled: i32,
7084
+ specularIntensityMapEnabled: i32,
7085
+
7086
+ ior: f32,
7087
+
7088
+ transmissionFactor: f32,
7089
+ transmissionMapEnabled: i32,
7090
+
7091
+ thicknessFactor: f32,
7092
+ attenuationDistance: f32,
7093
+ attenuationColor: vec3f,
7094
+
7095
+ clearcoatFactor: f32,
7096
+ clearcoatRoughnessFactor: f32,
7097
+ clearcoatMapEnabled: i32,
7098
+ clearcoatRoughnessMapEnabled: i32,
7099
+
7100
+ sheenColorFactor: vec3f,
7101
+ sheenRoughnessFactor: f32,
7102
+ sheenColorMapEnabled: i32,
7103
+ sheenRoughnessMapEnabled: i32,
7104
+
7105
+ iridescenceFactor: f32,
7106
+ iridescenceIor: f32,
7107
+ iridescenceThicknessRange: vec2f,
7108
+ iridescenceMapEnabled: i32,
7109
+
7110
+ anisotropyStrength: f32,
7111
+ anisotropyRotation: f32,
7112
+ anisotropyDirection: vec2f,
7113
+ anisotropyMapEnabled: i32,
7114
+
7115
+ emissiveStrength: f32,
5577
7116
 
5578
7117
  // IBL
5579
7118
  IBLenabled: i32,
@@ -5586,38 +7125,77 @@ struct pbrMaterialUniforms {
5586
7125
  // #endif
5587
7126
  }
5588
7127
 
5589
- @binding(2) @group(0) var<uniform> pbrMaterial : pbrMaterialUniforms;
7128
+ @group(3) @binding(auto) var<uniform> pbrMaterial : pbrMaterialUniforms;
5590
7129
 
5591
7130
  // Samplers
5592
7131
  #ifdef HAS_BASECOLORMAP
5593
- @binding(3) @group(0) var pbr_baseColorSampler: texture_2d<f32>;
5594
- @binding(4) @group(0) var pbr_baseColorSamplerSampler: sampler;
7132
+ @group(3) @binding(auto) var pbr_baseColorSampler: texture_2d<f32>;
7133
+ @group(3) @binding(auto) var pbr_baseColorSamplerSampler: sampler;
5595
7134
  #endif
5596
7135
  #ifdef HAS_NORMALMAP
5597
- @binding(5) @group(0) var pbr_normalSampler: texture_2d<f32>;
5598
- @binding(6) @group(0) var pbr_normalSamplerSampler: sampler;
7136
+ @group(3) @binding(auto) var pbr_normalSampler: texture_2d<f32>;
7137
+ @group(3) @binding(auto) var pbr_normalSamplerSampler: sampler;
5599
7138
  #endif
5600
7139
  #ifdef HAS_EMISSIVEMAP
5601
- @binding(7) @group(0) var pbr_emissiveSampler: texture_2d<f32>;
5602
- @binding(8) @group(0) var pbr_emissiveSamplerSampler: sampler;
7140
+ @group(3) @binding(auto) var pbr_emissiveSampler: texture_2d<f32>;
7141
+ @group(3) @binding(auto) var pbr_emissiveSamplerSampler: sampler;
5603
7142
  #endif
5604
7143
  #ifdef HAS_METALROUGHNESSMAP
5605
- @binding(9) @group(0) var pbr_metallicRoughnessSampler: texture_2d<f32>;
5606
- @binding(10) @group(0) var pbr_metallicRoughnessSamplerSampler: sampler;
7144
+ @group(3) @binding(auto) var pbr_metallicRoughnessSampler: texture_2d<f32>;
7145
+ @group(3) @binding(auto) var pbr_metallicRoughnessSamplerSampler: sampler;
5607
7146
  #endif
5608
7147
  #ifdef HAS_OCCLUSIONMAP
5609
- @binding(11) @group(0) var pbr_occlusionSampler: texture_2d<f32>;
5610
- @binding(12) @group(0) var pbr_occlusionSamplerSampler: sampler;
7148
+ @group(3) @binding(auto) var pbr_occlusionSampler: texture_2d<f32>;
7149
+ @group(3) @binding(auto) var pbr_occlusionSamplerSampler: sampler;
5611
7150
  #endif
5612
- #ifdef USE_IBL
5613
- @binding(13) @group(0) var pbr_diffuseEnvSampler: texture_cube<f32>;
5614
- @binding(14) @group(0) var pbr_diffuseEnvSamplerSampler: sampler;
5615
- @binding(15) @group(0) var pbr_specularEnvSampler: texture_cube<f32>;
5616
- @binding(16) @group(0) var pbr_specularEnvSamplerSampler: sampler;
5617
- @binding(17) @group(0) var pbr_BrdfLUT: texture_2d<f32>;
5618
- @binding(18) @group(0) var pbr_BrdfLUTSampler: sampler;
7151
+ #ifdef HAS_SPECULARCOLORMAP
7152
+ @group(3) @binding(auto) var pbr_specularColorSampler: texture_2d<f32>;
7153
+ @group(3) @binding(auto) var pbr_specularColorSamplerSampler: sampler;
7154
+ #endif
7155
+ #ifdef HAS_SPECULARINTENSITYMAP
7156
+ @group(3) @binding(auto) var pbr_specularIntensitySampler: texture_2d<f32>;
7157
+ @group(3) @binding(auto) var pbr_specularIntensitySamplerSampler: sampler;
7158
+ #endif
7159
+ #ifdef HAS_TRANSMISSIONMAP
7160
+ @group(3) @binding(auto) var pbr_transmissionSampler: texture_2d<f32>;
7161
+ @group(3) @binding(auto) var pbr_transmissionSamplerSampler: sampler;
7162
+ #endif
7163
+ #ifdef HAS_THICKNESSMAP
7164
+ @group(3) @binding(auto) var pbr_thicknessSampler: texture_2d<f32>;
7165
+ @group(3) @binding(auto) var pbr_thicknessSamplerSampler: sampler;
7166
+ #endif
7167
+ #ifdef HAS_CLEARCOATMAP
7168
+ @group(3) @binding(auto) var pbr_clearcoatSampler: texture_2d<f32>;
7169
+ @group(3) @binding(auto) var pbr_clearcoatSamplerSampler: sampler;
7170
+ #endif
7171
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
7172
+ @group(3) @binding(auto) var pbr_clearcoatRoughnessSampler: texture_2d<f32>;
7173
+ @group(3) @binding(auto) var pbr_clearcoatRoughnessSamplerSampler: sampler;
7174
+ #endif
7175
+ #ifdef HAS_CLEARCOATNORMALMAP
7176
+ @group(3) @binding(auto) var pbr_clearcoatNormalSampler: texture_2d<f32>;
7177
+ @group(3) @binding(auto) var pbr_clearcoatNormalSamplerSampler: sampler;
7178
+ #endif
7179
+ #ifdef HAS_SHEENCOLORMAP
7180
+ @group(3) @binding(auto) var pbr_sheenColorSampler: texture_2d<f32>;
7181
+ @group(3) @binding(auto) var pbr_sheenColorSamplerSampler: sampler;
7182
+ #endif
7183
+ #ifdef HAS_SHEENROUGHNESSMAP
7184
+ @group(3) @binding(auto) var pbr_sheenRoughnessSampler: texture_2d<f32>;
7185
+ @group(3) @binding(auto) var pbr_sheenRoughnessSamplerSampler: sampler;
7186
+ #endif
7187
+ #ifdef HAS_IRIDESCENCEMAP
7188
+ @group(3) @binding(auto) var pbr_iridescenceSampler: texture_2d<f32>;
7189
+ @group(3) @binding(auto) var pbr_iridescenceSamplerSampler: sampler;
7190
+ #endif
7191
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
7192
+ @group(3) @binding(auto) var pbr_iridescenceThicknessSampler: texture_2d<f32>;
7193
+ @group(3) @binding(auto) var pbr_iridescenceThicknessSamplerSampler: sampler;
7194
+ #endif
7195
+ #ifdef HAS_ANISOTROPYMAP
7196
+ @group(3) @binding(auto) var pbr_anisotropySampler: texture_2d<f32>;
7197
+ @group(3) @binding(auto) var pbr_anisotropySamplerSampler: sampler;
5619
7198
  #endif
5620
-
5621
7199
  // Encapsulate the various inputs used by the various functions in the shading equation
5622
7200
  // We store values in this struct to simplify the integration of alternative implementations
5623
7201
  // of the shading terms, outlined in the Readme.MD Appendix.
@@ -5658,11 +7236,9 @@ fn SRGBtoLINEAR(srgbIn: vec4f ) -> vec4f
5658
7236
  return vec4f(linOut, srgbIn.w);
5659
7237
  }
5660
7238
 
5661
- // Find the normal for this fragment, pulling either from a predefined normal map
5662
- // or from the interpolated mesh normal and tangent attributes.
5663
- fn getNormal() -> vec3f
7239
+ // Build the tangent basis from interpolated attributes or screen-space derivatives.
7240
+ fn getTBN() -> mat3x3f
5664
7241
  {
5665
- // Retrieve the tangent space matrix
5666
7242
  let pos_dx: vec3f = dpdx(fragmentInputs.pbr_vPosition);
5667
7243
  let pos_dy: vec3f = dpdy(fragmentInputs.pbr_vPosition);
5668
7244
  let tex_dx: vec3f = dpdx(vec3f(fragmentInputs.pbr_vUV, 0.0));
@@ -5680,16 +7256,52 @@ fn getNormal() -> vec3f
5680
7256
  tbn = fragmentInputs.pbr_vTBN;
5681
7257
  #endif
5682
7258
 
7259
+ return tbn;
7260
+ }
7261
+
7262
+ // Find the normal for this fragment, pulling either from a predefined normal map
7263
+ // or from the interpolated mesh normal and tangent attributes.
7264
+ fn getMappedNormal(
7265
+ normalSampler: texture_2d<f32>,
7266
+ normalSamplerBinding: sampler,
7267
+ tbn: mat3x3f,
7268
+ normalScale: f32
7269
+ ) -> vec3f
7270
+ {
7271
+ let n = textureSample(normalSampler, normalSamplerBinding, fragmentInputs.pbr_vUV).rgb;
7272
+ return normalize(tbn * ((2.0 * n - 1.0) * vec3f(normalScale, normalScale, 1.0)));
7273
+ }
7274
+
7275
+ fn getNormal(tbn: mat3x3f) -> vec3f
7276
+ {
5683
7277
  // The tbn matrix is linearly interpolated, so we need to re-normalize
5684
7278
  var n: vec3f = normalize(tbn[2].xyz);
5685
7279
  #ifdef HAS_NORMALMAP
5686
- n = textureSample(pbr_normalSampler, pbr_normalSamplerSampler, fragmentInputs.pbr_vUV).rgb;
5687
- n = normalize(tbn * ((2.0 * n - 1.0) * vec3f(pbrMaterial.normalScale, pbrMaterial.normalScale, 1.0)));
7280
+ n = getMappedNormal(
7281
+ pbr_normalSampler,
7282
+ pbr_normalSamplerSampler,
7283
+ tbn,
7284
+ pbrMaterial.normalScale
7285
+ );
5688
7286
  #endif
5689
7287
 
5690
7288
  return n;
5691
7289
  }
5692
7290
 
7291
+ fn getClearcoatNormal(tbn: mat3x3f, baseNormal: vec3f) -> vec3f
7292
+ {
7293
+ #ifdef HAS_CLEARCOATNORMALMAP
7294
+ return getMappedNormal(
7295
+ pbr_clearcoatNormalSampler,
7296
+ pbr_clearcoatNormalSamplerSampler,
7297
+ tbn,
7298
+ 1.0
7299
+ );
7300
+ #else
7301
+ return baseNormal;
7302
+ #endif
7303
+ }
7304
+
5693
7305
  // Calculation of the lighting contribution from an optional Image Based Light source.
5694
7306
  // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
5695
7307
  // See our README.md on Environment Maps [3] for additional discussion.
@@ -5700,17 +7312,25 @@ fn getIBLContribution(pbrInfo: PBRInfo, n: vec3f, reflection: vec3f) -> vec3f
5700
7312
  let lod: f32 = pbrInfo.perceptualRoughness * mipCount;
5701
7313
  // retrieve a scale and bias to F0. See [1], Figure 3
5702
7314
  let brdf = SRGBtoLINEAR(
5703
- textureSample(
5704
- pbr_BrdfLUT,
5705
- pbr_BrdfLUTSampler,
5706
- vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness)
7315
+ textureSampleLevel(
7316
+ pbr_brdfLUT,
7317
+ pbr_brdfLUTSampler,
7318
+ vec2f(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness),
7319
+ 0.0
5707
7320
  )
5708
7321
  ).rgb;
5709
7322
  let diffuseLight =
5710
- SRGBtoLINEAR(textureSample(pbr_diffuseEnvSampler, pbr_diffuseEnvSamplerSampler, n)).rgb;
5711
- let specularLightDefault =
5712
- SRGBtoLINEAR(textureSample(pbr_specularEnvSampler, pbr_specularEnvSamplerSampler, reflection)).rgb;
5713
- var specularLight = specularLightDefault;
7323
+ SRGBtoLINEAR(
7324
+ textureSampleLevel(pbr_diffuseEnvSampler, pbr_diffuseEnvSamplerSampler, n, 0.0)
7325
+ ).rgb;
7326
+ var specularLight = SRGBtoLINEAR(
7327
+ textureSampleLevel(
7328
+ pbr_specularEnvSampler,
7329
+ pbr_specularEnvSamplerSampler,
7330
+ reflection,
7331
+ 0.0
7332
+ )
7333
+ ).rgb;
5714
7334
  #ifdef USE_TEX_LOD
5715
7335
  specularLight = SRGBtoLINEAR(
5716
7336
  textureSampleLevel(
@@ -5771,6 +7391,172 @@ fn microfacetDistribution(pbrInfo: PBRInfo) -> f32 {
5771
7391
  return roughnessSq / (M_PI * f * f);
5772
7392
  }
5773
7393
 
7394
+ fn maxComponent(value: vec3f) -> f32 {
7395
+ return max(max(value.r, value.g), value.b);
7396
+ }
7397
+
7398
+ fn getDielectricF0(ior: f32) -> f32 {
7399
+ let clampedIor = max(ior, 1.0);
7400
+ let ratio = (clampedIor - 1.0) / (clampedIor + 1.0);
7401
+ return ratio * ratio;
7402
+ }
7403
+
7404
+ fn normalizeDirection(direction: vec2f) -> vec2f {
7405
+ let directionLength = length(direction);
7406
+ if (directionLength > 0.0001) {
7407
+ return direction / directionLength;
7408
+ }
7409
+
7410
+ return vec2f(1.0, 0.0);
7411
+ }
7412
+
7413
+ fn rotateDirection(direction: vec2f, rotation: f32) -> vec2f {
7414
+ let s = sin(rotation);
7415
+ let c = cos(rotation);
7416
+ return vec2f(direction.x * c - direction.y * s, direction.x * s + direction.y * c);
7417
+ }
7418
+
7419
+ fn getIridescenceTint(iridescence: f32, thickness: f32, NdotV: f32) -> vec3f {
7420
+ if (iridescence <= 0.0) {
7421
+ return vec3f(1.0);
7422
+ }
7423
+
7424
+ let phase = 0.015 * thickness * pbrMaterial.iridescenceIor + (1.0 - NdotV) * 6.0;
7425
+ let thinFilmTint =
7426
+ 0.5 +
7427
+ 0.5 *
7428
+ cos(vec3f(phase, phase + 2.0943951, phase + 4.1887902));
7429
+ return mix(vec3f(1.0), thinFilmTint, iridescence);
7430
+ }
7431
+
7432
+ fn getVolumeAttenuation(thickness: f32) -> vec3f {
7433
+ if (thickness <= 0.0) {
7434
+ return vec3f(1.0);
7435
+ }
7436
+
7437
+ let attenuationCoefficient =
7438
+ -log(max(pbrMaterial.attenuationColor, vec3f(0.0001))) /
7439
+ max(pbrMaterial.attenuationDistance, 0.0001);
7440
+ return exp(-attenuationCoefficient * thickness);
7441
+ }
7442
+
7443
+ fn createClearcoatPBRInfo(
7444
+ basePBRInfo: PBRInfo,
7445
+ clearcoatNormal: vec3f,
7446
+ clearcoatRoughness: f32
7447
+ ) -> PBRInfo {
7448
+ let perceptualRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
7449
+ let alphaRoughness = perceptualRoughness * perceptualRoughness;
7450
+ let NdotV = clamp(abs(dot(clearcoatNormal, basePBRInfo.v)), 0.001, 1.0);
7451
+
7452
+ return PBRInfo(
7453
+ basePBRInfo.NdotL,
7454
+ NdotV,
7455
+ basePBRInfo.NdotH,
7456
+ basePBRInfo.LdotH,
7457
+ basePBRInfo.VdotH,
7458
+ perceptualRoughness,
7459
+ 0.0,
7460
+ vec3f(0.04),
7461
+ vec3f(1.0),
7462
+ alphaRoughness,
7463
+ vec3f(0.0),
7464
+ vec3f(0.04),
7465
+ clearcoatNormal,
7466
+ basePBRInfo.v
7467
+ );
7468
+ }
7469
+
7470
+ fn calculateClearcoatContribution(
7471
+ pbrInfo: PBRInfo,
7472
+ lightColor: vec3f,
7473
+ clearcoatNormal: vec3f,
7474
+ clearcoatFactor: f32,
7475
+ clearcoatRoughness: f32
7476
+ ) -> vec3f {
7477
+ if (clearcoatFactor <= 0.0) {
7478
+ return vec3f(0.0);
7479
+ }
7480
+
7481
+ let clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
7482
+ return calculateFinalColor(clearcoatPBRInfo, lightColor) * clearcoatFactor;
7483
+ }
7484
+
7485
+ #ifdef USE_IBL
7486
+ fn calculateClearcoatIBLContribution(
7487
+ pbrInfo: PBRInfo,
7488
+ clearcoatNormal: vec3f,
7489
+ reflection: vec3f,
7490
+ clearcoatFactor: f32,
7491
+ clearcoatRoughness: f32
7492
+ ) -> vec3f {
7493
+ if (clearcoatFactor <= 0.0) {
7494
+ return vec3f(0.0);
7495
+ }
7496
+
7497
+ let clearcoatPBRInfo = createClearcoatPBRInfo(pbrInfo, clearcoatNormal, clearcoatRoughness);
7498
+ return getIBLContribution(clearcoatPBRInfo, clearcoatNormal, reflection) * clearcoatFactor;
7499
+ }
7500
+ #endif
7501
+
7502
+ fn calculateSheenContribution(
7503
+ pbrInfo: PBRInfo,
7504
+ lightColor: vec3f,
7505
+ sheenColor: vec3f,
7506
+ sheenRoughness: f32
7507
+ ) -> vec3f {
7508
+ if (maxComponent(sheenColor) <= 0.0) {
7509
+ return vec3f(0.0);
7510
+ }
7511
+
7512
+ let sheenFresnel = pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0);
7513
+ let sheenVisibility = mix(1.0, pbrInfo.NdotL * pbrInfo.NdotV, sheenRoughness);
7514
+ return pbrInfo.NdotL *
7515
+ lightColor *
7516
+ sheenColor *
7517
+ (0.25 + 0.75 * sheenFresnel) *
7518
+ sheenVisibility *
7519
+ (1.0 - pbrInfo.metalness);
7520
+ }
7521
+
7522
+ fn calculateAnisotropyBoost(
7523
+ pbrInfo: PBRInfo,
7524
+ anisotropyTangent: vec3f,
7525
+ anisotropyStrength: f32
7526
+ ) -> f32 {
7527
+ if (anisotropyStrength <= 0.0) {
7528
+ return 1.0;
7529
+ }
7530
+
7531
+ let anisotropyBitangent = normalize(cross(pbrInfo.n, anisotropyTangent));
7532
+ let bitangentViewAlignment = abs(dot(pbrInfo.v, anisotropyBitangent));
7533
+ return mix(1.0, 0.65 + 0.7 * bitangentViewAlignment, anisotropyStrength);
7534
+ }
7535
+
7536
+ fn calculateMaterialLightColor(
7537
+ pbrInfo: PBRInfo,
7538
+ lightColor: vec3f,
7539
+ clearcoatNormal: vec3f,
7540
+ clearcoatFactor: f32,
7541
+ clearcoatRoughness: f32,
7542
+ sheenColor: vec3f,
7543
+ sheenRoughness: f32,
7544
+ anisotropyTangent: vec3f,
7545
+ anisotropyStrength: f32
7546
+ ) -> vec3f {
7547
+ let anisotropyBoost = calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
7548
+ var color = calculateFinalColor(pbrInfo, lightColor) * anisotropyBoost;
7549
+ color += calculateClearcoatContribution(
7550
+ pbrInfo,
7551
+ lightColor,
7552
+ clearcoatNormal,
7553
+ clearcoatFactor,
7554
+ clearcoatRoughness
7555
+ );
7556
+ color += calculateSheenContribution(pbrInfo, lightColor, sheenColor, sheenRoughness);
7557
+ return color;
7558
+ }
7559
+
5774
7560
  fn PBRInfo_setAmbientLight(pbrInfo: ptr<function, PBRInfo>) {
5775
7561
  (*pbrInfo).NdotL = 1.0;
5776
7562
  (*pbrInfo).NdotH = 0.0;
@@ -5795,6 +7581,11 @@ fn PBRInfo_setPointLight(pbrInfo: ptr<function, PBRInfo>, pointLight: PointLight
5795
7581
  PBRInfo_setDirectionalLight(pbrInfo, light_direction);
5796
7582
  }
5797
7583
 
7584
+ fn PBRInfo_setSpotLight(pbrInfo: ptr<function, PBRInfo>, spotLight: SpotLight) {
7585
+ let light_direction = normalize(spotLight.position - fragmentInputs.pbr_vPosition);
7586
+ PBRInfo_setDirectionalLight(pbrInfo, light_direction);
7587
+ }
7588
+
5798
7589
  fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
5799
7590
  // Calculate the shading terms for the microfacet specular shading model
5800
7591
  let F = specularReflection(pbrInfo);
@@ -5824,6 +7615,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5824
7615
  #endif
5825
7616
 
5826
7617
  var color = vec3<f32>(0.0, 0.0, 0.0);
7618
+ var transmission = 0.0;
5827
7619
 
5828
7620
  if (pbrMaterial.unlit != 0u) {
5829
7621
  color = baseColor.rgb;
@@ -5846,14 +7638,308 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5846
7638
  #endif
5847
7639
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
5848
7640
  metallic = clamp(metallic, 0.0, 1.0);
7641
+ let tbn = getTBN();
7642
+ let n = getNormal(tbn); // normal at surface point
7643
+ let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
7644
+ let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
7645
+ var useExtendedPBR = false;
7646
+ #ifdef USE_MATERIAL_EXTENSIONS
7647
+ useExtendedPBR =
7648
+ pbrMaterial.specularColorMapEnabled != 0 ||
7649
+ pbrMaterial.specularIntensityMapEnabled != 0 ||
7650
+ abs(pbrMaterial.specularIntensityFactor - 1.0) > 0.0001 ||
7651
+ maxComponent(abs(pbrMaterial.specularColorFactor - vec3f(1.0))) > 0.0001 ||
7652
+ abs(pbrMaterial.ior - 1.5) > 0.0001 ||
7653
+ pbrMaterial.transmissionMapEnabled != 0 ||
7654
+ pbrMaterial.transmissionFactor > 0.0001 ||
7655
+ pbrMaterial.clearcoatMapEnabled != 0 ||
7656
+ pbrMaterial.clearcoatRoughnessMapEnabled != 0 ||
7657
+ pbrMaterial.clearcoatFactor > 0.0001 ||
7658
+ pbrMaterial.clearcoatRoughnessFactor > 0.0001 ||
7659
+ pbrMaterial.sheenColorMapEnabled != 0 ||
7660
+ pbrMaterial.sheenRoughnessMapEnabled != 0 ||
7661
+ maxComponent(pbrMaterial.sheenColorFactor) > 0.0001 ||
7662
+ pbrMaterial.sheenRoughnessFactor > 0.0001 ||
7663
+ pbrMaterial.iridescenceMapEnabled != 0 ||
7664
+ pbrMaterial.iridescenceFactor > 0.0001 ||
7665
+ abs(pbrMaterial.iridescenceIor - 1.3) > 0.0001 ||
7666
+ abs(pbrMaterial.iridescenceThicknessRange.x - 100.0) > 0.0001 ||
7667
+ abs(pbrMaterial.iridescenceThicknessRange.y - 400.0) > 0.0001 ||
7668
+ pbrMaterial.anisotropyMapEnabled != 0 ||
7669
+ pbrMaterial.anisotropyStrength > 0.0001 ||
7670
+ abs(pbrMaterial.anisotropyRotation) > 0.0001 ||
7671
+ length(pbrMaterial.anisotropyDirection - vec2f(1.0, 0.0)) > 0.0001;
7672
+ #endif
7673
+
7674
+ if (!useExtendedPBR) {
7675
+ let alphaRoughness = perceptualRoughness * perceptualRoughness;
7676
+
7677
+ let f0 = vec3<f32>(0.04);
7678
+ var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
7679
+ diffuseColor *= 1.0 - metallic;
7680
+ let specularColor = mix(f0, baseColor.rgb, metallic);
7681
+
7682
+ let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
7683
+ let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
7684
+ let specularEnvironmentR0 = specularColor;
7685
+ let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
7686
+ let reflection = -normalize(reflect(v, n));
7687
+
7688
+ var pbrInfo = PBRInfo(
7689
+ 0.0, // NdotL
7690
+ NdotV,
7691
+ 0.0, // NdotH
7692
+ 0.0, // LdotH
7693
+ 0.0, // VdotH
7694
+ perceptualRoughness,
7695
+ metallic,
7696
+ specularEnvironmentR0,
7697
+ specularEnvironmentR90,
7698
+ alphaRoughness,
7699
+ diffuseColor,
7700
+ specularColor,
7701
+ n,
7702
+ v
7703
+ );
7704
+
7705
+ #ifdef USE_LIGHTS
7706
+ PBRInfo_setAmbientLight(&pbrInfo);
7707
+ color += calculateFinalColor(pbrInfo, lighting.ambientColor);
7708
+
7709
+ for (var i = 0; i < lighting.directionalLightCount; i++) {
7710
+ if (i < lighting.directionalLightCount) {
7711
+ PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
7712
+ color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
7713
+ }
7714
+ }
7715
+
7716
+ for (var i = 0; i < lighting.pointLightCount; i++) {
7717
+ if (i < lighting.pointLightCount) {
7718
+ PBRInfo_setPointLight(&pbrInfo, lighting_getPointLight(i));
7719
+ let attenuation = getPointLightAttenuation(
7720
+ lighting_getPointLight(i),
7721
+ distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
7722
+ );
7723
+ color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
7724
+ }
7725
+ }
7726
+
7727
+ for (var i = 0; i < lighting.spotLightCount; i++) {
7728
+ if (i < lighting.spotLightCount) {
7729
+ PBRInfo_setSpotLight(&pbrInfo, lighting_getSpotLight(i));
7730
+ let attenuation = getSpotLightAttenuation(
7731
+ lighting_getSpotLight(i),
7732
+ fragmentInputs.pbr_vPosition
7733
+ );
7734
+ color += calculateFinalColor(pbrInfo, lighting_getSpotLight(i).color / attenuation);
7735
+ }
7736
+ }
7737
+ #endif
7738
+
7739
+ #ifdef USE_IBL
7740
+ if (pbrMaterial.IBLenabled != 0) {
7741
+ color += getIBLContribution(pbrInfo, n, reflection);
7742
+ }
7743
+ #endif
7744
+
7745
+ #ifdef HAS_OCCLUSIONMAP
7746
+ if (pbrMaterial.occlusionMapEnabled != 0) {
7747
+ let ao =
7748
+ textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, fragmentInputs.pbr_vUV).r;
7749
+ color = mix(color, color * ao, pbrMaterial.occlusionStrength);
7750
+ }
7751
+ #endif
7752
+
7753
+ var emissive = pbrMaterial.emissiveFactor;
7754
+ #ifdef HAS_EMISSIVEMAP
7755
+ if (pbrMaterial.emissiveMapEnabled != 0u) {
7756
+ emissive *= SRGBtoLINEAR(
7757
+ textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
7758
+ ).rgb;
7759
+ }
7760
+ #endif
7761
+ color += emissive * pbrMaterial.emissiveStrength;
7762
+
7763
+ #ifdef PBR_DEBUG
7764
+ color = mix(color, baseColor.rgb, pbrMaterial.scaleDiffBaseMR.y);
7765
+ color = mix(color, vec3<f32>(metallic), pbrMaterial.scaleDiffBaseMR.z);
7766
+ color = mix(color, vec3<f32>(perceptualRoughness), pbrMaterial.scaleDiffBaseMR.w);
7767
+ #endif
7768
+
7769
+ return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), baseColor.a);
7770
+ }
7771
+
7772
+ var specularIntensity = pbrMaterial.specularIntensityFactor;
7773
+ #ifdef HAS_SPECULARINTENSITYMAP
7774
+ if (pbrMaterial.specularIntensityMapEnabled != 0) {
7775
+ specularIntensity *= textureSample(
7776
+ pbr_specularIntensitySampler,
7777
+ pbr_specularIntensitySamplerSampler,
7778
+ fragmentInputs.pbr_vUV
7779
+ ).a;
7780
+ }
7781
+ #endif
7782
+
7783
+ var specularFactor = pbrMaterial.specularColorFactor;
7784
+ #ifdef HAS_SPECULARCOLORMAP
7785
+ if (pbrMaterial.specularColorMapEnabled != 0) {
7786
+ specularFactor *= SRGBtoLINEAR(
7787
+ textureSample(
7788
+ pbr_specularColorSampler,
7789
+ pbr_specularColorSamplerSampler,
7790
+ fragmentInputs.pbr_vUV
7791
+ )
7792
+ ).rgb;
7793
+ }
7794
+ #endif
7795
+
7796
+ transmission = pbrMaterial.transmissionFactor;
7797
+ #ifdef HAS_TRANSMISSIONMAP
7798
+ if (pbrMaterial.transmissionMapEnabled != 0) {
7799
+ transmission *= textureSample(
7800
+ pbr_transmissionSampler,
7801
+ pbr_transmissionSamplerSampler,
7802
+ fragmentInputs.pbr_vUV
7803
+ ).r;
7804
+ }
7805
+ #endif
7806
+ transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
7807
+ var thickness = max(pbrMaterial.thicknessFactor, 0.0);
7808
+ #ifdef HAS_THICKNESSMAP
7809
+ thickness *= textureSample(
7810
+ pbr_thicknessSampler,
7811
+ pbr_thicknessSamplerSampler,
7812
+ fragmentInputs.pbr_vUV
7813
+ ).g;
7814
+ #endif
7815
+
7816
+ var clearcoatFactor = pbrMaterial.clearcoatFactor;
7817
+ var clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
7818
+ #ifdef HAS_CLEARCOATMAP
7819
+ if (pbrMaterial.clearcoatMapEnabled != 0) {
7820
+ clearcoatFactor *= textureSample(
7821
+ pbr_clearcoatSampler,
7822
+ pbr_clearcoatSamplerSampler,
7823
+ fragmentInputs.pbr_vUV
7824
+ ).r;
7825
+ }
7826
+ #endif
7827
+ #ifdef HAS_CLEARCOATROUGHNESSMAP
7828
+ if (pbrMaterial.clearcoatRoughnessMapEnabled != 0) {
7829
+ clearcoatRoughness *= textureSample(
7830
+ pbr_clearcoatRoughnessSampler,
7831
+ pbr_clearcoatRoughnessSamplerSampler,
7832
+ fragmentInputs.pbr_vUV
7833
+ ).g;
7834
+ }
7835
+ #endif
7836
+ clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
7837
+ clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
7838
+ let clearcoatNormal = getClearcoatNormal(tbn, n);
7839
+
7840
+ var sheenColor = pbrMaterial.sheenColorFactor;
7841
+ var sheenRoughness = pbrMaterial.sheenRoughnessFactor;
7842
+ #ifdef HAS_SHEENCOLORMAP
7843
+ if (pbrMaterial.sheenColorMapEnabled != 0) {
7844
+ sheenColor *= SRGBtoLINEAR(
7845
+ textureSample(
7846
+ pbr_sheenColorSampler,
7847
+ pbr_sheenColorSamplerSampler,
7848
+ fragmentInputs.pbr_vUV
7849
+ )
7850
+ ).rgb;
7851
+ }
7852
+ #endif
7853
+ #ifdef HAS_SHEENROUGHNESSMAP
7854
+ if (pbrMaterial.sheenRoughnessMapEnabled != 0) {
7855
+ sheenRoughness *= textureSample(
7856
+ pbr_sheenRoughnessSampler,
7857
+ pbr_sheenRoughnessSamplerSampler,
7858
+ fragmentInputs.pbr_vUV
7859
+ ).a;
7860
+ }
7861
+ #endif
7862
+ sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
7863
+
7864
+ var iridescence = pbrMaterial.iridescenceFactor;
7865
+ #ifdef HAS_IRIDESCENCEMAP
7866
+ if (pbrMaterial.iridescenceMapEnabled != 0) {
7867
+ iridescence *= textureSample(
7868
+ pbr_iridescenceSampler,
7869
+ pbr_iridescenceSamplerSampler,
7870
+ fragmentInputs.pbr_vUV
7871
+ ).r;
7872
+ }
7873
+ #endif
7874
+ iridescence = clamp(iridescence, 0.0, 1.0);
7875
+ var iridescenceThickness = mix(
7876
+ pbrMaterial.iridescenceThicknessRange.x,
7877
+ pbrMaterial.iridescenceThicknessRange.y,
7878
+ 0.5
7879
+ );
7880
+ #ifdef HAS_IRIDESCENCETHICKNESSMAP
7881
+ iridescenceThickness = mix(
7882
+ pbrMaterial.iridescenceThicknessRange.x,
7883
+ pbrMaterial.iridescenceThicknessRange.y,
7884
+ textureSample(
7885
+ pbr_iridescenceThicknessSampler,
7886
+ pbr_iridescenceThicknessSamplerSampler,
7887
+ fragmentInputs.pbr_vUV
7888
+ ).g
7889
+ );
7890
+ #endif
7891
+
7892
+ var anisotropyStrength = clamp(pbrMaterial.anisotropyStrength, 0.0, 1.0);
7893
+ var anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
7894
+ #ifdef HAS_ANISOTROPYMAP
7895
+ if (pbrMaterial.anisotropyMapEnabled != 0) {
7896
+ let anisotropySample = textureSample(
7897
+ pbr_anisotropySampler,
7898
+ pbr_anisotropySamplerSampler,
7899
+ fragmentInputs.pbr_vUV
7900
+ ).rgb;
7901
+ anisotropyStrength *= anisotropySample.b;
7902
+ let mappedDirection = anisotropySample.rg * 2.0 - 1.0;
7903
+ if (length(mappedDirection) > 0.0001) {
7904
+ anisotropyDirection = normalize(mappedDirection);
7905
+ }
7906
+ }
7907
+ #endif
7908
+ anisotropyDirection = rotateDirection(anisotropyDirection, pbrMaterial.anisotropyRotation);
7909
+ var anisotropyTangent =
7910
+ normalize(tbn[0] * anisotropyDirection.x + tbn[1] * anisotropyDirection.y);
7911
+ if (length(anisotropyTangent) < 0.0001) {
7912
+ anisotropyTangent = normalize(tbn[0]);
7913
+ }
7914
+ let anisotropyViewAlignment = abs(dot(v, anisotropyTangent));
7915
+ perceptualRoughness = mix(
7916
+ perceptualRoughness,
7917
+ clamp(perceptualRoughness * (1.0 - 0.6 * anisotropyViewAlignment), c_MinRoughness, 1.0),
7918
+ anisotropyStrength
7919
+ );
7920
+
5849
7921
  // Roughness is authored as perceptual roughness; as is convention,
5850
7922
  // convert to material roughness by squaring the perceptual roughness [2].
5851
7923
  let alphaRoughness = perceptualRoughness * perceptualRoughness;
5852
7924
 
5853
- let f0 = vec3<f32>(0.04);
5854
- var diffuseColor = baseColor.rgb * (vec3<f32>(1.0) - f0);
5855
- diffuseColor *= 1.0 - metallic;
5856
- let specularColor = mix(f0, baseColor.rgb, metallic);
7925
+ let dielectricF0 = getDielectricF0(pbrMaterial.ior);
7926
+ var dielectricSpecularF0 = min(
7927
+ vec3f(dielectricF0) * specularFactor * specularIntensity,
7928
+ vec3f(1.0)
7929
+ );
7930
+ let iridescenceTint = getIridescenceTint(iridescence, iridescenceThickness, NdotV);
7931
+ dielectricSpecularF0 = mix(
7932
+ dielectricSpecularF0,
7933
+ dielectricSpecularF0 * iridescenceTint,
7934
+ iridescence
7935
+ );
7936
+ var diffuseColor = baseColor.rgb * (vec3f(1.0) - dielectricSpecularF0);
7937
+ diffuseColor *= (1.0 - metallic) * (1.0 - transmission);
7938
+ var specularColor = mix(dielectricSpecularF0, baseColor.rgb, metallic);
7939
+
7940
+ let baseLayerEnergy = 1.0 - clearcoatFactor * 0.25;
7941
+ diffuseColor *= baseLayerEnergy;
7942
+ specularColor *= baseLayerEnergy;
5857
7943
 
5858
7944
  // Compute reflectance.
5859
7945
  let reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
@@ -5865,11 +7951,6 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5865
7951
  let reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
5866
7952
  let specularEnvironmentR0 = specularColor;
5867
7953
  let specularEnvironmentR90 = vec3<f32>(1.0, 1.0, 1.0) * reflectance90;
5868
-
5869
- let n = getNormal(); // normal at surface point
5870
- let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
5871
-
5872
- let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
5873
7954
  let reflection = -normalize(reflect(v, n));
5874
7955
 
5875
7956
  var pbrInfo = PBRInfo(
@@ -5892,13 +7973,33 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5892
7973
  #ifdef USE_LIGHTS
5893
7974
  // Apply ambient light
5894
7975
  PBRInfo_setAmbientLight(&pbrInfo);
5895
- color += calculateFinalColor(pbrInfo, lighting.ambientColor);
7976
+ color += calculateMaterialLightColor(
7977
+ pbrInfo,
7978
+ lighting.ambientColor,
7979
+ clearcoatNormal,
7980
+ clearcoatFactor,
7981
+ clearcoatRoughness,
7982
+ sheenColor,
7983
+ sheenRoughness,
7984
+ anisotropyTangent,
7985
+ anisotropyStrength
7986
+ );
5896
7987
 
5897
7988
  // Apply directional light
5898
7989
  for (var i = 0; i < lighting.directionalLightCount; i++) {
5899
7990
  if (i < lighting.directionalLightCount) {
5900
7991
  PBRInfo_setDirectionalLight(&pbrInfo, lighting_getDirectionalLight(i).direction);
5901
- color += calculateFinalColor(pbrInfo, lighting_getDirectionalLight(i).color);
7992
+ color += calculateMaterialLightColor(
7993
+ pbrInfo,
7994
+ lighting_getDirectionalLight(i).color,
7995
+ clearcoatNormal,
7996
+ clearcoatFactor,
7997
+ clearcoatRoughness,
7998
+ sheenColor,
7999
+ sheenRoughness,
8000
+ anisotropyTangent,
8001
+ anisotropyStrength
8002
+ );
5902
8003
  }
5903
8004
  }
5904
8005
 
@@ -5910,7 +8011,35 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5910
8011
  lighting_getPointLight(i),
5911
8012
  distance(lighting_getPointLight(i).position, fragmentInputs.pbr_vPosition)
5912
8013
  );
5913
- color += calculateFinalColor(pbrInfo, lighting_getPointLight(i).color / attenuation);
8014
+ color += calculateMaterialLightColor(
8015
+ pbrInfo,
8016
+ lighting_getPointLight(i).color / attenuation,
8017
+ clearcoatNormal,
8018
+ clearcoatFactor,
8019
+ clearcoatRoughness,
8020
+ sheenColor,
8021
+ sheenRoughness,
8022
+ anisotropyTangent,
8023
+ anisotropyStrength
8024
+ );
8025
+ }
8026
+ }
8027
+
8028
+ for (var i = 0; i < lighting.spotLightCount; i++) {
8029
+ if (i < lighting.spotLightCount) {
8030
+ PBRInfo_setSpotLight(&pbrInfo, lighting_getSpotLight(i));
8031
+ let attenuation = getSpotLightAttenuation(lighting_getSpotLight(i), fragmentInputs.pbr_vPosition);
8032
+ color += calculateMaterialLightColor(
8033
+ pbrInfo,
8034
+ lighting_getSpotLight(i).color / attenuation,
8035
+ clearcoatNormal,
8036
+ clearcoatFactor,
8037
+ clearcoatRoughness,
8038
+ sheenColor,
8039
+ sheenRoughness,
8040
+ anisotropyTangent,
8041
+ anisotropyStrength
8042
+ );
5914
8043
  }
5915
8044
  }
5916
8045
  #endif
@@ -5918,7 +8047,16 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5918
8047
  // Calculate lighting contribution from image based lighting source (IBL)
5919
8048
  #ifdef USE_IBL
5920
8049
  if (pbrMaterial.IBLenabled != 0) {
5921
- color += getIBLContribution(pbrInfo, n, reflection);
8050
+ color += getIBLContribution(pbrInfo, n, reflection) *
8051
+ calculateAnisotropyBoost(pbrInfo, anisotropyTangent, anisotropyStrength);
8052
+ color += calculateClearcoatIBLContribution(
8053
+ pbrInfo,
8054
+ clearcoatNormal,
8055
+ -normalize(reflect(v, clearcoatNormal)),
8056
+ clearcoatFactor,
8057
+ clearcoatRoughness
8058
+ );
8059
+ color += sheenColor * pbrMaterial.scaleIBLAmbient.x * (1.0 - sheenRoughness) * 0.25;
5922
8060
  }
5923
8061
  #endif
5924
8062
 
@@ -5931,14 +8069,19 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5931
8069
  }
5932
8070
  #endif
5933
8071
 
8072
+ var emissive = pbrMaterial.emissiveFactor;
5934
8073
  #ifdef HAS_EMISSIVEMAP
5935
8074
  if (pbrMaterial.emissiveMapEnabled != 0u) {
5936
- let emissive = SRGBtoLINEAR(
8075
+ emissive *= SRGBtoLINEAR(
5937
8076
  textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
5938
- ).rgb * pbrMaterial.emissiveFactor;
5939
- color += emissive;
8077
+ ).rgb;
5940
8078
  }
5941
8079
  #endif
8080
+ color += emissive * pbrMaterial.emissiveStrength;
8081
+
8082
+ if (transmission > 0.0) {
8083
+ color = mix(color, color * getVolumeAttenuation(thickness), transmission);
8084
+ }
5942
8085
 
5943
8086
  // This section uses mix to override final color for reference app visualization
5944
8087
  // of various parameters in the lighting equation.
@@ -5957,7 +8100,8 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5957
8100
  #endif
5958
8101
  }
5959
8102
 
5960
- return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), baseColor.a);
8103
+ let alpha = clamp(baseColor.a * (1.0 - transmission), 0.0, 1.0);
8104
+ return vec4<f32>(pow(color, vec3<f32>(1.0 / 2.2)), alpha);
5961
8105
  }
5962
8106
  `
5963
8107
  );
@@ -5982,11 +8126,12 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
5982
8126
  camera: vec3<f32>
5983
8127
  };
5984
8128
 
5985
- @binding(0) @group(0) var<uniform> pbrProjection: pbrProjectionUniforms;
8129
+ @group(0) @binding(auto) var<uniform> pbrProjection: pbrProjectionUniforms;
5986
8130
  `
5987
8131
  );
5988
8132
  var pbrProjection = {
5989
8133
  name: "pbrProjection",
8134
+ bindingLayout: [{ name: "pbrProjection", group: 0 }],
5990
8135
  source: wgslUniformBlock,
5991
8136
  vs: uniformBlock,
5992
8137
  fs: uniformBlock,
@@ -6004,8 +8149,75 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6004
8149
  var pbrMaterial = {
6005
8150
  props: {},
6006
8151
  uniforms: {},
8152
+ defaultUniforms: {
8153
+ unlit: false,
8154
+ baseColorMapEnabled: false,
8155
+ baseColorFactor: [1, 1, 1, 1],
8156
+ normalMapEnabled: false,
8157
+ normalScale: 1,
8158
+ emissiveMapEnabled: false,
8159
+ emissiveFactor: [0, 0, 0],
8160
+ metallicRoughnessValues: [1, 1],
8161
+ metallicRoughnessMapEnabled: false,
8162
+ occlusionMapEnabled: false,
8163
+ occlusionStrength: 1,
8164
+ alphaCutoffEnabled: false,
8165
+ alphaCutoff: 0.5,
8166
+ IBLenabled: false,
8167
+ scaleIBLAmbient: [1, 1],
8168
+ scaleDiffBaseMR: [0, 0, 0, 0],
8169
+ scaleFGDSpec: [0, 0, 0, 0],
8170
+ specularColorFactor: [1, 1, 1],
8171
+ specularIntensityFactor: 1,
8172
+ specularColorMapEnabled: false,
8173
+ specularIntensityMapEnabled: false,
8174
+ ior: 1.5,
8175
+ transmissionFactor: 0,
8176
+ transmissionMapEnabled: false,
8177
+ thicknessFactor: 0,
8178
+ attenuationDistance: 1e9,
8179
+ attenuationColor: [1, 1, 1],
8180
+ clearcoatFactor: 0,
8181
+ clearcoatRoughnessFactor: 0,
8182
+ clearcoatMapEnabled: false,
8183
+ clearcoatRoughnessMapEnabled: false,
8184
+ sheenColorFactor: [0, 0, 0],
8185
+ sheenRoughnessFactor: 0,
8186
+ sheenColorMapEnabled: false,
8187
+ sheenRoughnessMapEnabled: false,
8188
+ iridescenceFactor: 0,
8189
+ iridescenceIor: 1.3,
8190
+ iridescenceThicknessRange: [100, 400],
8191
+ iridescenceMapEnabled: false,
8192
+ anisotropyStrength: 0,
8193
+ anisotropyRotation: 0,
8194
+ anisotropyDirection: [1, 0],
8195
+ anisotropyMapEnabled: false,
8196
+ emissiveStrength: 1
8197
+ },
6007
8198
  name: "pbrMaterial",
6008
- dependencies: [lighting, pbrProjection],
8199
+ firstBindingSlot: 0,
8200
+ bindingLayout: [
8201
+ { name: "pbrMaterial", group: 3 },
8202
+ { name: "pbr_baseColorSampler", group: 3 },
8203
+ { name: "pbr_normalSampler", group: 3 },
8204
+ { name: "pbr_emissiveSampler", group: 3 },
8205
+ { name: "pbr_metallicRoughnessSampler", group: 3 },
8206
+ { name: "pbr_occlusionSampler", group: 3 },
8207
+ { name: "pbr_specularColorSampler", group: 3 },
8208
+ { name: "pbr_specularIntensitySampler", group: 3 },
8209
+ { name: "pbr_transmissionSampler", group: 3 },
8210
+ { name: "pbr_thicknessSampler", group: 3 },
8211
+ { name: "pbr_clearcoatSampler", group: 3 },
8212
+ { name: "pbr_clearcoatRoughnessSampler", group: 3 },
8213
+ { name: "pbr_clearcoatNormalSampler", group: 3 },
8214
+ { name: "pbr_sheenColorSampler", group: 3 },
8215
+ { name: "pbr_sheenRoughnessSampler", group: 3 },
8216
+ { name: "pbr_iridescenceSampler", group: 3 },
8217
+ { name: "pbr_iridescenceThicknessSampler", group: 3 },
8218
+ { name: "pbr_anisotropySampler", group: 3 }
8219
+ ],
8220
+ dependencies: [lighting, ibl, pbrProjection],
6009
8221
  source: source3,
6010
8222
  vs: vs3,
6011
8223
  fs: fs4,
@@ -6019,10 +8231,16 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6019
8231
  HAS_SPECULARCOLORMAP: false,
6020
8232
  HAS_SPECULARINTENSITYMAP: false,
6021
8233
  HAS_TRANSMISSIONMAP: false,
8234
+ HAS_THICKNESSMAP: false,
6022
8235
  HAS_CLEARCOATMAP: false,
8236
+ HAS_CLEARCOATROUGHNESSMAP: false,
8237
+ HAS_CLEARCOATNORMALMAP: false,
6023
8238
  HAS_SHEENCOLORMAP: false,
8239
+ HAS_SHEENROUGHNESSMAP: false,
6024
8240
  HAS_IRIDESCENCEMAP: false,
8241
+ HAS_IRIDESCENCETHICKNESSMAP: false,
6025
8242
  HAS_ANISOTROPYMAP: false,
8243
+ USE_MATERIAL_EXTENSIONS: false,
6026
8244
  ALPHA_CUTOFF: false,
6027
8245
  USE_IBL: false,
6028
8246
  PBR_DEBUG: false
@@ -6048,14 +8266,6 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6048
8266
  alphaCutoffEnabled: "i32",
6049
8267
  alphaCutoff: "f32",
6050
8268
  // #ifdef ALPHA_CUTOFF
6051
- // IBL
6052
- IBLenabled: "i32",
6053
- scaleIBLAmbient: "vec2<f32>",
6054
- // #ifdef USE_IBL
6055
- // debugging flags used for shader output of intermediate PBR variables
6056
- // #ifdef PBR_DEBUG
6057
- scaleDiffBaseMR: "vec4<f32>",
6058
- scaleFGDSpec: "vec4<f32>",
6059
8269
  specularColorFactor: "vec3<f32>",
6060
8270
  specularIntensityFactor: "f32",
6061
8271
  specularColorMapEnabled: "i32",
@@ -6069,9 +8279,11 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6069
8279
  clearcoatFactor: "f32",
6070
8280
  clearcoatRoughnessFactor: "f32",
6071
8281
  clearcoatMapEnabled: "i32",
8282
+ clearcoatRoughnessMapEnabled: "i32",
6072
8283
  sheenColorFactor: "vec3<f32>",
6073
8284
  sheenRoughnessFactor: "f32",
6074
8285
  sheenColorMapEnabled: "i32",
8286
+ sheenRoughnessMapEnabled: "i32",
6075
8287
  iridescenceFactor: "f32",
6076
8288
  iridescenceIor: "f32",
6077
8289
  iridescenceThicknessRange: "vec2<f32>",
@@ -6080,7 +8292,15 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
6080
8292
  anisotropyRotation: "f32",
6081
8293
  anisotropyDirection: "vec2<f32>",
6082
8294
  anisotropyMapEnabled: "i32",
6083
- emissiveStrength: "f32"
8295
+ emissiveStrength: "f32",
8296
+ // IBL
8297
+ IBLenabled: "i32",
8298
+ scaleIBLAmbient: "vec2<f32>",
8299
+ // #ifdef USE_IBL
8300
+ // debugging flags used for shader output of intermediate PBR variables
8301
+ // #ifdef PBR_DEBUG
8302
+ scaleDiffBaseMR: "vec4<f32>",
8303
+ scaleFGDSpec: "vec4<f32>"
6084
8304
  }
6085
8305
  };
6086
8306
  return __toCommonJS(bundle_exports);