Browse Source

Merge pull request #16560 from Mugen87/dev33

JSM: Added module and TS file for WebGLDeferredRenderer.
Michael Herzog 6 years ago
parent
commit
83497492c2

+ 1 - 0
docs/manual/en/introduction/Import-via-modules.html

@@ -214,6 +214,7 @@
 						<li>SoftwareRenderer</li>
 						<li>SVGRenderer</li>
 						<li>RaytracingRenderer</li>
+						<li>WebGLDeferredRenderer</li>
 					</ul>
 				</li>
 				<li>shaders

+ 7 - 7
examples/js/renderers/WebGLDeferredRenderer.js

@@ -418,7 +418,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function createDeferredNormalDepthMaterial( originalMaterial ) {
+	function createDeferredNormalDepthMaterial() {
 
 		var shader = ( _lightPrePass ) ? THREE.ShaderDeferred[ 'normalDepthShininess' ] : THREE.ShaderDeferred[ 'normalDepth' ];
 
@@ -448,7 +448,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredNormalDepthUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredNormalDepthUniforms( renderer, scene, camera, geometry, material ) {
 
 		if ( ! _lightPrePass ) return;
 
@@ -512,7 +512,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredColorUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredColorUniforms( renderer, scene, camera, geometry, material ) {
 
 		var originalMaterial = _originalMaterialsTable[ material.uuid ];
 		var uniforms = material.uniforms;
@@ -677,7 +677,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 		var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
 		var mesh = new THREE.Mesh( geometry, material );
 
-		mesh.onBeforeRender = function ( renderer, scene, camera, geometry, material, group ) {
+		mesh.onBeforeRender = function ( renderer, scene, camera, geometry, material ) {
 
 			material.uniforms.samplerColor.value = _compColor.renderTarget2.texture;
 
@@ -840,7 +840,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredPointLightUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredPointLightUniforms( renderer, scene, camera, geometry, material ) {
 
 		var light = this;
 
@@ -886,7 +886,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredSpotLightUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredSpotLightUniforms() {
 
 		var light = this;
 
@@ -929,7 +929,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	}
 
-	function updateDeferredDirectionalLightUniforms( renderer, scene, camera, geometry, material, group ) {
+	function updateDeferredDirectionalLightUniforms() {
 
 		var light = this;
 

+ 26 - 0
examples/jsm/renderers/WebGLDeferredRenderer.d.ts

@@ -0,0 +1,26 @@
+import {
+  Camera,
+  Scene,
+  Vector2,
+  WebGLRenderer
+} from '../../../src/Three';
+
+export interface WebGLDeferredRendererParameters {
+  antialias?: boolean;
+  cacheKeepAlive?: boolean;
+  height?: Vector2;
+  renderer?: WebGLRenderer;
+  width?: Vector2;
+}
+
+export class WebGLDeferredRenderer {
+  constructor(parameters: WebGLDeferredRendererParameters);
+  domElement: HTMLElement;
+  forwardRendering: boolean;
+  renderer: WebGLRenderer;
+
+  enableLightPrePass(enabled: boolean): void;
+  render(scene: Scene, camera: Camera): void;
+  setAntialias(enabled: boolean): void;
+  setSize(width: number, height: number): void;
+}

+ 2528 - 0
examples/jsm/renderers/WebGLDeferredRenderer.js

@@ -0,0 +1,2528 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author MPanknin / http://www.redplant.de/
+ * @author takahiro / https://github.com/takahirox
+ *
+ * WebGLDeferredRenderer supports two types of Deferred Renderings.
+ * One is Classic Deferred Rendering and the other one is
+ * Light Pre-Pass (Deferred Lighting).
+ * Classic Deferred Rendering is default. You can use Light Pre-Pass
+ * by calling .enableLightPrePass( true ) method.
+ *
+ * Dependencies
+ *  - CopyShader
+ *  - RenderPass
+ *  - ShaderPass
+ *  - EffectComposer
+ *  - FXAAShader
+ *
+ * TODO
+ *  - reuse existing glsl
+ *  - shadow
+ *  - optimization
+ *  - MRT (when it's available on Three.js)
+ *  - AmbientLight
+ *  - HemisphereLight
+ *  - PointLight (distance < 0)
+ *  - morphNormals
+ *  - BumpMap
+ *  - ToneMap
+ *  - envMap
+ *  - wrapAround
+ *  - addEffect
+ */
+
+import {
+	AdditiveBlending,
+	BackSide,
+	Color,
+	DepthStencilFormat,
+	DepthTexture,
+	FloatType,
+	GreaterEqualDepth,
+	LinearFilter,
+	Matrix4,
+	Mesh,
+	NearestFilter,
+	NoBlending,
+	OrthographicCamera,
+	PlaneBufferGeometry,
+	RGBAFormat,
+	RGBFormat,
+	Scene,
+	ShaderMaterial,
+	SphereBufferGeometry,
+	Uniform,
+	UnsignedByteType,
+	UnsignedInt248Type,
+	Vector2,
+	Vector3,
+	Vector4,
+	WebGLRenderTarget,
+	WebGLRenderer
+} from "../../../build/three.module.js";
+import { EffectComposer } from "../postprocessing/EffectComposer.js";
+import { ShaderPass } from "../postprocessing/ShaderPass.js";
+import { RenderPass } from "../postprocessing/RenderPass.js";
+import { FXAAShader } from "../shaders/FXAAShader.js";
+import { CopyShader } from "../shaders/CopyShader.js";
+
+var WebGLDeferredRenderer = function ( parameters ) {
+
+	parameters = parameters || {};
+
+	// private properties
+
+	var _this = this;
+
+	var _context;
+	var _state;
+
+	var _width, _height;
+
+	// for Classic Deferred Rendering
+	var _compColor;
+	var _passColor, _passForward, _passCopy;
+
+	// for Light Pre-Pass
+	var _compReconstruction;
+	var _passReconstruction;
+
+	// for Common
+	var _compNormalDepth, _compLight, _compFinal;
+	var _passNormalDepth, _passLight, _passLightFullscreen, _passFinal, _passFXAA;
+
+	var _depthTexture;
+
+	var _currentCamera;
+
+	var _lightScene, _lightFullscreenScene;
+
+	var _antialias = false;
+	var _hasTransparentObject = false;
+	var _lightPrePass = false;
+	var _cacheKeepAlive = false;
+
+	var _tmpMaterial = new ShaderMaterial( { visible: false } );
+	var _tmpVector3 = new Vector3();
+
+	// scene/material/light cache for deferred rendering.
+	// save them at the creation and release
+	// if they're unused removeThresholdCount frames
+	// unless _cacheKeepAlive is true.
+
+	// scene.uuid -> lightScene, lightFullscreenScene
+	var _lightScenesCache = {};
+	var _lightFullscreenScenesCache = {};
+
+	// object.material.uuid -> deferredMaterial or
+	// object.material[ n ].uuid -> deferredMaterial
+	var _normalDepthMaterialsCache = {};
+	var _normalDepthShininessMaterialsCache = {};
+	var _colorMaterialsCache = {};
+	var _reconstructionMaterialsCache = {};
+
+	// originalLight.uuid -> deferredLight
+	var _deferredLightsCache = {};
+
+	// deferredLight.uuid -> deferredLightMaterial
+	var _classicDeferredLightMaterialsCache = {};
+	var _lightPrePassMaterialsCache = {};
+
+	var _removeThresholdCount = 60;
+
+	// deferredMaterials.uuid -> object.material or
+	// deferredMaterials.uuid -> object.material[ n ]
+	// save before render and release after render.
+	var _originalMaterialsTable = {};
+
+	// object.uuid -> originalOnBeforeRender
+	// save before render and release after render.
+	var _originalOnBeforeRendersTable = {};
+
+	// object.material.uuid -> object.material.visible or
+	// object.material[ i ].uuid -> object.material[ i ].visible or
+	// save before render and release after render.
+	var _originalVisibleTable = {};
+
+	// external properties
+
+	this.renderer = undefined;
+	this.domElement = undefined;
+
+	this.forwardRendering = false; // for debug
+
+	// private methods
+
+	function init( parameters ) {
+
+		_this.renderer = parameters.renderer !== undefined ? parameters.renderer : new WebGLRenderer();
+		_this.domElement = _this.renderer.domElement;
+
+		_context = _this.renderer.context;
+		_state = _this.renderer.state;
+
+		_width = parameters.width !== undefined ? parameters.width : _this.renderer.getSize( new Vector2() ).width;
+		_height = parameters.height !== undefined ? parameters.height : _this.renderer.getSize( new Vector2() ).height;
+
+		var antialias = parameters.antialias !== undefined ? parameters.antialias : false;
+
+		if ( parameters.cacheKeepAlive !== undefined ) _cacheKeepAlive = parameters.cacheKeepAlive;
+
+		initDepthTexture();
+
+		initPassNormalDepth();
+		initPassColor();
+		initPassLight();
+		initPassReconstruction();
+		initPassFinal();
+
+		_this.setSize( _width, _height );
+		_this.setAntialias( antialias );
+		_this.enableLightPrePass( false );
+
+	}
+
+	function initDepthTexture() {
+
+		_depthTexture = new DepthTexture(
+			_width,
+			_height,
+			UnsignedInt248Type,
+			undefined,
+			undefined,
+			undefined,
+			undefined,
+			undefined,
+			undefined,
+			DepthStencilFormat
+		);
+
+	}
+
+	function initPassNormalDepth() {
+
+		_passNormalDepth = new RenderPass();
+		_passNormalDepth.clear = true;
+
+		var rt = new WebGLRenderTarget( _width, _height, {
+			minFilter: NearestFilter,
+			magFilter: NearestFilter,
+			format: RGBAFormat,
+			type: FloatType,
+			stencilBuffer: true,
+			depthTexture: _depthTexture
+		} );
+
+		rt.texture.generateMipamps = false;
+
+		_compNormalDepth = new EffectComposer( _this.renderer, rt );
+		_compNormalDepth.renderToScreen = false;
+		_compNormalDepth.addPass( _passNormalDepth );
+
+	}
+
+	function initPassColor() {
+
+		_passColor = new RenderPass();
+		_passColor.clear = true;
+
+		var rt = new WebGLRenderTarget( _width, _height, {
+			minFilter: NearestFilter,
+			magFilter: NearestFilter,
+			format: RGBAFormat,
+			type: FloatType,
+			depthTexture: _depthTexture
+		} );
+
+		rt.texture.generateMipamps = false;
+
+		_compColor = new EffectComposer( _this.renderer, rt );
+		_compColor.renderToScreen = false;
+		_compColor.addPass( _passColor );
+
+	}
+
+	function initPassLight() {
+
+		_passLightFullscreen = new RenderPass();
+		_passLightFullscreen.clear = true;
+		_passLightFullscreen.camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+
+		_passLight = new RenderPass();
+		_passLight.clear = false;
+
+		var rt = new WebGLRenderTarget( _width, _height, {
+			minFilter: NearestFilter,
+			magFilter: NearestFilter,
+			format: RGBAFormat,
+			type: FloatType,
+			depthTexture: _depthTexture
+		} );
+
+		rt.texture.generateMipamps = false;
+
+		_compLight = new EffectComposer( _this.renderer, rt );
+		_compLight.renderToScreen = false;
+		_compLight.addPass( _passLightFullscreen );
+		_compLight.addPass( _passLight );
+
+	}
+
+	function initPassReconstruction() {
+
+		_passReconstruction = new RenderPass();
+		_passReconstruction.clear = true;
+
+		var rt = new WebGLRenderTarget( _width, _height, {
+			minFilter: NearestFilter,
+			magFilter: NearestFilter,
+			format: RGBAFormat,
+			type: FloatType,
+			depthTexture: _depthTexture
+		} );
+
+		rt.texture.generateMipamps = false;
+
+		_compReconstruction = new EffectComposer( _this.renderer, rt );
+		_compReconstruction.renderToScreen = false;
+		_compReconstruction.addPass( _passReconstruction );
+
+	}
+
+	function initPassFinal() {
+
+		_passFinal = new ShaderPass( ShaderDeferred[ 'final' ] );
+		_passFinal.clear = true;
+		_passFinal.uniforms.samplerResult.value = _compLight.renderTarget2.texture;
+		_passFinal.material.blending = NoBlending;
+		_passFinal.material.depthWrite = false;
+		_passFinal.material.depthTest = false;
+
+		_passForward = new RenderPass();
+		_passForward.clear = false;
+
+		_passCopy = new ShaderPass( CopyShader );
+
+		_passFXAA = new ShaderPass( FXAAShader );
+
+		var rt = new WebGLRenderTarget( _width, _height, {
+			minFilter: NearestFilter,
+			magFilter: LinearFilter,
+			format: RGBFormat,
+			type: UnsignedByteType,
+			depthTexture: _depthTexture
+		} );
+
+		rt.texture.generateMipamps = false;
+
+		_compFinal = new EffectComposer( _this.renderer, rt );
+		_compFinal.addPass( _passFinal );
+		_compFinal.addPass( _passForward );
+		_compFinal.addPass( _passCopy );
+		_compFinal.addPass( _passFXAA );
+
+	}
+
+	function initLightScene( scene ) {
+
+		var lightSceneData = _lightScenesCache[ scene.uuid ];
+		var lightFullscreenSceneData = _lightFullscreenScenesCache[ scene.uuid ];
+
+		if ( lightSceneData === undefined ) {
+
+			var s = new Scene();
+			s.userData.lights = {};
+
+			lightSceneData = createCacheData();
+			lightSceneData.scene = s;
+
+			_lightScenesCache[ scene.uuid ] = lightSceneData;
+
+		}
+
+		if ( lightFullscreenSceneData === undefined ) {
+
+			var s = new Scene();
+			s.userData.lights = {};
+
+			var emissiveLight = createDeferredEmissiveLight();
+
+			s.userData.emissiveLight = emissiveLight;
+			s.add( emissiveLight );
+
+			lightFullscreenSceneData = createCacheData();
+			lightFullscreenSceneData.scene = s;
+
+			_lightFullscreenScenesCache[ scene.uuid ] = lightFullscreenSceneData;
+
+		}
+
+		lightSceneData.used = true;
+		lightFullscreenSceneData.used = true;
+
+		var lightScene = lightSceneData.scene;
+		var lightFullscreenScene = lightFullscreenSceneData.scene;
+
+		// emissiveLight is only for Classic Deferred Rendering
+		lightFullscreenScene.userData.emissiveLight.visible = ! _lightPrePass;
+
+		_lightScene = lightScene;
+		_lightFullscreenScene = lightFullscreenScene;
+
+	}
+
+	function getMaterialFromCacheOrCreate( originalMaterial, cache, createFunc, updateFunc ) {
+
+		var data = cache[ originalMaterial.uuid ];
+
+		if ( data === undefined ) {
+
+			data = createCacheData();
+			data.material = createFunc( originalMaterial );
+			cache[ originalMaterial.uuid ] = data;
+
+		}
+
+		data.used = true;
+
+		updateFunc( data.material, originalMaterial );
+
+		_originalMaterialsTable[ data.material.uuid ] = originalMaterial;
+
+		return data.material;
+
+	}
+
+	function overrideMaterialAndOnBeforeRender( object, getMaterialFunc, onBeforeRender ) {
+
+		if ( object.material === undefined ) return;
+
+		if ( Array.isArray( object.material ) ) {
+
+			for ( var i = 0, il = object.material.length; i < il; i ++ ) {
+
+				object.material[ i ] = getMaterialFunc( object.material[ i ] );
+
+			}
+
+		} else {
+
+			object.material = getMaterialFunc( object.material );
+
+		}
+
+		object.onBeforeRender = onBeforeRender;
+
+	}
+
+	function restoreOriginalMaterial( object ) {
+
+		if ( object.material === undefined ) return;
+
+		if ( Array.isArray( object.material ) ) {
+
+			for ( var i = 0, il = object.material.length; i < il; i ++ ) {
+
+				object.material[ i ] = _originalMaterialsTable[ object.material[ i ].uuid ];
+
+			}
+
+		} else {
+
+			object.material = _originalMaterialsTable[ object.material.uuid ];
+
+		}
+
+	}
+
+	function setMaterialNormalDepth( object ) {
+
+		overrideMaterialAndOnBeforeRender( object, getNormalDepthMaterial, updateDeferredNormalDepthUniforms );
+
+	}
+
+	function getNormalDepthMaterial( originalMaterial ) {
+
+		return getMaterialFromCacheOrCreate(
+			originalMaterial,
+			( _lightPrePass ) ? _normalDepthShininessMaterialsCache : _normalDepthMaterialsCache,
+			createDeferredNormalDepthMaterial,
+			updateDeferredNormalDepthMaterial
+		);
+
+	}
+
+	function createDeferredNormalDepthMaterial() {
+
+		var shader = ( _lightPrePass ) ? ShaderDeferred[ 'normalDepthShininess' ] : ShaderDeferred[ 'normalDepth' ];
+
+		return new ShaderMaterial( {
+			uniforms: Object.assign( {}, shader.uniforms ),
+			fragmentShader: shader.fragmentShader,
+			vertexShader: shader.vertexShader,
+			blending: NoBlending
+		} );
+
+	}
+
+	function updateDeferredNormalDepthMaterial( material, originalMaterial ) {
+
+		if ( originalMaterial.skinning !== undefined ) material.skinning = originalMaterial.skinning;
+		if ( originalMaterial.morphTargets !== undefined ) material.morphTargets = originalMaterial.morphTargets;
+
+		if ( originalMaterial.visible === true ) {
+
+			material.visible = ! originalMaterial.transparent;
+
+		} else {
+
+			material.visible = false;
+
+		}
+
+	}
+
+	function updateDeferredNormalDepthUniforms( renderer, scene, camera, geometry, material ) {
+
+		if ( ! _lightPrePass ) return;
+
+		var originalMaterial = _originalMaterialsTable[ material.uuid ];
+
+		if ( originalMaterial === undefined || originalMaterial.shininess === undefined ) return;
+
+		material.uniforms.shininess.value = originalMaterial.shininess;
+
+	}
+
+	function setMaterialColor( object ) {
+
+		overrideMaterialAndOnBeforeRender( object, getColorMaterial, updateDeferredColorUniforms );
+
+	}
+
+	function getColorMaterial( originalMaterial ) {
+
+		return getMaterialFromCacheOrCreate(
+			originalMaterial,
+			_colorMaterialsCache,
+			createDeferredColorMaterial,
+			updateDeferredColorMaterial
+		);
+
+	}
+
+	function createDeferredColorMaterial( originalMaterial ) {
+
+		var shader = ShaderDeferred[ 'color' ];
+
+		var material = new ShaderMaterial( {
+			uniforms: Object.assign( {}, shader.uniforms ),
+			fragmentShader: shader.fragmentShader,
+			vertexShader: shader.vertexShader,
+			blending: NoBlending
+		} );
+
+		if ( originalMaterial.map !== undefined ) material.map = originalMaterial.map;
+
+		return material;
+
+	}
+
+	function updateDeferredColorMaterial( material, originalMaterial ) {
+
+		if ( originalMaterial.map !== undefined ) material.map = originalMaterial.map;
+		if ( originalMaterial.skinning !== undefined ) material.skinning = originalMaterial.skinning;
+		if ( originalMaterial.morphTargets !== undefined ) material.morphTargets = originalMaterial.morphTargets;
+
+		if ( originalMaterial.visible === true ) {
+
+			material.visible = ! originalMaterial.transparent;
+
+		} else {
+
+			material.visible = false;
+
+		}
+
+	}
+
+	function updateDeferredColorUniforms( renderer, scene, camera, geometry, material ) {
+
+		var originalMaterial = _originalMaterialsTable[ material.uuid ];
+		var uniforms = material.uniforms;
+
+		var diffuse, emissive;
+
+		if ( originalMaterial.isMeshBasicMaterial === true ) {
+
+			emissive = originalMaterial.color;
+
+		} else {
+
+			diffuse = originalMaterial.color;
+			emissive = originalMaterial.emissive;
+
+		}
+
+		var specular = originalMaterial.specular;
+		var shininess = originalMaterial.shininess;
+		var map = originalMaterial.map;
+
+		if ( diffuse !== undefined ) uniforms.diffuse.value.copy( diffuse );
+		if ( emissive !== undefined ) uniforms.emissive.value.copy( emissive );
+		if ( specular !== undefined ) uniforms.specular.value.copy( specular );
+		if ( shininess !== undefined && uniforms.shininess !== undefined ) uniforms.shininess.value = shininess;
+		if ( map !== undefined ) uniforms.map.value = map;
+
+	}
+
+	function setMaterialReconstruction( object ) {
+
+		overrideMaterialAndOnBeforeRender( object, getReconstructionMaterial, updateDeferredReconstructionUniforms );
+
+	}
+
+	function getReconstructionMaterial( originalMaterial ) {
+
+		if ( originalMaterial.transparent === true ) {
+
+			_originalMaterialsTable[ originalMaterial.uuid ] = originalMaterial;
+			return originalMaterial;
+
+		}
+
+		return getMaterialFromCacheOrCreate(
+			originalMaterial,
+			_reconstructionMaterialsCache,
+			createDeferredReconstructionMaterial,
+			updateDeferredReconstructionMaterial
+		);
+
+	}
+
+	function createDeferredReconstructionMaterial( originalMaterial ) {
+
+		var shader = ShaderDeferred[ 'reconstruction' ];
+
+		var material = new ShaderMaterial( {
+			uniforms: Object.assign( {}, shader.uniforms ),
+			fragmentShader: shader.fragmentShader,
+			vertexShader: shader.vertexShader,
+			blending: NoBlending
+		} );
+
+		if ( originalMaterial.map !== undefined ) material.map = originalMaterial.map;
+
+		return material;
+
+	}
+
+	function updateDeferredReconstructionMaterial( material, originalMaterial ) {
+
+		updateDeferredColorMaterial( material, originalMaterial );
+
+	}
+
+	function updateDeferredReconstructionUniforms( renderer, scene, camera, geometry, material, group ) {
+
+		if ( material.transparent === true ) {
+
+			// 'this' is object here because this method is set as object.onBefore()
+			var onBeforeRender = _originalOnBeforeRendersTable[ this.uuid ];
+
+			if ( onBeforeRender ) {
+
+				onBeforeRender.call( this, renderer, scene, camera, geometry, material, group );
+
+			}
+
+			return;
+
+		}
+
+		updateDeferredColorUniforms( renderer, scene, camera, geometry, material, group );
+
+		material.uniforms.samplerLight.value = _compLight.renderTarget2.texture;
+
+	}
+
+	function setVisibleForForwardRendering( object ) {
+
+		if ( object.material === undefined ) return;
+
+		if ( Array.isArray( object.material ) ) {
+
+			for ( var i = 0, il = object.material.length; i < il; i ++ ) {
+
+				if ( _originalVisibleTable[ object.material[ i ].uuid ] === undefined ) {
+
+					_originalVisibleTable[ object.material[ i ].uuid ] = object.material[ i ].visible;
+					object.material[ i ].visible = object.material[ i ].transparent && object.material[ i ].visible;
+
+				}
+
+			}
+
+		} else {
+
+			if ( _originalVisibleTable[ object.material.uuid ] === undefined ) {
+
+				_originalVisibleTable[ object.material.uuid ] = object.material.visible;
+				object.material.visible = object.material.transparent && object.material.visible;
+
+			}
+
+		}
+
+	}
+
+	function restoreVisible( object ) {
+
+		if ( object.material === undefined ) return;
+
+		if ( Array.isArray( object.material ) ) {
+
+			for ( var i = 0, il = object.material.length; i < il; i ++ ) {
+
+				object.material[ i ].visible = _originalVisibleTable[ object.material[ i ].uuid ];
+
+			}
+
+		} else {
+
+			object.material.visible = _originalVisibleTable[ object.material.uuid ];
+
+		}
+
+	}
+
+	function createDeferredEmissiveLight() {
+
+		var shader = ShaderDeferred[ 'emissiveLight' ];
+
+		var material = new ShaderMaterial( {
+			uniforms: Object.assign( {}, shader.uniforms ),
+			vertexShader: shader.vertexShader,
+			fragmentShader: shader.fragmentShader,
+			blending: NoBlending,
+			depthWrite: false
+		} );
+
+		var geometry = new PlaneBufferGeometry( 2, 2 );
+		var mesh = new Mesh( geometry, material );
+
+		mesh.onBeforeRender = function ( renderer, scene, camera, geometry, material ) {
+
+			material.uniforms.samplerColor.value = _compColor.renderTarget2.texture;
+
+		};
+
+		return mesh;
+
+	}
+
+	function createDeferredLight( originalLight ) {
+
+		if ( originalLight.isPointLight ) {
+
+			return createDeferredPointLight( originalLight );
+
+		} else if ( originalLight.isSpotLight ) {
+
+			return createDeferredSpotLight( originalLight );
+
+		} else if ( originalLight.isDirectionalLight ) {
+
+			return createDeferredDirectionalLight( originalLight );
+
+		}
+
+		return null;
+
+	}
+
+	function createDeferredLightMaterial( originalLight ) {
+
+		if ( originalLight.isPointLight ) {
+
+			return createDeferredPointLightMaterial();
+
+		} else if ( originalLight.isSpotLight ) {
+
+			return createDeferredSpotLightMaterial();
+
+		} else if ( originalLight.isDirectionalLight ) {
+
+			return createDeferredDirectionalLightMaterial();
+
+		}
+
+		return null;
+
+	}
+
+	function getDeferredLightMaterial( light ) {
+
+		var cache = ( _lightPrePass ) ? _lightPrePassMaterialsCache : _classicDeferredLightMaterialsCache;
+
+		var data = cache[ light.uuid ];
+
+		if ( data === undefined ) {
+
+			data = createCacheData();
+			data.material = createDeferredLightMaterial( light.userData.originalLight );
+			cache[ light.uuid ] = data;
+
+		}
+
+		data.used = true;
+
+		return data.material;
+
+	}
+
+	function updateDeferredLight( light ) {
+
+		var originalLight = light.userData.originalLight;
+
+		if ( originalLight.isPointLight ) {
+
+			updateDeferredPointLight( light );
+
+		}
+
+	}
+
+	function createDeferredLightMesh( light, geometry ) {
+
+		var mesh = new Mesh( geometry, _tmpMaterial );
+
+		mesh.userData.originalLight = light;
+
+		return mesh;
+
+	}
+
+	function createDeferredLightShaderMaterial( shader ) {
+
+		var material = new ShaderMaterial( {
+			uniforms: Object.assign( {}, shader.uniforms ),
+			vertexShader: shader.vertexShader,
+			fragmentShader: shader.fragmentShader,
+			transparent: true,
+			blending: AdditiveBlending,
+			depthWrite: false
+		} );
+
+		if ( _lightPrePass ) material.premultipliedAlpha = true;
+
+		return material;
+
+	}
+
+	function updateDeferredLightCommonUniforms( uniforms ) {
+
+		if ( _lightPrePass ) {
+
+			uniforms.samplerNormalDepthShininess.value = _compNormalDepth.renderTarget2.texture;
+
+		} else {
+
+			uniforms.samplerNormalDepth.value = _compNormalDepth.renderTarget2.texture;
+			uniforms.samplerColor.value = _compColor.renderTarget2.texture;
+
+		}
+
+	}
+
+	function createDeferredPointLight( light ) {
+
+		var mesh = createDeferredLightMesh( light, new SphereBufferGeometry( 1, 16, 8 ) );
+		mesh.onBeforeRender = updateDeferredPointLightUniforms;
+		return mesh;
+
+	}
+
+	/*
+	 * optimization:
+	 * Renders PointLight only back face with stencil test.
+	 */
+	function createDeferredPointLightMaterial() {
+
+		var shader = ( _lightPrePass ) ? ShaderDeferred[ 'pointLightPre' ] : ShaderDeferred[ 'pointLight' ];
+
+		var material = createDeferredLightShaderMaterial( shader );
+
+		material.side = BackSide;
+		material.depthFunc = GreaterEqualDepth;
+
+		return material;
+
+	}
+
+	function updateDeferredPointLight( light ) {
+
+		var originalLight = light.userData.originalLight;
+		var distance = originalLight.distance;
+
+		if ( distance > 0 ) {
+
+			light.scale.set( 1, 1, 1 ).multiplyScalar( distance );
+			light.position.setFromMatrixPosition( originalLight.matrixWorld );
+
+		}
+
+	}
+
+	function updateDeferredPointLightUniforms( renderer, scene, camera, geometry, material ) {
+
+		var light = this;
+
+		var originalLight = light.userData.originalLight;
+		var distance = originalLight.distance;
+		var uniforms = material.uniforms;
+
+		uniforms.lightColor.value.copy( originalLight.color );
+
+		if ( distance > 0 ) {
+
+			uniforms.lightRadius.value = distance;
+			uniforms.lightIntensity.value = originalLight.intensity;
+			uniforms.lightPositionVS.value.setFromMatrixPosition( originalLight.matrixWorld ).applyMatrix4( _currentCamera.matrixWorldInverse );
+
+		} else {
+
+			uniforms.lightRadius.value = Infinity;
+
+		}
+
+		updateDeferredLightCommonUniforms( uniforms );
+
+	}
+
+	function createDeferredSpotLight( light ) {
+
+		var mesh = createDeferredLightMesh( light, new PlaneBufferGeometry( 2, 2 ) );
+		mesh.onBeforeRender = updateDeferredSpotLightUniforms;
+		return mesh;
+
+	}
+
+	function createDeferredSpotLightMaterial() {
+
+		var shader = ( _lightPrePass ) ? ShaderDeferred[ 'spotLightPre' ] : ShaderDeferred[ 'spotLight' ];
+
+		var material = createDeferredLightShaderMaterial( shader );
+
+		material.depthTest = false;
+
+		return material;
+
+	}
+
+	function updateDeferredSpotLightUniforms() {
+
+		var light = this;
+
+		var originalLight = light.userData.originalLight;
+		var uniforms = light.material.uniforms;
+
+		uniforms.lightAngle.value = originalLight.angle;
+		uniforms.lightColor.value.copy( originalLight.color );
+		uniforms.lightIntensity.value = originalLight.intensity;
+		uniforms.lightPositionVS.value.setFromMatrixPosition( originalLight.matrixWorld ).applyMatrix4( _currentCamera.matrixWorldInverse );
+
+		var vec = uniforms.lightDirectionVS.value;
+		var vec2 = _tmpVector3;
+
+		vec.setFromMatrixPosition( originalLight.matrixWorld );
+		vec2.setFromMatrixPosition( originalLight.target.matrixWorld );
+		vec.sub( vec2 ).normalize().transformDirection( _currentCamera.matrixWorldInverse );
+
+		updateDeferredLightCommonUniforms( uniforms );
+
+	}
+
+	function createDeferredDirectionalLight( light ) {
+
+		var mesh = createDeferredLightMesh( light, new PlaneBufferGeometry( 2, 2 ) );
+		mesh.onBeforeRender = updateDeferredDirectionalLightUniforms;
+		return mesh;
+
+	}
+
+	function createDeferredDirectionalLightMaterial() {
+
+		var shader = ( _lightPrePass ) ? ShaderDeferred[ 'directionalLightPre' ] : ShaderDeferred[ 'directionalLight' ];
+
+		var material = createDeferredLightShaderMaterial( shader );
+
+		material.depthTest = false;
+
+		return material;
+
+	}
+
+	function updateDeferredDirectionalLightUniforms() {
+
+		var light = this;
+
+		var originalLight = light.userData.originalLight;
+		var uniforms = light.material.uniforms;
+
+		uniforms.lightColor.value.copy( originalLight.color );
+		uniforms.lightIntensity.value = originalLight.intensity;
+
+		var vec = uniforms.lightDirectionVS.value;
+		var vec2 = _tmpVector3;
+
+		vec.setFromMatrixPosition( originalLight.matrixWorld );
+		vec2.setFromMatrixPosition( originalLight.target.matrixWorld );
+		vec.sub( vec2 ).normalize().transformDirection( _currentCamera.matrixWorldInverse );
+
+		updateDeferredLightCommonUniforms( uniforms );
+
+	}
+
+	function saveOriginalOnBeforeRenderAndCheckTransparency( object ) {
+
+		if ( object.material === undefined ) return;
+
+		_originalOnBeforeRendersTable[ object.uuid ] = object.onBeforeRender;
+
+		// _hasTransparentObject is used only for Classic Deferred Rendering
+		if ( _hasTransparentObject || _lightPrePass ) return;
+
+		if ( ! object.visible ) return;
+
+		if ( Array.isArray( object.material ) ) {
+
+			for ( var i = 0, il = object.material.length; i < il; i ++ ) {
+
+				if ( object.material[ i ].visible === true && object.material[ i ].transparent === true ) {
+
+					_hasTransparentObject = true;
+					break;
+
+				}
+
+			}
+
+		} else {
+
+			if ( object.material.visible === true && object.material.transparent === true ) _hasTransparentObject = true;
+
+		}
+
+	}
+
+	function restoreOriginalOnBeforeRender( object ) {
+
+		if ( object.material === undefined ) return;
+
+		object.onBeforeRender = _originalOnBeforeRendersTable[ object.uuid ];
+
+	}
+
+	function addDeferredLightsToLightScene( object ) {
+
+		if ( object.isLight !== true ) return;
+
+		var data = _deferredLightsCache[ object.uuid ];
+
+		if ( data === undefined ) {
+
+			data = createCacheData();
+			data.light = createDeferredLight( object );
+			_deferredLightsCache[ object.uuid ] = data;
+
+		}
+
+		data.used = true;
+
+		var light = data.light;
+
+		if ( light === null ) return;
+
+		var scene = ( object.isPointLight === true ) ? _lightScene : _lightFullscreenScene;
+
+		var lights = scene.userData.lights;
+
+		if ( lights[ light.uuid ] === undefined ) {
+
+			scene.add( light );
+
+			lights[ light.uuid ] = {
+				light: light,
+				found: true
+			};
+
+		}
+
+		lights[ light.uuid ].found = true;
+
+	}
+
+	function updateDeferredLightsInLightScene( scene ) {
+
+		var lights = scene.userData.lights;
+		var keys = Object.keys( lights );
+
+		for ( var i = 0, il = keys.length; i < il; i ++ ) {
+
+			var key = keys[ i ];
+
+			if ( lights[ key ].found === false ) {
+
+				scene.remove( lights[ key ].light );
+				delete lights[ key ];
+
+			} else {
+
+				var light = lights[ key ].light;
+				light.material = getDeferredLightMaterial( light );
+
+				updateDeferredLight( light );
+				lights[ key ].found = false;
+
+			}
+
+		}
+
+	}
+
+	function updateDeferredCommonUniforms( camera ) {
+
+		var uniforms = ShaderDeferredCommon[ 'commonUniforms' ];
+
+		uniforms.viewWidth.value = _width;
+		uniforms.viewHeight.value = _height;
+
+		uniforms.matProjInverse.value.getInverse( camera.projectionMatrix );
+
+	}
+
+	function enableFinalPasses() {
+
+		if ( _lightPrePass ) {
+
+			_passForward.enabled = false;
+			_passCopy.enabled = false;
+
+			if ( _antialias ) {
+
+				_passFXAA.enabled = true;
+
+			} else {
+
+				_passFXAA.enabled = false;
+
+			}
+
+		} else {
+
+			if ( _hasTransparentObject ) {
+
+				if ( _antialias ) {
+
+					_passForward.enabled = true;
+					_passCopy.enabled = false;
+					_passFXAA.enabled = true;
+
+				} else {
+
+					_passForward.enabled = true;
+					_passCopy.enabled = true;
+					_passFXAA.enabled = false;
+
+				}
+
+			} else {
+
+				if ( _antialias ) {
+
+					_passForward.enabled = false;
+					_passCopy.enabled = false;
+					_passFXAA.enabled = true;
+
+				} else {
+
+					_passForward.enabled = false;
+					_passCopy.enabled = false;
+					_passFXAA.enabled = false;
+
+				}
+
+			}
+
+		}
+
+	}
+
+	function createCacheData() {
+
+		return {
+			used: true,
+			keepAlive: _cacheKeepAlive,
+			count: 0
+		};
+
+	}
+
+	function cleanupCache( cache ) {
+
+		var keys = Object.keys( cache );
+
+		for ( var i = 0, il = keys.length; i < il; i ++ ) {
+
+			var key = keys[ i ];
+
+			if ( cache[ key ].used === false ) {
+
+				cache[ key ].count ++;
+
+				if ( cache[ key ].keepAlive === false && cache[ key ].count > _removeThresholdCount ) {
+
+					delete cache[ key ];
+
+				}
+
+			} else {
+
+				cache[ key ].used = false;
+				cache[ key ].count = 0;
+
+			}
+
+		}
+
+	}
+
+	function cleanupTable( table ) {
+
+		var keys = Object.keys( table );
+
+		for ( var i = 0, il = keys.length; i < il; i ++ ) {
+
+			var key = keys[ i ];
+
+			table[ key ] = undefined;
+
+		}
+
+	}
+
+	function cleanupCaches() {
+
+		cleanupCache( _lightScenesCache );
+		cleanupCache( _lightFullscreenScenesCache );
+		cleanupCache( _normalDepthMaterialsCache );
+		cleanupCache( _normalDepthShininessMaterialsCache );
+		cleanupCache( _colorMaterialsCache );
+		cleanupCache( _reconstructionMaterialsCache );
+		cleanupCache( _classicDeferredLightMaterialsCache );
+		cleanupCache( _lightPrePassMaterialsCache );
+		cleanupCache( _deferredLightsCache );
+
+		cleanupTable( _originalMaterialsTable );
+		cleanupTable( _originalOnBeforeRendersTable );
+		cleanupTable( _originalVisibleTable );
+
+	}
+
+	/*
+	 * Classic Deferred Rendering
+	 *
+	 * 1) g-buffer normal + depth pass
+	 *
+	 * RGB: normal
+	 *   A: depth
+	 *
+	 *
+	 * Light Pre-Pass Rendering
+	 *
+	 * 1') g-buffer normal + depth pass + shininess
+	 *
+	 *  RG: normal
+	 *   B: shininess
+	 *   A: depth
+	 */
+
+	function renderNormalDepth( scene, camera ) {
+
+		scene.traverse( setMaterialNormalDepth );
+
+		_passNormalDepth.scene = scene;
+		_passNormalDepth.camera = camera;
+
+		_this.renderer.autoClearDepth = true;
+		_this.renderer.autoClearStencil = true;
+
+		_state.buffers.stencil.setTest( true );
+		_state.buffers.stencil.setFunc( _context.ALWAYS, 1, 0xffffffff );
+		_state.buffers.stencil.setOp( _context.REPLACE, _context.REPLACE, _context.REPLACE );
+
+		_compNormalDepth.render();
+
+		scene.traverse( restoreOriginalMaterial );
+
+	}
+
+	/*
+	 * Classic Deferred Rendering
+	 *
+	 * 2) g-buffer color pass
+	 *
+	 * R: diffuse
+	 * G: emissive
+	 * B: specular
+	 * A: shininess
+	 */
+
+	function renderColor( scene, camera ) {
+
+		scene.traverse( setMaterialColor );
+
+		_passColor.scene = scene;
+		_passColor.camera = camera;
+
+		_this.renderer.autoClearDepth = false;
+		_this.renderer.autoClearStencil = false;
+
+		_state.buffers.stencil.setFunc( _context.EQUAL, 1, 0xffffffff );
+		_state.buffers.stencil.setOp( _context.KEEP, _context.KEEP, _context.KEEP );
+
+		_compColor.render();
+
+		scene.traverse( restoreOriginalMaterial );
+
+	}
+
+	/*
+	 * Classic Deferred Rendering
+	 *
+	 * 3) light pass
+	 */
+
+	function renderLight( scene, camera ) {
+
+		scene.traverse( addDeferredLightsToLightScene );
+
+		updateDeferredLightsInLightScene( _lightScene );
+		updateDeferredLightsInLightScene( _lightFullscreenScene );
+
+		_passLight.scene = _lightScene;
+		_passLight.camera = camera;
+
+		_passLightFullscreen.scene = _lightFullscreenScene;
+
+		_this.renderer.autoClearDepth = false;
+		_this.renderer.autoClearStencil = false;
+
+		_compLight.render();
+
+		_state.buffers.stencil.setTest( false );
+
+	}
+
+	/*
+	 * Light Pre-Pass Rendering
+	 *
+	 * 2') Light pre pass
+	 */
+
+	function renderLightPre( scene, camera ) {
+
+		scene.traverse( addDeferredLightsToLightScene );
+
+		updateDeferredLightsInLightScene( _lightScene );
+		updateDeferredLightsInLightScene( _lightFullscreenScene );
+
+		_passLight.scene = _lightScene;
+		_passLight.camera = camera;
+
+		_passLightFullscreen.scene = _lightFullscreenScene;
+
+		_this.renderer.autoClearDepth = false;
+		_this.renderer.autoClearStencil = false;
+
+		_state.buffers.stencil.setFunc( _context.EQUAL, 1, 0xffffffff );
+		_state.buffers.stencil.setOp( _context.KEEP, _context.KEEP, _context.KEEP );
+
+		_compLight.render();
+
+	}
+
+	/*
+	 * Light Pre-Pass Rendering
+	 *
+	 * 3') Reconstruction pass
+	 *
+	 * Transprency handling:
+	 * Here renders transparent objects with normal forward rendering.
+	 */
+
+	function renderReconstruction( scene, camera ) {
+
+		scene.traverse( setMaterialReconstruction );
+
+		_passReconstruction.scene = scene;
+		_passReconstruction.camera = camera;
+
+		_this.renderer.autoClearDepth = false;
+		_this.renderer.autoClearStencil = false;
+
+		_compReconstruction.render();
+
+		_state.buffers.stencil.setTest( false );
+
+		scene.traverse( restoreOriginalMaterial );
+
+	}
+
+	/*
+	 * Classic Deferred Rendering
+	 *
+	 * 4) Final pass
+	 *
+	 * transparency handling:
+	 * If there's any transparent objects, here renders them on the deferred rendering result
+	 * with normal forward rendering. This may be the easist way but heavy.
+	 * We should consider any better ways someday.
+	 *
+	 *
+	 * Light Pre-Pass Rendering
+	 *
+	 * 4') Final pass
+	 *
+	 *
+	 * Common
+	 *
+	 * antialias handling:
+	 * Here uses postprocessing FXAA for antialias.
+	 *
+	 */
+
+	function renderFinal( scene, camera ) {
+
+		if ( ! _lightPrePass && _hasTransparentObject ) {
+
+			scene.traverse( setVisibleForForwardRendering );
+			scene.traverse( restoreOriginalOnBeforeRender );
+
+			_passForward.scene = scene;
+			_passForward.camera = camera;
+
+		}
+
+		enableFinalPasses();
+
+		_this.renderer.autoClearDepth = false;
+		_this.renderer.autoClearStencil = false;
+
+		_compFinal.render();
+
+		if ( ! _lightPrePass && _hasTransparentObject ) {
+
+			scene.traverse( restoreVisible );
+
+		}
+
+	}
+
+	// external APIs
+
+	this.setSize = function ( width, height ) {
+
+		_width = width;
+		_height = height;
+
+		this.renderer.setSize( _width, _height );
+
+		_compNormalDepth.setSize( _width, _height );
+		_compColor.setSize( _width, _height );
+		_compLight.setSize( _width, _height );
+		_compReconstruction.setSize( _width, _height );
+		_compFinal.setSize( _width, _height );
+
+		_depthTexture.image.width = _width;
+		_depthTexture.image.height = _height;
+		_depthTexture.needsUpdate = true;
+
+		_passFXAA.uniforms.resolution.value.set( 1 / _width, 1 / _height );
+
+	};
+
+	this.setAntialias = function ( enabled ) {
+
+		_antialias = enabled;
+
+	};
+
+	this.enableLightPrePass = function ( enabled ) {
+
+		_lightPrePass = enabled;
+
+		_passFinal.uniforms.samplerResult.value = ( _lightPrePass ) ? _compReconstruction.renderTarget2.texture : _compLight.renderTarget2.texture;
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		// for debug to compare with normal forward rendering
+
+		if ( this.forwardRendering ) {
+
+			this.renderer.render( scene, camera );
+			return;
+
+		}
+
+		var currentSceneAutoUpdate = scene.autoUpdate;
+		var currentAutoClearColor = this.renderer.autoClearColor;
+		var currentAutoClearDepth = this.renderer.autoClearDepth;
+		var currentAutoClearStencil = this.renderer.autoClearStencil;
+
+		_currentCamera = camera;
+
+		initLightScene( scene );
+
+		scene.autoUpdate = false;
+		scene.updateMatrixWorld();
+
+		_hasTransparentObject = false;
+
+		scene.traverse( saveOriginalOnBeforeRenderAndCheckTransparency );
+
+		updateDeferredCommonUniforms( camera );
+
+		renderNormalDepth( scene, camera );
+
+		if ( _lightPrePass ) {
+
+			renderLightPre( scene, camera );
+			renderReconstruction( scene, camera );
+
+		} else {
+
+			renderColor( scene, camera );
+			renderLight( scene, camera );
+
+		}
+
+		renderFinal( scene, camera );
+
+		scene.traverse( restoreOriginalOnBeforeRender );
+
+		cleanupCaches();
+
+		scene.autoUpdate = currentSceneAutoUpdate;
+		this.renderer.autoClearColor = currentAutoClearColor;
+		this.renderer.autoClearDepth = currentAutoClearDepth;
+		this.renderer.autoClearStencil = currentAutoClearStencil;
+
+	};
+
+	// initialize
+
+	init( parameters );
+
+};
+
+var DeferredShaderChunk = {
+
+	packVector3: [
+
+		"float vec3_to_float( vec3 data ) {",
+
+		"	const float unit = 255.0/256.0;",
+		"	highp float compressed = fract( data.x * unit ) + floor( data.y * unit * 255.0 ) + floor( data.z * unit * 255.0 ) * 255.0;",
+		"	return compressed;",
+
+		"}"
+
+	].join( "\n" ),
+
+	unpackFloat: [
+
+		"vec3 float_to_vec3( float data ) {",
+
+		"	const float unit = 255.0;",
+		"	vec3 uncompressed;",
+		"	uncompressed.x = fract( data );",
+		"	float zInt = floor( data / unit );",
+		"	uncompressed.z = fract( zInt / unit );",
+		"	uncompressed.y = fract( floor( data - ( zInt * unit ) ) / unit );",
+		"	return uncompressed;",
+
+		"}"
+
+	].join( "\n" ),
+
+	// Refer to http://aras-p.info/texts/CompactNormalStorage.html
+	packNormal: [
+
+		"vec2 normal_to_vec2( vec3 normal ) {",
+
+		"	return normal.xy / sqrt( normal.z * 8.0 + 8.0 ) + 0.5;",
+
+		"}"
+
+	].join( "\n" ),
+
+	unpackVector2: [
+
+		"vec3 vec2_to_normal( vec2 data ) {",
+
+		"	vec2 fenc = data * 4.0 - 2.0;",
+		"	float f = dot( fenc, fenc );",
+		"	float g = sqrt( 1.0 - f / 4.0 );",
+		"	vec3 normal;",
+		"	normal.xy = fenc * g;",
+		"	normal.z = 1.0 - f / 2.0;",
+		"	return normal;",
+
+		"}"
+
+	].join( "\n" ),
+
+	computeTextureCoord: [
+
+		"vec2 texCoord = gl_FragCoord.xy / vec2( viewWidth, viewHeight );"
+
+	].join( "\n" ),
+
+	packNormalDepth: [
+
+		"vec4 packedNormalDepth;",
+		"packedNormalDepth.xyz = normal * 0.5 + 0.5;",
+		"packedNormalDepth.w = position.z / position.w;"
+
+	].join( "\n" ),
+
+	unpackNormalDepth: [
+
+		"vec4 normalDepthMap = texture2D( samplerNormalDepth, texCoord );",
+		"float depth = normalDepthMap.w;",
+
+		"if ( depth == 0.0 ) discard;",
+
+		"vec3 normal = normalDepthMap.xyz * 2.0 - 1.0;"
+
+	].join( "\n" ),
+
+	packNormalDepthShininess: [
+
+		"vec4 packedNormalDepthShininess;",
+		"packedNormalDepthShininess.xy = normal_to_vec2( normal );",
+		"packedNormalDepthShininess.z = shininess;",
+		"packedNormalDepthShininess.w = position.z / position.w;"
+
+	].join( "\n" ),
+
+	unpackNormalDepthShininess: [
+
+		"vec4 normalDepthMap = texture2D( samplerNormalDepthShininess, texCoord );",
+		"float depth = normalDepthMap.w;",
+
+		"if ( depth == 0.0 ) discard;",
+
+		"vec3 normal = vec2_to_normal( normalDepthMap.xy );",
+		"float shininess = normalDepthMap.z;"
+
+	].join( "\n" ),
+
+	packColor: [
+
+		"vec4 packedColor;",
+		"packedColor.x = vec3_to_float( diffuseColor.rgb );",
+		"packedColor.y = vec3_to_float( emissiveColor );",
+		"packedColor.z = vec3_to_float( specularColor );",
+		"packedColor.w = shininess;"
+
+	].join( "\n" ),
+
+	unpackColor: [
+
+		"vec4 colorMap = texture2D( samplerColor, texCoord );",
+		"vec3 diffuseColor = float_to_vec3( colorMap.x );",
+		"vec3 emissiveColor = float_to_vec3( colorMap.y );",
+		"vec3 specularColor = float_to_vec3( colorMap.z );",
+		"float shininess = colorMap.w;"
+
+	].join( "\n" ),
+
+	packLight: [
+
+		"vec4 packedLight;",
+		"packedLight.xyz = lightIntensity * lightColor * max( dot( lightVector, normal ), 0.0 ) * attenuation;",
+		"packedLight.w = lightIntensity * specular * max( dot( lightVector, normal ), 0.0 ) * attenuation;"
+
+	].join( "\n" ),
+
+	computeVertexPositionVS: [
+
+		"vec2 xy = texCoord * 2.0 - 1.0;",
+		"vec4 vertexPositionProjected = vec4( xy, depth, 1.0 );",
+		"vec4 vertexPositionVS = matProjInverse * vertexPositionProjected;",
+		"vertexPositionVS.xyz /= vertexPositionVS.w;",
+		"vertexPositionVS.w = 1.0;"
+
+	].join( "\n" ),
+
+	// TODO: calculate schlick
+	computeSpecular: [
+
+		"vec3 halfVector = normalize( lightVector - normalize( vertexPositionVS.xyz ) );",
+		"float dotNormalHalf = max( dot( normal, halfVector ), 0.0 );",
+		"float specular = 0.31830988618 * ( shininess * 0.5 + 1.0 ) * pow( dotNormalHalf, shininess );"
+
+	].join( "\n" ),
+
+	combine: [
+
+		"gl_FragColor = vec4( lightIntensity * lightColor * max( dot( lightVector, normal ), 0.0 ) * ( diffuseColor + specular * specularColor ) * attenuation, 1.0 );"
+
+	].join( "\n" )
+
+};
+
+var ShaderDeferredCommon = {
+
+	commonUniforms: {
+
+		matProjInverse: new Uniform( new Matrix4() ),
+
+		viewWidth: new Uniform( 800 ),
+		viewHeight: new Uniform( 600 )
+
+	}
+
+};
+
+var ShaderDeferred = {
+
+	normalDepth: {
+
+		uniforms: {},
+
+		vertexShader: [
+
+			"varying vec3 vNormal;",
+			"varying vec4 vPosition;",
+
+			"#include <morphtarget_pars_vertex>",
+			"#include <skinning_pars_vertex>",
+
+			"void main() {",
+
+			"#include <begin_vertex>",
+			"#include <beginnormal_vertex>",
+			"#include <skinbase_vertex>",
+			"#include <skinnormal_vertex>",
+			"#include <defaultnormal_vertex>",
+			"#include <morphtarget_vertex>",
+			"#include <skinning_vertex>",
+			"#include <project_vertex>",
+
+			"	vNormal = normalize( transformedNormal );",
+			"	vPosition = gl_Position;",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"varying vec3 vNormal;",
+			"varying vec4 vPosition;",
+
+			"void main() {",
+
+			"	vec3 normal = vNormal;",
+			"	vec4 position = vPosition;",
+
+			DeferredShaderChunk[ "packNormalDepth" ],
+
+			"	gl_FragColor = packedNormalDepth;",
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+	color: {
+
+		uniforms: {
+
+			map: new Uniform( null ),
+			offsetRepeat: new Uniform( new Vector4( 0, 0, 1, 1 ) ),
+
+			diffuse: new Uniform( new Color( 0x000000 ) ),
+			emissive: new Uniform( new Color( 0x000000 ) ),
+			specular: new Uniform( new Color( 0x000000 ) ),
+			shininess: new Uniform( 30.0 )
+
+		},
+
+		vertexShader: [
+
+			"#include <uv_pars_vertex>",
+			"#include <morphtarget_pars_vertex>",
+			"#include <skinning_pars_vertex>",
+
+			"void main() {",
+
+			"#include <uv_vertex>",
+			"#include <begin_vertex>",
+			"#include <beginnormal_vertex>",
+			"#include <skinbase_vertex>",
+			"#include <skinnormal_vertex>",
+			"#include <defaultnormal_vertex>",
+			"#include <morphtarget_vertex>",
+			"#include <skinning_vertex>",
+			"#include <project_vertex>",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform vec3 diffuse;",
+			"uniform vec3 emissive;",
+			"uniform vec3 specular;",
+			"uniform float shininess;",
+
+			"#include <uv_pars_fragment>",
+			"#include <map_pars_fragment>",
+			DeferredShaderChunk[ "packVector3" ],
+
+			"void main() {",
+
+			"	vec4 diffuseColor = vec4( diffuse, 1.0 );",
+			"	vec3 emissiveColor = emissive;",
+			"	vec3 specularColor = specular;",
+
+			"#include <map_fragment>",
+			DeferredShaderChunk[ "packColor" ],
+
+			"	gl_FragColor = packedColor;",
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+	emissiveLight: {
+
+		uniforms: Object.assign(
+
+			{
+
+				samplerColor: new Uniform( null )
+
+			},
+
+			ShaderDeferredCommon[ 'commonUniforms' ]
+
+		),
+
+		vertexShader: [
+
+			"void main() { ",
+
+			"	gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );",
+
+			"}"
+
+		].join( '\n' ),
+
+		fragmentShader: [
+
+			"uniform sampler2D samplerColor;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+			DeferredShaderChunk[ "unpackFloat" ],
+
+			"void main() {",
+
+			DeferredShaderChunk[ "computeTextureCoord" ],
+			DeferredShaderChunk[ "unpackColor" ],
+
+			"	gl_FragColor = vec4( emissiveColor, 1.0 );",
+
+			"}"
+
+		].join( '\n' )
+
+	},
+
+	pointLight: {
+
+		uniforms: Object.assign(
+
+			{
+
+				samplerNormalDepth: new Uniform( null ),
+				samplerColor: new Uniform( null ),
+
+				lightColor: new Uniform( new Color( 0x000000 ) ),
+				lightPositionVS: new Uniform( new Vector3( 0, 1, 0 ) ),
+				lightIntensity: new Uniform( 1.0 ),
+				lightRadius: new Uniform( 1.0 )
+
+			},
+
+			ShaderDeferredCommon[ 'commonUniforms' ]
+
+		),
+
+		vertexShader: [
+
+			"void main() {",
+
+			"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform sampler2D samplerNormalDepth;",
+			"uniform sampler2D samplerColor;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+			"uniform vec3 lightColor;",
+			"uniform vec3 lightPositionVS;",
+			"uniform float lightIntensity;",
+			"uniform float lightRadius;",
+
+			"uniform mat4 matProjInverse;",
+
+			DeferredShaderChunk[ "unpackFloat" ],
+
+			"void main() {",
+
+			DeferredShaderChunk[ "computeTextureCoord" ],
+			DeferredShaderChunk[ "unpackNormalDepth" ],
+			DeferredShaderChunk[ "computeVertexPositionVS" ],
+
+			"	vec3 lightVector = lightPositionVS - vertexPositionVS.xyz;",
+			"	float distance = length( lightVector );",
+
+			"	if ( distance > lightRadius ) discard;",
+
+			"	lightVector = normalize( lightVector );",
+
+			DeferredShaderChunk[ "unpackColor" ],
+			DeferredShaderChunk[ "computeSpecular" ],
+
+			"	//float cutoff = 0.3;",
+			"	//float denom = distance / lightRadius + 1.0;",
+			"	//float attenuation = 1.0 / ( denom * denom );",
+			"	//attenuation = ( attenuation - cutoff ) / ( 1.0 - cutoff );",
+			"	//attenuation = max( attenuation, 0.0 );",
+			"	//attenuation *= attenuation;",
+
+			"	//diffuseColor *= saturate( -distance / lightRadius + 1.0 );",
+			"	//float attenuation = 1.0;",
+
+			"	float attenuation = saturate( -distance / lightRadius + 1.0 );",
+
+			DeferredShaderChunk[ "combine" ],
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+	spotLight: {
+
+		uniforms: Object.assign(
+
+			{
+
+				samplerNormalDepth: new Uniform( null ),
+				samplerColor: new Uniform( null ),
+
+				lightColor: new Uniform( new Color( 0x000000 ) ),
+				lightDirectionVS: new Uniform( new Vector3( 0, 1, 0 ) ),
+				lightPositionVS: new Uniform( new Vector3( 0, 1, 0 ) ),
+				lightAngle: new Uniform( 1.0 ),
+				lightIntensity: new Uniform( 1.0 )
+
+			},
+
+			ShaderDeferredCommon[ 'commonUniforms' ]
+
+		),
+
+		vertexShader: [
+
+			"void main() { ",
+
+			"	gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform sampler2D samplerNormalDepth;",
+			"uniform sampler2D samplerColor;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+			"uniform vec3 lightColor;",
+			"uniform vec3 lightPositionVS;",
+			"uniform vec3 lightDirectionVS;",
+			"uniform float lightAngle;",
+			"uniform float lightIntensity;",
+
+			"uniform mat4 matProjInverse;",
+
+			DeferredShaderChunk[ "unpackFloat" ],
+
+			"void main() {",
+
+			DeferredShaderChunk[ "computeTextureCoord" ],
+			DeferredShaderChunk[ "unpackNormalDepth" ],
+			DeferredShaderChunk[ "computeVertexPositionVS" ],
+			DeferredShaderChunk[ "unpackColor" ],
+
+			"	vec3 lightVector = normalize( lightPositionVS.xyz - vertexPositionVS.xyz );",
+
+			"	float rho = dot( lightDirectionVS, lightVector );",
+			"	float rhoMax = cos( lightAngle );",
+
+			"	if ( rho <= rhoMax ) discard;",
+
+			"	float theta = rhoMax + 0.0001;",
+			"	float phi = rhoMax + 0.05;",
+			"	float falloff = 4.0;",
+
+			"	float spot = 0.0;",
+
+			"	if ( rho >= phi ) {",
+
+			"		spot = 1.0;",
+
+			"	} else if ( rho <= theta ) {",
+
+			"		spot = 0.0;",
+
+			"	} else { ",
+
+			"		spot = pow( ( rho - theta ) / ( phi - theta ), falloff );",
+
+			"	}",
+
+			"	diffuseColor *= spot;",
+
+			DeferredShaderChunk[ "computeSpecular" ],
+
+			"	const float attenuation = 1.0;",
+
+			DeferredShaderChunk[ "combine" ],
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+	directionalLight: {
+
+		uniforms: Object.assign(
+
+			{
+
+				samplerNormalDepth: new Uniform( null ),
+				samplerColor: new Uniform( null ),
+
+				lightColor: new Uniform( new Color( 0x000000 ) ),
+				lightDirectionVS: new Uniform( new Vector3( 0, 1, 0 ) ),
+				lightIntensity: new Uniform( 1.0 )
+			},
+
+			ShaderDeferredCommon[ 'commonUniforms' ]
+
+		),
+
+		vertexShader: [
+
+			"void main() { ",
+
+			"	gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );",
+
+			"}"
+
+		].join( '\n' ),
+
+		fragmentShader: [
+
+			"uniform sampler2D samplerNormalDepth;",
+			"uniform sampler2D samplerColor;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+			"uniform vec3 lightColor;",
+			"uniform vec3 lightDirectionVS;",
+			"uniform float lightIntensity;",
+
+			"uniform mat4 matProjInverse;",
+
+			DeferredShaderChunk[ "unpackFloat" ],
+
+			"void main() {",
+
+			DeferredShaderChunk[ "computeTextureCoord" ],
+			DeferredShaderChunk[ "unpackNormalDepth" ],
+			DeferredShaderChunk[ "computeVertexPositionVS" ],
+			DeferredShaderChunk[ "unpackColor" ],
+
+			"	vec3 lightVector = normalize( lightDirectionVS );",
+
+			DeferredShaderChunk[ "computeSpecular" ],
+
+			"	const float attenuation = 1.0;",
+
+			DeferredShaderChunk[ "combine" ],
+
+			"}"
+
+		].join( '\n' )
+
+	},
+
+	normalDepthShininess: {
+
+		uniforms: {
+
+			shininess: new Uniform( 30.0 )
+
+		},
+
+		vertexShader: [
+
+			"varying vec3 vNormal;",
+			"varying vec4 vPosition;",
+
+			"#include <morphtarget_pars_vertex>",
+			"#include <skinning_pars_vertex>",
+
+			"void main() {",
+
+			"#include <begin_vertex>",
+			"#include <beginnormal_vertex>",
+			"#include <skinbase_vertex>",
+			"#include <skinnormal_vertex>",
+			"#include <defaultnormal_vertex>",
+			"#include <morphtarget_vertex>",
+			"#include <skinning_vertex>",
+			"#include <project_vertex>",
+
+			"	vNormal = normalize( transformedNormal );",
+			"	vPosition = gl_Position;",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"varying vec3 vNormal;",
+			"varying vec4 vPosition;",
+
+			"uniform float shininess;",
+
+			DeferredShaderChunk[ "packNormal" ],
+
+			"void main() {",
+
+			"	vec3 normal = vNormal;",
+			"	vec4 position = vPosition;",
+
+			DeferredShaderChunk[ "packNormalDepthShininess" ],
+
+			"	gl_FragColor = packedNormalDepthShininess;",
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+	pointLightPre: {
+
+		uniforms: Object.assign(
+
+			{
+
+				samplerNormalDepthShininess: new Uniform( null ),
+
+				lightColor: new Uniform( new Color( 0x000000 ) ),
+				lightPositionVS: new Uniform( new Vector3( 0, 1, 0 ) ),
+				lightIntensity: new Uniform( 1.0 ),
+				lightRadius: new Uniform( 1.0 )
+			},
+
+			ShaderDeferredCommon[ 'commonUniforms' ]
+
+		),
+
+
+		vertexShader: [
+
+			"void main() {",
+
+			"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform sampler2D samplerNormalDepthShininess;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+			"uniform vec3 lightColor;",
+			"uniform vec3 lightPositionVS;",
+			"uniform float lightIntensity;",
+			"uniform float lightRadius;",
+
+			"uniform mat4 matProjInverse;",
+
+			DeferredShaderChunk[ "unpackFloat" ],
+			DeferredShaderChunk[ "unpackVector2" ],
+
+			"void main() {",
+
+			DeferredShaderChunk[ "computeTextureCoord" ],
+			DeferredShaderChunk[ "unpackNormalDepthShininess" ],
+			DeferredShaderChunk[ "computeVertexPositionVS" ],
+
+			"	vec3 lightVector = lightPositionVS - vertexPositionVS.xyz;",
+			"	float distance = length( lightVector );",
+
+			"	if ( distance > lightRadius ) discard;",
+
+			"	lightVector = normalize( lightVector );",
+
+			DeferredShaderChunk[ "computeSpecular" ],
+
+			"	float attenuation = saturate( -distance / lightRadius + 1.0 );",
+
+			DeferredShaderChunk[ "packLight" ],
+
+			"	gl_FragColor = packedLight;",
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+	spotLightPre: {
+
+		uniforms: Object.assign(
+
+			{
+
+				samplerNormalDepthShininess: new Uniform( null ),
+
+				lightColor: new Uniform( new Color( 0x000000 ) ),
+				lightDirectionVS: new Uniform( new Vector3( 0, 1, 0 ) ),
+				lightPositionVS: new Uniform( new Vector3( 0, 1, 0 ) ),
+				lightAngle: new Uniform( 1.0 ),
+				lightIntensity: new Uniform( 1.0 )
+
+			},
+
+			ShaderDeferredCommon[ 'commonUniforms' ]
+
+		),
+
+		vertexShader: [
+
+			"void main() { ",
+
+			"	gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform sampler2D samplerNormalDepthShininess;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+			"uniform vec3 lightColor;",
+			"uniform vec3 lightPositionVS;",
+			"uniform vec3 lightDirectionVS;",
+			"uniform float lightAngle;",
+			"uniform float lightIntensity;",
+
+			"uniform mat4 matProjInverse;",
+
+			DeferredShaderChunk[ "unpackFloat" ],
+			DeferredShaderChunk[ "unpackVector2" ],
+
+			"void main() {",
+
+			DeferredShaderChunk[ "computeTextureCoord" ],
+			DeferredShaderChunk[ "unpackNormalDepthShininess" ],
+			DeferredShaderChunk[ "computeVertexPositionVS" ],
+
+			"	vec3 lightVector = normalize( lightPositionVS.xyz - vertexPositionVS.xyz );",
+
+			"	float rho = dot( lightDirectionVS, lightVector );",
+			"	float rhoMax = cos( lightAngle );",
+
+			"	if ( rho <= rhoMax ) discard;",
+
+			"	float theta = rhoMax + 0.0001;",
+			"	float phi = rhoMax + 0.05;",
+			"	float falloff = 4.0;",
+
+			"	float spot = 0.0;",
+
+			"	if ( rho >= phi ) {",
+
+			"		spot = 1.0;",
+
+			"	} else if ( rho <= theta ) {",
+
+			"		spot = 0.0;",
+
+			"	} else { ",
+
+			"		spot = pow( ( rho - theta ) / ( phi - theta ), falloff );",
+
+			"	}",
+
+			DeferredShaderChunk[ "computeSpecular" ],
+
+			"	const float attenuation = 1.0;",
+
+			DeferredShaderChunk[ "packLight" ],
+
+			"	gl_FragColor = spot * packedLight;",
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+	directionalLightPre: {
+
+		uniforms: Object.assign(
+
+			{
+
+				samplerNormalDepthShininess: new Uniform( null ),
+
+				lightColor: new Uniform( new Color( 0x000000 ) ),
+				lightDirectionVS: new Uniform( new Vector3( 0, 1, 0 ) ),
+				lightIntensity: new Uniform( 1.0 )
+
+			},
+
+			ShaderDeferredCommon[ 'commonUniforms' ]
+
+		),
+
+		vertexShader: [
+
+			"void main() { ",
+
+			"	gl_Position = vec4( sign( position.xy ), 0.0, 1.0 );",
+
+			"}"
+
+		].join( '\n' ),
+
+		fragmentShader: [
+
+			"uniform sampler2D samplerNormalDepthShininess;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+			"uniform vec3 lightColor;",
+			"uniform vec3 lightDirectionVS;",
+			"uniform float lightIntensity;",
+
+			"uniform mat4 matProjInverse;",
+
+			DeferredShaderChunk[ "unpackFloat" ],
+			DeferredShaderChunk[ "unpackVector2" ],
+
+			"void main() {",
+
+			DeferredShaderChunk[ "computeTextureCoord" ],
+			DeferredShaderChunk[ "unpackNormalDepthShininess" ],
+			DeferredShaderChunk[ "computeVertexPositionVS" ],
+
+			"	vec3 lightVector = normalize( lightDirectionVS );",
+
+			DeferredShaderChunk[ "computeSpecular" ],
+
+			"	const float attenuation = 1.0;",
+
+			DeferredShaderChunk[ "packLight" ],
+
+			"	gl_FragColor = packedLight;",
+
+			"}"
+
+		].join( '\n' )
+
+	},
+
+	reconstruction: {
+
+		uniforms: Object.assign(
+
+			{
+
+				samplerLight: new Uniform( null ),
+
+				map: new Uniform( null ),
+				offsetRepeat: new Uniform( new Vector4( 0, 0, 1, 1 ) ),
+
+				diffuse: new Uniform( new Color( 0x000000 ) ),
+				emissive: new Uniform( new Color( 0x000000 ) ),
+				specular: new Uniform( new Color( 0x000000 ) ),
+				shininess: new Uniform( 30.0 )
+
+			},
+
+			ShaderDeferredCommon[ 'commonUniforms' ]
+
+		),
+
+		vertexShader: [
+
+			"#include <uv_pars_vertex>",
+			"#include <morphtarget_pars_vertex>",
+			"#include <skinning_pars_vertex>",
+
+			"void main() {",
+
+			"#include <uv_vertex>",
+			"#include <begin_vertex>",
+			"#include <beginnormal_vertex>",
+			"#include <skinbase_vertex>",
+			"#include <skinnormal_vertex>",
+			"#include <defaultnormal_vertex>",
+			"#include <morphtarget_vertex>",
+			"#include <skinning_vertex>",
+			"#include <project_vertex>",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform sampler2D samplerLight;",
+
+			"uniform vec3 diffuse;",
+			"uniform vec3 emissive;",
+			"uniform vec3 specular;",
+			"uniform float shininess;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+			"#include <uv_pars_fragment>",
+			"#include <map_pars_fragment>",
+
+			DeferredShaderChunk[ "unpackFloat" ],
+
+			"void main() {",
+
+			"	vec4 diffuseColor = vec4( diffuse, 1.0 );",
+			"	vec3 emissiveColor = emissive;",
+			"	vec3 specularColor = specular;",
+
+			DeferredShaderChunk[ "computeTextureCoord" ],
+
+			"	vec4 light = texture2D( samplerLight, texCoord );",
+
+			"#include <map_fragment>",
+
+			"	vec3 diffuseFinal = diffuseColor.rgb * light.rgb;",
+			"	vec3 emissiveFinal = emissiveColor;",
+			"	vec3 specularFinal = specularColor * light.rgb * light.a;",
+
+			"	gl_FragColor = vec4( diffuseFinal + emissiveFinal + specularFinal, 1.0 );",
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+	// TODO: implement tone mapping
+	final: {
+
+		uniforms: {
+
+			samplerResult: new Uniform( null )
+
+		},
+
+		vertexShader: [
+
+			"varying vec2 texCoord;",
+
+			"void main() {",
+
+			"	vec4 pos = vec4( sign( position.xy ), 0.0, 1.0 );",
+			"	texCoord = pos.xy * vec2( 0.5 ) + 0.5;",
+			"	gl_Position = pos;",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"varying vec2 texCoord;",
+			"uniform sampler2D samplerResult;",
+
+			"void main() {",
+
+			"	gl_FragColor = texture2D( samplerResult, texCoord );",
+
+			"}"
+
+		].join( "\n" )
+
+	}
+
+};
+
+export { WebGLDeferredRenderer, DeferredShaderChunk, ShaderDeferredCommon, ShaderDeferred };

+ 1 - 1
examples/webgldeferred_animation.html

@@ -119,7 +119,7 @@
 
 					model.traverse( function ( o ) {
 
-						if ( !o.isMesh ) return;
+						if ( ! o.isMesh ) return;
 
 						o.material.emissive = o.material.color.clone().multiplyScalar( 0.3 );
 

+ 2 - 1
utils/modularize.js

@@ -112,9 +112,10 @@ var files = [
 	{ path: 'renderers/CSS2DRenderer.js', dependencies: [], ignoreList: [] },
 	{ path: 'renderers/CSS3DRenderer.js', dependencies: [], ignoreList: [] },
 	{ path: 'renderers/Projector.js', dependencies: [], ignoreList: [] },
+	{ path: 'renderers/RaytracingRenderer.js', dependencies: [], ignoreList: [] },
 	{ path: 'renderers/SoftwareRenderer.js', dependencies: [ { name: 'Projector', path: 'renderers/Projector.js' }, { name: 'RenderableFace', path: 'renderers/Projector.js' }, { name: 'RenderableLine', path: 'renderers/Projector.js' }, { name: 'RenderableSprite', path: 'renderers/Projector.js' } ], ignoreList: [] },
 	{ path: 'renderers/SVGRenderer.js', dependencies: [ { name: 'Projector', path: 'renderers/Projector.js' }, { name: 'RenderableFace', path: 'renderers/Projector.js' }, { name: 'RenderableLine', path: 'renderers/Projector.js' }, { name: 'RenderableSprite', path: 'renderers/Projector.js' } ], ignoreList: [] },
-	{ path: 'renderers/RaytracingRenderer.js', dependencies: [], ignoreList: [] },
+	{ path: 'renderers/WebGLDeferredRenderer.js', dependencies: [ { name: 'EffectComposer', path: 'postprocessing/EffectComposer.js' }, { name: 'ShaderPass', path: 'postprocessing/ShaderPass.js' }, { name: 'RenderPass', path: 'postprocessing/RenderPass.js' }, { name: 'FXAAShader', path: 'shaders/FXAAShader.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' } ], ignoreList: [] },
 
 	{ path: 'shaders/AfterimageShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/BasicShader.js', dependencies: [], ignoreList: [] },