Browse Source

Added LensFlares. Note that a new blending mode has been added. Added an (quite ugly example). NOTE: this doesn't seem to work on Chrome+Mac+NVIDIA unless you turn off antialias (which I've done in the example).

Mikael Emtinger 14 years ago
parent
commit
ac6f422a6f

File diff suppressed because it is too large
+ 255 - 239
build/Three.js


BIN
examples/textures/lensflare0.png


BIN
examples/textures/lensflare1.png


+ 0 - 1
examples/webgl_stencil.html

@@ -150,7 +150,6 @@
 				lightCube.position.copy( light.position );
 				lightCube.position.multiplyScalar( 200 );
 
-
 				t += 0.02;
 
 				camera.position.x += ( mouseX - camera.position.x ) * .05;

+ 186 - 0
examples/webgl_stencilLensFlare.html

@@ -0,0 +1,186 @@
+<!DOCTYPE HTML>
+<html lang="en">
+	<head>
+		<title>three.js - webgl</title>
+		<meta charset="utf-8">
+		<style type="text/css">
+			body {
+				background:#fff;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
+			}
+		</style>
+	</head>
+
+	<body>
+		<script type="text/javascript" src="../build/Three.js"></script>
+		<script type="text/javascript" src="js/Stats.js"></script>
+
+		<script type="text/javascript">
+			var statsEnabled = true;
+
+			var container, stats;
+
+			var camera, scene, renderer;
+
+			var mesh, boxMesh, light, lightCube, light2, lightCube2, zmesh, lightMesh, geometry;
+
+			var mouseX = 0, mouseY = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+			document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+
+			init();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.Camera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.z = 250;
+
+				scene = new THREE.Scene();
+	
+
+				// world
+
+				var cube = new Cube( 300, 300, 10 );
+				var material0 = new THREE.MeshPhongMaterial( { color:0xff00ff } );
+				var material1 = new THREE.MeshLambertMaterial( { color:0x00ff00 } );
+				var material2 = new THREE.MeshLambertMaterial( { color:0x0000ff } );
+
+				var mesh1 = new THREE.Mesh( cube, material0 );
+				mesh1.position.z = -150;
+				scene.addChild( mesh1 );
+
+				var mesh2 = new THREE.Mesh( cube, material1 );
+				mesh2.position.x = -150;
+				mesh2.rotation.y = 90 * Math.PI / 180;
+				scene.addChild( mesh2 );
+
+				var mesh3 = new THREE.Mesh( cube, material2 );
+				mesh3.position.y = -150;
+				mesh3.rotation.x = 90 * Math.PI / 180;
+				scene.addChild( mesh3 );
+
+				new THREE.ShadowVolume( mesh1 )
+				new THREE.ShadowVolume( mesh2 )
+				new THREE.ShadowVolume( mesh3 )
+	
+	
+				// moving objects
+
+				var cube = new Cube( 40, 40, 40 );
+				var torus = new Torus( 40, 10 );
+				var sphere = new Sphere( 40 );
+				var cylinder = new Cylinder( 10, 10, 20, 40, 0, 0 );
+				mesh = new THREE.Mesh( torus, material1 );
+				scene.addChild( mesh );
+
+				boxMesh = new THREE.Mesh( cube, material2 );
+				scene.addChild( boxMesh );
+				
+				new THREE.ShadowVolume( mesh );
+				new THREE.ShadowVolume( boxMesh );
+
+
+				// lights
+	
+				light = new THREE.PointLight( 0xffffff );
+				scene.addChild( light );
+
+
+				light = new THREE.DirectionalLight( 0xffffff );
+				light.position.set( 0, 1, 0 );
+				scene.addChild( light );
+
+
+				var cube = new Sphere( 2 );
+				lightCube = new THREE.Mesh( cube, material2 );
+				lightCube.visible = false;
+				scene.addChild( lightCube );
+
+				var lensFlare = new THREE.LensFlare( ImageUtils.loadTexture( "textures/lensflare0.png" ), 128, 0.0, THREE.AdditiveAlphaBlending );
+				lensFlare.add( ImageUtils.loadTexture( "textures/lensflare1.png" ), 256, 0.33, THREE.AdditiveAlphaBlending );
+				lensFlare.add( lensFlare.lensFlares[ 1 ].texture, 300, 0.66, THREE.AdditiveAlphaBlending );
+				lensFlare.add( lensFlare.lensFlares[ 1 ].texture, 400, 1.0, THREE.AdditiveAlphaBlending );
+				
+				lightCube.addChild( lensFlare );
+
+
+				// renderer
+
+				renderer = new THREE.WebGLRenderer( { antialias: false } );
+				renderer.setClearColorHex( 0x222222, 1 );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				if ( statsEnabled ) {
+
+					stats = new Stats();
+					stats.domElement.style.position = 'absolute';
+					stats.domElement.style.top = '0px';
+					stats.domElement.style.zIndex = 100;
+					container.appendChild( stats.domElement );
+
+				}
+
+				setInterval( loop, 1000 / 60 );
+			}
+
+
+			function onDocumentMouseMove(event) {
+
+				mouseX = ( event.clientX - windowHalfX );
+				mouseY = ( event.clientY - windowHalfY );
+
+			}
+
+			var t = 0;
+
+			function loop() {
+
+				mesh.position.x = Math.sin( t ) * 100;
+				mesh.position.y = Math.cos( t ) * 100;
+
+				mesh.rotation.x += 0.5 * Math.PI / 180;
+				mesh.rotation.y += 1.0 * Math.PI / 180;
+				mesh.rotation.z += 1.5 * Math.PI / 180;
+
+				boxMesh.position.z = Math.sin( t ) * 100;
+				boxMesh.rotation.x = Math.sin( t ) * 180 * Math.PI / 180;
+
+				light.position.x = Math.sin( t );
+				light.position.y = 0.5;
+				light.position.normalize();
+
+				lightCube.position.copy( light.position );
+				lightCube.position.multiplyScalar( 200 );
+
+
+				t += 0.015;
+
+				camera.position.x += ( mouseX - camera.position.x ) * .05;
+				camera.position.y += ( - mouseY - camera.position.y ) * .05;
+
+				renderer.render( scene, camera );
+
+				if ( statsEnabled ) stats.update();
+
+			}
+
+			function log( text ) {
+
+				var e = document.getElementById("log");
+				e.innerHTML = text + "<br/>" + e.innerHTML;
+
+			}
+		</script>
+
+	</body>
+</html>

+ 44 - 6
src/lights/LensFlare.js

@@ -2,7 +2,7 @@
  * @author Mikael Emtinger
  */
  
-THREE.LensFlare = function ( texture, size ) {
+THREE.LensFlare = function ( texture, size, distance, blending ) {
 
 	THREE.Object3D.call( this );
 
@@ -10,7 +10,11 @@ THREE.LensFlare = function ( texture, size ) {
 	this.lensFlares = [];
 	this.customUpdateCallback = undefined;
 
-	this.add( texture, size, 0 );
+	if( texture !== undefined ) {
+		
+		this.add( texture, size, distance, blending );
+		
+	}
 };
 
 THREE.LensFlare.prototype = new THREE.Object3D();
@@ -22,11 +26,23 @@ THREE.LensFlare.prototype.supr = THREE.Object3D.prototype;
  * Add: adds another flare 
  */
 
-THREE.LensFlare.prototype.add = function( texture, size, distance ) {
+THREE.LensFlare.prototype.add = function( texture, size, distance, blending ) {
+	
+	if( size === undefined ) size = -1;
+	if( distance === undefined ) distance = 0;
+	if( blending === undefined ) blending = THREE.BillboardBlending;
 	
 	distance = Math.min( distance, Math.max( 0, distance ));
 
-	this.lensFlares.push( { texture: texture, size: size, distance: distance, position: new THREE.Vector3() } );
+	this.lensFlares.push( { texture: texture, 			// THREE.Texture
+		                    size: size, 				// size in pixels (-1 = use texture.width)
+		                    distance: distance, 		// distance (0-1) from light source (0=at light source)
+		                    x: 0, y: 0, z: 0,			// screen position (-1 => 1) z = 0 is ontop z = 1 is back 
+		                    scale: 1, 					// scale
+		                    rotation: 1, 				// rotation
+		                    opacity: 1,					// opacity
+		                    blending: blending } );		// blending
+
 }
 
 
@@ -35,9 +51,31 @@ THREE.LensFlare.prototype.add = function( texture, size, distance ) {
  * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
  */
 
-THREE.LensFlare.updateLensFlares = function() {
+THREE.LensFlare.prototype.updateLensFlares = function( visibility ) {
+	
+	var f, fl = this.lensFlares.length;
+	var flare;
+	var vecX = -this.positionScreen.x * 2;
+	var vecY = -this.positionScreen.y * 2; 
 	
-	// todo: update lens halo positions here
+	
+	for( f = 0; f < fl; f++ ) {
+		
+		flare = this.lensFlares[ f ];
+		
+		flare.x = this.positionScreen.x + vecX * flare.distance;
+		flare.y = this.positionScreen.y + vecY * flare.distance;
+
+		flare.wantedScale = ( visibility * 0.2 + 0.8 );
+		flare.wantedRotation = flare.x * Math.PI * 0.25;
+		flare.wantedOpacity = visibility;
+		
+		flare.scale += ( flare.wantedScale - flare.scale ) * 0.25;
+		flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
+		flare.opacity += ( flare.wantedOpacity - flare.opacity ) * 0.5;
+
+	}
+
 }
 
 

+ 1 - 0
src/materials/Material.js

@@ -15,5 +15,6 @@ THREE.AdditiveBlending = 1;
 THREE.SubtractiveBlending = 2;
 THREE.BillboardBlending = 3;
 THREE.ReverseSubtractiveBlending = 4;
+THREE.AdditiveAlphaBlending = 5;
 
 THREE.MaterialCounter = { value: 0 };

+ 239 - 122
src/renderers/WebGLRenderer.js

@@ -160,6 +160,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 	_lensFlare.vertexBuffer  = _gl.createBuffer();
 	_lensFlare.elementBuffer = _gl.createBuffer();
 	_lensFlare.tempTexture   = _gl.createTexture();
+	_lensFlare.readBackPixels = new Uint8Array( 16*16*4 );
 	
 	_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
 	_gl.bufferData( _gl.ARRAY_BUFFER,  _lensFlare.vertices, _gl.STATIC_DRAW );
@@ -193,119 +194,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 	_lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" );
 	_lensFlare.uniforms.renderPink     = _gl.getUniformLocation( _lensFlare.program, "renderPink" );
 
-	/*
-	 * Render lens flares
-	 * Method: renders 9 0xff00ff-colored points scattered over the light source area, 
-	 *         reads these back and calculates occlusion.  
-	 *         Then LensFlare.updateLensFlares() is called to re-position and 
-	 *         update transparency of flares. Then they are rendered.
-	 * 
-	 */
-
-	function renderLensFlares( scene, camera, renderTarget ) {
-		
-		var object, geometryGroup, material;
-		var o, ol = scene.__webglLensFlares.length;
-		var tempPosition = new THREE.Vector3();
-		var invAspect = _viewportHeight / _viewportWidth;
-		var halfViewportWidth = _viewportWidth * 0.5;
-		var halfViewportHeight = _viewportHeight * 0.5;
-		var size = 16 / _viewportHeight;
-		var restoreScale = [ size * invAspect, size ];
-		var screenPosition = [ 1, 1, 0 ];
-		var screenPositionPixels = [ 1, 1 ];
-
-		// set lensflare program
-
-		if( _oldProgram !== _lensFlare.program ) {
-			
-			_gl.useProgram( _lensFlare.program );
-			_oldProgram = _lensFlare.program;
-		}
-
-
-		// loop through all lens flares to update their occlusion and positions
-		// setup gl and common used attribs/unforms
-
-		_gl.uniform1i( _lensFlare.uniforms.map, 0 );
-		_gl.activeTexture( _gl.TEXTURE0 );
-		
-		_gl.uniform1f( _lensFlare.uniforms.opacity, 1 );
-		_gl.uniform1f( _lensFlare.uniforms.rotation, 0 );
-		_gl.uniform2fv( _lensFlare.uniforms.scale, restoreScale );
-
-		_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
-		_gl.vertexAttribPointer( _lensFlare.attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 );
-		_gl.vertexAttribPointer( _lensFlare.attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
-
-		_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
-
-		_gl.cullFace( _gl.BACK );
-		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
-
-
-		for( o = 0; o < ol; o++ ) {
-			
-			// calc object screen position
-			
-			object = scene.__webglLensFlares[ o ].object;
-			
-			tempPosition.set( object.matrixWorld.n14, object.matrixWorld.n24, object.matrixWorld.n34 );
-			
-			camera.matrixWorldInverse.multiplyVector3( tempPosition );
-			camera.projectionMatrix.multiplyVector3( tempPosition );
-			
-			
-			// setup arrays for gl programs
-			
-			screenPosition[ 0 ] = tempPosition.x;
-			screenPosition[ 1 ] = tempPosition.y;
-			screenPosition[ 2 ] = tempPosition.z;
-			
-			screenPositionPixels[ 0 ] = screenPosition[ 0 ] * halfViewportWidth + halfViewportWidth;
-			screenPositionPixels[ 1 ] = screenPosition[ 1 ] * halfViewportHeight + halfViewportHeight;
-
-			// todo: viewport culling
-			// save current RGB to temp texture
-			
-			_gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 );
-
-	
-			// render pink quad
-
-			_gl.uniform3fv( _lensFlare.uniforms.screenPosition, screenPosition );
-			_gl.uniform1i( _lensFlare.uniforms.renderPink, 1 );
-
-			_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
-
-
-			// restore graphics
-		
-			screenPosition[ 2 ] = 0;
-		
-			_gl.uniform1i( _lensFlare.uniforms.renderPink, 0 );
-			_gl.uniform3fv( _lensFlare.uniforms.screenPosition, screenPosition );
-
-			_gl.disable( _gl.DEPTH_TEST );
-			_gl.depthMask( false );
-
-			_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
-
-
-			
-			
-			// copy existing -> temp texture
-			// render pink quad
-			// read back pixles
-			// copy temp -> back
-			// call flare update
-			// render flares			
-			
-			
-
-		}
-		
-	}
 
 
 
@@ -2712,15 +2600,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 		}
 
-		// render flares
-		
-		if( scene.__webglLensFlares.length ) {
-			
-			renderLensFlares( scene, camera, renderTarget );
-		
-		}
-
-
 		// render stencil shadows
 
 		if( stencil && scene.__webglShadowVolumes.length && scene.lights.length ) {
@@ -2729,6 +2608,14 @@ THREE.WebGLRenderer = function ( parameters ) {
 		
 		}
 
+		// render lens flares
+		
+		if( scene.__webglLensFlares.length ) {
+			
+			renderLensFlares( scene, camera );
+		
+		}
+
 
 		// Generate mipmap if we're using any kind of mipmap filtering
 
@@ -2882,6 +2769,229 @@ THREE.WebGLRenderer = function ( parameters ) {
 	    _gl.depthMask( true );
 	}
 
+	/*
+	 * Render lens flares
+	 * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, 
+	 *         reads these back and calculates occlusion.  
+	 *         Then LensFlare.updateLensFlares() is called to re-position and 
+	 *         update transparency of flares. Then they are rendered.
+	 * 
+	 */
+
+	function renderLensFlares( scene, camera ) {
+		
+		var object, objectZ, geometryGroup, material;
+		var o, ol = scene.__webglLensFlares.length;
+		var f, fl, flare;
+		var tempPosition = new THREE.Vector3();
+		var invAspect = _viewportHeight / _viewportWidth;
+		var halfViewportWidth = _viewportWidth * 0.5;
+		var halfViewportHeight = _viewportHeight * 0.5;
+		var size = 16 / _viewportHeight;
+		var scale = [ size * invAspect, size ];
+		var screenPosition = [ 1, 1, 0 ];
+		var screenPositionPixels = [ 1, 1 ];
+		var sampleX, sampleY, readBackPixels = _lensFlare.readBackPixels;
+		var sampleMidX = 7 * 4;
+		var sampleMidY = 7 * 16 * 4;
+		var sampleIndex, visibility;
+		var uniforms = _lensFlare.uniforms;
+		var attributes = _lensFlare.attributes;
+
+
+		// set lensflare program and reset blending
+
+		_gl.useProgram( _lensFlare.program );
+		_oldProgram = _lensFlare.program;
+		_oldBlending = "";
+
+
+		// loop through all lens flares to update their occlusion and positions
+		// setup gl and common used attribs/unforms
+
+		_gl.uniform1i( uniforms.map, 0 );
+		_gl.activeTexture( _gl.TEXTURE0 );
+		
+		_gl.uniform1f( uniforms.opacity, 1 );
+		_gl.uniform1f( uniforms.rotation, 0 );
+		_gl.uniform2fv( uniforms.scale, scale );
+
+		_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
+		_gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 );
+		_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
+
+		_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
+
+		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
+
+		_gl.disable( _gl.CULL_FACE );
+		_gl.depthMask( false );
+
+
+		for( o = 0; o < ol; o++ ) {
+			
+			// calc object screen position
+			
+			object = scene.__webglLensFlares[ o ].object;
+			
+			tempPosition.set( object.matrixWorld.n14, object.matrixWorld.n24, object.matrixWorld.n34 );
+			
+			camera.matrixWorldInverse.multiplyVector3( tempPosition );
+			objectZ = tempPosition.z;
+			camera.projectionMatrix.multiplyVector3( tempPosition );
+			
+			
+			// setup arrays for gl programs
+			
+			screenPosition[ 0 ] = tempPosition.x;
+			screenPosition[ 1 ] = tempPosition.y;
+			screenPosition[ 2 ] = tempPosition.z;
+			
+			screenPositionPixels[ 0 ] = screenPosition[ 0 ] * halfViewportWidth + halfViewportWidth;
+			screenPositionPixels[ 1 ] = screenPosition[ 1 ] * halfViewportHeight + halfViewportHeight;
+	
+
+			// save current RGB to temp texture
+			
+			_gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 );
+
+	
+			// render pink quad
+
+			_gl.uniform3fv( uniforms.screenPosition, screenPosition );
+			_gl.uniform1i( uniforms.renderPink, 1 );
+
+			_gl.enable( _gl.DEPTH_TEST );
+
+			_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+
+
+			// read back
+
+			try {
+				
+				_gl.readPixels( screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16, _gl.RGBA, _gl.UNSIGNED_BYTE, _lensFlare.readBackPixels );
+				
+			}
+			catch( error ) {
+				
+				console.log( "WebGLRenderer.renderLensFlare: readPixels failed!" );
+			}
+
+			if( _gl.getError()) {
+				
+				console.log( "WebGLRenderer.renderLensFlare: readPixels failed!" );
+			}
+
+
+			// sample read back pixels
+
+			sampleDistance = parseInt( 6 * ( 1 - Math.max( 0, Math.min( -objectZ, camera.far )) / camera.far ), 10 ) + 1;
+			sampleX = sampleDistance * 4;
+			sampleY = sampleDistance * 4 * 16;
+
+			visibility = 0;
+
+			sampleIndex = ( sampleMidX - sampleX ) + ( sampleMidY - sampleY );	// upper left
+			if( _lensFlare.readBackPixels[ sampleIndex + 0 ] === 255 && 
+				_lensFlare.readBackPixels[ sampleIndex + 1 ] === 0 &&
+				_lensFlare.readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;			
+
+			sampleIndex = ( sampleMidX + sampleX ) + ( sampleMidY - sampleY );	// upper right
+			if( readBackPixels[ sampleIndex + 0 ] === 255 && 
+				readBackPixels[ sampleIndex + 1 ] === 0 &&
+				readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;			
+
+			sampleIndex = ( sampleMidX + sampleX ) + ( sampleMidY + sampleY );	// lower right
+			if( readBackPixels[ sampleIndex + 0 ] === 255 && 
+				readBackPixels[ sampleIndex + 1 ] === 0 &&
+				readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;			
+
+			sampleIndex = ( sampleMidX - sampleX ) + ( sampleMidY + sampleY );	// lower left
+			if( readBackPixels[ sampleIndex + 0 ] === 255 && 
+				readBackPixels[ sampleIndex + 1 ] === 0 &&
+				readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;			
+
+			sampleIndex = sampleMidX + sampleMidY;								// center
+			if( readBackPixels[ sampleIndex + 0 ] === 255 && 
+				readBackPixels[ sampleIndex + 1 ] === 0 &&
+				readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;			
+
+
+			object.positionScreen.x = screenPosition[ 0 ];
+			object.positionScreen.y = screenPosition[ 1 ];
+			object.positionScreen.z = screenPosition[ 2 ];
+
+			if( object.customUpdateCallback ) {
+				
+				object.customUpdateCallback( visibility, object );
+				
+			} else {
+				
+				object.updateLensFlares( visibility );
+				
+			}
+
+
+			// restore graphics
+		
+			_gl.uniform1i( uniforms.renderPink, 0 );
+			_gl.disable( _gl.DEPTH_TEST );
+			_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+		}
+		
+		
+		// loop through all lens flares and draw their flares
+		// setup gl
+		
+		_gl.enable( _gl.BLEND );
+		
+		for( o = 0; o < ol; o++ ) {
+		
+			object = scene.__webglLensFlares[ o ].object;
+
+			for( f = 0, fl = object.lensFlares.length; f < fl; f++ ) {
+				
+				flare = object.lensFlares[ f ];
+				
+				if( flare.opacity > 0.001 && flare.scale > 0.001 ) {
+
+					screenPosition[ 0 ] = flare.x;
+					screenPosition[ 1 ] = flare.y;
+					screenPosition[ 2 ] = flare.z;
+	
+					size = flare.size * flare.scale / _viewportHeight;
+					scale[ 0 ] = size * invAspect;
+					scale[ 1 ] = size;
+						
+	
+					_gl.uniform3fv( uniforms.screenPosition, screenPosition );
+					_gl.uniform1f( uniforms.rotation, flare.rotation );
+					_gl.uniform2fv( uniforms.scale, scale );
+					_gl.uniform1f( uniforms.opacity, flare.opacity );
+	
+					setBlending( flare.blending );
+					setTexture( flare.texture, 0 );
+	
+					// todo: only draw if loaded
+			
+					_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+				}
+				
+			}
+			
+		}
+
+
+		// restore gl
+	
+		_gl.enable( _gl.CULL_FACE );
+		_gl.enable( _gl.DEPTH_TEST );
+		_gl.depthMask( true );
+		_gl.disable( _gl.BLEND );
+	}
+
+
 
 	function setupMatrices ( object, camera ) {
 
@@ -3519,6 +3629,13 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 			switch ( blending ) {
 
+				case THREE.AdditiveAlphaBlending:
+				
+					_gl.blendEquation( _gl.FUNC_ADD );
+					_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE );
+				
+					break;
+
 				case THREE.AdditiveBlending:
 
 					_gl.blendEquation( _gl.FUNC_ADD );

+ 5 - 1
src/renderers/WebGLShaders.js

@@ -586,6 +586,8 @@ THREE.ShaderLib = {
 			"#endif",		
 
 			"uniform	sampler2D	map;",
+			"uniform	float		opacity;",
+			
 			"uniform    int         renderPink;",
 			"varying	vec2		vUV;",
 	
@@ -594,7 +596,9 @@ THREE.ShaderLib = {
 				"if( renderPink == 1 ) {",
 					"gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );",
 				"} else {",
-					"gl_FragColor = texture2D( map, vUV );",
+					"vec4 color = texture2D( map, vUV );",
+					"color.a *= opacity;",
+					"gl_FragColor = color;",
 				"}",
 			"}"
 		].join( "\n" )

+ 1 - 0
utils/build.py

@@ -36,6 +36,7 @@ COMMON_FILES = [
 'lights/AmbientLight.js',
 'lights/DirectionalLight.js',
 'lights/PointLight.js',
+'lights/LensFlare.js',
 'materials/Material.js',
 'materials/Mappings.js',
 'materials/LineBasicMaterial.js',

Some files were not shown because too many files changed in this diff