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 năm trước cách đây
mục cha
commit
ac6f422a6f

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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',

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác