Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BatchedMesh: Add built-in material support #27115

Merged
merged 13 commits into from
Nov 6, 2023
108 changes: 3 additions & 105 deletions examples/jsm/objects/BatchedMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
RGBAFormat
} from 'three';

const ID_ATTR_NAME = '_batch_id_';
const ID_ATTR_NAME = 'batchId';
const _identityMatrix = new Matrix4();
const _zeroScaleMatrix = new Matrix4().set(
0, 0, 0, 0,
Expand All @@ -18,48 +18,6 @@ const _zeroScaleMatrix = new Matrix4().set(
0, 0, 0, 1,
);

// Custom shaders
const batchingParsVertex = /* glsl */`
#ifdef BATCHING
attribute float ${ ID_ATTR_NAME };
uniform highp sampler2D batchingTexture;
mat4 getBatchingMatrix( const in float i ) {

int size = textureSize( batchingTexture, 0 ).x;
int j = int( i ) * 4;
int x = j % size;
int y = j / size;
vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 );
vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 );
vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 );
vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 );
return mat4( v1, v2, v3, v4 );

}
#endif
`;

const batchingbaseVertex = /* glsl */`
#ifdef BATCHING
mat4 batchingMatrix = getBatchingMatrix( ${ ID_ATTR_NAME } );
#endif
`;

const batchingnormalVertex = /* glsl */`
#ifdef BATCHING
objectNormal = vec4( batchingMatrix * vec4( objectNormal, 0.0 ) ).xyz;
#ifdef USE_TANGENT
objectTangent = vec4( batchingMatrix * vec4( objectTangent, 0.0 ) ).xyz;
#endif
#endif
`;

const batchingVertex = /* glsl */`
#ifdef BATCHING
transformed = ( batchingMatrix * vec4( transformed, 1.0 ) ).xyz;
#endif
`;

// @TODO: SkinnedMesh support?
// @TODO: Future work if needed. Move into the core. Can be optimized more with WEBGL_multi_draw.

Expand Down Expand Up @@ -126,12 +84,7 @@ class BatchedMesh extends Mesh {
// @TODO: Calculate the entire binding box and make frustumCulled true
this.frustumCulled = false;

this._customUniforms = {
batchingTexture: { value: null }
};

this._initMatricesTexture();
this._initShader();

}

Expand All @@ -152,53 +105,6 @@ class BatchedMesh extends Mesh {
const matricesTexture = new DataTexture( matricesArray, size, size, RGBAFormat, FloatType );

this._matricesTexture = matricesTexture;
this._customUniforms.batchingTexture.value = this._matricesTexture;

}

_initShader() {

const material = this.material;
const currentOnBeforeCompile = material.onBeforeCompile;
const customUniforms = this._customUniforms;

material.onBeforeCompile = function onBeforeCompile( parameters, renderer ) {

// Is this replacement stable across any materials?
parameters.vertexShader = parameters.vertexShader
.replace(
'#include <skinning_pars_vertex>',
'#include <skinning_pars_vertex>\n'
+ batchingParsVertex
)
.replace(
'#include <uv_vertex>',
'#include <uv_vertex>\n'
+ batchingbaseVertex
)
.replace(
'#include <skinnormal_vertex>',
'#include <skinnormal_vertex>\n'
+ batchingnormalVertex
)
.replace(
'#include <skinning_vertex>',
'#include <skinning_vertex>\n'
+ batchingVertex
);

for ( const uniformName in customUniforms ) {

parameters.uniforms[ uniformName ] = customUniforms[ uniformName ];

}

currentOnBeforeCompile.call( this, parameters, renderer );

};

material.defines = material.defines || {};
material.defines.BATCHING = false;

}

Expand Down Expand Up @@ -706,13 +612,11 @@ class BatchedMesh extends Mesh {

}

onBeforeRender( _renderer, _scene, _camera, _geometry, material/*, _group*/ ) {

material.defines.BATCHING = true;
onBeforeRender( _renderer, _scene, _camera, geometry ) {

// the indexed version of the multi draw function requires specifying the start
// offset in bytes.
const index = _geometry.getIndex();
const index = geometry.getIndex();
const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;

const visible = this._visible;
Expand Down Expand Up @@ -742,12 +646,6 @@ class BatchedMesh extends Mesh {

}

onAfterRender( _renderer, _scene, _camera, _geometry, material/*, _group*/ ) {

material.defines.BATCHING = false;

}

}

export { BatchedMesh };
16 changes: 16 additions & 0 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,7 @@ class WebGLRenderer {
const materialProperties = properties.get( material );

materialProperties.outputColorSpace = parameters.outputColorSpace;
materialProperties.batching = parameters.batching;
materialProperties.instancing = parameters.instancing;
materialProperties.instancingColor = parameters.instancingColor;
materialProperties.skinning = parameters.skinning;
Expand Down Expand Up @@ -1799,6 +1800,14 @@ class WebGLRenderer {

needsProgramChange = true;

} else if ( object.isBatchedMesh && materialProperties.batching === false ) {

needsProgramChange = true;

} else if ( ! object.isBatchedMesh && materialProperties.batching === true ) {

needsProgramChange = true;

} else if ( object.isInstancedMesh && materialProperties.instancing === false ) {

needsProgramChange = true;
Expand Down Expand Up @@ -1987,6 +1996,13 @@ class WebGLRenderer {

}

if ( object.isBatchedMesh ) {

p_uniforms.setOptional( _gl, object, 'batchingTexture' );
p_uniforms.setValue( _gl, 'batchingTexture', object._matricesTexture, textures );

}

const morphAttributes = geometry.morphAttributes;

if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) {
Expand Down
2 changes: 2 additions & 0 deletions src/renderers/shaders/ShaderChunk.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import alphatest_fragment from './ShaderChunk/alphatest_fragment.glsl.js';
import alphatest_pars_fragment from './ShaderChunk/alphatest_pars_fragment.glsl.js';
import aomap_fragment from './ShaderChunk/aomap_fragment.glsl.js';
import aomap_pars_fragment from './ShaderChunk/aomap_pars_fragment.glsl.js';
import batching_pars_vertex from './ShaderChunk/batching_pars_vertex.glsl.js';
import begin_vertex from './ShaderChunk/begin_vertex.glsl.js';
import beginnormal_vertex from './ShaderChunk/beginnormal_vertex.glsl.js';
import bsdfs from './ShaderChunk/bsdfs.glsl.js';
Expand Down Expand Up @@ -131,6 +132,7 @@ export const ShaderChunk = {
alphatest_pars_fragment: alphatest_pars_fragment,
aomap_fragment: aomap_fragment,
aomap_pars_fragment: aomap_pars_fragment,
batching_pars_vertex: batching_pars_vertex,
begin_vertex: begin_vertex,
beginnormal_vertex: beginnormal_vertex,
bsdfs: bsdfs,
Expand Down
19 changes: 19 additions & 0 deletions src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export default /* glsl */`
#ifdef USE_BATCHING
attribute float batchId;
uniform highp sampler2D batchingTexture;
mat4 getBatchingMatrix( const in float i ) {

int size = textureSize( batchingTexture, 0 ).x;
int j = int( i ) * 4;
int x = j % size;
int y = j / size;
vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 );
vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 );
vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 );
vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 );
return mat4( v1, v2, v3, v4 );

}
#endif
`;
20 changes: 20 additions & 0 deletions src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
export default /* glsl */`

#ifdef USE_BATCHING

// this is in lieu of a per-instance normal-matrix
// shear transforms in the instance matrix are not supported

mat4 batchingMatrix = getBatchingMatrix( batchId );
mat3 bm = mat3( batchingMatrix );
objectNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) );
objectNormal = bm * objectNormal;

#ifdef USE_TANGENT

objectTangent /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) );
objectTangent = bm * objectTangent;

#endif

#endif

vec3 transformedNormal = objectNormal;

#ifdef USE_INSTANCING
Expand Down
6 changes: 6 additions & 0 deletions src/renderers/shaders/ShaderChunk/project_vertex.glsl.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
export default /* glsl */`
vec4 mvPosition = vec4( transformed, 1.0 );

#ifdef USE_BATCHING

mvPosition = batchingMatrix * mvPosition;

#endif

#ifdef USE_INSTANCING

mvPosition = instanceMatrix * mvPosition;
Expand Down
6 changes: 6 additions & 0 deletions src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ export default /* glsl */`

vec4 worldPosition = vec4( transformed, 1.0 );

#ifdef USE_BATCHING

worldPosition = batchingMatrix * worldPosition;

#endif

#ifdef USE_INSTANCING

worldPosition = instanceMatrix * worldPosition;
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/depth.glsl.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const vertex = /* glsl */`
#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <morphtarget_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/distanceRGBA.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const vertex = /* glsl */`
varying vec3 vWorldPosition;

#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <morphtarget_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/meshbasic.glsl.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const vertex = /* glsl */`
#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <envmap_pars_vertex>
#include <color_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/meshlambert.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const vertex = /* glsl */`
varying vec3 vViewPosition;

#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <envmap_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/meshmatcap.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const vertex = /* glsl */`
varying vec3 vViewPosition;

#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <color_pars_vertex>
#include <displacementmap_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/meshnormal.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const vertex = /* glsl */`
#endif

#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <normal_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/meshphong.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const vertex = /* glsl */`
varying vec3 vViewPosition;

#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <envmap_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/meshphysical.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ varying vec3 vViewPosition;
#endif

#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/meshtoon.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const vertex = /* glsl */`
varying vec3 vViewPosition;

#include <common>
#include <batching_pars_vertex>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/shaders/ShaderLib/shadow.glsl.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const vertex = /* glsl */`
#include <common>
#include <batching_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
Expand Down
1 change: 1 addition & 0 deletions src/renderers/webgl/WebGLProgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {

customDefines,

parameters.batching ? '#define USE_BATCHING' : '',
parameters.instancing ? '#define USE_INSTANCING' : '',
parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '',

Expand Down
4 changes: 4 additions & 0 deletions src/renderers/webgl/WebGLPrograms.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
const currentRenderTarget = renderer.getRenderTarget();

const IS_INSTANCEDMESH = object.isInstancedMesh === true;
const IS_BATCHEDMESH = object.isBatchedMesh === true;

const HAS_MAP = !! material.map;
const HAS_MATCAP = !! material.matcap;
Expand Down Expand Up @@ -193,6 +194,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities

precision: precision,

batching: IS_BATCHEDMESH,
instancing: IS_INSTANCEDMESH,
instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null,

Expand Down Expand Up @@ -503,6 +505,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
_programLayers.enable( 17 );
if ( parameters.alphaHash )
_programLayers.enable( 18 );
if ( parameters.batching )
_programLayers.enable( 19 );

array.push( _programLayers.mask );
_programLayers.disableAll();
Expand Down