Skip to content

Commit

Permalink
WebGPURenderer: Support BatchMesh (#27937)
Browse files Browse the repository at this point in the history
* init batch and fixes

* smaller example

* pup

* fix dynamic example

* fix demo
  • Loading branch information
RenaudRohlinger authored Mar 19, 2024
1 parent e6ec298 commit 7059b85
Show file tree
Hide file tree
Showing 11 changed files with 613 additions and 29 deletions.
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@
"webgpu_multisampled_renderbuffers",
"webgpu_materials_texture_anisotropy",
"webgpu_storage_buffer",
"webgpu_mesh_batch",
"webgpu_instancing_morph"
],
"webaudio": [
Expand Down
1 change: 1 addition & 0 deletions examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export { default as CameraNode, cameraProjectionMatrix, cameraProjectionMatrixIn
export { default as VertexColorNode, vertexColor } from './accessors/VertexColorNode.js';
export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTextureNode.js';
export { default as InstanceNode, instance } from './accessors/InstanceNode.js';
export { default as BatchNode, batch } from './accessors/BatchNode.js';
export { default as MaterialNode, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialSpecularStrength, materialReflectivity, materialRoughness, materialMetalness, materialNormal, materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialRotation, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLineScale, materialLineDashSize, materialLineGapSize, materialLineWidth, materialLineDashOffset, materialPointWidth } from './accessors/MaterialNode.js';
export { default as MaterialReferenceNode, materialReference } from './accessors/MaterialReferenceNode.js';
export { default as RendererReferenceNode, rendererReference } from './accessors/RendererReferenceNode.js';
Expand Down
84 changes: 84 additions & 0 deletions examples/jsm/nodes/accessors/BatchNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import Node, { addNodeClass } from '../core/Node.js';
import { normalLocal } from './NormalNode.js';
import { positionLocal } from './PositionNode.js';
import { nodeProxy, vec3, mat3, mat4, int, ivec2, float } from '../shadernode/ShaderNode.js';
import { textureLoad } from './TextureNode.js';
import { textureSize } from './TextureSizeNode.js';
import { Float32BufferAttribute } from 'three';
import { bufferAttribute } from './BufferAttributeNode.js';
import { tangentLocal } from './TangentNode.js';

class BatchNode extends Node {

constructor( batchMesh ) {

super( 'void' );

this.batchMesh = batchMesh;


this.instanceColorNode = null;

this.batchingIdNode = null;

}

setup( builder ) {

// POSITION

if ( this.batchingIdNode === null ) {

const batchingAttribute = this.batchMesh.geometry.getAttribute( 'batchId' );
const array = new Float32Array( batchingAttribute.array );

const buffer = new Float32BufferAttribute( array, 1 );

this.batchingIdNode = bufferAttribute( buffer, 'float' ).toVar();

}

const matriceTexture = this.batchMesh._matricesTexture;

const size = textureSize( textureLoad( matriceTexture ), 0 );
const j = float( int( this.batchingIdNode ) ).mul( 4 ).toVar();

const x = int( j.mod( size ) );
const y = int( j ).div( int( size ) );
const batchingMatrix = mat4(
textureLoad( matriceTexture, ivec2( x, y ) ),
textureLoad( matriceTexture, ivec2( x.add( 1 ), y ) ),
textureLoad( matriceTexture, ivec2( x.add( 2 ), y ) ),
textureLoad( matriceTexture, ivec2( x.add( 3 ), y ) )
);


const bm = mat3(
batchingMatrix[ 0 ].xyz,
batchingMatrix[ 1 ].xyz,
batchingMatrix[ 2 ].xyz
);

positionLocal.assign( batchingMatrix.mul( positionLocal ) );

const transformedNormal = normalLocal.div( vec3( bm[ 0 ].dot( bm[ 0 ] ), bm[ 1 ].dot( bm[ 1 ] ), bm[ 2 ].dot( bm[ 2 ] ) ) );

const batchingNormal = bm.mul( transformedNormal ).xyz;

normalLocal.assign( batchingNormal );

if ( builder.hasGeometryAttribute( 'tangent' ) ) {

tangentLocal.mulAssign( bm );

}

}

}

export default BatchNode;

export const batch = nodeProxy( BatchNode );

addNodeClass( 'batch', BatchNode );
8 changes: 8 additions & 0 deletions examples/jsm/nodes/materials/NodeMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { materialAlphaTest, materialColor, materialOpacity, materialEmissive, ma
import { modelViewProjection } from '../accessors/ModelViewProjectionNode.js';
import { transformedNormalView } from '../accessors/NormalNode.js';
import { instance } from '../accessors/InstanceNode.js';
import { batch } from '../accessors/BatchNode.js';

import { positionLocal, positionView } from '../accessors/PositionNode.js';
import { skinningReference } from '../accessors/SkinningNode.js';
import { morphReference } from '../accessors/MorphNode.js';
Expand Down Expand Up @@ -209,6 +211,12 @@ class NodeMaterial extends ShaderMaterial {

}

if ( object.isBatchedMesh ) {

batch( object ).append();

}

if ( this.positionNode !== null ) {

positionLocal.assign( this.positionNode );
Expand Down
1 change: 1 addition & 0 deletions examples/jsm/renderers/common/Animation.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Animation {
dispose() {

self.cancelAnimationFrame( this.requestId );
this.requestId = null;

}

Expand Down
6 changes: 3 additions & 3 deletions examples/jsm/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class Renderer {

if ( this._initialized === false ) await this.init();

this._renderScene( scene, camera );
await this._renderScene( scene, camera );

}

Expand All @@ -330,7 +330,7 @@ class Renderer {

}

_renderScene( scene, camera ) {
async _renderScene( scene, camera ) {

// preserve render tree

Expand Down Expand Up @@ -506,7 +506,7 @@ class Renderer {
sceneRef.onAfterRender( this, scene, camera, renderTarget );

//
this.backend.resolveTimestampAsync( renderContext, 'render' );
await this.backend.resolveTimestampAsync( renderContext, 'render' );

return renderContext;

Expand Down
56 changes: 30 additions & 26 deletions examples/jsm/renderers/webgl/WebGLBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import WebGLTextureUtils from './utils/WebGLTextureUtils.js';
import WebGLExtensions from './utils/WebGLExtensions.js';
import WebGLCapabilities from './utils/WebGLCapabilities.js';
import { GLFeatureName } from './utils/WebGLConstants.js';
import { WebGLBufferRenderer } from './WebGLBufferRenderer.js';

//

Expand Down Expand Up @@ -39,6 +40,8 @@ class WebGLBackend extends Backend {
this.capabilities = new WebGLCapabilities( this );
this.attributeUtils = new WebGLAttributeUtils( this );
this.textureUtils = new WebGLTextureUtils( this );
this.bufferRenderer = new WebGLBufferRenderer( this );

this.state = new WebGLState( this );
this.utils = new WebGLUtils( this );

Expand Down Expand Up @@ -644,68 +647,69 @@ class WebGLBackend extends Backend {

//

let mode;
if ( object.isPoints ) mode = gl.POINTS;
else if ( object.isLineSegments ) mode = gl.LINES;
else if ( object.isLine ) mode = gl.LINE_STRIP;
else if ( object.isLineLoop ) mode = gl.LINE_LOOP;
const renderer = this.bufferRenderer;

if ( object.isPoints ) renderer.mode = gl.POINTS;
else if ( object.isLineSegments ) renderer.mode = gl.LINES;
else if ( object.isLine ) renderer.mode = gl.LINE_STRIP;
else if ( object.isLineLoop ) renderer.mode = gl.LINE_LOOP;
else {

if ( material.wireframe === true ) {

state.setLineWidth( material.wireframeLinewidth * this.renderer.getPixelRatio() );
mode = gl.LINES;
renderer.mode = gl.LINES;

} else {

mode = gl.TRIANGLES;
renderer.mode = gl.TRIANGLES;

}

}

//

const instanceCount = this.getInstanceCount( renderObject );

let count;

renderer.object = object;

if ( index !== null ) {

const indexData = this.get( index );
const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;

if ( instanceCount > 1 ) {

gl.drawElementsInstanced( mode, index.count, indexData.type, firstVertex, instanceCount );
renderer.index = index.count;
renderer.type = indexData.type;

} else {
count = indexCount;

gl.drawElements( mode, index.count, indexData.type, firstVertex );
} else {

}
renderer.index = 0;

info.update( object, indexCount, 1 );
const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : geometry.attributes.position.count;

} else {
count = vertexCount;

const positionAttribute = geometry.attributes.position;
const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count;
}

if ( instanceCount > 1 ) {
const instanceCount = this.getInstanceCount( renderObject );

gl.drawArraysInstanced( mode, 0, vertexCount, instanceCount );
if ( object.isBatchedMesh ) {

} else {
renderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount );

gl.drawArrays( mode, 0, vertexCount );
} else if ( instanceCount > 1 ) {

}
renderer.renderInstances( firstVertex, count, instanceCount );

//gl.drawArrays( mode, vertexCount, gl.UNSIGNED_SHORT, firstVertex );
} else {

info.update( object, vertexCount, 1 );
renderer.render( firstVertex, count );

}

//

gl.bindVertexArray( null );
Expand Down
99 changes: 99 additions & 0 deletions examples/jsm/renderers/webgl/WebGLBufferRenderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
class WebGLBufferRenderer {

constructor( backend ) {

this.gl = backend.gl;
this.extensions = backend.extensions;
this.info = backend.renderer.info;
this.mode = null;
this.index = 0;
this.type = null;
this.object = null;

}

render( start, count ) {

const { gl, mode, object, type, info, index } = this;

if ( index !== 0 ) {

gl.drawElements( mode, count, type, start );

} else {

gl.drawArrays( mode, start, count );

}

info.update( object, count, mode, 1 );

}

renderInstances( start, count, primcount ) {

const { gl, mode, type, index, object, info } = this;

if ( primcount === 0 ) return;

if ( index !== 0 ) {

gl.drawElementsInstanced( mode, count, type, start, primcount );

} else {

gl.drawArraysInstanced( mode, start, count, primcount );

}

info.update( object, count, mode, primcount );

}

renderMultiDraw( starts, counts, drawCount ) {

const { extensions, mode, object, info } = this;

if ( drawCount === 0 ) return;

const extension = extensions.get( 'WEBGL_multi_draw' );

if ( extension === null ) {

for ( let i = 0; i < drawCount; i ++ ) {

this.render( starts[ i ], counts[ i ] );

}

} else {

if ( this.index !== 0 ) {

extension.multiDrawElementsWEBGL( mode, counts, 0, this.type, starts, 0, drawCount );

} else {

extension.multiDrawArraysWEBGL( mode, starts, 0, counts, 0, drawCount );

}

let elementCount = 0;
for ( let i = 0; i < drawCount; i ++ ) {

elementCount += counts[ i ];

}

info.update( object, elementCount, mode, 1 );

}

}

//

}


export { WebGLBufferRenderer };
Binary file added examples/screenshots/webgpu_mesh_batch.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 7059b85

Please sign in to comment.