diff --git a/docs/api/en/objects/BatchedMesh.html b/docs/api/en/objects/BatchedMesh.html
index 8c0a095b7e520..bd2c2aae60794 100644
--- a/docs/api/en/objects/BatchedMesh.html
+++ b/docs/api/en/objects/BatchedMesh.html
@@ -129,6 +129,19 @@
[method:this setCustomSort]( [param:Function sortFunction] )
in the list include a "z" field to perform a depth-ordered sort with.
+
+ [method:undefined getColorAt]( [param:Integer index], [param:Color color] )
+
+
+ [page:Integer index]: The index of a geometry. Values have to be in the
+ range [0, count].
+
+
+ [page:Color color]: This color object will be set to the color of the
+ defined geometry.
+
+ Get the color of the defined geometry.
+
[method:Matrix4 getMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )
@@ -151,6 +164,18 @@
Get whether the given instance is marked as "visible" or not.
+
+ [method:undefined setColorAt]( [param:Integer index], [param:Color color] )
+
+
+ [page:Integer index]: The index of a geometry. Values have to be in the
+ range [0, count].
+
+ [page:Color color]: The color of a single geometry.
+
+ Sets the given color to the defined geometry.
+
+
[method:this setMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )
diff --git a/examples/jsm/renderers/common/RenderObject.js b/examples/jsm/renderers/common/RenderObject.js
index 4628c2fe5edfb..7d8bdbf5d796e 100644
--- a/examples/jsm/renderers/common/RenderObject.js
+++ b/examples/jsm/renderers/common/RenderObject.js
@@ -225,6 +225,7 @@ export default class RenderObject {
if ( object.isBatchedMesh ) {
cacheKey += object._matricesTexture.uuid + ',';
+ cacheKey += object._colorsTexture.uuid + ',';
}
diff --git a/src/core/Object3D.js b/src/core/Object3D.js
index 6f47db133e62c..9cd4a5486b20e 100644
--- a/src/core/Object3D.js
+++ b/src/core/Object3D.js
@@ -761,6 +761,8 @@ class Object3D extends EventDispatcher {
object.matricesTexture = this._matricesTexture.toJSON( meta );
+ if ( this._colorsTexture !== null ) object.colorsTexture = this._colorsTexture.toJSON( meta );
+
if ( this.boundingSphere !== null ) {
object.boundingSphere = {
diff --git a/src/loaders/ObjectLoader.js b/src/loaders/ObjectLoader.js
index d430386baac68..4f34700d8de84 100644
--- a/src/loaders/ObjectLoader.js
+++ b/src/loaders/ObjectLoader.js
@@ -946,6 +946,7 @@ class ObjectLoader extends Loader {
object._geometryCount = data.geometryCount;
object._matricesTexture = getTexture( data.matricesTexture.uuid );
+ if ( data.colorsTexture !== undefined ) object._colorsTexture = getTexture( data.colorsTexture.uuid );
break;
diff --git a/src/objects/BatchedMesh.js b/src/objects/BatchedMesh.js
index 89e549476f271..8f09bd4f6a381 100644
--- a/src/objects/BatchedMesh.js
+++ b/src/objects/BatchedMesh.js
@@ -5,6 +5,7 @@ import { FloatType } from '../constants.js';
import { Matrix4 } from '../math/Matrix4.js';
import { Mesh } from './Mesh.js';
import { RGBAFormat } from '../constants.js';
+import { ColorManagement } from '../math/ColorManagement.js';
import { Box3 } from '../math/Box3.js';
import { Sphere } from '../math/Sphere.js';
import { Frustum } from '../math/Frustum.js';
@@ -160,6 +161,9 @@ class BatchedMesh extends Mesh {
this._initMatricesTexture();
+ // Local color per geometry by using data texture
+ this._colorsTexture = null;
+
}
_initMatricesTexture() {
@@ -182,6 +186,19 @@ class BatchedMesh extends Mesh {
}
+ _initColorsTexture() {
+
+ let size = Math.sqrt( this._maxGeometryCount );
+ size = Math.ceil( size );
+
+ const colorsArray = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel
+ const colorsTexture = new DataTexture( colorsArray, size, size, RGBAFormat, FloatType );
+ colorsTexture.colorSpace = ColorManagement.workingColorSpace;
+
+ this._colorsTexture = colorsTexture;
+
+ }
+
_initializeGeometry( reference ) {
const geometry = this.geometry;
@@ -746,6 +763,49 @@ class BatchedMesh extends Mesh {
}
+ setColorAt( geometryId, color ) {
+
+ if ( this._colorsTexture === null ) {
+
+ this._initColorsTexture();
+
+ }
+
+ // @TODO: Map geometryId to index of the arrays because
+ // optimize() can make geometryId mismatch the index
+
+ const active = this._active;
+ const colorsTexture = this._colorsTexture;
+ const colorsArray = this._colorsTexture.image.data;
+ const geometryCount = this._geometryCount;
+ if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
+
+ return this;
+
+ }
+
+ color.toArray( colorsArray, geometryId * 4 );
+ colorsTexture.needsUpdate = true;
+
+ return this;
+
+ }
+
+ getColorAt( geometryId, color ) {
+
+ const active = this._active;
+ const colorsArray = this._colorsTexture.image.data;
+ const geometryCount = this._geometryCount;
+ if ( geometryId >= geometryCount || active[ geometryId ] === false ) {
+
+ return null;
+
+ }
+
+ return color.fromArray( colorsArray, geometryId * 4 );
+
+ }
+
setVisibleAt( geometryId, value ) {
const visibility = this._visibility;
@@ -886,6 +946,13 @@ class BatchedMesh extends Mesh {
this._matricesTexture = source._matricesTexture.clone();
this._matricesTexture.image.data = this._matricesTexture.image.slice();
+ if ( this._colorsTexture !== null ) {
+
+ this._colorsTexture = source._colorsTexture.clone();
+ this._colorsTexture.image.data = this._colorsTexture.image.slice();
+
+ }
+
return this;
}
@@ -897,6 +964,14 @@ class BatchedMesh extends Mesh {
this._matricesTexture.dispose();
this._matricesTexture = null;
+
+ if ( this._colorsTexture !== null ) {
+
+ this._colorsTexture.dispose();
+ this._colorsTexture = null;
+
+ }
+
return this;
}
diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js
index d12b90e5b94e8..e43ba7233626e 100644
--- a/src/renderers/WebGLRenderer.js
+++ b/src/renderers/WebGLRenderer.js
@@ -1716,6 +1716,7 @@ class WebGLRenderer {
materialProperties.outputColorSpace = parameters.outputColorSpace;
materialProperties.batching = parameters.batching;
+ materialProperties.batchingColor = parameters.batchingColor;
materialProperties.instancing = parameters.instancing;
materialProperties.instancingColor = parameters.instancingColor;
materialProperties.instancingMorph = parameters.instancingMorph;
@@ -1805,6 +1806,14 @@ class WebGLRenderer {
needsProgramChange = true;
+ } else if ( object.isBatchedMesh && materialProperties.batchingColor === true && object.colorTexture === null ) {
+
+ needsProgramChange = true;
+
+ } else if ( object.isBatchedMesh && materialProperties.batchingColor === false && object.colorTexture !== null ) {
+
+ needsProgramChange = true;
+
} else if ( object.isInstancedMesh && materialProperties.instancing === false ) {
needsProgramChange = true;
@@ -1997,6 +2006,13 @@ class WebGLRenderer {
p_uniforms.setOptional( _gl, object, 'batchingTexture' );
p_uniforms.setValue( _gl, 'batchingTexture', object._matricesTexture, textures );
+ p_uniforms.setOptional( _gl, object, 'batchingColorTexture' );
+ if ( object._colorsTexture !== null ) {
+
+ p_uniforms.setValue( _gl, 'batchingColorTexture', object._colorsTexture, textures );
+
+ }
+
}
const morphAttributes = geometry.morphAttributes;
diff --git a/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js
index fb602a0f7d880..89c7829b61ab7 100644
--- a/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js
+++ b/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js
@@ -16,4 +16,19 @@ export default /* glsl */`
}
#endif
+
+#ifdef USE_BATCHING_COLOR
+
+ uniform sampler2D batchingColorTexture;
+ vec3 getBatchingColor( const in float i ) {
+
+ int size = textureSize( batchingColorTexture, 0 ).x;
+ int j = int( i );
+ int x = j % size;
+ int y = j / size;
+ return texelFetch( batchingColorTexture, ivec2( x, y ), 0 ).rgb;
+
+ }
+
+#endif
`;
diff --git a/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js
index 80c4934d555ee..944eedf9bf7c0 100644
--- a/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js
+++ b/src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl.js
@@ -3,7 +3,7 @@ export default /* glsl */`
varying vec4 vColor;
-#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )
+#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR )
varying vec3 vColor;
diff --git a/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js
index b288b7c472c0f..daf7164e60cb7 100644
--- a/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js
+++ b/src/renderers/shaders/ShaderChunk/color_vertex.glsl.js
@@ -3,7 +3,7 @@ export default /* glsl */`
vColor = vec4( 1.0 );
-#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )
+#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR )
vColor = vec3( 1.0 );
@@ -19,5 +19,13 @@ export default /* glsl */`
vColor.xyz *= instanceColor.xyz;
+#endif
+
+#ifdef USE_BATCHING_COLOR
+
+ vec3 batchingColor = getBatchingColor( batchId );
+
+ vColor.xyz *= batchingColor.xyz;
+
#endif
`;
diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js
index cfccc448bfb3a..b133801128fd9 100644
--- a/src/renderers/webgl/WebGLProgram.js
+++ b/src/renderers/webgl/WebGLProgram.js
@@ -522,6 +522,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
parameters.extensionClipCullDistance ? '#define USE_CLIP_DISTANCE' : '',
parameters.batching ? '#define USE_BATCHING' : '',
+ parameters.batchingColor ? '#define USE_BATCHING_COLOR' : '',
parameters.instancing ? '#define USE_INSTANCING' : '',
parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '',
parameters.instancingMorph ? '#define USE_INSTANCING_MORPH' : '',
@@ -799,7 +800,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '',
parameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '',
- parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '',
+ parameters.vertexColors || parameters.instancingColor || parameters.batchingColor ? '#define USE_COLOR' : '',
parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '',
parameters.vertexUv1s ? '#define USE_UV1' : '',
parameters.vertexUv2s ? '#define USE_UV2' : '',
diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js
index f4701d9006314..82a19b766e4aa 100644
--- a/src/renderers/webgl/WebGLPrograms.js
+++ b/src/renderers/webgl/WebGLPrograms.js
@@ -192,6 +192,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
precision: precision,
batching: IS_BATCHEDMESH,
+ batchingColor: IS_BATCHEDMESH && object._colorsTexture !== null,
instancing: IS_INSTANCEDMESH,
instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null,
instancingMorph: IS_INSTANCEDMESH && object.morphTexture !== null,
@@ -510,6 +511,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
_programLayers.enable( 19 );
if ( parameters.dispersion )
_programLayers.enable( 20 );
+ if ( parameters.batchingColor )
+ _programLayers.enable( 21 );
array.push( _programLayers.mask );
_programLayers.disableAll();