@luma.gl/shadertools 9.3.0-alpha.10 → 9.3.0-alpha.11

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 (43) hide show
  1. package/dist/dist.dev.js +734 -143
  2. package/dist/dist.min.js +337 -131
  3. package/dist/index.cjs +636 -127
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/lib/glsl-utils/shader-utils.js +4 -4
  6. package/dist/lib/glsl-utils/shader-utils.js.map +1 -1
  7. package/dist/lib/shader-assembly/assemble-shaders.d.ts.map +1 -1
  8. package/dist/lib/shader-assembly/assemble-shaders.js +98 -48
  9. package/dist/lib/shader-assembly/assemble-shaders.js.map +1 -1
  10. package/dist/lib/shader-assembly/wgsl-binding-debug.d.ts.map +1 -1
  11. package/dist/lib/shader-assembly/wgsl-binding-debug.js +7 -3
  12. package/dist/lib/shader-assembly/wgsl-binding-debug.js.map +1 -1
  13. package/dist/lib/shader-assembly/wgsl-binding-scan.d.ts +19 -0
  14. package/dist/lib/shader-assembly/wgsl-binding-scan.d.ts.map +1 -0
  15. package/dist/lib/shader-assembly/wgsl-binding-scan.js +151 -0
  16. package/dist/lib/shader-assembly/wgsl-binding-scan.js.map +1 -0
  17. package/dist/lib/shader-module/shader-module-uniform-layout.js +45 -1
  18. package/dist/lib/shader-module/shader-module-uniform-layout.js.map +1 -1
  19. package/dist/modules/lighting/no-material/dirlight.d.ts +2 -2
  20. package/dist/modules/lighting/no-material/dirlight.d.ts.map +1 -1
  21. package/dist/modules/lighting/no-material/dirlight.js +1 -1
  22. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts +2 -2
  23. package/dist/modules/lighting/pbr-material/pbr-material-glsl.d.ts.map +1 -1
  24. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js +137 -34
  25. package/dist/modules/lighting/pbr-material/pbr-material-glsl.js.map +1 -1
  26. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts +1 -1
  27. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.d.ts.map +1 -1
  28. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js +139 -35
  29. package/dist/modules/lighting/pbr-material/pbr-material-wgsl.js.map +1 -1
  30. package/dist/modules/lighting/pbr-material/pbr-material.d.ts +72 -4
  31. package/dist/modules/lighting/pbr-material/pbr-material.d.ts.map +1 -1
  32. package/dist/modules/lighting/pbr-material/pbr-material.js +70 -2
  33. package/dist/modules/lighting/pbr-material/pbr-material.js.map +1 -1
  34. package/package.json +2 -2
  35. package/src/lib/glsl-utils/shader-utils.ts +4 -4
  36. package/src/lib/shader-assembly/assemble-shaders.ts +189 -68
  37. package/src/lib/shader-assembly/wgsl-binding-debug.ts +14 -3
  38. package/src/lib/shader-assembly/wgsl-binding-scan.ts +228 -0
  39. package/src/lib/shader-module/shader-module-uniform-layout.ts +77 -3
  40. package/src/modules/lighting/no-material/dirlight.ts +1 -1
  41. package/src/modules/lighting/pbr-material/pbr-material-glsl.ts +137 -34
  42. package/src/modules/lighting/pbr-material/pbr-material-wgsl.ts +139 -35
  43. package/src/modules/lighting/pbr-material/pbr-material.ts +110 -3
package/dist/dist.dev.js CHANGED
@@ -561,11 +561,42 @@ ${inject[key]}` : inject[key];
561
561
  return true;
562
562
  }
563
563
  function formatShaderModuleUniformLayoutError(validationResult) {
564
- return `${validationResult.moduleName}: ${validationResult.stage} shader uniform block ${validationResult.uniformBlockName} does not match module.uniformTypes.
565
- Expected: ${validationResult.expectedUniformNames.join(
566
- ", "
567
- )}
568
- Actual: ${validationResult.actualUniformNames.join(", ")}`;
564
+ const { expectedUniformNames, actualUniformNames } = validationResult;
565
+ const missingUniformNames = expectedUniformNames.filter(
566
+ (uniformName) => !actualUniformNames.includes(uniformName)
567
+ );
568
+ const unexpectedUniformNames = actualUniformNames.filter(
569
+ (uniformName) => !expectedUniformNames.includes(uniformName)
570
+ );
571
+ const mismatchDetails = [
572
+ `Expected ${expectedUniformNames.length} fields, found ${actualUniformNames.length}.`
573
+ ];
574
+ const firstMismatchDescription = getFirstUniformMismatchDescription(
575
+ expectedUniformNames,
576
+ actualUniformNames
577
+ );
578
+ if (firstMismatchDescription) {
579
+ mismatchDetails.push(firstMismatchDescription);
580
+ }
581
+ if (missingUniformNames.length) {
582
+ mismatchDetails.push(
583
+ `Missing from shader block (${missingUniformNames.length}): ${formatUniformNameList(
584
+ missingUniformNames
585
+ )}.`
586
+ );
587
+ }
588
+ if (unexpectedUniformNames.length) {
589
+ mismatchDetails.push(
590
+ `Unexpected in shader block (${unexpectedUniformNames.length}): ${formatUniformNameList(
591
+ unexpectedUniformNames
592
+ )}.`
593
+ );
594
+ }
595
+ if (expectedUniformNames.length <= 12 && actualUniformNames.length <= 12 && (missingUniformNames.length || unexpectedUniformNames.length)) {
596
+ mismatchDetails.push(`Expected: ${expectedUniformNames.join(", ")}.`);
597
+ mismatchDetails.push(`Actual: ${actualUniformNames.join(", ")}.`);
598
+ }
599
+ return `${validationResult.moduleName}: ${validationResult.stage} shader uniform block ${validationResult.uniformBlockName} does not match module.uniformTypes. ${mismatchDetails.join(" ")}`;
569
600
  }
570
601
  function stripShaderComments(shaderSource) {
571
602
  return shaderSource.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
@@ -573,6 +604,28 @@ Actual: ${validationResult.actualUniformNames.join(", ")}`;
573
604
  function normalizeWhitespace(value) {
574
605
  return value.replace(/\s+/g, " ").trim();
575
606
  }
607
+ function getFirstUniformMismatchDescription(expectedUniformNames, actualUniformNames) {
608
+ const minimumLength = Math.min(expectedUniformNames.length, actualUniformNames.length);
609
+ for (let index = 0; index < minimumLength; index++) {
610
+ if (expectedUniformNames[index] !== actualUniformNames[index]) {
611
+ return `First mismatch at field ${index + 1}: expected ${expectedUniformNames[index]}, found ${actualUniformNames[index]}.`;
612
+ }
613
+ }
614
+ if (expectedUniformNames.length > actualUniformNames.length) {
615
+ return `Shader block ends after field ${actualUniformNames.length}; expected next field ${expectedUniformNames[actualUniformNames.length]}.`;
616
+ }
617
+ if (actualUniformNames.length > expectedUniformNames.length) {
618
+ return `Shader block has extra field ${actualUniformNames.length}: ${actualUniformNames[expectedUniformNames.length]}.`;
619
+ }
620
+ return null;
621
+ }
622
+ function formatUniformNameList(uniformNames, maxNames = 8) {
623
+ if (uniformNames.length <= maxNames) {
624
+ return uniformNames.join(", ");
625
+ }
626
+ const remainingCount = uniformNames.length - maxNames;
627
+ return `${uniformNames.slice(0, maxNames).join(", ")}, ... (${remainingCount} more)`;
628
+ }
576
629
 
577
630
  // src/lib/shader-assembly/platform-defines.ts
578
631
  function getPlatformShaderDefines(platformInfo) {
@@ -758,12 +811,194 @@ Actual: ${validationResult.actualUniformNames.join(", ")}`;
758
811
  return version;
759
812
  }
760
813
 
814
+ // src/lib/shader-assembly/wgsl-binding-scan.ts
815
+ var WGSL_BINDABLE_VARIABLE_PATTERN = "(?:var<\\s*(uniform|storage(?:\\s*,\\s*[A-Za-z_][A-Za-z0-9_]*)?)\\s*>|var)\\s+([A-Za-z_][A-Za-z0-9_]*)";
816
+ var WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN = "\\s*";
817
+ var MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
818
+ new RegExp(
819
+ `@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
820
+ "g"
821
+ ),
822
+ new RegExp(
823
+ `@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
824
+ "g"
825
+ )
826
+ ];
827
+ var WGSL_BINDING_DECLARATION_REGEXES = [
828
+ new RegExp(
829
+ `@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
830
+ "g"
831
+ ),
832
+ new RegExp(
833
+ `@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
834
+ "g"
835
+ )
836
+ ];
837
+ var WGSL_EXPLICIT_BINDING_DECLARATION_REGEXES = [
838
+ new RegExp(
839
+ `@binding\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
840
+ "g"
841
+ ),
842
+ new RegExp(
843
+ `@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`,
844
+ "g"
845
+ )
846
+ ];
847
+ var WGSL_AUTO_BINDING_DECLARATION_REGEXES = [
848
+ new RegExp(
849
+ `@binding\\(\\s*(auto)\\s*\\)\\s*@group\\(\\s*(\\d+)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}`,
850
+ "g"
851
+ ),
852
+ new RegExp(
853
+ `@group\\(\\s*(\\d+)\\s*\\)\\s*@binding\\(\\s*(auto)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}`,
854
+ "g"
855
+ ),
856
+ new RegExp(
857
+ `@binding\\(\\s*(auto)\\s*\\)\\s*@group\\(\\s*(\\d+)\\s*\\)(?:[\\s\\n\\r]*@[A-Za-z_][^\\n\\r]*)*[\\s\\n\\r]*${WGSL_BINDABLE_VARIABLE_PATTERN}`,
858
+ "g"
859
+ ),
860
+ new RegExp(
861
+ `@group\\(\\s*(\\d+)\\s*\\)\\s*@binding\\(\\s*(auto)\\s*\\)(?:[\\s\\n\\r]*@[A-Za-z_][^\\n\\r]*)*[\\s\\n\\r]*${WGSL_BINDABLE_VARIABLE_PATTERN}`,
862
+ "g"
863
+ )
864
+ ];
865
+ function maskWGSLComments(source4) {
866
+ const maskedCharacters = source4.split("");
867
+ let index = 0;
868
+ let blockCommentDepth = 0;
869
+ let inLineComment = false;
870
+ let inString = false;
871
+ let isEscaped = false;
872
+ while (index < source4.length) {
873
+ const character = source4[index];
874
+ const nextCharacter = source4[index + 1];
875
+ if (inString) {
876
+ if (isEscaped) {
877
+ isEscaped = false;
878
+ } else if (character === "\\") {
879
+ isEscaped = true;
880
+ } else if (character === '"') {
881
+ inString = false;
882
+ }
883
+ index++;
884
+ continue;
885
+ }
886
+ if (inLineComment) {
887
+ if (character === "\n" || character === "\r") {
888
+ inLineComment = false;
889
+ } else {
890
+ maskedCharacters[index] = " ";
891
+ }
892
+ index++;
893
+ continue;
894
+ }
895
+ if (blockCommentDepth > 0) {
896
+ if (character === "/" && nextCharacter === "*") {
897
+ maskedCharacters[index] = " ";
898
+ maskedCharacters[index + 1] = " ";
899
+ blockCommentDepth++;
900
+ index += 2;
901
+ continue;
902
+ }
903
+ if (character === "*" && nextCharacter === "/") {
904
+ maskedCharacters[index] = " ";
905
+ maskedCharacters[index + 1] = " ";
906
+ blockCommentDepth--;
907
+ index += 2;
908
+ continue;
909
+ }
910
+ if (character !== "\n" && character !== "\r") {
911
+ maskedCharacters[index] = " ";
912
+ }
913
+ index++;
914
+ continue;
915
+ }
916
+ if (character === '"') {
917
+ inString = true;
918
+ index++;
919
+ continue;
920
+ }
921
+ if (character === "/" && nextCharacter === "/") {
922
+ maskedCharacters[index] = " ";
923
+ maskedCharacters[index + 1] = " ";
924
+ inLineComment = true;
925
+ index += 2;
926
+ continue;
927
+ }
928
+ if (character === "/" && nextCharacter === "*") {
929
+ maskedCharacters[index] = " ";
930
+ maskedCharacters[index + 1] = " ";
931
+ blockCommentDepth = 1;
932
+ index += 2;
933
+ continue;
934
+ }
935
+ index++;
936
+ }
937
+ return maskedCharacters.join("");
938
+ }
939
+ function getWGSLBindingDeclarationMatches(source4, regexes) {
940
+ const maskedSource = maskWGSLComments(source4);
941
+ const matches = [];
942
+ for (const regex of regexes) {
943
+ regex.lastIndex = 0;
944
+ let match;
945
+ match = regex.exec(maskedSource);
946
+ while (match) {
947
+ const isBindingFirst = regex === regexes[0];
948
+ const index = match.index;
949
+ const length = match[0].length;
950
+ matches.push({
951
+ match: source4.slice(index, index + length),
952
+ index,
953
+ length,
954
+ bindingToken: match[isBindingFirst ? 1 : 2],
955
+ groupToken: match[isBindingFirst ? 2 : 1],
956
+ accessDeclaration: match[3]?.trim(),
957
+ name: match[4]
958
+ });
959
+ match = regex.exec(maskedSource);
960
+ }
961
+ }
962
+ return matches.sort((left, right) => left.index - right.index);
963
+ }
964
+ function replaceWGSLBindingDeclarationMatches(source4, regexes, replacer) {
965
+ const matches = getWGSLBindingDeclarationMatches(source4, regexes);
966
+ if (!matches.length) {
967
+ return source4;
968
+ }
969
+ let relocatedSource = "";
970
+ let lastIndex = 0;
971
+ for (const match of matches) {
972
+ relocatedSource += source4.slice(lastIndex, match.index);
973
+ relocatedSource += replacer(match);
974
+ lastIndex = match.index + match.length;
975
+ }
976
+ relocatedSource += source4.slice(lastIndex);
977
+ return relocatedSource;
978
+ }
979
+ function hasWGSLAutoBinding(source4) {
980
+ return /@binding\(\s*auto\s*\)/.test(maskWGSLComments(source4));
981
+ }
982
+ function getFirstWGSLAutoBindingDeclarationMatch(source4, regexes) {
983
+ const autoBindingRegexes = regexes === MODULE_WGSL_BINDING_DECLARATION_REGEXES || regexes === WGSL_BINDING_DECLARATION_REGEXES ? WGSL_AUTO_BINDING_DECLARATION_REGEXES : regexes;
984
+ return getWGSLBindingDeclarationMatches(source4, autoBindingRegexes).find(
985
+ (declarationMatch) => declarationMatch.bindingToken === "auto"
986
+ );
987
+ }
988
+
761
989
  // src/lib/shader-assembly/wgsl-binding-debug.ts
762
990
  var WGSL_BINDING_DEBUG_REGEXES = [
763
- /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g,
764
- /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*var(?:<([^>]+)>)?\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([^;]+);/g
991
+ new RegExp(
992
+ `@binding\\(\\s*(\\d+)\\s*\\)\\s*@group\\(\\s*(\\d+)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}\\s*:\\s*([^;]+);`,
993
+ "g"
994
+ ),
995
+ new RegExp(
996
+ `@group\\(\\s*(\\d+)\\s*\\)\\s*@binding\\(\\s*(\\d+)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}\\s*:\\s*([^;]+);`,
997
+ "g"
998
+ )
765
999
  ];
766
1000
  function getShaderBindingDebugRowsFromWGSL(source4, bindingAssignments = []) {
1001
+ const maskedSource = maskWGSLComments(source4);
767
1002
  const assignmentMap = /* @__PURE__ */ new Map();
768
1003
  for (const bindingAssignment of bindingAssignments) {
769
1004
  assignmentMap.set(
@@ -779,7 +1014,8 @@ Actual: ${validationResult.actualUniformNames.join(", ")}`;
779
1014
  for (const regex of WGSL_BINDING_DEBUG_REGEXES) {
780
1015
  regex.lastIndex = 0;
781
1016
  let match;
782
- while (match = regex.exec(source4)) {
1017
+ match = regex.exec(maskedSource);
1018
+ while (match) {
783
1019
  const isBindingFirst = regex === WGSL_BINDING_DEBUG_REGEXES[0];
784
1020
  const binding = Number(match[isBindingFirst ? 1 : 2]);
785
1021
  const group = Number(match[isBindingFirst ? 2 : 1]);
@@ -798,6 +1034,7 @@ Actual: ${validationResult.actualUniformNames.join(", ")}`;
798
1034
  resourceType
799
1035
  })
800
1036
  );
1037
+ match = regex.exec(maskedSource);
801
1038
  }
802
1039
  }
803
1040
  return rows.sort((left, right) => {
@@ -909,14 +1146,6 @@ Actual: ${validationResult.actualUniformNames.join(", ")}`;
909
1146
 
910
1147
  ${DECLARATION_INJECT_MARKER}
911
1148
  `;
912
- var MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
913
- /@binding\(\s*(auto|\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
914
- /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(auto|\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
915
- ];
916
- var WGSL_BINDING_DECLARATION_REGEXES = [
917
- /@binding\(\s*(\d+)\s*\)\s*@group\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g,
918
- /@group\(\s*(\d+)\s*\)\s*@binding\(\s*(\d+)\s*\)\s*(var(?:<[^>]+>)?\s+([A-Za-z_][A-Za-z0-9_]*))/g
919
- ];
920
1149
  var RESERVED_APPLICATION_GROUP_0_BINDING_LIMIT = 100;
921
1150
  var FRAGMENT_SHADER_PROLOGUE = (
922
1151
  /* glsl */
@@ -996,7 +1225,10 @@ ${DECLARATION_INJECT_MARKER}
996
1225
  }
997
1226
  }
998
1227
  const modulesToInject = modules;
999
- const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(coreSource);
1228
+ const applicationRelocation = relocateWGSLApplicationBindings(coreSource);
1229
+ const usedBindingsByGroup = getUsedBindingsByGroupFromApplicationWGSL(
1230
+ applicationRelocation.source
1231
+ );
1000
1232
  const reservedBindingKeysByGroup = reserveRegisteredModuleBindings(
1001
1233
  modulesToInject,
1002
1234
  options._bindingRegistry,
@@ -1037,7 +1269,7 @@ ${DECLARATION_INJECT_MARKER}
1037
1269
  assembledSource = injectShader(assembledSource, stage, declInjections);
1038
1270
  assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections);
1039
1271
  assembledSource += formatWGSLBindingAssignmentComments(bindingAssignments);
1040
- assembledSource += coreSource;
1272
+ assembledSource += applicationRelocation.source;
1041
1273
  assembledSource = injectShader(assembledSource, stage, mainInjections);
1042
1274
  assertNoUnresolvedAutoBindings(assembledSource);
1043
1275
  return { source: assembledSource, bindingAssignments };
@@ -1197,61 +1429,87 @@ ${getApplicationDefines(allDefines)}
1197
1429
  }
1198
1430
  function getUsedBindingsByGroupFromApplicationWGSL(source4) {
1199
1431
  const usedBindingsByGroup = /* @__PURE__ */ new Map();
1200
- for (const regex of WGSL_BINDING_DECLARATION_REGEXES) {
1201
- regex.lastIndex = 0;
1202
- let match;
1203
- while (match = regex.exec(source4)) {
1204
- const isBindingFirst = regex === WGSL_BINDING_DECLARATION_REGEXES[0];
1205
- const location = Number(match[isBindingFirst ? 1 : 2]);
1206
- const group = Number(match[isBindingFirst ? 2 : 1]);
1207
- const name = match[4];
1208
- validateApplicationWGSLBinding(group, location, name);
1209
- registerUsedBindingLocation(
1210
- usedBindingsByGroup,
1211
- group,
1212
- location,
1213
- `application binding "${name}"`
1214
- );
1215
- }
1432
+ for (const match of getWGSLBindingDeclarationMatches(
1433
+ source4,
1434
+ WGSL_EXPLICIT_BINDING_DECLARATION_REGEXES
1435
+ )) {
1436
+ const location = Number(match.bindingToken);
1437
+ const group = Number(match.groupToken);
1438
+ validateApplicationWGSLBinding(group, location, match.name);
1439
+ registerUsedBindingLocation(
1440
+ usedBindingsByGroup,
1441
+ group,
1442
+ location,
1443
+ `application binding "${match.name}"`
1444
+ );
1216
1445
  }
1217
1446
  return usedBindingsByGroup;
1218
1447
  }
1448
+ function relocateWGSLApplicationBindings(source4) {
1449
+ const declarationMatches = getWGSLBindingDeclarationMatches(
1450
+ source4,
1451
+ WGSL_BINDING_DECLARATION_REGEXES
1452
+ );
1453
+ const usedBindingsByGroup = /* @__PURE__ */ new Map();
1454
+ for (const declarationMatch of declarationMatches) {
1455
+ if (declarationMatch.bindingToken === "auto") {
1456
+ continue;
1457
+ }
1458
+ const location = Number(declarationMatch.bindingToken);
1459
+ const group = Number(declarationMatch.groupToken);
1460
+ validateApplicationWGSLBinding(group, location, declarationMatch.name);
1461
+ registerUsedBindingLocation(
1462
+ usedBindingsByGroup,
1463
+ group,
1464
+ location,
1465
+ `application binding "${declarationMatch.name}"`
1466
+ );
1467
+ }
1468
+ const relocationState = {
1469
+ sawSupportedBindingDeclaration: declarationMatches.length > 0
1470
+ };
1471
+ const relocatedSource = replaceWGSLBindingDeclarationMatches(
1472
+ source4,
1473
+ WGSL_BINDING_DECLARATION_REGEXES,
1474
+ (declarationMatch) => relocateWGSLApplicationBindingMatch(declarationMatch, usedBindingsByGroup, relocationState)
1475
+ );
1476
+ if (hasWGSLAutoBinding(source4) && !relocationState.sawSupportedBindingDeclaration) {
1477
+ throw new Error(
1478
+ 'Unsupported @binding(auto) declaration form in application WGSL. Use adjacent "@group(N)" and "@binding(auto)" decorators followed by a bindable "var" declaration.'
1479
+ );
1480
+ }
1481
+ return { source: relocatedSource };
1482
+ }
1219
1483
  function relocateWGSLModuleBindings(moduleSource, module, context) {
1220
1484
  const bindingAssignments = [];
1485
+ const declarationMatches = getWGSLBindingDeclarationMatches(
1486
+ moduleSource,
1487
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES
1488
+ );
1221
1489
  const relocationState = {
1222
- sawSupportedBindingDeclaration: false,
1490
+ sawSupportedBindingDeclaration: declarationMatches.length > 0,
1223
1491
  nextHintedBindingLocation: typeof module.firstBindingSlot === "number" ? module.firstBindingSlot : null
1224
1492
  };
1225
- let relocatedSource = relocateWGSLModuleBindingsWithRegex(
1493
+ const relocatedSource = replaceWGSLBindingDeclarationMatches(
1226
1494
  moduleSource,
1227
- MODULE_WGSL_BINDING_DECLARATION_REGEXES[0],
1228
- { isBindingFirst: true, module, context, bindingAssignments, relocationState }
1229
- );
1230
- relocatedSource = relocateWGSLModuleBindingsWithRegex(
1231
- relocatedSource,
1232
- MODULE_WGSL_BINDING_DECLARATION_REGEXES[1],
1233
- { isBindingFirst: false, module, context, bindingAssignments, relocationState }
1495
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES,
1496
+ (declarationMatch) => relocateWGSLModuleBindingMatch(declarationMatch, {
1497
+ module,
1498
+ context,
1499
+ bindingAssignments,
1500
+ relocationState
1501
+ })
1234
1502
  );
1235
- if (moduleSource.includes("@binding(auto)") && !relocationState.sawSupportedBindingDeclaration) {
1503
+ if (hasWGSLAutoBinding(moduleSource) && !relocationState.sawSupportedBindingDeclaration) {
1236
1504
  throw new Error(
1237
- `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.`
1505
+ `Unsupported @binding(auto) declaration form in module "${module.name}". Use adjacent "@group(N)" and "@binding(auto)" decorators followed by a bindable "var" declaration.`
1238
1506
  );
1239
1507
  }
1240
1508
  return { source: relocatedSource, bindingAssignments };
1241
1509
  }
1242
- function relocateWGSLModuleBindingsWithRegex(source4, regex, params) {
1243
- return source4.replace(
1244
- regex,
1245
- (...replaceArguments) => relocateWGSLModuleBindingMatch(replaceArguments, params)
1246
- );
1247
- }
1248
- function relocateWGSLModuleBindingMatch(replaceArguments, params) {
1249
- const { isBindingFirst, module, context, bindingAssignments, relocationState } = params;
1250
- relocationState.sawSupportedBindingDeclaration = true;
1251
- const match = replaceArguments[0];
1252
- const bindingToken = replaceArguments[isBindingFirst ? 1 : 2];
1253
- const groupToken = replaceArguments[isBindingFirst ? 2 : 1];
1254
- const name = replaceArguments[4];
1510
+ function relocateWGSLModuleBindingMatch(declarationMatch, params) {
1511
+ const { module, context, bindingAssignments, relocationState } = params;
1512
+ const { match, bindingToken, groupToken, name } = declarationMatch;
1255
1513
  const group = Number(groupToken);
1256
1514
  if (bindingToken === "auto") {
1257
1515
  const registryKey = getBindingRegistryKey(group, module.name, name);
@@ -1290,6 +1548,23 @@ ${getApplicationDefines(allDefines)}
1290
1548
  bindingAssignments.push({ moduleName: module.name, name, group, location });
1291
1549
  return match;
1292
1550
  }
1551
+ function relocateWGSLApplicationBindingMatch(declarationMatch, usedBindingsByGroup, relocationState) {
1552
+ const { match, bindingToken, groupToken, name } = declarationMatch;
1553
+ const group = Number(groupToken);
1554
+ if (bindingToken === "auto") {
1555
+ const location = allocateApplicationAutoBindingLocation(group, usedBindingsByGroup);
1556
+ validateApplicationWGSLBinding(group, location, name);
1557
+ registerUsedBindingLocation(
1558
+ usedBindingsByGroup,
1559
+ group,
1560
+ location,
1561
+ `application binding "${name}"`
1562
+ );
1563
+ return match.replace(/@binding\(\s*auto\s*\)/, `@binding(${location})`);
1564
+ }
1565
+ relocationState.sawSupportedBindingDeclaration = true;
1566
+ return match;
1567
+ }
1293
1568
  function reserveRegisteredModuleBindings(modules, bindingRegistry, usedBindingsByGroup) {
1294
1569
  const reservedBindingKeysByGroup = /* @__PURE__ */ new Map();
1295
1570
  if (!bindingRegistry) {
@@ -1339,16 +1614,14 @@ ${getApplicationDefines(allDefines)}
1339
1614
  function getModuleWGSLBindingDeclarations(module) {
1340
1615
  const declarations = [];
1341
1616
  const moduleSource = module.source || "";
1342
- for (const regex of MODULE_WGSL_BINDING_DECLARATION_REGEXES) {
1343
- regex.lastIndex = 0;
1344
- let match;
1345
- while (match = regex.exec(moduleSource)) {
1346
- const isBindingFirst = regex === MODULE_WGSL_BINDING_DECLARATION_REGEXES[0];
1347
- declarations.push({
1348
- name: match[4],
1349
- group: Number(match[isBindingFirst ? 2 : 1])
1350
- });
1351
- }
1617
+ for (const match of getWGSLBindingDeclarationMatches(
1618
+ moduleSource,
1619
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES
1620
+ )) {
1621
+ declarations.push({
1622
+ name: match.name,
1623
+ group: Number(match.groupToken)
1624
+ });
1352
1625
  }
1353
1626
  return declarations;
1354
1627
  }
@@ -1384,10 +1657,36 @@ ${getApplicationDefines(allDefines)}
1384
1657
  }
1385
1658
  return nextBinding;
1386
1659
  }
1660
+ function allocateApplicationAutoBindingLocation(group, usedBindingsByGroup) {
1661
+ const usedBindings = usedBindingsByGroup.get(group) || /* @__PURE__ */ new Set();
1662
+ let nextBinding = 0;
1663
+ while (usedBindings.has(nextBinding)) {
1664
+ nextBinding++;
1665
+ }
1666
+ return nextBinding;
1667
+ }
1387
1668
  function assertNoUnresolvedAutoBindings(source4) {
1388
- if (/@binding\(\s*auto\s*\)/.test(source4)) {
1389
- throw new Error("Unresolved @binding(auto) remained in assembled WGSL source.");
1669
+ const unresolvedBinding = getFirstWGSLAutoBindingDeclarationMatch(
1670
+ source4,
1671
+ MODULE_WGSL_BINDING_DECLARATION_REGEXES
1672
+ );
1673
+ if (!unresolvedBinding) {
1674
+ return;
1675
+ }
1676
+ const moduleName = getWGSLModuleNameAtIndex(source4, unresolvedBinding.index);
1677
+ if (moduleName) {
1678
+ throw new Error(
1679
+ `Unresolved @binding(auto) for module "${moduleName}" binding "${unresolvedBinding.name}" remained in assembled WGSL source.`
1680
+ );
1390
1681
  }
1682
+ if (isInApplicationWGSLSection(source4, unresolvedBinding.index)) {
1683
+ throw new Error(
1684
+ `Unresolved @binding(auto) for application binding "${unresolvedBinding.name}" remained in assembled WGSL source.`
1685
+ );
1686
+ }
1687
+ throw new Error(
1688
+ `Unresolved @binding(auto) remained in assembled WGSL source near "${formatWGSLSourceSnippet(unresolvedBinding.match)}".`
1689
+ );
1391
1690
  }
1392
1691
  function formatWGSLBindingAssignmentComments(bindingAssignments) {
1393
1692
  if (bindingAssignments.length === 0) {
@@ -1404,6 +1703,24 @@ ${getApplicationDefines(allDefines)}
1404
1703
  function getBindingRegistryKey(group, moduleName, bindingName) {
1405
1704
  return `${group}:${moduleName}:${bindingName}`;
1406
1705
  }
1706
+ function getWGSLModuleNameAtIndex(source4, index) {
1707
+ const moduleHeaderRegex = /^\/\/ ----- MODULE ([^\n]+) ---------------$/gm;
1708
+ let moduleName;
1709
+ let match;
1710
+ match = moduleHeaderRegex.exec(source4);
1711
+ while (match && match.index <= index) {
1712
+ moduleName = match[1];
1713
+ match = moduleHeaderRegex.exec(source4);
1714
+ }
1715
+ return moduleName;
1716
+ }
1717
+ function isInApplicationWGSLSection(source4, index) {
1718
+ const injectionMarkerIndex = source4.indexOf(INJECT_SHADER_DECLARATIONS);
1719
+ return injectionMarkerIndex >= 0 ? index > injectionMarkerIndex : true;
1720
+ }
1721
+ function formatWGSLSourceSnippet(source4) {
1722
+ return source4.replace(/\s+/g, " ").trim();
1723
+ }
1407
1724
 
1408
1725
  // src/lib/preprocessor/preprocessor.ts
1409
1726
  var DEFINE_NAME_PATTERN = "([a-zA-Z_][a-zA-Z0-9_]*)";
@@ -5546,8 +5863,7 @@ uniform sampler2D pbr_brdfLUT;
5546
5863
  // src/modules/lighting/no-material/dirlight.ts
5547
5864
  var SOURCE_WGSL = (
5548
5865
  /* WGSL */
5549
- `
5550
- struct dirlightUniforms {
5866
+ `struct dirlightUniforms {
5551
5867
  lightDirection: vec3<f32>,
5552
5868
  };
5553
5869
 
@@ -6068,7 +6384,8 @@ fn lighting_getSpecularLightColor(cameraPosition: vec3<f32>, position_worldspace
6068
6384
  var vs3 = (
6069
6385
  /* glsl */
6070
6386
  `out vec3 pbr_vPosition;
6071
- out vec2 pbr_vUV;
6387
+ out vec2 pbr_vUV0;
6388
+ out vec2 pbr_vUV1;
6072
6389
 
6073
6390
  #ifdef HAS_NORMALS
6074
6391
  # ifdef HAS_TANGENTS
@@ -6078,7 +6395,13 @@ out vec3 pbr_vNormal;
6078
6395
  # endif
6079
6396
  #endif
6080
6397
 
6081
- void pbr_setPositionNormalTangentUV(vec4 position, vec4 normal, vec4 tangent, vec2 uv)
6398
+ void pbr_setPositionNormalTangentUV(
6399
+ vec4 position,
6400
+ vec4 normal,
6401
+ vec4 tangent,
6402
+ vec2 uv0,
6403
+ vec2 uv1
6404
+ )
6082
6405
  {
6083
6406
  vec4 pos = pbrProjection.modelMatrix * position;
6084
6407
  pbr_vPosition = vec3(pos.xyz) / pos.w;
@@ -6095,10 +6418,12 @@ void pbr_setPositionNormalTangentUV(vec4 position, vec4 normal, vec4 tangent, ve
6095
6418
  #endif
6096
6419
 
6097
6420
  #ifdef HAS_UV
6098
- pbr_vUV = uv;
6421
+ pbr_vUV0 = uv0;
6099
6422
  #else
6100
- pbr_vUV = vec2(0.,0.);
6423
+ pbr_vUV0 = vec2(0.,0.);
6101
6424
  #endif
6425
+
6426
+ pbr_vUV1 = uv1;
6102
6427
  }
6103
6428
  `
6104
6429
  );
@@ -6174,6 +6499,41 @@ layout(std140) uniform pbrMaterialUniforms {
6174
6499
  vec4 scaleDiffBaseMR;
6175
6500
  vec4 scaleFGDSpec;
6176
6501
  // #endif
6502
+
6503
+ int baseColorUVSet;
6504
+ mat3 baseColorUVTransform;
6505
+ int metallicRoughnessUVSet;
6506
+ mat3 metallicRoughnessUVTransform;
6507
+ int normalUVSet;
6508
+ mat3 normalUVTransform;
6509
+ int occlusionUVSet;
6510
+ mat3 occlusionUVTransform;
6511
+ int emissiveUVSet;
6512
+ mat3 emissiveUVTransform;
6513
+ int specularColorUVSet;
6514
+ mat3 specularColorUVTransform;
6515
+ int specularIntensityUVSet;
6516
+ mat3 specularIntensityUVTransform;
6517
+ int transmissionUVSet;
6518
+ mat3 transmissionUVTransform;
6519
+ int thicknessUVSet;
6520
+ mat3 thicknessUVTransform;
6521
+ int clearcoatUVSet;
6522
+ mat3 clearcoatUVTransform;
6523
+ int clearcoatRoughnessUVSet;
6524
+ mat3 clearcoatRoughnessUVTransform;
6525
+ int clearcoatNormalUVSet;
6526
+ mat3 clearcoatNormalUVTransform;
6527
+ int sheenColorUVSet;
6528
+ mat3 sheenColorUVTransform;
6529
+ int sheenRoughnessUVSet;
6530
+ mat3 sheenRoughnessUVTransform;
6531
+ int iridescenceUVSet;
6532
+ mat3 iridescenceUVTransform;
6533
+ int iridescenceThicknessUVSet;
6534
+ mat3 iridescenceThicknessUVTransform;
6535
+ int anisotropyUVSet;
6536
+ mat3 anisotropyUVTransform;
6177
6537
  } pbrMaterial;
6178
6538
 
6179
6539
  // Samplers
@@ -6231,7 +6591,8 @@ uniform sampler2D pbr_anisotropySampler;
6231
6591
  // Inputs from vertex shader
6232
6592
 
6233
6593
  in vec3 pbr_vPosition;
6234
- in vec2 pbr_vUV;
6594
+ in vec2 pbr_vUV0;
6595
+ in vec2 pbr_vUV1;
6235
6596
 
6236
6597
  #ifdef HAS_NORMALS
6237
6598
  #ifdef HAS_TANGENTS
@@ -6281,14 +6642,20 @@ vec4 SRGBtoLINEAR(vec4 srgbIn)
6281
6642
  #endif //MANUAL_SRGB
6282
6643
  }
6283
6644
 
6645
+ vec2 getMaterialUV(int uvSet, mat3 uvTransform)
6646
+ {
6647
+ vec2 baseUV = uvSet == 1 ? pbr_vUV1 : pbr_vUV0;
6648
+ return (uvTransform * vec3(baseUV, 1.0)).xy;
6649
+ }
6650
+
6284
6651
  // Build the tangent basis from interpolated attributes or screen-space derivatives.
6285
- mat3 getTBN()
6652
+ mat3 getTBN(vec2 uv)
6286
6653
  {
6287
6654
  #ifndef HAS_TANGENTS
6288
6655
  vec3 pos_dx = dFdx(pbr_vPosition);
6289
6656
  vec3 pos_dy = dFdy(pbr_vPosition);
6290
- vec3 tex_dx = dFdx(vec3(pbr_vUV, 0.0));
6291
- vec3 tex_dy = dFdy(vec3(pbr_vUV, 0.0));
6657
+ vec3 tex_dx = dFdx(vec3(uv, 0.0));
6658
+ vec3 tex_dy = dFdy(vec3(uv, 0.0));
6292
6659
  vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);
6293
6660
 
6294
6661
  #ifdef HAS_NORMALS
@@ -6309,16 +6676,16 @@ mat3 getTBN()
6309
6676
 
6310
6677
  // Find the normal for this fragment, pulling either from a predefined normal map
6311
6678
  // or from the interpolated mesh normal and tangent attributes.
6312
- vec3 getMappedNormal(sampler2D normalSampler, mat3 tbn, float normalScale)
6679
+ vec3 getMappedNormal(sampler2D normalSampler, mat3 tbn, float normalScale, vec2 uv)
6313
6680
  {
6314
- vec3 n = texture(normalSampler, pbr_vUV).rgb;
6681
+ vec3 n = texture(normalSampler, uv).rgb;
6315
6682
  return normalize(tbn * ((2.0 * n - 1.0) * vec3(normalScale, normalScale, 1.0)));
6316
6683
  }
6317
6684
 
6318
- vec3 getNormal(mat3 tbn)
6685
+ vec3 getNormal(mat3 tbn, vec2 uv)
6319
6686
  {
6320
6687
  #ifdef HAS_NORMALMAP
6321
- vec3 n = getMappedNormal(pbr_normalSampler, tbn, pbrMaterial.normalScale);
6688
+ vec3 n = getMappedNormal(pbr_normalSampler, tbn, pbrMaterial.normalScale, uv);
6322
6689
  #else
6323
6690
  // The tbn matrix is linearly interpolated, so we need to re-normalize
6324
6691
  vec3 n = normalize(tbn[2].xyz);
@@ -6327,10 +6694,10 @@ vec3 getNormal(mat3 tbn)
6327
6694
  return n;
6328
6695
  }
6329
6696
 
6330
- vec3 getClearcoatNormal(mat3 tbn, vec3 baseNormal)
6697
+ vec3 getClearcoatNormal(mat3 tbn, vec3 baseNormal, vec2 uv)
6331
6698
  {
6332
6699
  #ifdef HAS_CLEARCOATNORMALMAP
6333
- return getMappedNormal(pbr_clearcoatNormalSampler, tbn, 1.0);
6700
+ return getMappedNormal(pbr_clearcoatNormalSampler, tbn, 1.0, uv);
6334
6701
  #else
6335
6702
  return baseNormal;
6336
6703
  #endif
@@ -6618,9 +6985,61 @@ vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) {
6618
6985
 
6619
6986
  vec4 pbr_filterColor(vec4 colorUnused)
6620
6987
  {
6988
+ vec2 baseColorUV = getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform);
6989
+ vec2 metallicRoughnessUV = getMaterialUV(
6990
+ pbrMaterial.metallicRoughnessUVSet,
6991
+ pbrMaterial.metallicRoughnessUVTransform
6992
+ );
6993
+ vec2 normalUV = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
6994
+ vec2 occlusionUV = getMaterialUV(pbrMaterial.occlusionUVSet, pbrMaterial.occlusionUVTransform);
6995
+ vec2 emissiveUV = getMaterialUV(pbrMaterial.emissiveUVSet, pbrMaterial.emissiveUVTransform);
6996
+ vec2 specularColorUV = getMaterialUV(
6997
+ pbrMaterial.specularColorUVSet,
6998
+ pbrMaterial.specularColorUVTransform
6999
+ );
7000
+ vec2 specularIntensityUV = getMaterialUV(
7001
+ pbrMaterial.specularIntensityUVSet,
7002
+ pbrMaterial.specularIntensityUVTransform
7003
+ );
7004
+ vec2 transmissionUV = getMaterialUV(
7005
+ pbrMaterial.transmissionUVSet,
7006
+ pbrMaterial.transmissionUVTransform
7007
+ );
7008
+ vec2 thicknessUV = getMaterialUV(pbrMaterial.thicknessUVSet, pbrMaterial.thicknessUVTransform);
7009
+ vec2 clearcoatUV = getMaterialUV(pbrMaterial.clearcoatUVSet, pbrMaterial.clearcoatUVTransform);
7010
+ vec2 clearcoatRoughnessUV = getMaterialUV(
7011
+ pbrMaterial.clearcoatRoughnessUVSet,
7012
+ pbrMaterial.clearcoatRoughnessUVTransform
7013
+ );
7014
+ vec2 clearcoatNormalUV = getMaterialUV(
7015
+ pbrMaterial.clearcoatNormalUVSet,
7016
+ pbrMaterial.clearcoatNormalUVTransform
7017
+ );
7018
+ vec2 sheenColorUV = getMaterialUV(
7019
+ pbrMaterial.sheenColorUVSet,
7020
+ pbrMaterial.sheenColorUVTransform
7021
+ );
7022
+ vec2 sheenRoughnessUV = getMaterialUV(
7023
+ pbrMaterial.sheenRoughnessUVSet,
7024
+ pbrMaterial.sheenRoughnessUVTransform
7025
+ );
7026
+ vec2 iridescenceUV = getMaterialUV(
7027
+ pbrMaterial.iridescenceUVSet,
7028
+ pbrMaterial.iridescenceUVTransform
7029
+ );
7030
+ vec2 iridescenceThicknessUV = getMaterialUV(
7031
+ pbrMaterial.iridescenceThicknessUVSet,
7032
+ pbrMaterial.iridescenceThicknessUVTransform
7033
+ );
7034
+ vec2 anisotropyUV = getMaterialUV(
7035
+ pbrMaterial.anisotropyUVSet,
7036
+ pbrMaterial.anisotropyUVTransform
7037
+ );
7038
+
6621
7039
  // The albedo may be defined from a base texture or a flat color
6622
7040
  #ifdef HAS_BASECOLORMAP
6623
- vec4 baseColor = SRGBtoLINEAR(texture(pbr_baseColorSampler, pbr_vUV)) * pbrMaterial.baseColorFactor;
7041
+ vec4 baseColor =
7042
+ SRGBtoLINEAR(texture(pbr_baseColorSampler, baseColorUV)) * pbrMaterial.baseColorFactor;
6624
7043
  #else
6625
7044
  vec4 baseColor = pbrMaterial.baseColorFactor;
6626
7045
  #endif
@@ -6647,14 +7066,14 @@ vec4 pbr_filterColor(vec4 colorUnused)
6647
7066
  #ifdef HAS_METALROUGHNESSMAP
6648
7067
  // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
6649
7068
  // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
6650
- vec4 mrSample = texture(pbr_metallicRoughnessSampler, pbr_vUV);
7069
+ vec4 mrSample = texture(pbr_metallicRoughnessSampler, metallicRoughnessUV);
6651
7070
  perceptualRoughness = mrSample.g * perceptualRoughness;
6652
7071
  metallic = mrSample.b * metallic;
6653
7072
  #endif
6654
7073
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
6655
7074
  metallic = clamp(metallic, 0.0, 1.0);
6656
- mat3 tbn = getTBN();
6657
- vec3 n = getNormal(tbn); // normal at surface point
7075
+ mat3 tbn = getTBN(normalUV);
7076
+ vec3 n = getNormal(tbn, normalUV); // normal at surface point
6658
7077
  vec3 v = normalize(pbrProjection.camera - pbr_vPosition); // Vector from surface point to camera
6659
7078
  float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
6660
7079
  #ifdef USE_MATERIAL_EXTENSIONS
@@ -6755,7 +7174,7 @@ vec4 pbr_filterColor(vec4 colorUnused)
6755
7174
 
6756
7175
  #ifdef HAS_OCCLUSIONMAP
6757
7176
  if (pbrMaterial.occlusionMapEnabled) {
6758
- float ao = texture(pbr_occlusionSampler, pbr_vUV).r;
7177
+ float ao = texture(pbr_occlusionSampler, occlusionUV).r;
6759
7178
  color = mix(color, color * ao, pbrMaterial.occlusionStrength);
6760
7179
  }
6761
7180
  #endif
@@ -6763,7 +7182,7 @@ vec4 pbr_filterColor(vec4 colorUnused)
6763
7182
  vec3 emissive = pbrMaterial.emissiveFactor;
6764
7183
  #ifdef HAS_EMISSIVEMAP
6765
7184
  if (pbrMaterial.emissiveMapEnabled) {
6766
- emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
7185
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, emissiveUV)).rgb;
6767
7186
  }
6768
7187
  #endif
6769
7188
  color += emissive * pbrMaterial.emissiveStrength;
@@ -6780,55 +7199,55 @@ vec4 pbr_filterColor(vec4 colorUnused)
6780
7199
  float specularIntensity = pbrMaterial.specularIntensityFactor;
6781
7200
  #ifdef HAS_SPECULARINTENSITYMAP
6782
7201
  if (pbrMaterial.specularIntensityMapEnabled) {
6783
- specularIntensity *= texture(pbr_specularIntensitySampler, pbr_vUV).a;
7202
+ specularIntensity *= texture(pbr_specularIntensitySampler, specularIntensityUV).a;
6784
7203
  }
6785
7204
  #endif
6786
7205
 
6787
7206
  vec3 specularFactor = pbrMaterial.specularColorFactor;
6788
7207
  #ifdef HAS_SPECULARCOLORMAP
6789
7208
  if (pbrMaterial.specularColorMapEnabled) {
6790
- specularFactor *= SRGBtoLINEAR(texture(pbr_specularColorSampler, pbr_vUV)).rgb;
7209
+ specularFactor *= SRGBtoLINEAR(texture(pbr_specularColorSampler, specularColorUV)).rgb;
6791
7210
  }
6792
7211
  #endif
6793
7212
 
6794
7213
  transmission = pbrMaterial.transmissionFactor;
6795
7214
  #ifdef HAS_TRANSMISSIONMAP
6796
7215
  if (pbrMaterial.transmissionMapEnabled) {
6797
- transmission *= texture(pbr_transmissionSampler, pbr_vUV).r;
7216
+ transmission *= texture(pbr_transmissionSampler, transmissionUV).r;
6798
7217
  }
6799
7218
  #endif
6800
7219
  transmission = clamp(transmission * (1.0 - metallic), 0.0, 1.0);
6801
7220
  float thickness = max(pbrMaterial.thicknessFactor, 0.0);
6802
7221
  #ifdef HAS_THICKNESSMAP
6803
- thickness *= texture(pbr_thicknessSampler, pbr_vUV).g;
7222
+ thickness *= texture(pbr_thicknessSampler, thicknessUV).g;
6804
7223
  #endif
6805
7224
 
6806
7225
  float clearcoatFactor = pbrMaterial.clearcoatFactor;
6807
7226
  float clearcoatRoughness = pbrMaterial.clearcoatRoughnessFactor;
6808
7227
  #ifdef HAS_CLEARCOATMAP
6809
7228
  if (pbrMaterial.clearcoatMapEnabled) {
6810
- clearcoatFactor *= texture(pbr_clearcoatSampler, pbr_vUV).r;
7229
+ clearcoatFactor *= texture(pbr_clearcoatSampler, clearcoatUV).r;
6811
7230
  }
6812
7231
  #endif
6813
7232
  #ifdef HAS_CLEARCOATROUGHNESSMAP
6814
7233
  if (pbrMaterial.clearcoatRoughnessMapEnabled) {
6815
- clearcoatRoughness *= texture(pbr_clearcoatRoughnessSampler, pbr_vUV).g;
7234
+ clearcoatRoughness *= texture(pbr_clearcoatRoughnessSampler, clearcoatRoughnessUV).g;
6816
7235
  }
6817
7236
  #endif
6818
7237
  clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
6819
7238
  clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
6820
- vec3 clearcoatNormal = getClearcoatNormal(tbn, n);
7239
+ vec3 clearcoatNormal = getClearcoatNormal(getTBN(clearcoatNormalUV), n, clearcoatNormalUV);
6821
7240
 
6822
7241
  vec3 sheenColor = pbrMaterial.sheenColorFactor;
6823
7242
  float sheenRoughness = pbrMaterial.sheenRoughnessFactor;
6824
7243
  #ifdef HAS_SHEENCOLORMAP
6825
7244
  if (pbrMaterial.sheenColorMapEnabled) {
6826
- sheenColor *= SRGBtoLINEAR(texture(pbr_sheenColorSampler, pbr_vUV)).rgb;
7245
+ sheenColor *= SRGBtoLINEAR(texture(pbr_sheenColorSampler, sheenColorUV)).rgb;
6827
7246
  }
6828
7247
  #endif
6829
7248
  #ifdef HAS_SHEENROUGHNESSMAP
6830
7249
  if (pbrMaterial.sheenRoughnessMapEnabled) {
6831
- sheenRoughness *= texture(pbr_sheenRoughnessSampler, pbr_vUV).a;
7250
+ sheenRoughness *= texture(pbr_sheenRoughnessSampler, sheenRoughnessUV).a;
6832
7251
  }
6833
7252
  #endif
6834
7253
  sheenRoughness = clamp(sheenRoughness, c_MinRoughness, 1.0);
@@ -6836,7 +7255,7 @@ vec4 pbr_filterColor(vec4 colorUnused)
6836
7255
  float iridescence = pbrMaterial.iridescenceFactor;
6837
7256
  #ifdef HAS_IRIDESCENCEMAP
6838
7257
  if (pbrMaterial.iridescenceMapEnabled) {
6839
- iridescence *= texture(pbr_iridescenceSampler, pbr_vUV).r;
7258
+ iridescence *= texture(pbr_iridescenceSampler, iridescenceUV).r;
6840
7259
  }
6841
7260
  #endif
6842
7261
  iridescence = clamp(iridescence, 0.0, 1.0);
@@ -6849,7 +7268,7 @@ vec4 pbr_filterColor(vec4 colorUnused)
6849
7268
  iridescenceThickness = mix(
6850
7269
  pbrMaterial.iridescenceThicknessRange.x,
6851
7270
  pbrMaterial.iridescenceThicknessRange.y,
6852
- texture(pbr_iridescenceThicknessSampler, pbr_vUV).g
7271
+ texture(pbr_iridescenceThicknessSampler, iridescenceThicknessUV).g
6853
7272
  );
6854
7273
  #endif
6855
7274
 
@@ -6857,7 +7276,7 @@ vec4 pbr_filterColor(vec4 colorUnused)
6857
7276
  vec2 anisotropyDirection = normalizeDirection(pbrMaterial.anisotropyDirection);
6858
7277
  #ifdef HAS_ANISOTROPYMAP
6859
7278
  if (pbrMaterial.anisotropyMapEnabled) {
6860
- vec3 anisotropySample = texture(pbr_anisotropySampler, pbr_vUV).rgb;
7279
+ vec3 anisotropySample = texture(pbr_anisotropySampler, anisotropyUV).rgb;
6861
7280
  anisotropyStrength *= anisotropySample.b;
6862
7281
  vec2 mappedDirection = anisotropySample.rg * 2.0 - 1.0;
6863
7282
  if (length(mappedDirection) > 0.0001) {
@@ -7020,7 +7439,7 @@ vec4 pbr_filterColor(vec4 colorUnused)
7020
7439
  // Apply optional PBR terms for additional (optional) shading
7021
7440
  #ifdef HAS_OCCLUSIONMAP
7022
7441
  if (pbrMaterial.occlusionMapEnabled) {
7023
- float ao = texture(pbr_occlusionSampler, pbr_vUV).r;
7442
+ float ao = texture(pbr_occlusionSampler, occlusionUV).r;
7024
7443
  color = mix(color, color * ao, pbrMaterial.occlusionStrength);
7025
7444
  }
7026
7445
  #endif
@@ -7028,7 +7447,7 @@ vec4 pbr_filterColor(vec4 colorUnused)
7028
7447
  vec3 emissive = pbrMaterial.emissiveFactor;
7029
7448
  #ifdef HAS_EMISSIVEMAP
7030
7449
  if (pbrMaterial.emissiveMapEnabled) {
7031
- emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, pbr_vUV)).rgb;
7450
+ emissive *= SRGBtoLINEAR(texture(pbr_emissiveSampler, emissiveUV)).rgb;
7032
7451
  }
7033
7452
  #endif
7034
7453
  color += emissive * pbrMaterial.emissiveStrength;
@@ -7066,14 +7485,21 @@ vec4 pbr_filterColor(vec4 colorUnused)
7066
7485
  /* wgsl */
7067
7486
  `struct PBRFragmentInputs {
7068
7487
  pbr_vPosition: vec3f,
7069
- pbr_vUV: vec2f,
7488
+ pbr_vUV0: vec2f,
7489
+ pbr_vUV1: vec2f,
7070
7490
  pbr_vTBN: mat3x3f,
7071
7491
  pbr_vNormal: vec3f
7072
7492
  };
7073
7493
 
7074
7494
  var<private> fragmentInputs: PBRFragmentInputs;
7075
7495
 
7076
- fn pbr_setPositionNormalTangentUV(position: vec4f, normal: vec4f, tangent: vec4f, uv: vec2f)
7496
+ fn pbr_setPositionNormalTangentUV(
7497
+ position: vec4f,
7498
+ normal: vec4f,
7499
+ tangent: vec4f,
7500
+ uv0: vec2f,
7501
+ uv1: vec2f
7502
+ )
7077
7503
  {
7078
7504
  var pos: vec4f = pbrProjection.modelMatrix * position;
7079
7505
  fragmentInputs.pbr_vPosition = pos.xyz / pos.w;
@@ -7083,7 +7509,8 @@ fn pbr_setPositionNormalTangentUV(position: vec4f, normal: vec4f, tangent: vec4f
7083
7509
  vec3f(0.0, 1.0, 0.0),
7084
7510
  vec3f(0.0, 0.0, 1.0)
7085
7511
  );
7086
- fragmentInputs.pbr_vUV = vec2f(0.0, 0.0);
7512
+ fragmentInputs.pbr_vUV0 = vec2f(0.0, 0.0);
7513
+ fragmentInputs.pbr_vUV1 = uv1;
7087
7514
 
7088
7515
  #ifdef HAS_NORMALS
7089
7516
  let normalW: vec3f = normalize((pbrProjection.normalMatrix * vec4f(normal.xyz, 0.0)).xyz);
@@ -7096,7 +7523,7 @@ fn pbr_setPositionNormalTangentUV(position: vec4f, normal: vec4f, tangent: vec4f
7096
7523
  #endif
7097
7524
 
7098
7525
  #ifdef HAS_UV
7099
- fragmentInputs.pbr_vUV = uv;
7526
+ fragmentInputs.pbr_vUV0 = uv0;
7100
7527
  #endif
7101
7528
  }
7102
7529
 
@@ -7168,6 +7595,41 @@ struct pbrMaterialUniforms {
7168
7595
  scaleDiffBaseMR: vec4f,
7169
7596
  scaleFGDSpec: vec4f,
7170
7597
  // #endif
7598
+
7599
+ baseColorUVSet: i32,
7600
+ baseColorUVTransform: mat3x3f,
7601
+ metallicRoughnessUVSet: i32,
7602
+ metallicRoughnessUVTransform: mat3x3f,
7603
+ normalUVSet: i32,
7604
+ normalUVTransform: mat3x3f,
7605
+ occlusionUVSet: i32,
7606
+ occlusionUVTransform: mat3x3f,
7607
+ emissiveUVSet: i32,
7608
+ emissiveUVTransform: mat3x3f,
7609
+ specularColorUVSet: i32,
7610
+ specularColorUVTransform: mat3x3f,
7611
+ specularIntensityUVSet: i32,
7612
+ specularIntensityUVTransform: mat3x3f,
7613
+ transmissionUVSet: i32,
7614
+ transmissionUVTransform: mat3x3f,
7615
+ thicknessUVSet: i32,
7616
+ thicknessUVTransform: mat3x3f,
7617
+ clearcoatUVSet: i32,
7618
+ clearcoatUVTransform: mat3x3f,
7619
+ clearcoatRoughnessUVSet: i32,
7620
+ clearcoatRoughnessUVTransform: mat3x3f,
7621
+ clearcoatNormalUVSet: i32,
7622
+ clearcoatNormalUVTransform: mat3x3f,
7623
+ sheenColorUVSet: i32,
7624
+ sheenColorUVTransform: mat3x3f,
7625
+ sheenRoughnessUVSet: i32,
7626
+ sheenRoughnessUVTransform: mat3x3f,
7627
+ iridescenceUVSet: i32,
7628
+ iridescenceUVTransform: mat3x3f,
7629
+ iridescenceThicknessUVSet: i32,
7630
+ iridescenceThicknessUVTransform: mat3x3f,
7631
+ anisotropyUVSet: i32,
7632
+ anisotropyUVTransform: mat3x3f,
7171
7633
  }
7172
7634
 
7173
7635
  @group(3) @binding(auto) var<uniform> pbrMaterial : pbrMaterialUniforms;
@@ -7281,13 +7743,22 @@ fn SRGBtoLINEAR(srgbIn: vec4f ) -> vec4f
7281
7743
  return vec4f(linOut, srgbIn.w);
7282
7744
  }
7283
7745
 
7746
+ fn getMaterialUV(uvSet: i32, uvTransform: mat3x3f) -> vec2f
7747
+ {
7748
+ var baseUV = fragmentInputs.pbr_vUV0;
7749
+ if (uvSet == 1) {
7750
+ baseUV = fragmentInputs.pbr_vUV1;
7751
+ }
7752
+ return (uvTransform * vec3f(baseUV, 1.0)).xy;
7753
+ }
7754
+
7284
7755
  // Build the tangent basis from interpolated attributes or screen-space derivatives.
7285
- fn getTBN() -> mat3x3f
7756
+ fn getTBN(uv: vec2f) -> mat3x3f
7286
7757
  {
7287
7758
  let pos_dx: vec3f = dpdx(fragmentInputs.pbr_vPosition);
7288
7759
  let pos_dy: vec3f = dpdy(fragmentInputs.pbr_vPosition);
7289
- let tex_dx: vec3f = dpdx(vec3f(fragmentInputs.pbr_vUV, 0.0));
7290
- let tex_dy: vec3f = dpdy(vec3f(fragmentInputs.pbr_vUV, 0.0));
7760
+ let tex_dx: vec3f = dpdx(vec3f(uv, 0.0));
7761
+ let tex_dy: vec3f = dpdy(vec3f(uv, 0.0));
7291
7762
  var t: vec3f = (tex_dy.y * pos_dx - tex_dx.y * pos_dy) / (tex_dx.x * tex_dy.y - tex_dy.x * tex_dx.y);
7292
7763
 
7293
7764
  var ng: vec3f = cross(pos_dx, pos_dy);
@@ -7310,14 +7781,15 @@ fn getMappedNormal(
7310
7781
  normalSampler: texture_2d<f32>,
7311
7782
  normalSamplerBinding: sampler,
7312
7783
  tbn: mat3x3f,
7313
- normalScale: f32
7784
+ normalScale: f32,
7785
+ uv: vec2f
7314
7786
  ) -> vec3f
7315
7787
  {
7316
- let n = textureSample(normalSampler, normalSamplerBinding, fragmentInputs.pbr_vUV).rgb;
7788
+ let n = textureSample(normalSampler, normalSamplerBinding, uv).rgb;
7317
7789
  return normalize(tbn * ((2.0 * n - 1.0) * vec3f(normalScale, normalScale, 1.0)));
7318
7790
  }
7319
7791
 
7320
- fn getNormal(tbn: mat3x3f) -> vec3f
7792
+ fn getNormal(tbn: mat3x3f, uv: vec2f) -> vec3f
7321
7793
  {
7322
7794
  // The tbn matrix is linearly interpolated, so we need to re-normalize
7323
7795
  var n: vec3f = normalize(tbn[2].xyz);
@@ -7326,21 +7798,23 @@ fn getNormal(tbn: mat3x3f) -> vec3f
7326
7798
  pbr_normalSampler,
7327
7799
  pbr_normalSamplerSampler,
7328
7800
  tbn,
7329
- pbrMaterial.normalScale
7801
+ pbrMaterial.normalScale,
7802
+ uv
7330
7803
  );
7331
7804
  #endif
7332
7805
 
7333
7806
  return n;
7334
7807
  }
7335
7808
 
7336
- fn getClearcoatNormal(tbn: mat3x3f, baseNormal: vec3f) -> vec3f
7809
+ fn getClearcoatNormal(tbn: mat3x3f, baseNormal: vec3f, uv: vec2f) -> vec3f
7337
7810
  {
7338
7811
  #ifdef HAS_CLEARCOATNORMALMAP
7339
7812
  return getMappedNormal(
7340
7813
  pbr_clearcoatNormalSampler,
7341
7814
  pbr_clearcoatNormalSamplerSampler,
7342
7815
  tbn,
7343
- 1.0
7816
+ 1.0,
7817
+ uv
7344
7818
  );
7345
7819
  #else
7346
7820
  return baseNormal;
@@ -7645,11 +8119,62 @@ fn calculateFinalColor(pbrInfo: PBRInfo, lightColor: vec3<f32>) -> vec3<f32> {
7645
8119
  }
7646
8120
 
7647
8121
  fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
8122
+ let baseColorUV = getMaterialUV(pbrMaterial.baseColorUVSet, pbrMaterial.baseColorUVTransform);
8123
+ let metallicRoughnessUV = getMaterialUV(
8124
+ pbrMaterial.metallicRoughnessUVSet,
8125
+ pbrMaterial.metallicRoughnessUVTransform
8126
+ );
8127
+ let normalUV = getMaterialUV(pbrMaterial.normalUVSet, pbrMaterial.normalUVTransform);
8128
+ let occlusionUV = getMaterialUV(pbrMaterial.occlusionUVSet, pbrMaterial.occlusionUVTransform);
8129
+ let emissiveUV = getMaterialUV(pbrMaterial.emissiveUVSet, pbrMaterial.emissiveUVTransform);
8130
+ let specularColorUV = getMaterialUV(
8131
+ pbrMaterial.specularColorUVSet,
8132
+ pbrMaterial.specularColorUVTransform
8133
+ );
8134
+ let specularIntensityUV = getMaterialUV(
8135
+ pbrMaterial.specularIntensityUVSet,
8136
+ pbrMaterial.specularIntensityUVTransform
8137
+ );
8138
+ let transmissionUV = getMaterialUV(
8139
+ pbrMaterial.transmissionUVSet,
8140
+ pbrMaterial.transmissionUVTransform
8141
+ );
8142
+ let thicknessUV = getMaterialUV(pbrMaterial.thicknessUVSet, pbrMaterial.thicknessUVTransform);
8143
+ let clearcoatUV = getMaterialUV(pbrMaterial.clearcoatUVSet, pbrMaterial.clearcoatUVTransform);
8144
+ let clearcoatRoughnessUV = getMaterialUV(
8145
+ pbrMaterial.clearcoatRoughnessUVSet,
8146
+ pbrMaterial.clearcoatRoughnessUVTransform
8147
+ );
8148
+ let clearcoatNormalUV = getMaterialUV(
8149
+ pbrMaterial.clearcoatNormalUVSet,
8150
+ pbrMaterial.clearcoatNormalUVTransform
8151
+ );
8152
+ let sheenColorUV = getMaterialUV(
8153
+ pbrMaterial.sheenColorUVSet,
8154
+ pbrMaterial.sheenColorUVTransform
8155
+ );
8156
+ let sheenRoughnessUV = getMaterialUV(
8157
+ pbrMaterial.sheenRoughnessUVSet,
8158
+ pbrMaterial.sheenRoughnessUVTransform
8159
+ );
8160
+ let iridescenceUV = getMaterialUV(
8161
+ pbrMaterial.iridescenceUVSet,
8162
+ pbrMaterial.iridescenceUVTransform
8163
+ );
8164
+ let iridescenceThicknessUV = getMaterialUV(
8165
+ pbrMaterial.iridescenceThicknessUVSet,
8166
+ pbrMaterial.iridescenceThicknessUVTransform
8167
+ );
8168
+ let anisotropyUV = getMaterialUV(
8169
+ pbrMaterial.anisotropyUVSet,
8170
+ pbrMaterial.anisotropyUVTransform
8171
+ );
8172
+
7648
8173
  // The albedo may be defined from a base texture or a flat color
7649
8174
  var baseColor: vec4<f32> = pbrMaterial.baseColorFactor;
7650
8175
  #ifdef HAS_BASECOLORMAP
7651
8176
  baseColor = SRGBtoLINEAR(
7652
- textureSample(pbr_baseColorSampler, pbr_baseColorSamplerSampler, fragmentInputs.pbr_vUV)
8177
+ textureSample(pbr_baseColorSampler, pbr_baseColorSamplerSampler, baseColorUV)
7653
8178
  ) * pbrMaterial.baseColorFactor;
7654
8179
  #endif
7655
8180
 
@@ -7676,15 +8201,15 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7676
8201
  let mrSample = textureSample(
7677
8202
  pbr_metallicRoughnessSampler,
7678
8203
  pbr_metallicRoughnessSamplerSampler,
7679
- fragmentInputs.pbr_vUV
8204
+ metallicRoughnessUV
7680
8205
  );
7681
8206
  perceptualRoughness = mrSample.g * perceptualRoughness;
7682
8207
  metallic = mrSample.b * metallic;
7683
8208
  #endif
7684
8209
  perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
7685
8210
  metallic = clamp(metallic, 0.0, 1.0);
7686
- let tbn = getTBN();
7687
- let n = getNormal(tbn); // normal at surface point
8211
+ let tbn = getTBN(normalUV);
8212
+ let n = getNormal(tbn, normalUV); // normal at surface point
7688
8213
  let v = normalize(pbrProjection.camera - fragmentInputs.pbr_vPosition); // Vector from surface point to camera
7689
8214
  let NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
7690
8215
  var useExtendedPBR = false;
@@ -7789,8 +8314,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7789
8314
 
7790
8315
  #ifdef HAS_OCCLUSIONMAP
7791
8316
  if (pbrMaterial.occlusionMapEnabled != 0) {
7792
- let ao =
7793
- textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, fragmentInputs.pbr_vUV).r;
8317
+ let ao = textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, occlusionUV).r;
7794
8318
  color = mix(color, color * ao, pbrMaterial.occlusionStrength);
7795
8319
  }
7796
8320
  #endif
@@ -7799,7 +8323,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7799
8323
  #ifdef HAS_EMISSIVEMAP
7800
8324
  if (pbrMaterial.emissiveMapEnabled != 0u) {
7801
8325
  emissive *= SRGBtoLINEAR(
7802
- textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
8326
+ textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, emissiveUV)
7803
8327
  ).rgb;
7804
8328
  }
7805
8329
  #endif
@@ -7820,7 +8344,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7820
8344
  specularIntensity *= textureSample(
7821
8345
  pbr_specularIntensitySampler,
7822
8346
  pbr_specularIntensitySamplerSampler,
7823
- fragmentInputs.pbr_vUV
8347
+ specularIntensityUV
7824
8348
  ).a;
7825
8349
  }
7826
8350
  #endif
@@ -7832,7 +8356,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7832
8356
  textureSample(
7833
8357
  pbr_specularColorSampler,
7834
8358
  pbr_specularColorSamplerSampler,
7835
- fragmentInputs.pbr_vUV
8359
+ specularColorUV
7836
8360
  )
7837
8361
  ).rgb;
7838
8362
  }
@@ -7844,7 +8368,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7844
8368
  transmission *= textureSample(
7845
8369
  pbr_transmissionSampler,
7846
8370
  pbr_transmissionSamplerSampler,
7847
- fragmentInputs.pbr_vUV
8371
+ transmissionUV
7848
8372
  ).r;
7849
8373
  }
7850
8374
  #endif
@@ -7854,7 +8378,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7854
8378
  thickness *= textureSample(
7855
8379
  pbr_thicknessSampler,
7856
8380
  pbr_thicknessSamplerSampler,
7857
- fragmentInputs.pbr_vUV
8381
+ thicknessUV
7858
8382
  ).g;
7859
8383
  #endif
7860
8384
 
@@ -7865,7 +8389,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7865
8389
  clearcoatFactor *= textureSample(
7866
8390
  pbr_clearcoatSampler,
7867
8391
  pbr_clearcoatSamplerSampler,
7868
- fragmentInputs.pbr_vUV
8392
+ clearcoatUV
7869
8393
  ).r;
7870
8394
  }
7871
8395
  #endif
@@ -7874,13 +8398,13 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7874
8398
  clearcoatRoughness *= textureSample(
7875
8399
  pbr_clearcoatRoughnessSampler,
7876
8400
  pbr_clearcoatRoughnessSamplerSampler,
7877
- fragmentInputs.pbr_vUV
8401
+ clearcoatRoughnessUV
7878
8402
  ).g;
7879
8403
  }
7880
8404
  #endif
7881
8405
  clearcoatFactor = clamp(clearcoatFactor, 0.0, 1.0);
7882
8406
  clearcoatRoughness = clamp(clearcoatRoughness, c_MinRoughness, 1.0);
7883
- let clearcoatNormal = getClearcoatNormal(tbn, n);
8407
+ let clearcoatNormal = getClearcoatNormal(getTBN(clearcoatNormalUV), n, clearcoatNormalUV);
7884
8408
 
7885
8409
  var sheenColor = pbrMaterial.sheenColorFactor;
7886
8410
  var sheenRoughness = pbrMaterial.sheenRoughnessFactor;
@@ -7890,7 +8414,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7890
8414
  textureSample(
7891
8415
  pbr_sheenColorSampler,
7892
8416
  pbr_sheenColorSamplerSampler,
7893
- fragmentInputs.pbr_vUV
8417
+ sheenColorUV
7894
8418
  )
7895
8419
  ).rgb;
7896
8420
  }
@@ -7900,7 +8424,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7900
8424
  sheenRoughness *= textureSample(
7901
8425
  pbr_sheenRoughnessSampler,
7902
8426
  pbr_sheenRoughnessSamplerSampler,
7903
- fragmentInputs.pbr_vUV
8427
+ sheenRoughnessUV
7904
8428
  ).a;
7905
8429
  }
7906
8430
  #endif
@@ -7912,7 +8436,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7912
8436
  iridescence *= textureSample(
7913
8437
  pbr_iridescenceSampler,
7914
8438
  pbr_iridescenceSamplerSampler,
7915
- fragmentInputs.pbr_vUV
8439
+ iridescenceUV
7916
8440
  ).r;
7917
8441
  }
7918
8442
  #endif
@@ -7929,7 +8453,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7929
8453
  textureSample(
7930
8454
  pbr_iridescenceThicknessSampler,
7931
8455
  pbr_iridescenceThicknessSamplerSampler,
7932
- fragmentInputs.pbr_vUV
8456
+ iridescenceThicknessUV
7933
8457
  ).g
7934
8458
  );
7935
8459
  #endif
@@ -7941,7 +8465,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
7941
8465
  let anisotropySample = textureSample(
7942
8466
  pbr_anisotropySampler,
7943
8467
  pbr_anisotropySamplerSampler,
7944
- fragmentInputs.pbr_vUV
8468
+ anisotropyUV
7945
8469
  ).rgb;
7946
8470
  anisotropyStrength *= anisotropySample.b;
7947
8471
  let mappedDirection = anisotropySample.rg * 2.0 - 1.0;
@@ -8108,8 +8632,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
8108
8632
  // Apply optional PBR terms for additional (optional) shading
8109
8633
  #ifdef HAS_OCCLUSIONMAP
8110
8634
  if (pbrMaterial.occlusionMapEnabled != 0) {
8111
- let ao =
8112
- textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, fragmentInputs.pbr_vUV).r;
8635
+ let ao = textureSample(pbr_occlusionSampler, pbr_occlusionSamplerSampler, occlusionUV).r;
8113
8636
  color = mix(color, color * ao, pbrMaterial.occlusionStrength);
8114
8637
  }
8115
8638
  #endif
@@ -8118,7 +8641,7 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
8118
8641
  #ifdef HAS_EMISSIVEMAP
8119
8642
  if (pbrMaterial.emissiveMapEnabled != 0u) {
8120
8643
  emissive *= SRGBtoLINEAR(
8121
- textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, fragmentInputs.pbr_vUV)
8644
+ textureSample(pbr_emissiveSampler, pbr_emissiveSamplerSampler, emissiveUV)
8122
8645
  ).rgb;
8123
8646
  }
8124
8647
  #endif
@@ -8238,7 +8761,41 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
8238
8761
  anisotropyRotation: 0,
8239
8762
  anisotropyDirection: [1, 0],
8240
8763
  anisotropyMapEnabled: false,
8241
- emissiveStrength: 1
8764
+ emissiveStrength: 1,
8765
+ baseColorUVSet: 0,
8766
+ baseColorUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8767
+ metallicRoughnessUVSet: 0,
8768
+ metallicRoughnessUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8769
+ normalUVSet: 0,
8770
+ normalUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8771
+ occlusionUVSet: 0,
8772
+ occlusionUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8773
+ emissiveUVSet: 0,
8774
+ emissiveUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8775
+ specularColorUVSet: 0,
8776
+ specularColorUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8777
+ specularIntensityUVSet: 0,
8778
+ specularIntensityUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8779
+ transmissionUVSet: 0,
8780
+ transmissionUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8781
+ thicknessUVSet: 0,
8782
+ thicknessUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8783
+ clearcoatUVSet: 0,
8784
+ clearcoatUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8785
+ clearcoatRoughnessUVSet: 0,
8786
+ clearcoatRoughnessUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8787
+ clearcoatNormalUVSet: 0,
8788
+ clearcoatNormalUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8789
+ sheenColorUVSet: 0,
8790
+ sheenColorUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8791
+ sheenRoughnessUVSet: 0,
8792
+ sheenRoughnessUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8793
+ iridescenceUVSet: 0,
8794
+ iridescenceUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8795
+ iridescenceThicknessUVSet: 0,
8796
+ iridescenceThicknessUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1],
8797
+ anisotropyUVSet: 0,
8798
+ anisotropyUVTransform: [1, 0, 0, 0, 1, 0, 0, 0, 1]
8242
8799
  },
8243
8800
  name: "pbrMaterial",
8244
8801
  firstBindingSlot: 0,
@@ -8345,7 +8902,41 @@ fn pbr_filterColor(colorUnused: vec4<f32>) -> vec4<f32> {
8345
8902
  // debugging flags used for shader output of intermediate PBR variables
8346
8903
  // #ifdef PBR_DEBUG
8347
8904
  scaleDiffBaseMR: "vec4<f32>",
8348
- scaleFGDSpec: "vec4<f32>"
8905
+ scaleFGDSpec: "vec4<f32>",
8906
+ baseColorUVSet: "i32",
8907
+ baseColorUVTransform: "mat3x3<f32>",
8908
+ metallicRoughnessUVSet: "i32",
8909
+ metallicRoughnessUVTransform: "mat3x3<f32>",
8910
+ normalUVSet: "i32",
8911
+ normalUVTransform: "mat3x3<f32>",
8912
+ occlusionUVSet: "i32",
8913
+ occlusionUVTransform: "mat3x3<f32>",
8914
+ emissiveUVSet: "i32",
8915
+ emissiveUVTransform: "mat3x3<f32>",
8916
+ specularColorUVSet: "i32",
8917
+ specularColorUVTransform: "mat3x3<f32>",
8918
+ specularIntensityUVSet: "i32",
8919
+ specularIntensityUVTransform: "mat3x3<f32>",
8920
+ transmissionUVSet: "i32",
8921
+ transmissionUVTransform: "mat3x3<f32>",
8922
+ thicknessUVSet: "i32",
8923
+ thicknessUVTransform: "mat3x3<f32>",
8924
+ clearcoatUVSet: "i32",
8925
+ clearcoatUVTransform: "mat3x3<f32>",
8926
+ clearcoatRoughnessUVSet: "i32",
8927
+ clearcoatRoughnessUVTransform: "mat3x3<f32>",
8928
+ clearcoatNormalUVSet: "i32",
8929
+ clearcoatNormalUVTransform: "mat3x3<f32>",
8930
+ sheenColorUVSet: "i32",
8931
+ sheenColorUVTransform: "mat3x3<f32>",
8932
+ sheenRoughnessUVSet: "i32",
8933
+ sheenRoughnessUVTransform: "mat3x3<f32>",
8934
+ iridescenceUVSet: "i32",
8935
+ iridescenceUVTransform: "mat3x3<f32>",
8936
+ iridescenceThicknessUVSet: "i32",
8937
+ iridescenceThicknessUVTransform: "mat3x3<f32>",
8938
+ anisotropyUVSet: "i32",
8939
+ anisotropyUVTransform: "mat3x3<f32>"
8349
8940
  }
8350
8941
  };
8351
8942