Bläddra i källkod

BufferedGeometry Instancing

Hardware instancing using ANGLE_instanced_arrays for BufferedGeometry
Ben Adams 10 år sedan
förälder
incheckning
fc8af1f17f

+ 273 - 0
examples/webgl_buffergeometry_instancing.html

@@ -0,0 +1,273 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>three.js webgl - instancing test</title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <style>
+        body {
+            color: #ffffff;
+            font-family: Monospace;
+            font-size: 13px;
+            text-align: center;
+            font-weight: bold;
+            background-color: #000000;
+            margin: 0px;
+            overflow: hidden;
+        }
+
+        #info {
+            position: absolute;
+            top: 0px;
+            width: 100%;
+            padding: 5px;
+        }
+
+        a {
+            color: #ffffff;
+        }
+
+        #oldie a {
+            color: #da0;
+        }
+        #notSupported {
+          width: 50%;
+          margin: auto;
+          border: 2px red solid;
+          margin-top: 20px;
+          padding: 10px;
+        }
+    </style>
+</head>
+<body>
+
+    <div id="container"></div>
+    <div id="info">
+        <a href="http://threejs.org" target="_blank">three.js</a> - instancing demo
+        <div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
+    </div>
+
+    <script src="../build/three.js"></script>
+
+    <script src="js/Detector.js"></script>
+    <script src="js/libs/stats.min.js"></script>
+
+    <script id="vertexShader" type="x-shader/x-vertex">
+
+        precision highp float;
+
+        uniform float sineTime;
+
+        uniform mat4 modelViewMatrix; // optional
+        uniform mat4 projectionMatrix; // optional
+
+        attribute vec3 position;
+        attribute vec3 offset;
+        attribute vec4 color;
+        attribute vec4 rotationStart;
+        attribute vec4 rotationEnd;
+
+        varying vec3 vPosition;
+        varying vec4 vColor;
+
+        void main()	{
+
+        vPosition = offset * max(abs(sineTime * 2.0 + 1.0), 0.5) + position;
+        vec4 rotation = normalize(mix(rotationStart, rotationEnd, sineTime));
+        vec3 vcV = cross(rotation.xyz, vPosition);
+        vPosition = vcV * (2.0 * rotation.w) + (cross(rotation.xyz, vcV) * 2.0 + vPosition);
+
+        vColor = color;
+
+        gl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition, 1.0 );
+
+        }
+
+    </script>
+
+    <script id="fragmentShader" type="x-shader/x-fragment">
+
+        precision highp float;
+
+        uniform float time;
+
+        varying vec3 vPosition;
+        varying vec4 vColor;
+
+        void main()	{
+
+        vec4 color = vec4( vColor );
+        color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
+
+        gl_FragColor = color;
+
+        }
+
+    </script>
+
+    <script>
+
+        if ( !Detector.webgl ) Detector.addGetWebGLMessage();
+
+        var container, stats;
+
+        var camera, scene, renderer;
+
+        init();
+        animate();
+
+        function init() {
+
+            container = document.getElementById( 'container' );
+
+            camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10 );
+            camera.position.z = 2;
+
+            scene = new THREE.Scene();
+
+            // geometry
+
+            var triangles = 1;
+            var instances = 65000;
+
+            var geometry = new THREE.InstancedBufferGeometry();
+
+            var vertices = new THREE.BufferAttribute( new Float32Array( triangles * 3 * 3 ), 3 );
+
+            vertices.setXYZ( 0, 0.025, -0.025, 0 );
+            vertices.setXYZ( 1, -0.025, 0.025, 0 );
+            vertices.setXYZ( 2, 0, 0, 0.025 );
+
+            geometry.addAttribute( 'position', vertices );
+
+            var offsets = new THREE.InstancedBufferAttribute( new Float32Array( instances * 3 ), 3, 1, false );
+
+            for ( var i = 0, ul = offsets.length / 3; i < ul; i++ ) {
+
+                offsets.setXYZ( i, Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 );
+
+            }
+
+            geometry.addAttribute( 'offset', offsets );
+
+            var colors = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, false );
+
+            for ( var i = 0, ul = colors.length / 4; i < ul; i++ ) {
+
+                colors.setXYZW( i, Math.random(), Math.random(), Math.random(), Math.random() );
+
+            }
+
+            geometry.addAttribute( 'color', colors );
+
+            var vector = new THREE.Vector4();
+
+            var rotationsStart = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, false );
+
+            for ( var i = 0, ul = rotationsStart.length / 4; i < ul; i++ ) {
+
+                vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
+                vector.normalize();
+
+                rotationsStart.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+
+            }
+
+            geometry.addAttribute( 'rotationStart', rotationsStart );
+
+            var rotationsEnd = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, false );
+
+            for ( var i = 0, ul = rotationsEnd.length / 4; i < ul; i++ ) {
+
+                vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
+                vector.normalize();
+
+                rotationsEnd.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+
+            }
+
+            geometry.addAttribute( 'rotationEnd', rotationsEnd );
+
+            // material
+
+            var material = new THREE.RawShaderMaterial( {
+
+                uniforms: {
+                    time: { type: "f", value: 1.0 },
+                    sineTime: { type: "f", value: 1.0 }
+                },
+                vertexShader: document.getElementById( 'vertexShader' ).textContent,
+                fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
+                side: THREE.DoubleSide,
+                transparent: true,
+                attributes: { 'position': 0, 'offset': 1, 'color': 2, 'rotationStart': 3, 'rotationEnd': 4 }
+
+            } );
+
+            var mesh = new THREE.Mesh( geometry, material );
+            scene.add( mesh );
+
+            renderer = new THREE.WebGLRenderer();
+
+            if ( !renderer.supportsInstancedArrays ) {
+                document.getElementById( "notSupported" ).style.display = "";
+                return;
+            }
+
+            renderer.setClearColor( 0x101010 );
+            renderer.setPixelRatio( window.devicePixelRatio );
+            renderer.setSize( window.innerWidth, window.innerHeight );
+            container.appendChild( renderer.domElement );
+
+            stats = new Stats();
+            stats.domElement.style.position = 'absolute';
+            stats.domElement.style.top = '0px';
+            container.appendChild( stats.domElement );
+
+            window.addEventListener( 'resize', onWindowResize, false );
+
+        }
+
+        function onWindowResize( event ) {
+
+            camera.aspect = window.innerWidth / window.innerHeight;
+            camera.updateProjectionMatrix();
+
+            renderer.setSize( window.innerWidth, window.innerHeight );
+
+        }
+
+        //
+
+        function animate() {
+
+            requestAnimationFrame( animate );
+
+            render();
+            stats.update();
+
+        }
+
+        var lastTime = 0;
+
+        function render() {
+
+            var time = performance.now();
+
+
+            var object = scene.children[0];
+
+            object.rotation.y = time * 0.0005;
+            object.material.uniforms.time.value = time * 0.005;
+            object.material.uniforms.sineTime.value = Math.sin( object.material.uniforms.time.value * 0.05 );
+
+            renderer.render( scene, camera );
+
+            lastTime = time;
+        }
+
+    </script>
+
+</body>
+
+</html>

+ 341 - 0
examples/webgl_buffergeometry_instancing_dynamic.html

@@ -0,0 +1,341 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>three.js webgl - indexed instancing, dynamic updates</title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+    <style>
+        body {
+            color: #ffffff;
+            font-family: Monospace;
+            font-size: 13px;
+            text-align: center;
+            font-weight: bold;
+            background-color: #000000;
+            margin: 0px;
+            overflow: hidden;
+        }
+
+        #info {
+            position: absolute;
+            top: 0px;
+            width: 100%;
+            padding: 5px;
+        }
+
+        a {
+            color: #ffffff;
+        }
+
+        #oldie a {
+            color: #da0;
+        }
+        #notSupported {
+          width: 50%;
+          margin: auto;
+          border: 2px red solid;
+          margin-top: 20px;
+          padding: 10px;
+        }
+    </style>
+</head>
+<body>
+
+    <div id="container"></div>
+    <div id="info">
+        <a href="http://threejs.org" target="_blank">three.js</a> - indexed instancing, dynamic updates
+        <div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
+    </div>
+
+    <script src="../build/three.js"></script>
+
+    <script src="js/Detector.js"></script>
+    <script src="js/libs/stats.min.js"></script>
+
+    <script id="vertexShader" type="x-shader/x-vertex">
+        precision highp float;
+        
+        uniform mat4 modelViewMatrix; // optional
+        uniform mat4 projectionMatrix; // optional
+
+        attribute vec3 position;
+        attribute vec3 offset;
+        attribute vec2 uv;
+        attribute vec4 rotation;
+
+        varying vec2 vUv;
+
+        void main()	{
+
+        vec3 vPosition = position;
+        vec3 vcV = cross(rotation.xyz, vPosition);
+        vPosition = vcV * (2.0 * rotation.w) + (cross(rotation.xyz, vcV) * 2.0 + vPosition);
+
+        vUv = uv;
+
+        gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );
+
+        }
+
+    </script>
+
+    <script id="fragmentShader" type="x-shader/x-fragment">
+        precision highp float;
+
+        uniform sampler2D map;
+
+        varying vec2 vUv;
+
+        void main()	{
+
+        gl_FragColor = texture2D(map, vUv);
+
+        }
+
+    </script>
+
+    <script>
+
+        if ( !Detector.webgl ) Detector.addGetWebGLMessage();
+
+        var container, stats;
+
+        var camera, scene, renderer;
+        var rotations;
+
+
+        function init() {
+
+            container = document.getElementById( 'container' );
+
+            camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
+            //camera.position.z = 20;
+
+            renderer = new THREE.WebGLRenderer();
+            scene = new THREE.Scene();
+
+            // geometry
+
+            var instances = 5000;
+
+            var geometry = new THREE.InstancedBufferGeometry();
+
+            // per mesh data
+            var vertices = new THREE.BufferAttribute( new Float32Array( [
+                // Front
+                -1, 1, 1,
+                1, 1, 1,
+                -1, -1, 1,
+                1, -1, 1,
+                // Back
+                1, 1, -1,
+                -1, 1, -1,
+                1, -1, -1,
+                -1, -1, -1,
+                // Left
+                -1, 1, -1,
+                -1, 1, 1,
+                -1, -1, -1,
+                -1, -1, 1,
+                // Right
+                1, 1, 1,
+                1, 1, -1,
+                1, -1, 1,
+                1, -1, -1,
+                // Top
+                -1, 1, 1,
+                1, 1, 1,
+                -1, 1, -1,
+                1, 1, -1,
+                // Bottom
+                1, -1, 1,
+                -1, -1, 1,
+                1, -1, -1,
+                -1, -1, -1
+            ] ), 3 );
+
+            geometry.addAttribute( 'position', vertices );
+
+            var uvs = new THREE.BufferAttribute( new Float32Array( [
+                        //x    y    z
+                        // Front
+                        0, 0,
+                        1, 0,
+                        0, 1,
+                        1, 1,
+                        // Back
+                        1, 0,
+                        0, 0,
+                        1, 1,
+                        0, 1,
+                        // Left
+                        1, 1,
+                        1, 0,
+                        0, 1,
+                        0, 0,
+                        // Right
+                        1, 0,
+                        1, 1,
+                        0, 0,
+                        0, 1,
+                        // Top
+                        0, 0,
+                        1, 0,
+                        0, 1,
+                        1, 1,
+                        // Bottom
+                        1, 0,
+                        0, 0,
+                        1, 1,
+                        0, 1
+            ] ), 2 );
+
+            geometry.addAttribute( 'uv', uvs );
+
+            var indices = new Uint16Array( [
+                0, 1, 2,
+                2, 1, 3,
+                4, 5, 6,
+                6, 5, 7,
+                8, 9, 10,
+                10, 9, 11,
+                12, 13, 14,
+                14, 13, 15,
+                16, 17, 18,
+                18, 17, 19,
+                20, 21, 22,
+                22, 21, 23
+            ] );
+
+            geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
+
+            // per instance data
+            var offsets = new THREE.InstancedBufferAttribute( new Float32Array( instances * 3 ), 3, 1, false );
+
+            var vector = new THREE.Vector4();
+            for ( var i = 0, ul = offsets.length / 3; i < ul; i++ ) {
+                var x = Math.random() * 100 - 50;
+                var y = Math.random() * 100 - 50;
+                var z = Math.random() * 100 - 50;
+                vector.set( x, y, z, 0 ).normalize();
+                // move out at least 5 units from center in current direction
+                offsets.setXYZ( i, x + vector.x * 5, y + vector.y * 5, z + vector.z * 5 );
+
+            }
+
+            geometry.addAttribute( 'offset', offsets ); // per mesh translation
+
+
+            rotations = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1, true );
+
+            for ( var i = 0, ul = rotations.length / 4; i < ul; i++ ) {
+
+                vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
+                vector.normalize();
+
+                rotations.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+
+            }
+
+            geometry.addAttribute( 'rotation', rotations ); // per mesh rotation
+
+            // material
+            var texture = THREE.ImageUtils.loadTexture( 'textures/crate.gif' );
+            texture.anisotropy = renderer.getMaxAnisotropy();
+
+            var material = new THREE.RawShaderMaterial( {
+
+                uniforms: {
+                    map: { type: "t", value: texture }
+                },
+                vertexShader: document.getElementById( 'vertexShader' ).textContent,
+                fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
+                side: THREE.DoubleSide,
+                transparent: false,
+                attributes: { 'position': 0, 'offset': 1, 'rotation': 2, 'uv': 3 }
+
+            } );
+
+            var mesh = new THREE.Mesh( geometry, material );
+            scene.add( mesh );
+
+
+            if ( !renderer.supportsInstancedArrays ) {
+                document.getElementById( "notSupported" ).style.display = "";
+                return;
+            }
+
+            renderer.setClearColor( 0x101010 );
+            renderer.setPixelRatio( window.devicePixelRatio );
+            renderer.setSize( window.innerWidth, window.innerHeight );
+            container.appendChild( renderer.domElement );
+
+            stats = new Stats();
+            stats.domElement.style.position = 'absolute';
+            stats.domElement.style.top = '0px';
+            container.appendChild( stats.domElement );
+
+            window.addEventListener( 'resize', onWindowResize, false );
+
+        }
+
+        function onWindowResize( event ) {
+
+            camera.aspect = window.innerWidth / window.innerHeight;
+            camera.updateProjectionMatrix();
+
+            renderer.setSize( window.innerWidth, window.innerHeight );
+
+        }
+
+        //
+
+        function animate() {
+
+            requestAnimationFrame( animate );
+
+            render();
+            stats.update();
+
+        }
+
+        var lastTime = 0;
+
+        var moveQ = ( new THREE.Quaternion( .5, .5, .5, 0.0 ) ).normalize();
+        var tmpQ = new THREE.Quaternion();
+        var currentQ = new THREE.Quaternion();
+        function render() {
+
+            var time = performance.now();
+
+
+            var object = scene.children[0];
+
+            object.rotation.y = time * 0.00005;
+
+            renderer.render( scene, camera );
+
+            var delta = ( time - lastTime ) / 5000;
+            tmpQ.set( moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1 ).normalize();
+
+            for ( var i = 0, ul = rotations.length / 4; i < ul; i++ ) {
+                var index = i * 4;
+                currentQ.set( rotations.array[index], rotations.array[index + 1], rotations.array[index + 2], rotations.array[index + 3] );
+                currentQ.multiply( tmpQ );
+                
+                rotations.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
+
+            }
+            rotations.needsUpdate = true;
+            lastTime = time;
+        }
+
+        init();
+        animate();
+    </script>
+
+</body>
+
+
+
+</html>

+ 21 - 0
src/core/InstancedBufferAttribute.js

@@ -0,0 +1,21 @@
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedBufferAttribute = function (array, itemSize, meshPerAttribute, dynamic) {
+
+    THREE.DynamicBufferAttribute.call( this, array, itemSize );
+
+	this.dynamic = dynamic || false;
+	this.meshPerAttribute = meshPerAttribute || 1;
+
+};
+
+THREE.InstancedBufferAttribute.prototype = Object.create( THREE.DynamicBufferAttribute.prototype );
+THREE.InstancedBufferAttribute.prototype.constructor = THREE.InstancedBufferAttribute;
+
+THREE.InstancedBufferAttribute.prototype.clone = function () {
+
+    return new THREE.InstancedBufferAttribute(new this.array.constructor(this.array), this.itemSize, meshPerAttribute, dynamic);
+
+};

+ 60 - 0
src/core/InstancedBufferGeometry.js

@@ -0,0 +1,60 @@
+/**
+ * @author benaadams / https://twitter.com/ben_a_adams
+ */
+
+THREE.InstancedBufferGeometry = function () {
+
+    THREE.BufferGeometry.call( this );
+
+    this.type = 'InstancedBufferGeometry';
+    this.maxInstancedCount = -1;
+
+};
+
+THREE.InstancedBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.InstancedBufferGeometry.prototype.constructor = THREE.InstancedBufferGeometry;
+
+THREE.InstancedBufferGeometry.prototype.addDrawCall = function ( start, count, indexOffset, instances ) {
+
+    this.drawcalls.push( {
+
+        start: start,
+        count: count,
+        index: indexOffset !== undefined ? indexOffset : 0,
+        instances: instances
+    } );
+
+},
+
+THREE.InstancedBufferGeometry.prototype.clone = function () {
+
+    var geometry = new THREE.InstancedBufferGeometry();
+
+    for ( var attr in this.attributes ) {
+
+        var sourceAttr = this.attributes[attr];
+        geometry.addAttribute( attr, sourceAttr.clone() );
+
+    }
+
+    for ( var i = 0, il = this.offsets.length; i < il; i++ ) {
+
+        var offset = this.offsets[i];
+
+        geometry.offsets.push( {
+
+            start: offset.start,
+            index: offset.index,
+            count: offset.count,
+            instances: offset.instances
+
+        } );
+
+    }
+
+    return geometry;
+
+};
+
+
+THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype );

+ 95 - 4
src/renderers/WebGLRenderer.js

@@ -222,6 +222,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 	extensions.get( 'OES_texture_half_float' );
 	extensions.get( 'OES_texture_half_float_linear' );
 	extensions.get( 'OES_standard_derivatives' );
+	extensions.get( 'ANGLE_instanced_arrays' );
 
 	if ( _logarithmicDepthBuffer ) {
 
@@ -294,6 +295,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	var _supportsVertexTextures = _maxVertexTextures > 0;
 	var _supportsBoneTextures = _supportsVertexTextures && extensions.get( 'OES_texture_float' );
+	var _supportsInstancedArrays = extensions.get( 'ANGLE_instanced_arrays' );
 
 	//
 
@@ -390,6 +392,12 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
+	this.supportsInstancedArrays = function () {
+
+	    return _supportsInstancedArrays;
+
+	};
+
 	this.supportsFloatTextures = function () {
 
 		return extensions.get( 'OES_texture_float' );
@@ -2412,6 +2420,21 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	function setupVertexAttributes( material, program, geometry, startIndex ) {
 
+	    var extension;
+
+	    if ( geometry instanceof THREE.InstancedBufferGeometry ) {
+
+	        extension = extensions.get( 'ANGLE_instanced_arrays' );
+	        
+	        if ( extension === null ) {
+
+	            THREE.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+	            return;
+
+	        }
+
+	    }
+
 		var geometryAttributes = geometry.attributes;
 
 		var programAttributes = program.attributes;
@@ -2436,6 +2459,25 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					_gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32
 
+					if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) {
+
+					    if ( extension === null ) {
+
+					        THREE.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferAttribute but hardware does not support extension ANGLE_instanced_arrays.' );
+					        return;
+
+					    }
+
+					    extension.vertexAttribDivisorANGLE( programAttribute, geometryAttribute.meshPerAttribute );
+
+					    if ( geometry.maxInstancedCount === -1 ) {
+
+					        geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * ( geometryAttribute.array.length / geometryAttribute.itemSize );
+
+					    }
+
+					}
+
 				} else if ( material.defaultAttributeValues !== undefined ) {
 
 					if ( material.defaultAttributeValues[ key ].length === 2 ) {
@@ -2520,8 +2562,24 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					}
 
-					_gl.drawElements( mode, index.array.length, type, 0 );
+					if ( geometry instanceof THREE.InstancedBufferGeometry ) {
+
+					    var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+					    if ( extension === null ) {
+
+					        THREE.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+					        return;
+
+					    }
 
+					    extension.drawElementsInstancedANGLE( mode, index.array.length, type, 0, geometry.maxInstancedCount ); // Draw the instanced meshes
+
+					} else {
+
+					    _gl.drawElements( mode, index.array.length, type, 0 );
+
+					}
 					_this.info.render.calls ++;
 					_this.info.render.vertices += index.array.length; // not really true, here vertices can be shared
 					_this.info.render.faces += index.array.length / 3;
@@ -2546,8 +2604,24 @@ THREE.WebGLRenderer = function ( parameters ) {
 						}
 
 						// render indexed triangles
+                        
+						if ( geometry instanceof THREE.InstancedBufferGeometry ) {
 
-						_gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size );
+						    var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+						    if ( extension === null ) {
+
+						        THREE.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+						        return;
+
+						    }
+
+						    extension.drawElementsInstancedANGLE( mode, offsets[i].count, type, offsets[i].start * size, offsets[i].count, type, offsets[i].instances ); // Draw the instanced meshes
+
+						} else {
+
+						    _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size );
+						}
 
 						_this.info.render.calls ++;
 						_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
@@ -2570,8 +2644,25 @@ THREE.WebGLRenderer = function ( parameters ) {
 				var position = geometry.attributes[ 'position' ];
 
 				// render non-indexed triangles
+                
+				if ( geometry instanceof THREE.InstancedBufferGeometry ) {
+
+				    var extension = extensions.get( 'ANGLE_instanced_arrays' );
+
+				    if ( extension === null ) {
+
+				        THREE.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
+				        return;
+
+				    }
 
-				_gl.drawArrays( mode, 0, position.array.length / position.itemSize );
+				    extension.drawArraysInstancedANGLE( mode, 0, position.array.length / position.itemSize, geometry.maxInstancedCount ); // Draw the instanced meshes
+
+				} else {
+
+				    _gl.drawArrays( mode, 0, position.array.length / position.itemSize );
+
+				}
 
 				_this.info.render.calls ++;
 				_this.info.render.vertices += position.array.length / position.itemSize;
@@ -3914,7 +4005,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					attribute.buffer = _gl.createBuffer();
 					_gl.bindBuffer( bufferType, attribute.buffer );
-					_gl.bufferData( bufferType, attribute.array, ( attribute instanceof THREE.DynamicBufferAttribute ) ? _gl.DYNAMIC_DRAW : _gl.STATIC_DRAW );
+					_gl.bufferData( bufferType, attribute.array, ( attribute instanceof THREE.DynamicBufferAttribute && !( attribute instanceof THREE.InstancedBufferAttribute && attribute.dynamic === false ) ) ? _gl.DYNAMIC_DRAW : _gl.STATIC_DRAW );
 
 					attribute.needsUpdate = false;
 

+ 3 - 1
utils/build/includes/common.json

@@ -26,8 +26,10 @@
 	"src/core/Face4.js",
 	"src/core/BufferAttribute.js",
 	"src/core/DynamicBufferAttribute.js",
-	"src/core/BufferGeometry.js",
+	"src/core/InstancedBufferAttribute.js",
 	"src/core/Geometry.js",
+	"src/core/BufferGeometry.js",
+	"src/core/InstancedBufferGeometry.js",
 	"src/cameras/Camera.js",
 	"src/cameras/CubeCamera.js",
 	"src/cameras/OrthographicCamera.js",