diff --git a/examples/webgpu_caustics.html b/examples/webgpu_caustics.html
index b5355f7028d91a..ee7fdcc067d3f1 100644
--- a/examples/webgpu_caustics.html
+++ b/examples/webgpu_caustics.html
@@ -179,7 +179,7 @@
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
renderer.shadowMap.enabled = true;
- renderer.shadowMap.color = true;
+ renderer.shadowMap.colored = true;
renderer.inspector = new Inspector();
document.body.appendChild( renderer.domElement );
diff --git a/examples/webgpu_shadowmap_opacity.html b/examples/webgpu_shadowmap_opacity.html
index 459c2b6f243f8a..3e3224e8cff39b 100644
--- a/examples/webgpu_shadowmap_opacity.html
+++ b/examples/webgpu_shadowmap_opacity.html
@@ -59,7 +59,7 @@
renderer.toneMapping = THREE.AgXToneMapping;
renderer.toneMappingExposure = 1.5;
renderer.shadowMap.enabled = true;
- renderer.shadowMap.color = true;
+ renderer.shadowMap.colored = true;
renderer.inspector = new Inspector();
container.appendChild( renderer.domElement );
diff --git a/examples/webgpu_volume_caustics.html b/examples/webgpu_volume_caustics.html
index d9dc37582b08b6..de5cde95015c79 100644
--- a/examples/webgpu_volume_caustics.html
+++ b/examples/webgpu_volume_caustics.html
@@ -165,7 +165,7 @@
renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.shadowMap.enabled = true;
- renderer.shadowMap.color = true;
+ renderer.shadowMap.colored = true;
renderer.inspector = new Inspector();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js
index 527ddc54c0e930..d83ca104d85921 100644
--- a/src/nodes/core/NodeBuilder.js
+++ b/src/nodes/core/NodeBuilder.js
@@ -653,7 +653,16 @@ class NodeBuilder {
const uniforms = bindings[ shaderStage ][ groupName ];
const groupUniforms = groups[ groupName ] || ( groups[ groupName ] = [] );
- groupUniforms.push( ...uniforms );
+
+ for ( const uniform of uniforms ) {
+
+ if ( groupUniforms.includes( uniform ) === false ) {
+
+ groupUniforms.push( uniform );
+
+ }
+
+ }
}
diff --git a/src/nodes/lighting/ShadowNode.js b/src/nodes/lighting/ShadowNode.js
index 0d1db19cf77bc9..fa15566fb55fc9 100644
--- a/src/nodes/lighting/ShadowNode.js
+++ b/src/nodes/lighting/ShadowNode.js
@@ -518,7 +518,7 @@ class ShadowNode extends ShadowBaseNode {
let shadowColor;
- if ( renderer.shadowMap.color === true ) {
+ if ( renderer.shadowMap.colored === true ) {
if ( shadowMap.texture.isCubeTexture ) {
diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js
index 1685be3b1c9040..f75fe884f273d1 100644
--- a/src/renderers/common/Renderer.js
+++ b/src/renderers/common/Renderer.js
@@ -658,7 +658,7 @@ class Renderer {
* Shadow map configuration
* @typedef {Object} ShadowMapConfig
* @property {boolean} enabled - Whether to globally enable shadows or not.
- * @property {boolean} color - Whether to include shadow color or not.
+ * @property {boolean} colored - Whether shadows can have a custom color or not.
* @property {number} type - The shadow map type.
*/
@@ -669,7 +669,7 @@ class Renderer {
*/
this.shadowMap = {
enabled: false,
- color: false,
+ colored: false,
type: PCFShadowMap
};
@@ -3053,9 +3053,9 @@ class Renderer {
shadowRGB = material.castShadowNode.rgb;
shadowAlpha = material.castShadowNode.a;
- if ( this.shadowMap.color !== true ) {
+ if ( this.shadowMap.colored !== true ) {
- warnOnce( 'Renderer: `shadowMap.color` needs to be enabled when using `material.castShadowNode`.' );
+ warnOnce( 'Renderer: `shadowMap.colored` needs to be set to `true` when using `material.castShadowNode`.' );
}
diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
index 7370d60124d3f5..22511d2415462b 100644
--- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
+++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
@@ -177,6 +177,14 @@ class WGSLNodeBuilder extends NodeBuilder {
*/
this.uniformGroups = {};
+ /**
+ * A dictionary that holds the assigned binding indices for each uniform group.
+ * This ensures the same binding index is used across all shader stages.
+ *
+ * @type {Object}
+ */
+ this.uniformGroupsBindings = {};
+
/**
* A dictionary that holds for each shader stage a Map of builtins.
*
@@ -1859,7 +1867,7 @@ ${ flowData.code }
const groupName = uniform.groupNode.name;
- // Check if this group has already been processed
+ // Check if this group has already been processed in this shader stage
if ( uniformGroups[ groupName ] === undefined ) {
// Get the shared uniform group that contains uniforms from all stages
@@ -1878,9 +1886,24 @@ ${ flowData.code }
}
+ // Check if this group already has an assigned binding index (from another shader stage)
+ let groupBinding = this.uniformGroupsBindings[ groupName ];
+
+ if ( groupBinding === undefined ) {
+
+ // First time processing this group - assign a new binding index
+ groupBinding = {
+ index: uniformIndexes.binding ++,
+ id: uniformIndexes.group
+ };
+
+ this.uniformGroupsBindings[ groupName ] = groupBinding;
+
+ }
+
uniformGroups[ groupName ] = {
- index: uniformIndexes.binding ++,
- id: uniformIndexes.group,
+ index: groupBinding.index,
+ id: groupBinding.id,
snippets: snippets
};