Selaa lähdekoodia

WebGLRenderer: Reverted non-hot-path Object.keys(). Moved hot-path ones to single-call paths. See #5186.

Mr.doob 11 vuotta sitten
vanhempi
commit
90606a278b

+ 1 - 1
examples/js/loaders/ctm/CTMLoader.js

@@ -248,7 +248,7 @@ THREE.CTMLoader.prototype.createModel = function ( file, callback ) {
 	geometry.computeOffsets();
 
 	// compute vertex normals if not present in the CTM model
-	if ( geometry.attributes[ "normal" ] === undefined ) {
+	if ( geometry.attributes.normal === undefined ) {
 		geometry.computeVertexNormals();
 	}
 

+ 7 - 31
examples/js/loaders/gltf/glTFLoader.js

@@ -102,18 +102,9 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
     ClassicGeometry.prototype.buildBufferGeometry = function() {
         // Build indexed mesh
         var geometry = this.geometry;
-        geometry.attributes.index = {
-        		itemSize: 1,
-        		array : this.indexArray
-        };
-
-		var offset = {
-				start: 0,
-				index: 0,
-				count: this.indexArray.length
-			};
 
-		geometry.offsets.push( offset );
+        geometry.addAttribute( 'index', new THREE.BufferAttribute( this.indexArray, 1 ) );
+        geometry.addDrawCall( 0, this.indexArray.length, 0 );
 
         geometry.computeBoundingSphere();
     }
@@ -226,16 +217,10 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
         if(semantic == "POSITION") {
             // TODO: Should be easy to take strides into account here
             floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type));
-            geom.geometry.attributes.position = {
-            		itemSize: 3,
-            		array : floatArray
-            };
+            geom.geometry.addAttribute( 'position', new THREE.BufferAttribute( floatArray, 3 ) );
         } else if(semantic == "NORMAL") {
             floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type));
-            geom.geometry.attributes.normal = {
-            		itemSize: 3,
-            		array : floatArray
-            };
+            geom.geometry.addAttribute( 'normal', new THREE.BufferAttribute( floatArray, 3 ) );
         } else if ((semantic == "TEXCOORD_0") || (semantic == "TEXCOORD" )) {
         	
         	nComponents = componentsPerElementForGLType(attribute.type);
@@ -244,26 +229,17 @@ THREE.glTFLoader.prototype.load = function( url, callback ) {
             for (i = 0; i < floatArray.length / 2; i++) {
             	floatArray[i*2+1] = 1.0 - floatArray[i*2+1];
             }
-            geom.geometry.attributes.uv = {
-            		itemSize: nComponents,
-            		array : floatArray
-            };
+            geom.geometry.addAttribute( 'uv', new THREE.BufferAttribute( floatArray, nComponents ) );
         }
         else if (semantic == "WEIGHT") {
         	nComponents = componentsPerElementForGLType(attribute.type);
             floatArray = new Float32Array(glResource, 0, attribute.count * nComponents);
-            geom.geometry.attributes.skinWeight = {
-            		itemSize: nComponents,
-            		array : floatArray
-            };        	
+            geom.geometry.addAttribute( 'skinWeight', new THREE.BufferAttribute( floatArray, nComponents ) );
         }
         else if (semantic == "JOINT") {
         	nComponents = componentsPerElementForGLType(attribute.type);
             floatArray = new Float32Array(glResource, 0, attribute.count * nComponents);
-            geom.geometry.attributes.skinIndex = {
-            		itemSize: nComponents,
-            		array : floatArray
-            };        	
+            geom.geometry.addAttribute( 'skinIndex', new THREE.BufferAttribute( floatArray, nComponents ) );
         }
     }
     

+ 18 - 20
examples/js/wip/TypedGeometry.js

@@ -8,13 +8,11 @@ THREE.TypedGeometry = function ( size ) {
 
 	if ( size !== undefined ) {
 
-		this.vertices = new Float32Array( size * 3 * 3 );
-		this.normals = new Float32Array( size * 3 * 3 );
-		this.uvs = new Float32Array( size * 3 * 2 );
-
-		this.attributes[ 'position' ] = { array: this.vertices, itemSize: 3 };
-		this.attributes[ 'normal' ] = { array: this.normals, itemSize: 3 };
-		this.attributes[ 'uv' ] = { array: this.uvs, itemSize: 2 };
+		this.setArrays( 
+			new Float32Array( size * 3 * 3 ),
+			new Float32Array( size * 3 * 3 ),
+			new Float32Array( size * 3 * 2 )
+		);
 
 	}
 
@@ -28,9 +26,9 @@ THREE.TypedGeometry.prototype.setArrays = function ( vertices, normals, uvs ) {
 	this.normals = normals;
 	this.uvs = uvs;
 
-	this.attributes[ 'position' ] = { array: vertices, itemSize: 3 };
-	this.attributes[ 'normal' ] = { array: normals, itemSize: 3 };
-	this.attributes[ 'uv' ] = { array: uvs, itemSize: 2 };
+	this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+	this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
+	this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
 
 	return this;
 
@@ -48,15 +46,15 @@ THREE.TypedGeometry.prototype.merge = ( function () {
 		var offset2 = offset * 2;
 		var offset3 = offset * 3;
 
-		var vertices = this.attributes[ 'position' ].array;
-		var normals = this.attributes[ 'normal' ].array;
-		var uvs = this.attributes[ 'uv' ].array;
+		var vertices = this.attributes.position.array;
+		var normals = this.attributes.normal.array;
+		var uvs = this.attributes.uv.array;
 
 		if ( geometry instanceof THREE.TypedGeometry ) {
 
-			var vertices2 = geometry.attributes[ 'position' ].array;
-			var normals2 = geometry.attributes[ 'normal' ].array;
-			var uvs2 = geometry.attributes[ 'uv' ].array;
+			var vertices2 = geometry.attributes.position.array;
+			var normals2 = geometry.attributes.normal.array;
+			var uvs2 = geometry.attributes.uv.array;
 
 			for ( var i = 0, l = vertices2.length; i < l; i += 3 ) {
 
@@ -75,10 +73,10 @@ THREE.TypedGeometry.prototype.merge = ( function () {
 
 		} else if ( geometry instanceof THREE.IndexedTypedGeometry ) {
 
-			var indices2 = geometry.attributes[ 'index' ].array;
-			var vertices2 = geometry.attributes[ 'position' ].array;
-			var normals2 = geometry.attributes[ 'normal' ].array;
-			var uvs2 = geometry.attributes[ 'uv' ].array;
+			var indices2 = geometry.attributes.index.array;
+			var vertices2 = geometry.attributes.position.array;
+			var normals2 = geometry.attributes.normal.array;
+			var uvs2 = geometry.attributes.uv.array;
 
 			for ( var i = 0, l = indices2.length; i < l; i ++ ) {
 

+ 8 - 12
examples/misc_controls_pointerlock.html

@@ -181,12 +181,8 @@
 				scene = new THREE.Scene();
 				scene.fog = new THREE.Fog( 0xffffff, 0, 750 );
 
-				var light = new THREE.DirectionalLight( 0xffffff, 1.5 );
-				light.position.set( 1, 1, 1 );
-				scene.add( light );
-
-				var light = new THREE.DirectionalLight( 0xffffff, 0.75 );
-				light.position.set( -1, - 0.5, -1 );
+				var light = new THREE.HemisphereLight( 0xeeeeff, 0x777788, 0.75 );
+				light.position.set( 0.5, 1, 0.75 );
 				scene.add( light );
 
 				controls = new THREE.PointerLockControls( camera );
@@ -211,9 +207,9 @@
 				for ( var i = 0, l = geometry.faces.length; i < l; i ++ ) {
 
 					var face = geometry.faces[ i ];
-					face.vertexColors[ 0 ] = new THREE.Color().setHSL( Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
-					face.vertexColors[ 1 ] = new THREE.Color().setHSL( Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
-					face.vertexColors[ 2 ] = new THREE.Color().setHSL( Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
+					face.vertexColors[ 0 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
+					face.vertexColors[ 1 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
+					face.vertexColors[ 2 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
 
 				}
 
@@ -229,9 +225,9 @@
 				for ( var i = 0, l = geometry.faces.length; i < l; i ++ ) {
 
 					var face = geometry.faces[ i ];
-					face.vertexColors[ 0 ] = new THREE.Color().setHSL( Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
-					face.vertexColors[ 1 ] = new THREE.Color().setHSL( Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
-					face.vertexColors[ 2 ] = new THREE.Color().setHSL( Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
+					face.vertexColors[ 0 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
+					face.vertexColors[ 1 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
+					face.vertexColors[ 2 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
 
 				}
 

+ 5 - 16
examples/webgl_nearestneighbour.html

@@ -139,26 +139,15 @@
 				var distanceFunction = function(a, b){
 					return Math.pow(a[0] - b[0], 2) +  Math.pow(a[1] - b[1], 2) +  Math.pow(a[2] - b[2], 2);
 				};
+
+				positions = new Float32Array( amountOfParticles * 3 );
+				alphas = new Float32Array( amountOfParticles );
 				
 				_particleGeom = new THREE.BufferGeometry();
-				_particleGeom.attributes = {
-
-					position: {
-						itemSize: 3,
-						array: new Float32Array( amountOfParticles * 3 )
-					},
-					
-					alpha: {
-						itemSize: 1,
-						array: new Float32Array( amountOfParticles )
-					}
-
-				};
-				positions = _particleGeom.attributes.position.array;
-				alphas = _particleGeom.attributes.alpha.array;
+				_particleGeom.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+				_particleGeom.addAttribute( 'alpha', new THREE.BufferAttribute( alphas, 1 ) );
 				
 				particles = new THREE.PointCloud( _particleGeom, pointShaderMaterial );
-				particles.dynamic = true;
 				
 				for (var x = 0; x < amountOfParticles; x++) {
 					positions[ x * 3 + 0 ] = Math.random() * 1000;

+ 24 - 26
src/core/BufferGeometry.js

@@ -12,6 +12,8 @@ THREE.BufferGeometry = function () {
 	this.type = 'BufferGeometry';
 
 	this.attributes = {};
+	this.attributesKeys = [];
+
 	this.drawcalls = [];
 	this.offsets = this.drawcalls; // backwards compatibility
 
@@ -37,6 +39,7 @@ THREE.BufferGeometry.prototype = {
 		}
 
 		this.attributes[ name ] = attribute;
+		this.attributesKeys = Object.keys( this.attributes );
 
 	},
 
@@ -242,7 +245,7 @@ THREE.BufferGeometry.prototype = {
 
 			}
 
-			var positions = this.attributes[ 'position' ].array;
+			var positions = this.attributes.position.array;
 
 			if ( positions ) {
 
@@ -288,7 +291,7 @@ THREE.BufferGeometry.prototype = {
 
 			}
 
-			var positions = this.attributes[ 'position' ].array;
+			var positions = this.attributes.position.array;
 
 			if ( positions ) {
 
@@ -347,15 +350,17 @@ THREE.BufferGeometry.prototype = {
 
 			if ( attributes.normal === undefined ) {
 
-				attributes.normal = new THREE.BufferAttribute( new Float32Array( positions.length ), 3 );
+				this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) );
 
 			} else {
 
 				// reset existing normals to zero
 
-				for ( var i = 0, il = attributes.normal.array.length; i < il; i ++ ) {
+				var normals = attributes.normal.array;
+
+				for ( var i = 0, il = normals.length; i < il; i ++ ) {
 
-					attributes.normal.array[ i ] = 0;
+					normals[ i ] = 0;
 
 				}
 
@@ -481,37 +486,30 @@ THREE.BufferGeometry.prototype = {
 		// based on http://www.terathon.com/code/tangent.html
 		// (per vertex tangents)
 
-		if ( this.attributes[ 'index' ] === undefined ||
-			 this.attributes[ 'position' ] === undefined ||
-			 this.attributes[ 'normal' ] === undefined ||
-			 this.attributes[ 'uv' ] === undefined ) {
+		if ( this.attributes.index === undefined ||
+			 this.attributes.position === undefined ||
+			 this.attributes.normal === undefined ||
+			 this.attributes.uv === undefined ) {
 
 			console.warn( 'Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' );
 			return;
 
 		}
 
-		var indices = this.attributes[ 'index' ].array;
-		var positions = this.attributes[ 'position' ].array;
-		var normals = this.attributes[ 'normal' ].array;
-		var uvs = this.attributes[ 'uv' ].array;
+		var indices = this.attributes.index.array;
+		var positions = this.attributes.position.array;
+		var normals = this.attributes.normal.array;
+		var uvs = this.attributes.uv.array;
 
 		var nVertices = positions.length / 3;
 
-		if ( this.attributes[ 'tangent' ] === undefined ) {
-
-			var nTangentElements = 4 * nVertices;
+		if ( this.attributes.tangent === undefined ) {
 
-			this.attributes[ 'tangent' ] = {
-
-				itemSize: 4,
-				array: new Float32Array( nTangentElements )
-
-			};
+			this.addAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) );
 
 		}
 
-		var tangents = this.attributes[ 'tangent' ].array;
+		var tangents = this.attributes.tangent.array;
 
 		var tan1 = [], tan2 = [];
 
@@ -690,8 +688,8 @@ THREE.BufferGeometry.prototype = {
 
 		var s = Date.now();
 
-		var indices = this.attributes[ 'index' ].array;
-		var vertices = this.attributes[ 'position' ].array;
+		var indices = this.attributes.index.array;
+		var vertices = this.attributes.position.array;
 
 		var verticesCount = ( vertices.length / 3 );
 		var facesCount = ( indices.length / 3 );
@@ -794,7 +792,7 @@ THREE.BufferGeometry.prototype = {
 
 	normalizeNormals: function () {
 
-		var normals = this.attributes[ 'normal' ].array;
+		var normals = this.attributes.normal.array;
 
 		var x, y, z, n;
 

+ 49 - 87
src/renderers/WebGLRenderer.js

@@ -588,14 +588,9 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		if ( geometry.__webglCustomAttributesList !== undefined ) {
 
-			var attributes = geometry.__webglCustomAttributesList;
-			var keys = Object.keys( attributes );
+			for ( var name in geometry.__webglCustomAttributesList ) {
 
-			for ( var i = 0; i < keys.length; i ++ ) {
-
-				var name = keys[ i ];
-
-				_gl.deleteBuffer( attributes[ name ].buffer );
+				_gl.deleteBuffer( geometry.__webglCustomAttributesList[ name ].buffer );
 
 			}
 
@@ -611,16 +606,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		if ( geometry instanceof THREE.BufferGeometry ) {
 
-			var attributes = geometry.attributes;
-			var keys = Object.keys( attributes );
-
-			for ( var i = 0; i < keys.length; i ++ ) {
-
-				var name = keys[ i ];
+			for ( var name in geometry.attributes ) {
 
-				if ( attributes[ name ].buffer !== undefined ) {
+				if ( geometry.attributes[ name ].buffer !== undefined ) {
 
-					_gl.deleteBuffer( attributes[ name ].buffer );
+					_gl.deleteBuffer( geometry.attributes[ name ].buffer );
 
 				}
 
@@ -794,11 +784,8 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
-			var keys = Object.keys( material.attributes );
+			for ( var name in material.attributes ) {
 
-			for ( var i = 0; i < keys.length; i ++ ) {
-
-				var name = keys[ i ];
 				var attribute = material.attributes[ name ];
 
 				if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) {
@@ -941,23 +928,17 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			}
 
-			var keys = Object.keys( material.attributes );
-
-			for ( var i = 0; i < keys.length; i ++ ) {
+			for ( var name in material.attributes ) {
 
 				// Do a shallow copy of the attribute object so different geometryGroup chunks use different
 				// attribute buffers which are correctly indexed in the setMeshBuffers function
 
-				var name = keys[ i ];
 				var originalAttribute = material.attributes[ name ];
 
 				var attribute = {};
 
-				var keys2 = Object.keys( originalAttribute );
+				for ( var property in originalAttribute ) {
 
-				for ( var j = 0; j < keys2.length; j ++ ) {
-
-					var property = keys2[ j ];
 					attribute[ property ] = originalAttribute[ property ];
 
 				}
@@ -2306,12 +2287,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 	function setDirectBuffers( geometry ) {
 
 		var attributes = geometry.attributes;
+		var attributesKeys = geometry.attributesKeys;
 
-		var keys = Object.keys( attributes );
-
-		for ( var i = 0; i < keys.length; i ++ ) {
+		for ( var i = 0, l = attributesKeys.length; i < l; i ++ ) {
 
-			var attributeName = keys[ i ];
+			var attributeName = attributesKeys[ i ];
 			var attributeItem = attributes[ attributeName ];
 
 			if ( attributeItem.buffer === undefined ) {
@@ -2435,28 +2415,34 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	};
 
-	function setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ) {
+	function setupVertexAttributes( material, program, geometry, startIndex ) {
 
-		var keys = Object.keys( programAttributes );
+		var geometryAttributes = geometry.attributes;
+
+		var programAttributes = program.attributes;
+		var programAttributesKeys = program.attributesKeys;
 
-		for ( var i = 0; i < keys.length; i ++ ) {
+		for ( var i = 0, l = programAttributesKeys.length; i < l; i ++ ) {
 
-			var attributeName = keys[ i ];
+			var attributeName = programAttributesKeys[ i ];
 
-			var attributeItem = geometryAttributes[ attributeName ];
 			var attributePointer = programAttributes[ attributeName ];
 
 			if ( attributePointer >= 0 ) {
 
-				if ( attributeItem ) {
+				var attributeItem = geometryAttributes[ attributeName ];
+
+				if ( attributeItem !== undefined ) {
 
 					var attributeSize = attributeItem.itemSize;
 
 					_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
+
 					enableAttribute( attributePointer );
+
 					_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32
 
-				} else if ( material.defaultAttributeValues ) {
+				} else if ( material.defaultAttributeValues !== undefined ) {
 
 					if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
 
@@ -2485,9 +2471,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		var program = setProgram( camera, lights, fog, material, object );
 
-		var programAttributes = program.attributes;
-		var geometryAttributes = geometry.attributes;
-
 		var updateBuffers = false,
 			wireframeBit = material.wireframe ? 1 : 0,
 			geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
@@ -2509,7 +2492,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		if ( object instanceof THREE.Mesh ) {
 
-			var index = geometryAttributes[ 'index' ];
+			var index = geometry.attributes.index;
 
 			if ( index ) {
 
@@ -2535,7 +2518,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					if ( updateBuffers ) {
 
-						setupVertexAttributes( material, programAttributes, geometryAttributes, 0 );
+						setupVertexAttributes( material, program, geometry, 0 );
 						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
 					}
@@ -2560,7 +2543,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						if ( updateBuffers ) {
 
-							setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex );
+							setupVertexAttributes( material, program, geometry, startIndex );
 							_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
 						}
@@ -2583,7 +2566,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				if ( updateBuffers ) {
 
-					setupVertexAttributes( material, programAttributes, geometryAttributes, 0 );
+					setupVertexAttributes( material, program, geometry, 0 );
 
 				}
 
@@ -2605,11 +2588,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			if ( updateBuffers ) {
 
-				setupVertexAttributes( material, programAttributes, geometryAttributes, 0 );
+				setupVertexAttributes( material, program, geometry, 0 );
 
 			}
 
-			var position = geometryAttributes[ 'position' ];
+			var position = geometry.attributes.position;
 
 			// render particles
 
@@ -2624,7 +2607,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			setLineWidth( material.linewidth );
 
-			var index = geometryAttributes[ 'index' ];
+			var index = geometry.attributes.index;
 
 			if ( index ) {
 
@@ -2650,7 +2633,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 					if ( updateBuffers ) {
 
-						setupVertexAttributes( material, programAttributes, geometryAttributes, 0 );
+						setupVertexAttributes( material, program, geometry, 0 );
 						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
 					}
@@ -2674,7 +2657,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 						if ( updateBuffers ) {
 
-							setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex );
+							setupVertexAttributes( material, program, geometry, startIndex );
 							_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
 						}
@@ -2696,11 +2679,11 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 				if ( updateBuffers ) {
 
-					setupVertexAttributes( material, programAttributes, geometryAttributes, 0 );
+					setupVertexAttributes( material, program, geometry, 0 );
 
 				}
 
-				var position = geometryAttributes[ 'position' ];
+				var position = geometry.attributes.position;
 
 				_gl.drawArrays( mode, 0, position.array.length / 3 );
 
@@ -2796,7 +2779,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 					enableAttribute( attributes.color );
 					_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
 
-				} else if ( material.defaultAttributeValues ) {
+				} else if ( material.defaultAttributeValues !== undefined ) {
 
 
 					_gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color );
@@ -2835,7 +2818,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 					enableAttribute( attributes.uv );
 					_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
 
-				} else if ( material.defaultAttributeValues ) {
+				} else if ( material.defaultAttributeValues !== undefined ) {
 
 
 					_gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv );
@@ -2852,7 +2835,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 					enableAttribute( attributes.uv2 );
 					_gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 );
 
-				} else if ( material.defaultAttributeValues ) {
+				} else if ( material.defaultAttributeValues !== undefined ) {
 
 
 					_gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 );
@@ -3870,11 +3853,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	function areCustomAttributesDirty( material ) {
 
-		var keys = Object.keys( material.attributes );
-
-		for ( var i = 0; i < keys.length; i ++ ) {
-
-			var name = keys[ i ];
+		for ( var name in material.attributes ) {
 
 			if ( material.attributes[ name ].needsUpdate ) return true;
 
@@ -3886,11 +3865,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 	function clearCustomAttributes( material ) {
 
-		var keys = Object.keys( material.attributes );
-
-		for ( var i = 0; i < keys.length; i ++ ) {
-
-			var name = keys[ i ];
+		for ( var name in material.attributes ) {
 
 			material.attributes[ name ].needsUpdate = false;
 
@@ -4072,31 +4047,21 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		var keys;
-
 		if ( material.defines !== undefined ) {
 
-			keys = Object.keys( material.defines );
+			for ( var name in material.defines ) {
 
-			for ( var i = 0; i < keys.length; i ++ ) {
-
-				var d = keys[ i ];
-
-				chunks.push( d );
-				chunks.push( material.defines[ d ] );
+				chunks.push( name );
+				chunks.push( material.defines[ name ] );
 
 			}
 
 		}
 
-		keys = Object.keys( parameters );
-
-		for ( var i = 0; i < keys.length; i ++ ) {
-
-			var p = keys[ i ];
+		for ( var name in parameters ) {
 
-			chunks.push( p );
-			chunks.push( parameters[ p ] );
+			chunks.push( name );
+			chunks.push( parameters[ name ] );
 
 		}
 
@@ -4132,7 +4097,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		material.program = program;
 
-		var attributes = material.program.attributes;
+		var attributes = program.attributes;
 
 		if ( material.morphTargets ) {
 
@@ -4140,7 +4105,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			var id, base = 'morphTarget';
 
-			for ( i = 0; i < _this.maxMorphTargets; i ++ ) {
+			for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {
 
 				id = base + i;
 
@@ -4176,11 +4141,8 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		material.uniformsList = [];
 
-		keys = Object.keys( material.__webglShader.uniforms );
-
-		for ( var i = 0; i < keys.length; i ++ ) {
+		for ( var u in material.__webglShader.uniforms ) {
 
-			var u = keys[ i ];
 			var location = material.program.uniforms[ u ];
 
 			if ( location ) {

+ 1 - 0
src/renderers/webgl/WebGLProgram.js

@@ -361,6 +361,7 @@ THREE.WebGLProgram = ( function () {
 		}
 
 		this.attributes = cacheAttributeLocations( _gl, program, identifiers );
+		this.attributesKeys = Object.keys( this.attributes );
 
 		//