2
0
arose 9 жил өмнө
parent
commit
b40d51cbef

+ 1 - 0
examples/index.html

@@ -235,6 +235,7 @@
 				"webgl_interactive_buffergeometry",
 				"webgl_interactive_buffergeometry",
 				"webgl_interactive_cubes",
 				"webgl_interactive_cubes",
 				"webgl_interactive_cubes_gpu",
 				"webgl_interactive_cubes_gpu",
+				"webgl_interactive_instances_gpu",
 				"webgl_interactive_cubes_ortho",
 				"webgl_interactive_cubes_ortho",
 				"webgl_interactive_draggablecubes",
 				"webgl_interactive_draggablecubes",
 				"webgl_interactive_lines",
 				"webgl_interactive_lines",

+ 905 - 0
examples/webgl_interactive_instances_gpu.html

@@ -0,0 +1,905 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<title>three.js webgl - interactive instances (gpu)</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 {
+			font-family: Monospace;
+			background-color: #f0f0f0;
+			margin: 0px;
+			overflow: hidden;
+		}
+
+		.info {
+			position: absolute;
+			background-color: black;
+			opacity: 0.8;
+			color: white;
+			text-align: center;
+			top: 0px;
+			width: 100%;
+		}
+
+		.info a {
+			color: #00ffff;
+		}
+
+		#notSupported {
+			width: 50%;
+			margin: auto;
+			border: 2px red solid;
+			margin-top: 20px;
+			padding: 10px;
+		}
+	</style>
+</head>
+<body>
+
+	<div class="info">
+
+		<a href="http://threejs.org" target="_blank">three.js</a> webgl - gpu picking of geometry instances using a single material
+
+		<div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
+
+		<br/><br/>
+
+		<div>
+
+			<span>number of instances </span>
+			<select id="instanceCount">
+				<option>100</option>
+				<option>500</option>
+				<option selected>1000</option>
+				<option>2000</option>
+				<option>3000</option>
+				<option>5000</option>
+				<option>10000</option>
+				<option>20000</option>
+				<option>30000</option>
+				<option>50000</option>
+				<option>100000</option>
+			</select>
+
+			&nbsp;&nbsp;&nbsp;
+
+			<span>method</span>
+			<select id="method">
+				<option>instanced</option>
+				<option selected>merged</option>
+				<option>singleMaterial</option>
+				<option>multiMaterial</option>
+			</select>
+
+			&nbsp;&nbsp;&nbsp;
+
+			<span>render continuously</span>
+			<input id="animate" type="checkbox" />
+
+		</div>
+
+	</div>
+
+	<div id="container"></div>
+
+	<script src="../build/three.min.js"></script>
+	<script src="../src/renderers/WebGLRenderer.js"></script>
+	<script src="js/controls/TrackballControls.js"></script>
+	<script src="js/libs/stats.min.js"></script>
+
+	<script id="vertMerged" type="x-shader/x-vertex">
+		#define SHADER_NAME vertMerged
+
+		precision highp float;
+
+		uniform mat4 modelViewMatrix;
+		uniform mat4 projectionMatrix;
+
+		attribute vec3 position;
+		#ifdef PICKING
+			attribute vec3 pickingColor;
+		#else
+			attribute vec3 color;
+		#endif
+
+		varying vec3 vColor;
+		varying vec3 vPosition;
+
+		void main()	{
+
+			#ifdef PICKING
+				vColor = pickingColor;
+			#else
+				vColor = color;
+			#endif
+			vPosition = ( modelViewMatrix * vec4( position, 1.0 ) ).xyz;
+
+			gl_Position = projectionMatrix * vec4( vPosition, 1.0 );
+
+		}
+
+	</script>
+
+	<script id="fragMerged" type="x-shader/x-fragment">
+		#define SHADER_NAME fragMerged
+
+		#extension GL_OES_standard_derivatives : enable
+
+		precision highp float;
+
+		varying vec3 vColor;
+		varying vec3 vPosition;
+
+		void main()	{
+
+			#ifdef PICKING
+				gl_FragColor = vec4( vColor, 1.0 );
+			#else
+				vec3 fdx = dFdx( vPosition );
+	            vec3 fdy = dFdy( vPosition );
+	            vec3 normal = normalize( cross( fdx, fdy ) );
+	            float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
+
+				gl_FragColor = vec4( diffuse * vColor, 1.0 );
+			#endif
+
+		}
+
+	</script>
+
+	<script id="vertInstanced" type="x-shader/x-vertex">
+		#define SHADER_NAME vertInstanced
+
+		precision highp float;
+
+		uniform mat4 modelViewMatrix;
+		uniform mat4 projectionMatrix;
+
+		attribute vec3 position;
+		// attribute mat4 matrix;
+		attribute vec3 mcol0;
+		attribute vec3 mcol1;
+		attribute vec3 mcol2;
+		attribute vec3 mcol3;
+		#ifdef PICKING
+			attribute vec3 pickingColor;
+		#else
+			attribute vec3 color;
+		#endif
+
+		varying vec3 vColor;
+		varying vec3 vPosition;
+
+		void main()	{
+
+			mat4 matrix = mat4(
+				vec4( mcol0, 0 ),
+				vec4( mcol1, 0 ),
+				vec4( mcol2, 0 ),
+				vec4( mcol3, 1 )
+			);
+
+			#ifdef PICKING
+				vColor = pickingColor;
+			#else
+				vColor = color;
+			#endif
+			vPosition = ( modelViewMatrix * matrix * vec4( position, 1.0 ) ).xyz;
+
+			gl_Position = projectionMatrix * vec4( vPosition, 1.0 );
+
+		}
+
+	</script>
+
+	<script id="fragInstanced" type="x-shader/x-fragment">
+		#define SHADER_NAME fragInstanced
+
+		#extension GL_OES_standard_derivatives : enable
+
+		precision highp float;
+
+		varying vec3 vColor;
+		varying vec3 vPosition;
+
+		void main()	{
+
+			#ifdef PICKING
+				gl_FragColor = vec4( vColor, 1.0 );
+			#else
+				vec3 fdx = dFdx( vPosition );
+	            vec3 fdy = dFdy( vPosition );
+	            vec3 normal = normalize( cross( fdx, fdy ) );
+	            float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
+
+				gl_FragColor = vec4( diffuse * vColor, 1.0 );
+			#endif
+
+		}
+
+	</script>
+
+	<script id="vertMaterial" type="x-shader/x-vertex">
+		#define SHADER_NAME vertMaterial
+
+		precision highp float;
+
+		uniform mat4 modelViewMatrix;
+		uniform mat4 projectionMatrix;
+
+		attribute vec3 position;
+
+		varying vec3 vPosition;
+
+		void main()	{
+
+			vPosition = ( modelViewMatrix * vec4( position, 1.0 ) ).xyz;
+
+			gl_Position = projectionMatrix * vec4( vPosition, 1.0 );
+
+		}
+
+	</script>
+
+	<script id="fragMaterial" type="x-shader/x-fragment">
+		#define SHADER_NAME fragMaterial
+
+		#extension GL_OES_standard_derivatives : enable
+
+		precision highp float;
+
+		uniform vec3 color;
+
+		varying vec3 vPosition;
+
+		void main()	{
+
+			#ifdef PICKING
+				gl_FragColor = vec4( color, 1.0 );
+			#else
+				vec3 fdx = dFdx( vPosition );
+	            vec3 fdy = dFdy( vPosition );
+	            vec3 normal = normalize( cross( fdx, fdy ) );
+	            float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
+
+				gl_FragColor = vec4( diffuse * color, 1.0 );
+			#endif
+
+		}
+
+	</script>
+
+	<script>
+
+		var container, stats;
+		var camera, controls, scene, renderer;
+		var pickingData, pickingTexture, pickingScene;
+		var highlightBox;
+		var materialList = [];
+		var geometryList = [];
+		var geometrySize;
+		var mouse = new THREE.Vector2();
+		var scale = 1.03;
+
+		var loader = new THREE.JSONLoader();
+
+		//create buffer for reading a single pixel
+		var pixelBuffer = new Uint8Array( 4 );
+
+		// gui
+		var instanceCount, method, doAnimate;
+
+		//
+
+		gui();
+		init();
+		initMesh();
+		if( doAnimate ) animate();
+
+		//
+
+		function gui(){
+
+			var instanceCountElm = document.getElementById( 'instanceCount' );
+
+			instanceCount = parseInt( instanceCountElm.value );
+
+			instanceCountElm.addEventListener( "change", function(){
+
+				instanceCount = parseInt( instanceCountElm.value );
+				initMesh();
+
+			} );
+
+			//
+
+			var methodElm = document.getElementById( 'method' );
+
+			method = methodElm.value;
+
+			methodElm.addEventListener( "change", function(){
+
+				method = methodElm.value;
+				initMesh();
+
+			} );
+
+			//
+
+			var animateElm = document.getElementById( 'animate' );
+
+			doAnimate = animateElm.checked;
+
+			animateElm.addEventListener( "click", function(){
+
+				doAnimate = animateElm.checked;
+				animate();
+
+			} );
+
+		}
+
+		function clean(){
+
+			THREE.Cache.clear();
+
+			materialList.forEach( function( m ){
+				m.dispose();
+			} );
+
+			geometryList.forEach( function( g ){
+				g.dispose();
+			} );
+
+			scene = new THREE.Scene();
+			scene.add( camera );
+			scene.add( highlightBox );
+
+			pickingScene = new THREE.Scene();
+			pickingData = {};
+			materialList = [];
+			geometryList = [];
+
+		}
+
+		var randomizeMatrix = function(){
+
+			var position = new THREE.Vector3();
+			var rotation = new THREE.Euler();
+			var quaternion = new THREE.Quaternion();
+			var scale = new THREE.Vector3();
+
+			return function( matrix ){
+
+				position.x = Math.random() * 40 - 20;
+				position.y = Math.random() * 40 - 20;
+				position.z = Math.random() * 40 - 20;
+
+				rotation.x = Math.random() * 2 * Math.PI;
+				rotation.y = Math.random() * 2 * Math.PI;
+				rotation.z = Math.random() * 2 * Math.PI;
+
+				quaternion.setFromEuler( rotation, false );
+
+				scale.x = scale.y = scale.z = Math.random() * 1;
+
+				matrix.compose( position, quaternion, scale );
+
+			};
+
+		}();
+
+		function initMesh(){
+
+			clean();
+
+			// make instances
+			loader.load( 'obj/Suzanne.js', function ( geo ) {
+
+				geo.computeBoundingBox();
+				geometrySize = geo.boundingBox.size();
+				geometryList.push( geo );
+
+				console.log( "method:", method );
+				console.log( "instanceCount:", instanceCount );
+
+				console.time( "init mesh" );
+
+				switch( method ){
+
+					case "merged":
+						makeMerged( geo );
+						break;
+
+					case "instanced":
+						makeInstanced( geo );
+						break;
+
+					case "singleMaterial":
+						makeSingleMaterial( geo );
+						break;
+
+					case "multiMaterial":
+						makeMultiMaterial( geo );
+						break;
+
+				}
+
+				render();
+
+				console.timeEnd( "init mesh", method );
+
+				console.log( "material count:", materialList.length );
+				console.log( "geometry count:", geometryList.length );
+				console.log( renderer.info.memory )
+				console.log( renderer.info.render )
+
+			} );
+
+		}
+
+		function makeMultiMaterial( geo ){
+
+			// material
+
+			var vert = document.getElementById( 'vertMaterial' ).textContent;
+			var frag = document.getElementById( 'fragMaterial' ).textContent;
+
+			var material = new THREE.RawShaderMaterial( {
+				vertexShader: vert,
+				fragmentShader: frag,
+				uniforms: {
+					color: {
+						type: "c",
+						value: new THREE.Color(),
+					}
+				}
+			} );
+
+			var pickingMaterial = new THREE.RawShaderMaterial( {
+				vertexShader: "#define PICKING\n" + vert,
+				fragmentShader: "#define PICKING\n" + frag,
+				uniforms: {
+					color: {
+						type: "c",
+						value: new THREE.Color(),
+					}
+				}
+			} );
+
+			// geometry / mesh
+
+			var matrix = new THREE.Matrix4();
+
+			for ( var i = 0; i < instanceCount; i ++ ) {
+
+				var object = new THREE.Mesh( geo, material );
+				randomizeMatrix( matrix );
+				object.applyMatrix( matrix );
+				var pickingObject = object.clone();
+
+				object.material = material.clone();
+				object.material.uniforms.color.value.setHex( Math.random() * 0xffffff );
+				materialList.push( object.material );
+
+				pickingObject.material = pickingMaterial.clone();
+				pickingObject.material.uniforms.color.value.setHex( i + 1 );
+				materialList.push( pickingObject.material );
+
+				pickingData[ i + 1 ] = object;
+
+				scene.add( object );
+				pickingScene.add( pickingObject );
+
+			}
+
+			material.dispose();
+			pickingMaterial.dispose();
+
+		}
+
+		function makeSingleMaterial( geo ){
+
+			// material
+
+			var vert = document.getElementById( 'vertMaterial' ).textContent;
+			var frag = document.getElementById( 'fragMaterial' ).textContent;
+
+			var material = new THREE.RawShaderMaterial( {
+				vertexShader: vert,
+				fragmentShader: frag,
+				uniforms: {
+					color: {
+						type: "c",
+						value: new THREE.Color(),
+						updateFunction: function( uniform, camera, object ){
+							uniform.value.setHex( object.userData.color );
+						}
+					}
+				}
+			} );
+			material.hasDynamicUniforms = true;
+			materialList.push( material );
+
+			var pickingMaterial = new THREE.RawShaderMaterial( {
+				vertexShader: "#define PICKING\n" + vert,
+				fragmentShader: "#define PICKING\n" + frag,
+				uniforms: {
+					color: {
+						type: "c",
+						value: new THREE.Color(),
+						updateFunction: function( uniform, camera, object ){
+							uniform.value.setHex( object.userData.color );
+						}
+					}
+				}
+			} );
+			pickingMaterial.hasDynamicUniforms = true;
+			materialList.push( pickingMaterial );
+
+			// geometry / mesh
+
+			var matrix = new THREE.Matrix4();
+
+			for ( var i = 0; i < instanceCount; i ++ ) {
+
+				var object = new THREE.Mesh( geo, material );
+				randomizeMatrix( matrix );
+				object.applyMatrix( matrix );
+				var pickingObject = object.clone();
+
+				object.material = material;
+				object.userData[ "color" ] = Math.random() * 0xffffff;
+
+				pickingObject.material = pickingMaterial;
+				pickingObject.userData[ "color" ] = i + 1;
+
+				pickingData[ i + 1 ] = object;
+
+				scene.add( object );
+				pickingScene.add( pickingObject );
+
+			}
+
+		}
+
+		function makeMerged( geo ){
+
+			// material
+
+			var vert = document.getElementById( 'vertMerged' ).textContent;
+			var frag = document.getElementById( 'fragMerged' ).textContent;
+
+			var material = new THREE.RawShaderMaterial( {
+				vertexShader: vert,
+				fragmentShader: frag,
+			} );
+			materialList.push( material );
+
+			var pickingMaterial = new THREE.RawShaderMaterial( {
+				vertexShader: "#define PICKING\n" + vert,
+				fragmentShader: "#define PICKING\n" + frag,
+			} );
+			materialList.push( pickingMaterial );
+
+			// geometry
+
+			var bgeo = new THREE.BufferGeometry().fromGeometry( geo );
+			geometryList.push( bgeo );
+
+			var mgeo = new THREE.BufferGeometry();
+			geometryList.push( mgeo );
+
+			var pos = bgeo.attributes.position;
+			var posLen = bgeo.attributes.position.count * 3;
+			var vertices = new THREE.BufferAttribute(
+				new Float32Array( instanceCount * posLen ), 3
+			);
+			var matrix = new THREE.Matrix4();
+			for ( var i = 0, ul = instanceCount; i < ul; i++ ) {
+				randomizeMatrix( matrix );
+				var object = new THREE.Object3D();
+				object.applyMatrix( matrix );
+				pickingData[ i + 1 ] = object;
+				vertices.set( pos.array, i * posLen );
+				matrix.applyToVector3Array( vertices.array, i * posLen, posLen )
+			}
+			mgeo.addAttribute( 'position', vertices );
+
+			var colCount = posLen / 3;
+			var colors = new THREE.BufferAttribute(
+				new Float32Array( instanceCount * colCount * 3 ), 3
+			);
+			var randCol = function(){ return Math.random(); };
+			for ( var i = 0, ul = instanceCount; i < ul; i++ ) {
+				var r = randCol(), g = randCol(), b = randCol();
+				for ( var j = i * colCount, jl = ( i + 1 ) * colCount; j < jl; j++ ){
+					colors.setXYZ( j, r, g, b );
+				}
+			}
+			mgeo.addAttribute( 'color', colors );
+
+			var col = new THREE.Color();
+			var pickingColors = new THREE.BufferAttribute(
+				new Float32Array( instanceCount * colCount * 3 ), 3
+			);
+			for ( var i = 0, ul = instanceCount; i < ul; i++ ) {
+				col.setHex( i + 1 );
+				for ( var j = i * colCount, jl = ( i + 1 ) * colCount; j < jl; j++ ){
+					pickingColors.setXYZ( j, col.r, col.g, col.b );
+				}
+			}
+			mgeo.addAttribute( 'pickingColor', pickingColors );
+
+			// mesh
+
+			var mesh = new THREE.Mesh( mgeo, material );
+			scene.add( mesh );
+
+			var pickingMesh = new THREE.Mesh( mgeo, pickingMaterial );
+			pickingScene.add( pickingMesh );
+
+		}
+
+		function makeInstanced( geo ){
+
+			// material
+
+			var vert = document.getElementById( 'vertInstanced' ).textContent;
+			var frag = document.getElementById( 'fragInstanced' ).textContent;
+
+			var material = new THREE.RawShaderMaterial( {
+				vertexShader: vert,
+				fragmentShader: frag,
+			} );
+			materialList.push( material );
+
+			var pickingMaterial = new THREE.RawShaderMaterial( {
+				vertexShader: "#define PICKING\n" + vert,
+				fragmentShader: "#define PICKING\n" + frag,
+			} );
+			materialList.push( pickingMaterial );
+
+			// geometry
+
+			var bgeo = new THREE.BufferGeometry().fromGeometry( geo );
+			geometryList.push( bgeo );
+
+			var igeo = new THREE.InstancedBufferGeometry();
+			geometryList.push( igeo );
+
+			var vertices = bgeo.attributes.position.clone();
+			igeo.addAttribute( 'position', vertices );
+
+			// var matrices = new THREE.InstancedBufferAttribute(
+			// 	new Float32Array( instanceCount * 16 ), 16, 1
+			// );
+			var mcol0 = new THREE.InstancedBufferAttribute(
+				new Float32Array( instanceCount * 3 ), 3, 1
+			);
+			var mcol1 = new THREE.InstancedBufferAttribute(
+				new Float32Array( instanceCount * 3 ), 3, 1
+			);
+			var mcol2 = new THREE.InstancedBufferAttribute(
+				new Float32Array( instanceCount * 3 ), 3, 1
+			);
+			var mcol3 = new THREE.InstancedBufferAttribute(
+				new Float32Array( instanceCount * 3 ), 3, 1
+			);
+			var matrix = new THREE.Matrix4();
+			var me = matrix.elements;
+			for ( var i = 0, ul = mcol0.count; i < ul; i++ ) {
+				randomizeMatrix( matrix );
+				var object = new THREE.Object3D();
+				object.applyMatrix( matrix );
+				pickingData[ i + 1 ] = object;
+				// matrices.set( matrix.elements, i * 16 );
+				mcol0.setXYZ( i, me[  0 ], me[  1 ], me[  2 ] );
+				mcol1.setXYZ( i, me[  4 ], me[  5 ], me[  6 ] );
+				mcol2.setXYZ( i, me[  8 ], me[  9 ], me[ 10 ] );
+				mcol3.setXYZ( i, me[ 12 ], me[ 13 ], me[ 14 ] );
+			}
+			// igeo.addAttribute( 'matrix', matrices );
+			igeo.addAttribute( 'mcol0', mcol0 );
+			igeo.addAttribute( 'mcol1', mcol1 );
+			igeo.addAttribute( 'mcol2', mcol2 );
+			igeo.addAttribute( 'mcol3', mcol3 );
+
+			var randCol = function(){ return Math.random(); };
+			var colors = new THREE.InstancedBufferAttribute(
+				new Float32Array( instanceCount * 3 ), 3, 1
+			);
+			for ( var i = 0, ul = colors.count; i < ul; i++ ) {
+				colors.setXYZ( i, randCol(), randCol(), randCol() );
+			}
+			igeo.addAttribute( 'color', colors );
+
+			var col = new THREE.Color();
+			var pickingColors = new THREE.InstancedBufferAttribute(
+				new Float32Array( instanceCount * 3 ), 3, 1
+			);
+			for ( var i = 0, ul = pickingColors.count; i < ul; i++ ) {
+				col.setHex( i + 1 );
+				pickingColors.setXYZ( i, col.r, col.g, col.b );
+			}
+			igeo.addAttribute( 'pickingColor', pickingColors );
+
+			// mesh
+
+			var mesh = new THREE.Mesh( igeo, material );
+			scene.add( mesh );
+
+			var pickingMesh = new THREE.Mesh( igeo, pickingMaterial );
+			pickingScene.add( pickingMesh );
+
+		}
+
+		function init() {
+
+			// camera
+
+			camera = new THREE.PerspectiveCamera(
+				70, window.innerWidth / window.innerHeight, 1, 100
+			);
+			camera.position.z = 40;
+
+			// picking texture
+
+			pickingTexture = new THREE.WebGLRenderTarget(
+				window.innerWidth, window.innerHeight
+			);
+			pickingTexture.generateMipmaps = false;
+			pickingTexture.minFilter = THREE.NearestFilter;
+
+			// highlight box
+
+			highlightBox = new THREE.Mesh(
+				new THREE.BoxGeometry( 1, 1, 1 ),
+				new THREE.MeshLambertMaterial( {
+					emissive: 0xffff00,
+					transparent: true,
+					opacity: 0.5,
+					side: THREE.FrontSide
+				} )
+			);
+
+			// renderer
+
+			container = document.getElementById( "container" );
+			renderer = new THREE.WebGLRenderer( {
+				antialias: true,
+				alpha: true
+			} );
+			if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === false ) {
+				document.getElementById( "notSupported" ).style.display = "";
+				return;
+			}
+			renderer.setClearColor( 0xffffff );
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			renderer.sortObjects = false;
+			container.appendChild( renderer.domElement );
+
+			if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === false ) {
+				throw 'ANGLE_instanced_arrays not supported';
+			}
+
+			// controls
+
+			controls = new THREE.TrackballControls(
+				camera, renderer.domElement
+			);
+			controls.staticMoving = true;
+
+			// stats
+
+			stats = new Stats();
+			stats.domElement.style.position = 'absolute';
+			stats.domElement.style.top = '0px';
+			container.appendChild( stats.domElement );
+
+			// listeners
+
+			renderer.domElement.addEventListener( 'mousemove', onMouseMove );
+
+			window.addEventListener( 'resize', onWindowResize, false );
+
+		}
+
+		//
+
+		function onMouseMove( e ) {
+
+			mouse.x = e.clientX;
+			mouse.y = e.clientY;
+
+			controls.update();
+			requestAnimationFrame( render );
+
+		}
+
+		function onWindowResize( event ) {
+
+			camera.aspect = window.innerWidth / window.innerHeight;
+			camera.updateProjectionMatrix();
+
+			renderer.setSize( window.innerWidth, window.innerHeight );
+
+		}
+
+		function animate() {
+
+			if( doAnimate ){
+				requestAnimationFrame( animate );
+			}
+
+			controls.update();
+			stats.update();
+
+			render();
+
+		}
+
+		function pick() {
+
+			// render the picking scene off-screen
+
+			renderer.render( pickingScene, camera, pickingTexture );
+
+			// read the pixel under the mouse from the texture
+
+			renderer.readRenderTargetPixels(
+				pickingTexture,
+				mouse.x,
+				pickingTexture.height - mouse.y,
+				1,
+				1,
+				pixelBuffer
+			);
+
+			// interpret the pixel as an ID
+
+			var id =
+				( pixelBuffer[0] << 16 ) |
+				( pixelBuffer[1] << 8 ) |
+				( pixelBuffer[2] );
+
+			var object = pickingData[ id ];
+
+			if ( object ) {
+
+				// move the highlightBox so that it surrounds the picked object
+
+				if ( object.position && object.rotation && object.scale ){
+
+					highlightBox.position.copy( object.position );
+					highlightBox.rotation.copy( object.rotation );
+
+					highlightBox.scale.copy( object.scale )
+						.multiply( geometrySize )
+						.multiplyScalar( scale );
+
+					highlightBox.visible = true;
+
+				}
+
+			} else {
+
+				highlightBox.visible = false;
+
+			}
+
+		}
+
+		function render() {
+
+			pick();
+			renderer.render( scene, camera );
+
+		}
+
+	</script>
+
+</body>
+</html>

+ 28 - 0
src/renderers/WebGLRenderer.js

@@ -1814,10 +1814,38 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 		}
 		}
 
 
+		if( material.hasDynamicUniforms === true ){
+
+			updateDynamicUniforms( materialProperties.uniformsList, camera, object );
+
+		}
+
 		return program;
 		return program;
 
 
 	}
 	}
 
 
+	function updateDynamicUniforms ( uniforms, camera, object ) {
+
+		var dynamicUniforms = [];
+
+		for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) {
+
+			var uniform = uniforms[ j ][ 0 ];
+			var updateFunction = uniform.updateFunction;
+
+			if( typeof updateFunction === "function" ){
+
+				updateFunction( uniform, camera, object );
+				dynamicUniforms.push( uniforms[ j ] );
+
+			}
+
+		}
+
+		loadUniformsGeneric( dynamicUniforms );
+
+	}
+
 	// Uniforms (refresh uniforms objects)
 	// Uniforms (refresh uniforms objects)
 
 
 	function refreshUniformsCommon ( uniforms, material ) {
 	function refreshUniformsCommon ( uniforms, material ) {