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

WebGPURenderer: Support BatchedMesh. #27937

Merged
merged 6 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,9 @@
"webgpu_mirror",
"webgpu_multisampled_renderbuffers",
"webgpu_materials_texture_anisotropy",
"webgpu_storage_buffer"
"webgpu_storage_buffer",
"webgpu_mesh_batch"

],
"webaudio": [
"webaudio_orientation",
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 ) {
RenaudRohlinger marked this conversation as resolved.
Show resolved Hide resolved

// 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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.