From 01c6bb4668d11606a518ef6660cb634598359ac5 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Mon, 16 Sep 2019 00:19:49 -0700 Subject: [PATCH 1/8] Implemented InstancedMesh --- examples/files.js | 1 + examples/webgl_instancing.html | 143 ++++++++++++++++++ src/Three.js | 1 + src/objects/InstancedMesh.js | 40 +++++ src/renderers/WebGLRenderer.js | 78 +++++++++- .../ShaderChunk/defaultnormal_vertex.glsl.js | 10 +- .../ShaderChunk/project_vertex.glsl.js | 10 +- .../ShaderChunk/worldpos_vertex.glsl.js | 10 +- src/renderers/webgl/WebGLObjects.js | 9 +- src/renderers/webgl/WebGLProgram.js | 8 + src/renderers/webgl/WebGLPrograms.js | 6 +- 11 files changed, 304 insertions(+), 12 deletions(-) create mode 100644 examples/webgl_instancing.html create mode 100644 src/objects/InstancedMesh.js diff --git a/examples/files.js b/examples/files.js index 3eb6312edbd55d..3e7fbd0ac3712b 100644 --- a/examples/files.js +++ b/examples/files.js @@ -47,6 +47,7 @@ var files = { "webgl_geometry_text_shapes", "webgl_geometry_text_stroke", "webgl_helpers", + "webgl_instancing", "webgl_interactive_buffergeometry", "webgl_interactive_cubes", "webgl_interactive_cubes_gpu", diff --git a/examples/webgl_instancing.html b/examples/webgl_instancing.html new file mode 100644 index 00000000000000..938d72075d9906 --- /dev/null +++ b/examples/webgl_instancing.html @@ -0,0 +1,143 @@ + + + + three.js webgl - instancing + + + + + + + + + + diff --git a/src/Three.js b/src/Three.js index 11165d499205cd..b95c7128b2b781 100644 --- a/src/Three.js +++ b/src/Three.js @@ -18,6 +18,7 @@ export { SkinnedMesh } from './objects/SkinnedMesh.js'; export { Skeleton } from './objects/Skeleton.js'; export { Bone } from './objects/Bone.js'; export { Mesh } from './objects/Mesh.js'; +export { InstancedMesh } from './objects/InstancedMesh.js'; export { LineSegments } from './objects/LineSegments.js'; export { LineLoop } from './objects/LineLoop.js'; export { Line } from './objects/Line.js'; diff --git a/src/objects/InstancedMesh.js b/src/objects/InstancedMesh.js new file mode 100644 index 00000000000000..291600611919c4 --- /dev/null +++ b/src/objects/InstancedMesh.js @@ -0,0 +1,40 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +import { BufferAttribute } from '../core/BufferAttribute.js'; +import { Matrix3 } from '../math/Matrix3.js'; +import { Mesh } from './Mesh.js'; + +var _matrix3 = new Matrix3(); + +function InstancedMesh( geometry, material, count ) { + + Mesh.call( this, geometry, material ); + + this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 ); + this.instanceNormalMatrix = new BufferAttribute( new Float32Array( count * 9 ), 9 ); + +} + +InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { + + constructor: InstancedMesh, + + isInstancedMesh: true, + + raycast: function () {}, + + setMatrixAt: function ( matrix, offset ) { + + matrix.toArray( this.instanceMatrix.array, offset * 16 ); + + _matrix3.getNormalMatrix( matrix ).toArray( this.instanceNormalMatrix.array, offset * 9 ); + + }, + + updateMorphTargets: function () {} + +} ); + +export { InstancedMesh }; diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 5058ddbd217cf0..29bdf7b15155ab 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -258,7 +258,7 @@ function WebGLRenderer( parameters ) { capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - if ( ! capabilities.isWebGL2 ) { + if ( capabilities.isWebGL2 === false ) { extensions.get( 'WEBGL_depth_texture' ); extensions.get( 'OES_texture_float' ); @@ -283,7 +283,7 @@ function WebGLRenderer( parameters ) { textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); attributes = new WebGLAttributes( _gl ); geometries = new WebGLGeometries( _gl, attributes, info ); - objects = new WebGLObjects( geometries, info ); + objects = new WebGLObjects( _gl, geometries, attributes, info ); morphtargets = new WebGLMorphtargets( _gl ); programCache = new WebGLPrograms( _this, extensions, capabilities ); renderLists = new WebGLRenderLists(); @@ -765,7 +765,7 @@ function WebGLRenderer( parameters ) { if ( updateBuffers ) { - setupVertexAttributes( material, program, geometry ); + setupVertexAttributes( object, geometry, material, program ); if ( index !== null ) { @@ -831,7 +831,6 @@ function WebGLRenderer( parameters ) { } - } else if ( object.isLine ) { var lineWidth = material.linewidth; @@ -864,7 +863,17 @@ function WebGLRenderer( parameters ) { } - if ( geometry && geometry.isInstancedBufferGeometry ) { + if ( object.isInstancedMesh ) { + + // HACK + + geometry.maxInstancedCount = object.instanceMatrix.count; + + renderer.renderInstances( geometry, drawStart, drawCount ); + + } else if ( geometry && geometry.isInstancedBufferGeometry ) { + + // TODO: Remove all this? if ( geometry.maxInstancedCount > 0 ) { @@ -880,9 +889,20 @@ function WebGLRenderer( parameters ) { }; - function setupVertexAttributes( material, program, geometry ) { + function setupVertexAttributes( object, geometry, material, program ) { + + if ( object.isInstancedMesh ) { + + if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { + + console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + } else if ( geometry && geometry.isInstancedBufferGeometry && ! capabilities.isWebGL2 ) { - if ( geometry && geometry.isInstancedBufferGeometry && ! capabilities.isWebGL2 ) { + // TODO Remove if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { @@ -972,6 +992,50 @@ function WebGLRenderer( parameters ) { } + } else if ( name === 'instanceMatrix' ) { + + var attribute = attributes.get( object.instanceMatrix ); + + // TODO Attribute may not be available on context restore + + if ( attribute === undefined ) continue; + + var buffer = attribute.buffer; + var type = attribute.type; + + state.enableAttributeAndDivisor( programAttribute + 0, 1 ); + state.enableAttributeAndDivisor( programAttribute + 1, 1 ); + state.enableAttributeAndDivisor( programAttribute + 2, 1 ); + state.enableAttributeAndDivisor( programAttribute + 3, 1 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + + _gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 ); + _gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 ); + _gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 ); + _gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 ); + + } else if ( name === 'instanceNormalMatrix' ) { + + var attribute = attributes.get( object.instanceNormalMatrix ); + + // TODO Attribute may not be available on context restore + + if ( attribute === undefined ) continue; + + var buffer = attribute.buffer; + var type = attribute.type; + + state.enableAttributeAndDivisor( programAttribute + 0, 1 ); + state.enableAttributeAndDivisor( programAttribute + 1, 1 ); + state.enableAttributeAndDivisor( programAttribute + 2, 1 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + + _gl.vertexAttribPointer( programAttribute + 0, 3, type, false, 36, 0 ); + _gl.vertexAttribPointer( programAttribute + 1, 3, type, false, 36, 12 ); + _gl.vertexAttribPointer( programAttribute + 2, 3, type, false, 36, 24 ); + } else if ( materialDefaultAttributeValues !== undefined ) { var value = materialDefaultAttributeValues[ name ]; diff --git a/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js index 7e02c01a7191bd..ab4c49cbe1823f 100644 --- a/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +++ b/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js @@ -1,5 +1,13 @@ export default /* glsl */` -vec3 transformedNormal = normalMatrix * objectNormal; +vec3 transformedNormal = objectNormal; + +#ifdef USE_INSTANCING + + transformedNormal = instanceNormalMatrix * transformedNormal; + +#endif + +transformedNormal = normalMatrix * transformedNormal; #ifdef FLIP_SIDED diff --git a/src/renderers/shaders/ShaderChunk/project_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/project_vertex.glsl.js index 54416c502f73d7..0c28ed857c5986 100644 --- a/src/renderers/shaders/ShaderChunk/project_vertex.glsl.js +++ b/src/renderers/shaders/ShaderChunk/project_vertex.glsl.js @@ -1,5 +1,13 @@ export default /* glsl */` -vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 ); +vec4 mvPosition = vec4( transformed, 1.0 ); + +#ifdef USE_INSTANCING + + mvPosition = instanceMatrix * mvPosition; + +#endif + +mvPosition = modelViewMatrix * mvPosition; gl_Position = projectionMatrix * mvPosition; `; diff --git a/src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js index 2bf384df7501fe..cdafefd3f837ba 100644 --- a/src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js +++ b/src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js @@ -1,7 +1,15 @@ export default /* glsl */` #if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) - vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 ); + vec4 worldPosition = vec4( transformed, 1.0 ); + + #ifdef USE_INSTANCING + + worldPosition = instanceMatrix * worldPosition; + + #endif + + worldPosition = modelMatrix * worldPosition; #endif `; diff --git a/src/renderers/webgl/WebGLObjects.js b/src/renderers/webgl/WebGLObjects.js index 8a2528e27ff77e..55b0dcc2f3e780 100644 --- a/src/renderers/webgl/WebGLObjects.js +++ b/src/renderers/webgl/WebGLObjects.js @@ -2,7 +2,7 @@ * @author mrdoob / http://mrdoob.com/ */ -function WebGLObjects( geometries, info ) { +function WebGLObjects( gl, geometries, attributes, info ) { var updateList = {}; @@ -29,6 +29,13 @@ function WebGLObjects( geometries, info ) { } + if ( object.isInstancedMesh ) { + + attributes.update( object.instanceMatrix, gl.ARRAY_BUFFER ); + attributes.update( object.instanceNormalMatrix, gl.ARRAY_BUFFER ); + + } + return buffergeometry; } diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index 528ab6868078e5..aae5ba9d3e7b60 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -383,6 +383,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters, customDefines, + parameters.instancing ? '#define USE_INSTANCING' : '', parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', '#define GAMMA_FACTOR ' + gammaFactorDefine, @@ -453,6 +454,13 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters, ].join( '\n' ), + '#ifdef USE_INSTANCING', + + ' attribute mat4 instanceMatrix;', + ' attribute mat3 instanceNormalMatrix;', + + '#endif', + 'attribute vec3 position;', 'attribute vec3 normal;', 'attribute vec2 uv;', diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index e6dda01b17e5b2..f597152c905fc9 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -28,7 +28,8 @@ function WebGLPrograms( renderer, extensions, capabilities ) { }; var parameterNames = [ - "precision", "supportsVertexTextures", "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", + "precision", "supportsVertexTextures", "instancing", + "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatNormalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "gradientMap", "alphaMap", "combine", "vertexColors", "vertexTangents", "fog", "useFog", "fogExp2", @@ -137,6 +138,9 @@ function WebGLPrograms( renderer, extensions, capabilities ) { shaderID: shaderID, precision: precision, + + instancing: object.isInstancedMesh === true, + supportsVertexTextures: capabilities.vertexTextures, outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), map: !! material.map, From 6d8059c95d12007e915838399ef8fe80ac2c9df9 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Mon, 16 Sep 2019 18:25:32 -0700 Subject: [PATCH 2/8] InstancedMesh: Changed setMatrixAt parameter order. --- examples/webgl_instancing.html | 2 +- src/objects/InstancedMesh.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/webgl_instancing.html b/examples/webgl_instancing.html index 938d72075d9906..3a281d00f2a4b4 100644 --- a/examples/webgl_instancing.html +++ b/examples/webgl_instancing.html @@ -57,7 +57,7 @@ dummy.updateMatrix(); - mesh.setMatrixAt( dummy.matrix, i ); + mesh.setMatrixAt( i, dummy.matrix ); } diff --git a/src/objects/InstancedMesh.js b/src/objects/InstancedMesh.js index 291600611919c4..21774b0341f297 100644 --- a/src/objects/InstancedMesh.js +++ b/src/objects/InstancedMesh.js @@ -25,11 +25,11 @@ InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { raycast: function () {}, - setMatrixAt: function ( matrix, offset ) { + setMatrixAt: function ( index, matrix ) { - matrix.toArray( this.instanceMatrix.array, offset * 16 ); + matrix.toArray( this.instanceMatrix.array, index * 16 ); - _matrix3.getNormalMatrix( matrix ).toArray( this.instanceNormalMatrix.array, offset * 9 ); + _matrix3.getNormalMatrix( matrix ).toArray( this.instanceNormalMatrix.array, index * 9 ); }, From 114eb01ad388fe39127cad5c365b5a5a6a612850 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 19 Sep 2019 15:24:15 +0100 Subject: [PATCH 3/8] InstancedMesh: Compute instanceNormalMatrix in vertex shader. --- src/objects/InstancedMesh.js | 6 ------ src/renderers/WebGLRenderer.js | 21 ------------------- .../ShaderChunk/defaultnormal_vertex.glsl.js | 2 +- src/renderers/webgl/WebGLObjects.js | 1 - src/renderers/webgl/WebGLProgram.js | 1 - 5 files changed, 1 insertion(+), 30 deletions(-) diff --git a/src/objects/InstancedMesh.js b/src/objects/InstancedMesh.js index 21774b0341f297..e25f7b073bb5b9 100644 --- a/src/objects/InstancedMesh.js +++ b/src/objects/InstancedMesh.js @@ -3,17 +3,13 @@ */ import { BufferAttribute } from '../core/BufferAttribute.js'; -import { Matrix3 } from '../math/Matrix3.js'; import { Mesh } from './Mesh.js'; -var _matrix3 = new Matrix3(); - function InstancedMesh( geometry, material, count ) { Mesh.call( this, geometry, material ); this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 ); - this.instanceNormalMatrix = new BufferAttribute( new Float32Array( count * 9 ), 9 ); } @@ -29,8 +25,6 @@ InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { matrix.toArray( this.instanceMatrix.array, index * 16 ); - _matrix3.getNormalMatrix( matrix ).toArray( this.instanceNormalMatrix.array, index * 9 ); - }, updateMorphTargets: function () {} diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 170429c961e52f..4bdad754e525d4 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -1015,27 +1015,6 @@ function WebGLRenderer( parameters ) { _gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 ); _gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 ); - } else if ( name === 'instanceNormalMatrix' ) { - - var attribute = attributes.get( object.instanceNormalMatrix ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) continue; - - var buffer = attribute.buffer; - var type = attribute.type; - - state.enableAttributeAndDivisor( programAttribute + 0, 1 ); - state.enableAttributeAndDivisor( programAttribute + 1, 1 ); - state.enableAttributeAndDivisor( programAttribute + 2, 1 ); - - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); - - _gl.vertexAttribPointer( programAttribute + 0, 3, type, false, 36, 0 ); - _gl.vertexAttribPointer( programAttribute + 1, 3, type, false, 36, 12 ); - _gl.vertexAttribPointer( programAttribute + 2, 3, type, false, 36, 24 ); - } else if ( materialDefaultAttributeValues !== undefined ) { var value = materialDefaultAttributeValues[ name ]; diff --git a/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js b/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js index ab4c49cbe1823f..e8f94fcff7aed0 100644 --- a/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js +++ b/src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js @@ -3,7 +3,7 @@ vec3 transformedNormal = objectNormal; #ifdef USE_INSTANCING - transformedNormal = instanceNormalMatrix * transformedNormal; + transformedNormal = mat3( instanceMatrix ) * transformedNormal; #endif diff --git a/src/renderers/webgl/WebGLObjects.js b/src/renderers/webgl/WebGLObjects.js index 55b0dcc2f3e780..9032ce4018fd21 100644 --- a/src/renderers/webgl/WebGLObjects.js +++ b/src/renderers/webgl/WebGLObjects.js @@ -32,7 +32,6 @@ function WebGLObjects( gl, geometries, attributes, info ) { if ( object.isInstancedMesh ) { attributes.update( object.instanceMatrix, gl.ARRAY_BUFFER ); - attributes.update( object.instanceNormalMatrix, gl.ARRAY_BUFFER ); } diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index 388ba0426a5a00..48afd518df5769 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -509,7 +509,6 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters, '#ifdef USE_INSTANCING', ' attribute mat4 instanceMatrix;', - ' attribute mat3 instanceNormalMatrix;', '#endif', From 06e83ff5830a2badc4e7ef126e0b3bfeefbc70a6 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 19 Sep 2019 15:30:45 +0100 Subject: [PATCH 4/8] WebGLRenderer.setupVertexAttributes: Simplified ANGLE_instanced_arrays check. --- src/renderers/WebGLRenderer.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 4bdad754e525d4..7ccd9e3efead10 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -891,25 +891,9 @@ function WebGLRenderer( parameters ) { function setupVertexAttributes( object, geometry, material, program ) { - if ( object.isInstancedMesh ) { - - if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { - - console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } + if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { - } else if ( geometry && geometry.isInstancedBufferGeometry && ! capabilities.isWebGL2 ) { - - // TODO Remove - - if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { - - console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } + if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return; } From 0dbb8154d0a82cd1fa2cc9a0b5a2c0955c4e4517 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 19 Sep 2019 17:20:14 +0100 Subject: [PATCH 5/8] Improved instancing example. --- examples/webgl_instancing.html | 92 ++++++++++++++-------------------- 1 file changed, 39 insertions(+), 53 deletions(-) diff --git a/examples/webgl_instancing.html b/examples/webgl_instancing.html index 3a281d00f2a4b4..f2b13d489f4857 100644 --- a/examples/webgl_instancing.html +++ b/examples/webgl_instancing.html @@ -16,52 +16,36 @@ var camera, scene, renderer, stats; - var mouseX = 0, mouseY = 0; - - var windowHalfX = window.innerWidth / 2; - var windowHalfY = window.innerHeight / 2; + var amount = 12; + var count = Math.pow( amount, 3 ); init(); animate(); function init() { - camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 ); - camera.position.z = 25; + camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 1000 ); + camera.position.set( 10, 10, 10 ); + camera.lookAt( 0, 0, 0 ); scene = new THREE.Scene(); - scene.background = new THREE.Color( 0xffffff ); var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 ); var material = new THREE.MeshNormalMaterial(); - - var count = 10000; + // var material = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.1, transparent: true } ); // overdraw var mesh = new THREE.InstancedMesh( geometry, material, count ); + scene.add( mesh ); - var dummy = new THREE.Object3D(); - - for ( var i = 0; i < count; i ++ ) { - - dummy.position.set( - Math.random() * 20 - 10, - Math.random() * 20 - 10, - Math.random() * 20 - 10 - ); - - dummy.rotation.set( - Math.random() * Math.PI, - Math.random() * Math.PI, - Math.random() * Math.PI - ); - - dummy.updateMatrix(); + var loader = new THREE.BufferGeometryLoader(); + loader.load( 'models/json/suzanne_buffergeometry.json', function ( geometry ) { - mesh.setMatrixAt( i, dummy.matrix ); + geometry.computeVertexNormals(); + geometry.scale( 0.5, 0.5, 0.5 ); - } + mesh.geometry = geometry; - scene.add( mesh ); + } ); // @@ -77,19 +61,12 @@ // - document.addEventListener( 'mousemove', onDocumentMouseMove, false ); - - // - window.addEventListener( 'resize', onWindowResize, false ); } function onWindowResize() { - windowHalfX = window.innerWidth / 2; - windowHalfY = window.innerHeight / 2; - camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); @@ -97,13 +74,6 @@ } - function onDocumentMouseMove( event ) { - - mouseX = ( event.clientX - windowHalfX ) / 10; - mouseY = ( event.clientY - windowHalfY ) / 10; - - } - // function animate() { @@ -119,19 +89,35 @@ var time = Date.now() * 0.001; - var rx = Math.sin( time * 0.7 ) * 0.5, - ry = Math.sin( time * 0.3 ) * 0.5, - rz = Math.sin( time * 0.2 ) * 0.5; + var mesh = scene.children[ 0 ]; + mesh.rotation.x = Math.sin( time / 4 ); + mesh.rotation.y = Math.sin( time / 2 ); + + var dummy = new THREE.Object3D(); - camera.position.x += ( mouseX - camera.position.x ) * 0.05; - camera.position.y += ( - mouseY - camera.position.y ) * 0.05; + var i = 0; - camera.lookAt( scene.position ); + for ( var x = 0; x < amount; x ++ ) { - var mesh = scene.children[ 0 ]; - mesh.rotation.x = rx; - mesh.rotation.y = ry; - mesh.rotation.z = rz; + for ( var y = 0; y < amount; y ++ ) { + + for ( var z = 0; z < amount; z ++ ) { + + dummy.position.set( 6 - x, 6 - y, 6 - z ); + dummy.rotation.y = ( Math.sin( x / 4 + time ) + Math.sin( y / 4 + time ) + Math.sin( z / 4 + time ) ); + dummy.rotation.z = dummy.rotation.y * 2; + + dummy.updateMatrix(); + + mesh.setMatrixAt( i ++, dummy.matrix ); + + } + + } + + } + + mesh.instanceMatrix.needsUpdate = true; renderer.render( scene, camera ); From e6f256761c4350da901070d8c78dda6e39d38098 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Tue, 24 Sep 2019 17:28:24 +0100 Subject: [PATCH 6/8] WebGLRenderer: Simplified instancing primcount handling. --- src/renderers/WebGLRenderer.js | 14 ++------------ src/renderers/webgl/WebGLBufferRenderer.js | 8 +++++--- src/renderers/webgl/WebGLIndexedBufferRenderer.js | 8 +++++--- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 7ccd9e3efead10..2c007529c049da 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -865,21 +865,11 @@ function WebGLRenderer( parameters ) { if ( object.isInstancedMesh ) { - // HACK - - geometry.maxInstancedCount = object.instanceMatrix.count; - - renderer.renderInstances( geometry, drawStart, drawCount ); + renderer.renderInstances( geometry, drawStart, drawCount, object.instanceMatrix.count ); } else if ( geometry && geometry.isInstancedBufferGeometry ) { - // TODO: Remove all this? - - if ( geometry.maxInstancedCount > 0 ) { - - renderer.renderInstances( geometry, drawStart, drawCount ); - - } + renderer.renderInstances( geometry, drawStart, drawCount, geometry.maxInstancedCount ); } else { diff --git a/src/renderers/webgl/WebGLBufferRenderer.js b/src/renderers/webgl/WebGLBufferRenderer.js index c630098bfa2ee6..e985928767ef13 100644 --- a/src/renderers/webgl/WebGLBufferRenderer.js +++ b/src/renderers/webgl/WebGLBufferRenderer.js @@ -20,7 +20,9 @@ function WebGLBufferRenderer( gl, extensions, info, capabilities ) { } - function renderInstances( geometry, start, count ) { + function renderInstances( geometry, start, count, primcount ) { + + if ( primcount === 0 ) return; var extension, methodName; @@ -43,9 +45,9 @@ function WebGLBufferRenderer( gl, extensions, info, capabilities ) { } - extension[ methodName ]( mode, start, count, geometry.maxInstancedCount ); + extension[ methodName ]( mode, start, count, primcount ); - info.update( count, mode, geometry.maxInstancedCount ); + info.update( count, mode, primcount ); } diff --git a/src/renderers/webgl/WebGLIndexedBufferRenderer.js b/src/renderers/webgl/WebGLIndexedBufferRenderer.js index a3d4322f028029..9db249000c03cc 100644 --- a/src/renderers/webgl/WebGLIndexedBufferRenderer.js +++ b/src/renderers/webgl/WebGLIndexedBufferRenderer.js @@ -29,7 +29,9 @@ function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { } - function renderInstances( geometry, start, count ) { + function renderInstances( geometry, start, count, primcount ) { + + if ( primcount === 0 ) return; var extension, methodName; @@ -52,9 +54,9 @@ function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { } - extension[ methodName ]( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount ); + extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); - info.update( count, mode, geometry.maxInstancedCount ); + info.update( count, mode, primcount ); } From 03ce4a9940d876d32ff201ad6a21155bb727ceb6 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Tue, 24 Sep 2019 17:37:09 +0100 Subject: [PATCH 7/8] WebGLRenderer: Minor clean up. --- src/renderers/WebGLRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 2c007529c049da..958eff244e5d8f 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -867,7 +867,7 @@ function WebGLRenderer( parameters ) { renderer.renderInstances( geometry, drawStart, drawCount, object.instanceMatrix.count ); - } else if ( geometry && geometry.isInstancedBufferGeometry ) { + } else if ( geometry.isInstancedBufferGeometry ) { renderer.renderInstances( geometry, drawStart, drawCount, geometry.maxInstancedCount ); From afcbf1a34865a7ce196fa6fed0c3906a3dc7bb1f Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Tue, 24 Sep 2019 18:02:02 +0100 Subject: [PATCH 8/8] Improved instancing example. --- examples/webgl_instancing.html | 45 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/examples/webgl_instancing.html b/examples/webgl_instancing.html index f2b13d489f4857..722b51b6c5c1fd 100644 --- a/examples/webgl_instancing.html +++ b/examples/webgl_instancing.html @@ -16,8 +16,10 @@ var camera, scene, renderer, stats; + var mesh; var amount = 12; var count = Math.pow( amount, 3 ); + var dummy = new THREE.Object3D(); init(); animate(); @@ -30,12 +32,10 @@ scene = new THREE.Scene(); - var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 ); var material = new THREE.MeshNormalMaterial(); - // var material = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.1, transparent: true } ); // overdraw + // check overdraw + // var material = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.1, transparent: true } ); - var mesh = new THREE.InstancedMesh( geometry, material, count ); - scene.add( mesh ); var loader = new THREE.BufferGeometryLoader(); loader.load( 'models/json/suzanne_buffergeometry.json', function ( geometry ) { @@ -43,7 +43,8 @@ geometry.computeVertexNormals(); geometry.scale( 0.5, 0.5, 0.5 ); - mesh.geometry = geometry; + mesh = new THREE.InstancedMesh( geometry, material, count ); + scene.add( mesh ); } ); @@ -81,43 +82,45 @@ requestAnimationFrame( animate ); render(); + stats.update(); } function render() { - var time = Date.now() * 0.001; + if ( mesh ) { + + var time = Date.now() * 0.001; - var mesh = scene.children[ 0 ]; - mesh.rotation.x = Math.sin( time / 4 ); - mesh.rotation.y = Math.sin( time / 2 ); + mesh.rotation.x = Math.sin( time / 4 ); + mesh.rotation.y = Math.sin( time / 2 ); - var dummy = new THREE.Object3D(); + var i = 0; - var i = 0; + for ( var x = 0; x < amount; x ++ ) { - for ( var x = 0; x < amount; x ++ ) { + for ( var y = 0; y < amount; y ++ ) { - for ( var y = 0; y < amount; y ++ ) { + for ( var z = 0; z < amount; z ++ ) { - for ( var z = 0; z < amount; z ++ ) { + dummy.position.set( 6 - x, 6 - y, 6 - z ); + dummy.rotation.y = ( Math.sin( x / 4 + time ) + Math.sin( y / 4 + time ) + Math.sin( z / 4 + time ) ); + dummy.rotation.z = dummy.rotation.y * 2; - dummy.position.set( 6 - x, 6 - y, 6 - z ); - dummy.rotation.y = ( Math.sin( x / 4 + time ) + Math.sin( y / 4 + time ) + Math.sin( z / 4 + time ) ); - dummy.rotation.z = dummy.rotation.y * 2; + dummy.updateMatrix(); - dummy.updateMatrix(); + mesh.setMatrixAt( i ++, dummy.matrix ); - mesh.setMatrixAt( i ++, dummy.matrix ); + } } } - } + mesh.instanceMatrix.needsUpdate = true; - mesh.instanceMatrix.needsUpdate = true; + } renderer.render( scene, camera );