Ver Fonte

Merge pull request #16548 from Mugen87/dev32

JSM: Added module and TS files for effects.
Michael Herzog há 6 anos atrás
pai
commit
b39de78500

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

@@ -103,6 +103,16 @@
 						<li>NURBSUtils</li>
 					</ul>
 				</li>
+				<li>effects
+					<ul>
+						<li>AnaglyphEffect</li>
+						<li>AsciiEffect</li>
+						<li>OutlineEffect</li>
+						<li>ParallaxBarrierEffect</li>
+						<li>PeppersGhostEffect</li>
+						<li>StereoEffect</li>
+					</ul>
+				</li>
 				<li>exporters
 					<ul>
 						<li>ColladaExporter</li>

+ 6 - 0
examples/js/effects/AsciiEffect.js

@@ -159,11 +159,13 @@ THREE.AsciiEffect = function ( renderer, charSet, options ) {
 	if ( strResolution == "low" ) {
 
 		switch ( iScale ) {
+
 			case 1 : fLetterSpacing = - 1; break;
 			case 2 :
 			case 3 : fLetterSpacing = - 2.1; break;
 			case 4 : fLetterSpacing = - 3.1; break;
 			case 5 : fLetterSpacing = - 4.15; break;
+
 		}
 
 	}
@@ -171,11 +173,13 @@ THREE.AsciiEffect = function ( renderer, charSet, options ) {
 	if ( strResolution == "medium" ) {
 
 		switch ( iScale ) {
+
 			case 1 : fLetterSpacing = 0; break;
 			case 2 : fLetterSpacing = - 1; break;
 			case 3 : fLetterSpacing = - 1.04; break;
 			case 4 :
 			case 5 : fLetterSpacing = - 2.1; break;
+
 		}
 
 	}
@@ -183,11 +187,13 @@ THREE.AsciiEffect = function ( renderer, charSet, options ) {
 	if ( strResolution == "high" ) {
 
 		switch ( iScale ) {
+
 			case 1 :
 			case 2 : fLetterSpacing = 0; break;
 			case 3 :
 			case 4 :
 			case 5 : fLetterSpacing = - 1; break;
+
 		}
 
 	}

+ 11 - 12
examples/js/effects/OutlineEffect.js

@@ -173,7 +173,6 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 		var shaderID = shaderIDs[ originalMaterial.type ];
 		var originalUniforms, originalVertexShader;
-		var outlineParameters = originalMaterial.userData.outlineParameters;
 
 		if ( shaderID !== undefined ) {
 
@@ -210,15 +209,15 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 		var uniforms = Object.assign( {}, originalUniforms, uniformsChunk );
 
 		var vertexShader = originalVertexShader
-					// put vertexShaderChunk right before "void main() {...}"
-					.replace( /void\s+main\s*\(\s*\)/, vertexShaderChunk + '\nvoid main()' )
-					// put vertexShaderChunk2 the end of "void main() {...}"
-					// Note: here assums originalVertexShader ends with "}" of "void main() {...}"
-					.replace( /\}\s*$/, vertexShaderChunk2 + '\n}' )
-					// remove any light related lines
-					// Note: here is very sensitive to originalVertexShader
-					// TODO: consider safer way
-					.replace( /#include\s+<[\w_]*light[\w_]*>/g, '' );
+			// put vertexShaderChunk right before "void main() {...}"
+			.replace( /void\s+main\s*\(\s*\)/, vertexShaderChunk + '\nvoid main()' )
+			// put vertexShaderChunk2 the end of "void main() {...}"
+			// Note: here assums originalVertexShader ends with "}" of "void main() {...}"
+			.replace( /\}\s*$/, vertexShaderChunk2 + '\n}' )
+			// remove any light related lines
+			// Note: here is very sensitive to originalVertexShader
+			// TODO: consider safer way
+			.replace( /#include\s+<[\w_]*light[\w_]*>/g, '' );
 
 		var defines = {};
 
@@ -320,7 +319,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 	}
 
-	function onBeforeRender( renderer, scene, camera, geometry, material, group ) {
+	function onBeforeRender( renderer, scene, camera, geometry, material ) {
 
 		var originalMaterial = originalMaterials[ material.uuid ];
 
@@ -416,7 +415,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 			if ( cache[ key ].used === false ) {
 
-				cache[ key ].count++;
+				cache[ key ].count ++;
 
 				if ( cache[ key ].keepAlive === false && cache[ key ].count > removeThresholdCount ) {
 

+ 16 - 0
examples/jsm/effects/AnaglyphEffect.d.ts

@@ -0,0 +1,16 @@
+import {
+  Camera,
+  Matrix3,
+  Scene,
+  WebGLRenderer
+} from '../../../src/Three';
+
+export class AnaglyphEffect {
+  constructor(renderer: WebGLRenderer, width?: number, height?: number);
+  colorMatrixLeft: Matrix3;
+  colorMatrixRight: Matrix3;
+
+  dispose(): void;
+  render(scene: Scene, camera: Camera): void;
+  setSize(width: number, height: number): void;
+}

+ 181 - 0
examples/jsm/effects/AnaglyphEffect.js

@@ -0,0 +1,181 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author marklundin / http://mark-lundin.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author tschw
+ */
+
+import {
+	LinearFilter,
+	Matrix3,
+	Mesh,
+	NearestFilter,
+	OrthographicCamera,
+	PlaneBufferGeometry,
+	RGBAFormat,
+	Scene,
+	ShaderMaterial,
+	StereoCamera,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+
+var AnaglyphEffect = function ( renderer, width, height ) {
+
+	// Matrices generated with angler.js https://github.com/tschw/angler.js/
+	// (in column-major element order, as accepted by WebGL)
+
+	this.colorMatrixLeft = new Matrix3().fromArray( [
+
+		1.0671679973602295, - 0.0016435992438346148, 0.0001777536963345483, // r out
+		- 0.028107794001698494, - 0.00019593400065787137, - 0.0002875397040043026, // g out
+		- 0.04279090091586113, 0.000015809757314855233, - 0.00024287120322696865 // b out
+
+	] );
+
+	//		red						green 						blue  						in
+
+	this.colorMatrixRight = new Matrix3().fromArray( [
+
+		- 0.0355340838432312, - 0.06440307199954987, 0.018319187685847282, // r out
+		- 0.10269022732973099, 0.8079727292060852, - 0.04835830628871918, // g out
+		0.0001224992738571018, - 0.009558862075209618, 0.567823588848114 // b out
+
+	] );
+
+	var _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+
+	var _scene = new Scene();
+
+	var _stereo = new StereoCamera();
+
+	var _params = { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat };
+
+	if ( width === undefined ) width = 512;
+	if ( height === undefined ) height = 512;
+
+	var _renderTargetL = new WebGLRenderTarget( width, height, _params );
+	var _renderTargetR = new WebGLRenderTarget( width, height, _params );
+
+	var _material = new ShaderMaterial( {
+
+		uniforms: {
+
+			"mapLeft": { value: _renderTargetL.texture },
+			"mapRight": { value: _renderTargetR.texture },
+
+			"colorMatrixLeft": { value: this.colorMatrixLeft },
+			"colorMatrixRight": { value: this.colorMatrixRight }
+
+		},
+
+		vertexShader: [
+
+			"varying vec2 vUv;",
+
+			"void main() {",
+
+			"	vUv = vec2( uv.x, uv.y );",
+			"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform sampler2D mapLeft;",
+			"uniform sampler2D mapRight;",
+			"varying vec2 vUv;",
+
+			"uniform mat3 colorMatrixLeft;",
+			"uniform mat3 colorMatrixRight;",
+
+			// These functions implement sRGB linearization and gamma correction
+
+			"float lin( float c ) {",
+			"	return c <= 0.04045 ? c * 0.0773993808 :",
+			"			pow( c * 0.9478672986 + 0.0521327014, 2.4 );",
+			"}",
+
+			"vec4 lin( vec4 c ) {",
+			"	return vec4( lin( c.r ), lin( c.g ), lin( c.b ), c.a );",
+			"}",
+
+			"float dev( float c ) {",
+			"	return c <= 0.0031308 ? c * 12.92",
+			"			: pow( c, 0.41666 ) * 1.055 - 0.055;",
+			"}",
+
+
+			"void main() {",
+
+			"	vec2 uv = vUv;",
+
+			"	vec4 colorL = lin( texture2D( mapLeft, uv ) );",
+			"	vec4 colorR = lin( texture2D( mapRight, uv ) );",
+
+			"	vec3 color = clamp(",
+			"			colorMatrixLeft * colorL.rgb +",
+			"			colorMatrixRight * colorR.rgb, 0., 1. );",
+
+			"	gl_FragColor = vec4(",
+			"			dev( color.r ), dev( color.g ), dev( color.b ),",
+			"			max( colorL.a, colorR.a ) );",
+
+			"}"
+
+		].join( "\n" )
+
+	} );
+
+	var _mesh = new Mesh( new PlaneBufferGeometry( 2, 2 ), _material );
+	_scene.add( _mesh );
+
+	this.setSize = function ( width, height ) {
+
+		renderer.setSize( width, height );
+
+		var pixelRatio = renderer.getPixelRatio();
+
+		_renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
+		_renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		var currentRenderTarget = renderer.getRenderTarget();
+
+		scene.updateMatrixWorld();
+
+		if ( camera.parent === null ) camera.updateMatrixWorld();
+
+		_stereo.update( camera );
+
+		renderer.setRenderTarget( _renderTargetL );
+		renderer.clear();
+		renderer.render( scene, _stereo.cameraL );
+
+		renderer.setRenderTarget( _renderTargetR );
+		renderer.clear();
+		renderer.render( scene, _stereo.cameraR );
+
+		renderer.setRenderTarget( null );
+		renderer.render( _scene, _camera );
+
+		renderer.setRenderTarget( currentRenderTarget );
+
+	};
+
+	this.dispose = function () {
+
+		if ( _renderTargetL ) _renderTargetL.dispose();
+		if ( _renderTargetR ) _renderTargetR.dispose();
+		if ( _mesh ) _mesh.geometry.dispose();
+		if ( _material ) _material.dispose();
+
+	};
+
+};
+
+export { AnaglyphEffect };

+ 22 - 0
examples/jsm/effects/AsciiEffect.d.ts

@@ -0,0 +1,22 @@
+import {
+  Camera,
+  Scene,
+  WebGLRenderer
+} from '../../../src/Three';
+
+export interface AsciiEffectOptions {
+  resolution?: number;
+  scale?: number;
+  color?: boolean;
+  alpha?: boolean;
+  block?: boolean;
+  invert?: boolean;
+}
+
+export class AsciiEffect {
+  constructor(renderer: WebGLRenderer, charSet?: string, options?: AsciiEffectOptions);
+  domElement: HTMLElement;
+
+  render(scene: Scene, camera: Camera): void;
+  setSize(width: number, height: number): void;
+}

+ 293 - 0
examples/jsm/effects/AsciiEffect.js

@@ -0,0 +1,293 @@
+/*
+ * @author zz85 / https://github.com/zz85
+ *
+ * Ascii generation is based on http://www.nihilogic.dk/labs/jsascii/
+ * Maybe more about this later with a blog post at http://lab4games.net/zz85/blog
+ *
+ * 16 April 2012 - @blurspline
+ */
+
+
+
+var AsciiEffect = function ( renderer, charSet, options ) {
+
+	// its fun to create one your own!
+
+	charSet = ( charSet === undefined ) ? ' .:-=+*#%@' : charSet;
+
+	// ' .,:;=|iI+hHOE#`$';
+	// darker bolder character set from https://github.com/saw/Canvas-ASCII-Art/
+	// ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'.split('');
+
+	if ( ! options ) options = {};
+
+	// Some ASCII settings
+
+	var bResolution = ! options[ 'resolution' ] ? 0.15 : options[ 'resolution' ]; // Higher for more details
+	var iScale = ! options[ 'scale' ] ? 1 : options[ 'scale' ];
+	var bColor = ! options[ 'color' ] ? false : options[ 'color' ]; // nice but slows down rendering!
+	var bAlpha = ! options[ 'alpha' ] ? false : options[ 'alpha' ]; // Transparency
+	var bBlock = ! options[ 'block' ] ? false : options[ 'block' ]; // blocked characters. like good O dos
+	var bInvert = ! options[ 'invert' ] ? false : options[ 'invert' ]; // black is white, white is black
+
+	var strResolution = 'low';
+
+	var width, height;
+
+	var domElement = document.createElement( 'div' );
+	domElement.style.cursor = 'default';
+
+	var oAscii = document.createElement( "table" );
+	domElement.appendChild( oAscii );
+
+	var iWidth, iHeight;
+	var oImg;
+
+	this.setSize = function ( w, h ) {
+
+		width = w;
+		height = h;
+
+		renderer.setSize( w, h );
+
+		initAsciiSize();
+
+	};
+
+
+	this.render = function ( scene, camera ) {
+
+		renderer.render( scene, camera );
+		asciifyImage( renderer, oAscii );
+
+	};
+
+	this.domElement = domElement;
+
+
+	// Throw in ascii library from http://www.nihilogic.dk/labs/jsascii/jsascii.js
+
+	/*
+	* jsAscii 0.1
+	* Copyright (c) 2008 Jacob Seidelin, [email protected], http://blog.nihilogic.dk/
+	* MIT License [http://www.nihilogic.dk/licenses/mit-license.txt]
+	*/
+
+	function initAsciiSize() {
+
+		iWidth = Math.round( width * fResolution );
+		iHeight = Math.round( height * fResolution );
+
+		oCanvas.width = iWidth;
+		oCanvas.height = iHeight;
+		// oCanvas.style.display = "none";
+		// oCanvas.style.width = iWidth;
+		// oCanvas.style.height = iHeight;
+
+		oImg = renderer.domElement;
+
+		if ( oImg.style.backgroundColor ) {
+
+			oAscii.rows[ 0 ].cells[ 0 ].style.backgroundColor = oImg.style.backgroundColor;
+			oAscii.rows[ 0 ].cells[ 0 ].style.color = oImg.style.color;
+
+		}
+
+		oAscii.cellSpacing = 0;
+		oAscii.cellPadding = 0;
+
+		var oStyle = oAscii.style;
+		oStyle.display = "inline";
+		oStyle.width = Math.round( iWidth / fResolution * iScale ) + "px";
+		oStyle.height = Math.round( iHeight / fResolution * iScale ) + "px";
+		oStyle.whiteSpace = "pre";
+		oStyle.margin = "0px";
+		oStyle.padding = "0px";
+		oStyle.letterSpacing = fLetterSpacing + "px";
+		oStyle.fontFamily = strFont;
+		oStyle.fontSize = fFontSize + "px";
+		oStyle.lineHeight = fLineHeight + "px";
+		oStyle.textAlign = "left";
+		oStyle.textDecoration = "none";
+
+	}
+
+
+	var aDefaultCharList = ( " .,:;i1tfLCG08@" ).split( "" );
+	var aDefaultColorCharList = ( " CGO08@" ).split( "" );
+	var strFont = "courier new, monospace";
+
+	var oCanvasImg = renderer.domElement;
+
+	var oCanvas = document.createElement( "canvas" );
+	if ( ! oCanvas.getContext ) {
+
+		return;
+
+	}
+
+	var oCtx = oCanvas.getContext( "2d" );
+	if ( ! oCtx.getImageData ) {
+
+		return;
+
+	}
+
+	var aCharList = ( bColor ? aDefaultColorCharList : aDefaultCharList );
+
+	if ( charSet ) aCharList = charSet;
+
+	var fResolution = 0.5;
+
+	switch ( strResolution ) {
+
+		case "low" : 	fResolution = 0.25; break;
+		case "medium" : fResolution = 0.5; break;
+		case "high" : 	fResolution = 1; break;
+
+	}
+
+	if ( bResolution ) fResolution = bResolution;
+
+	// Setup dom
+
+	var fFontSize = ( 2 / fResolution ) * iScale;
+	var fLineHeight = ( 2 / fResolution ) * iScale;
+
+	// adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width.
+
+	var fLetterSpacing = 0;
+
+	if ( strResolution == "low" ) {
+
+		switch ( iScale ) {
+
+			case 1 : fLetterSpacing = - 1; break;
+			case 2 :
+			case 3 : fLetterSpacing = - 2.1; break;
+			case 4 : fLetterSpacing = - 3.1; break;
+			case 5 : fLetterSpacing = - 4.15; break;
+
+		}
+
+	}
+
+	if ( strResolution == "medium" ) {
+
+		switch ( iScale ) {
+
+			case 1 : fLetterSpacing = 0; break;
+			case 2 : fLetterSpacing = - 1; break;
+			case 3 : fLetterSpacing = - 1.04; break;
+			case 4 :
+			case 5 : fLetterSpacing = - 2.1; break;
+
+		}
+
+	}
+
+	if ( strResolution == "high" ) {
+
+		switch ( iScale ) {
+
+			case 1 :
+			case 2 : fLetterSpacing = 0; break;
+			case 3 :
+			case 4 :
+			case 5 : fLetterSpacing = - 1; break;
+
+		}
+
+	}
+
+
+	// can't get a span or div to flow like an img element, but a table works?
+
+
+	// convert img element to ascii
+
+	function asciifyImage( canvasRenderer, oAscii ) {
+
+		oCtx.clearRect( 0, 0, iWidth, iHeight );
+		oCtx.drawImage( oCanvasImg, 0, 0, iWidth, iHeight );
+		var oImgData = oCtx.getImageData( 0, 0, iWidth, iHeight ).data;
+
+		// Coloring loop starts now
+		var strChars = "";
+
+		// console.time('rendering');
+
+		for ( var y = 0; y < iHeight; y += 2 ) {
+
+			for ( var x = 0; x < iWidth; x ++ ) {
+
+				var iOffset = ( y * iWidth + x ) * 4;
+
+				var iRed = oImgData[ iOffset ];
+				var iGreen = oImgData[ iOffset + 1 ];
+				var iBlue = oImgData[ iOffset + 2 ];
+				var iAlpha = oImgData[ iOffset + 3 ];
+				var iCharIdx;
+
+				var fBrightness;
+
+				fBrightness = ( 0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue ) / 255;
+				// fBrightness = (0.3*iRed + 0.5*iGreen + 0.3*iBlue) / 255;
+
+				if ( iAlpha == 0 ) {
+
+					// should calculate alpha instead, but quick hack :)
+					//fBrightness *= (iAlpha / 255);
+					fBrightness = 1;
+
+				}
+
+				iCharIdx = Math.floor( ( 1 - fBrightness ) * ( aCharList.length - 1 ) );
+
+				if ( bInvert ) {
+
+					iCharIdx = aCharList.length - iCharIdx - 1;
+
+				}
+
+				// good for debugging
+				//fBrightness = Math.floor(fBrightness * 10);
+				//strThisChar = fBrightness;
+
+				var strThisChar = aCharList[ iCharIdx ];
+
+				if ( strThisChar === undefined || strThisChar == " " )
+					strThisChar = "&nbsp;";
+
+				if ( bColor ) {
+
+					strChars += "<span style='"
+						+ "color:rgb(" + iRed + "," + iGreen + "," + iBlue + ");"
+						+ ( bBlock ? "background-color:rgb(" + iRed + "," + iGreen + "," + iBlue + ");" : "" )
+						+ ( bAlpha ? "opacity:" + ( iAlpha / 255 ) + ";" : "" )
+						+ "'>" + strThisChar + "</span>";
+
+				} else {
+
+					strChars += strThisChar;
+
+				}
+
+			}
+			strChars += "<br/>";
+
+		}
+
+		oAscii.innerHTML = "<tr><td>" + strChars + "</td></tr>";
+
+		// console.timeEnd('rendering');
+
+		// return oAscii;
+
+	}
+
+	// end modified asciifyImage block
+
+};
+
+export { AsciiEffect };

+ 36 - 0
examples/jsm/effects/OutlineEffect.d.ts

@@ -0,0 +1,36 @@
+import {
+  Camera,
+  Scene,
+  Vector2,
+  Vector4,
+  WebGLRenderer,
+  WebGLRenderTarget,
+  WebGLShadowMap
+} from '../../../src/Three';
+
+export interface OutlineEffectParameters {
+  defaultThickness?: number;
+  defaultColor?: number[];
+  defaultAlpha?: number;
+  defaultKeepAlive?: boolean;
+}
+
+export class OutlineEffect {
+  constructor(renderer: WebGLRenderer, parameters: OutlineEffectParameters);
+  enabled: boolean;
+  autoClear: boolean;
+  domElement: HTMLElement;
+  shadowMap: WebGLShadowMap;
+
+  clear(color?: boolean, depth?: boolean, stencil?: boolean): void;
+  getPixelRatio(): number;
+  getSize(target: Vector2): Vector2;
+  render(scene: Scene, camera: Camera): void;
+  renderOutline(scene: Scene, camera: Camera): void;
+  setRenderTarget(renderTarget: WebGLRenderTarget | null): void;
+  setPixelRatio(value: number): void;
+  setScissor(x: Vector4 | number, y?: number, width?: number, height?: number): void;
+  setScissorTest(enable: boolean): void;
+  setSize(width: number, height: number, updateStyle?: boolean): void;
+  setViewport(x: Vector4 | number, y?: number, width?: number, height?: number): void;
+}

+ 586 - 0
examples/jsm/effects/OutlineEffect.js

@@ -0,0 +1,586 @@
+/**
+ * @author takahirox / http://github.com/takahirox/
+ *
+ * Reference: https://en.wikipedia.org/wiki/Cel_shading
+ *
+ * API
+ *
+ * 1. Traditional
+ *
+ * var effect = new OutlineEffect( renderer );
+ *
+ * function render() {
+ *
+ * 	effect.render( scene, camera );
+ *
+ * }
+ *
+ * 2. VR compatible
+ *
+ * var effect = new OutlineEffect( renderer );
+ * var renderingOutline = false;
+ *
+ * scene.onAfterRender = function () {
+ *
+ * 	if ( renderingOutline ) return;
+ *
+ * 	renderingOutline = true;
+ *
+ * 	effect.renderOutline( scene, camera );
+ *
+ * 	renderingOutline = false;
+ *
+ * };
+ *
+ * function render() {
+ *
+ * 	renderer.render( scene, camera );
+ *
+ * }
+ *
+ * // How to set default outline parameters
+ * new OutlineEffect( renderer, {
+ * 	defaultThickness: 0.01,
+ * 	defaultColor: [ 0, 0, 0 ],
+ * 	defaultAlpha: 0.8,
+ * 	defaultKeepAlive: true // keeps outline material in cache even if material is removed from scene
+ * } );
+ *
+ * // How to set outline parameters for each material
+ * material.userData.outlineParameters = {
+ * 	thickness: 0.01,
+ * 	color: [ 0, 0, 0 ]
+ * 	alpha: 0.8,
+ * 	visible: true,
+ * 	keepAlive: true
+ * };
+ *
+ * TODO
+ *  - support shader material without objectNormal in its vertexShader
+ */
+
+import {
+	BackSide,
+	Color,
+	ShaderLib,
+	ShaderMaterial
+} from "../../../build/three.module.js";
+
+var OutlineEffect = function ( renderer, parameters ) {
+
+	parameters = parameters || {};
+
+	this.enabled = true;
+
+	var defaultThickness = parameters.defaultThickness !== undefined ? parameters.defaultThickness : 0.003;
+	var defaultColor = new Color().fromArray( parameters.defaultColor !== undefined ? parameters.defaultColor : [ 0, 0, 0 ] );
+	var defaultAlpha = parameters.defaultAlpha !== undefined ? parameters.defaultAlpha : 1.0;
+	var defaultKeepAlive = parameters.defaultKeepAlive !== undefined ? parameters.defaultKeepAlive : false;
+
+	// object.material.uuid -> outlineMaterial or
+	// object.material[ n ].uuid -> outlineMaterial
+	// save at the outline material creation and release
+	// if it's unused removeThresholdCount frames
+	// unless keepAlive is true.
+	var cache = {};
+
+	var removeThresholdCount = 60;
+
+	// outlineMaterial.uuid -> object.material or
+	// outlineMaterial.uuid -> object.material[ n ]
+	// save before render and release after render.
+	var originalMaterials = {};
+
+	// object.uuid -> originalOnBeforeRender
+	// save before render and release after render.
+	var originalOnBeforeRenders = {};
+
+	//this.cache = cache;  // for debug
+
+	// copied from WebGLPrograms and removed some materials
+	var shaderIDs = {
+		MeshBasicMaterial: 'basic',
+		MeshLambertMaterial: 'lambert',
+		MeshPhongMaterial: 'phong',
+		MeshToonMaterial: 'phong',
+		MeshStandardMaterial: 'physical',
+		MeshPhysicalMaterial: 'physical'
+	};
+
+	var uniformsChunk = {
+		outlineThickness: { value: defaultThickness },
+		outlineColor: { value: defaultColor },
+		outlineAlpha: { value: defaultAlpha }
+	};
+
+	var vertexShaderChunk = [
+
+		"uniform float outlineThickness;",
+
+		"vec4 calculateOutline( vec4 pos, vec3 objectNormal, vec4 skinned ) {",
+
+		"	float thickness = outlineThickness;",
+		"	const float ratio = 1.0;", // TODO: support outline thickness ratio for each vertex
+		"	vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + objectNormal, 1.0 );",
+		// NOTE: subtract pos2 from pos because BackSide objectNormal is negative
+		"	vec4 norm = normalize( pos - pos2 );",
+		"	return pos + norm * thickness * pos.w * ratio;",
+
+		"}"
+
+	].join( "\n" );
+
+	var vertexShaderChunk2 = [
+
+		"#if ! defined( LAMBERT ) && ! defined( PHONG ) && ! defined( TOON ) && ! defined( PHYSICAL )",
+		"	#ifndef USE_ENVMAP",
+		"		vec3 objectNormal = normalize( normal );",
+		"	#endif",
+		"#endif",
+
+		"#ifdef FLIP_SIDED",
+		"	objectNormal = -objectNormal;",
+		"#endif",
+
+		"#ifdef DECLARE_TRANSFORMED",
+		"	vec3 transformed = vec3( position );",
+		"#endif",
+
+		"gl_Position = calculateOutline( gl_Position, objectNormal, vec4( transformed, 1.0 ) );",
+
+		"#include <fog_vertex>"
+
+	].join( "\n" );
+
+	var fragmentShader = [
+
+		"#include <common>",
+		"#include <fog_pars_fragment>",
+
+		"uniform vec3 outlineColor;",
+		"uniform float outlineAlpha;",
+
+		"void main() {",
+
+		"	gl_FragColor = vec4( outlineColor, outlineAlpha );",
+
+		"	#include <fog_fragment>",
+
+		"}"
+
+	].join( "\n" );
+
+	function createInvisibleMaterial() {
+
+		return new ShaderMaterial( { name: 'invisible', visible: false } );
+
+	}
+
+	function createMaterial( originalMaterial ) {
+
+		var shaderID = shaderIDs[ originalMaterial.type ];
+		var originalUniforms, originalVertexShader;
+
+		if ( shaderID !== undefined ) {
+
+			var shader = ShaderLib[ shaderID ];
+			originalUniforms = shader.uniforms;
+			originalVertexShader = shader.vertexShader;
+
+		} else if ( originalMaterial.isRawShaderMaterial === true ) {
+
+			originalUniforms = originalMaterial.uniforms;
+			originalVertexShader = originalMaterial.vertexShader;
+
+			if ( ! /attribute\s+vec3\s+position\s*;/.test( originalVertexShader ) ||
+			     ! /attribute\s+vec3\s+normal\s*;/.test( originalVertexShader ) ) {
+
+				console.warn( 'THREE.OutlineEffect requires both vec3 position and normal attributes in vertex shader, ' +
+				              'does not draw outline for ' + originalMaterial.name + '(uuid:' + originalMaterial.uuid + ') material.' );
+
+				return createInvisibleMaterial();
+
+			}
+
+		} else if ( originalMaterial.isShaderMaterial === true ) {
+
+			originalUniforms = originalMaterial.uniforms;
+			originalVertexShader = originalMaterial.vertexShader;
+
+		} else {
+
+			return createInvisibleMaterial();
+
+		}
+
+		var uniforms = Object.assign( {}, originalUniforms, uniformsChunk );
+
+		var vertexShader = originalVertexShader
+			// put vertexShaderChunk right before "void main() {...}"
+			.replace( /void\s+main\s*\(\s*\)/, vertexShaderChunk + '\nvoid main()' )
+			// put vertexShaderChunk2 the end of "void main() {...}"
+			// Note: here assums originalVertexShader ends with "}" of "void main() {...}"
+			.replace( /\}\s*$/, vertexShaderChunk2 + '\n}' )
+			// remove any light related lines
+			// Note: here is very sensitive to originalVertexShader
+			// TODO: consider safer way
+			.replace( /#include\s+<[\w_]*light[\w_]*>/g, '' );
+
+		var defines = {};
+
+		if ( ! /vec3\s+transformed\s*=/.test( originalVertexShader ) &&
+		     ! /#include\s+<begin_vertex>/.test( originalVertexShader ) ) defines.DECLARE_TRANSFORMED = true;
+
+		return new ShaderMaterial( {
+			defines: defines,
+			uniforms: uniforms,
+			vertexShader: vertexShader,
+			fragmentShader: fragmentShader,
+			side: BackSide,
+			//wireframe: true,
+			skinning: false,
+			morphTargets: false,
+			morphNormals: false,
+			fog: false
+		} );
+
+	}
+
+	function getOutlineMaterialFromCache( originalMaterial ) {
+
+		var data = cache[ originalMaterial.uuid ];
+
+		if ( data === undefined ) {
+
+			data = {
+				material: createMaterial( originalMaterial ),
+				used: true,
+				keepAlive: defaultKeepAlive,
+				count: 0
+			};
+
+			cache[ originalMaterial.uuid ] = data;
+
+		}
+
+		data.used = true;
+
+		return data.material;
+
+	}
+
+	function getOutlineMaterial( originalMaterial ) {
+
+		var outlineMaterial = getOutlineMaterialFromCache( originalMaterial );
+
+		originalMaterials[ outlineMaterial.uuid ] = originalMaterial;
+
+		updateOutlineMaterial( outlineMaterial, originalMaterial );
+
+		return outlineMaterial;
+
+	}
+
+	function setOutlineMaterial( 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 ] = getOutlineMaterial( object.material[ i ] );
+
+			}
+
+		} else {
+
+			object.material = getOutlineMaterial( object.material );
+
+		}
+
+		originalOnBeforeRenders[ object.uuid ] = object.onBeforeRender;
+		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 ] = originalMaterials[ object.material[ i ].uuid ];
+
+			}
+
+		} else {
+
+			object.material = originalMaterials[ object.material.uuid ];
+
+		}
+
+		object.onBeforeRender = originalOnBeforeRenders[ object.uuid ];
+
+	}
+
+	function onBeforeRender( renderer, scene, camera, geometry, material ) {
+
+		var originalMaterial = originalMaterials[ material.uuid ];
+
+		// just in case
+		if ( originalMaterial === undefined ) return;
+
+		updateUniforms( material, originalMaterial );
+
+	}
+
+	function updateUniforms( material, originalMaterial ) {
+
+		var outlineParameters = originalMaterial.userData.outlineParameters;
+
+		material.uniforms.outlineAlpha.value = originalMaterial.opacity;
+
+		if ( outlineParameters !== undefined ) {
+
+			if ( outlineParameters.thickness !== undefined ) material.uniforms.outlineThickness.value = outlineParameters.thickness;
+			if ( outlineParameters.color !== undefined ) material.uniforms.outlineColor.value.fromArray( outlineParameters.color );
+			if ( outlineParameters.alpha !== undefined ) material.uniforms.outlineAlpha.value = outlineParameters.alpha;
+
+		}
+
+	}
+
+	function updateOutlineMaterial( material, originalMaterial ) {
+
+		if ( material.name === 'invisible' ) return;
+
+		var outlineParameters = originalMaterial.userData.outlineParameters;
+
+		material.skinning = originalMaterial.skinning;
+		material.morphTargets = originalMaterial.morphTargets;
+		material.morphNormals = originalMaterial.morphNormals;
+		material.fog = originalMaterial.fog;
+
+		if ( outlineParameters !== undefined ) {
+
+			if ( originalMaterial.visible === false ) {
+
+				material.visible = false;
+
+			} else {
+
+				material.visible = ( outlineParameters.visible !== undefined ) ? outlineParameters.visible : true;
+
+			}
+
+			material.transparent = ( outlineParameters.alpha !== undefined && outlineParameters.alpha < 1.0 ) ? true : originalMaterial.transparent;
+
+			if ( outlineParameters.keepAlive !== undefined ) cache[ originalMaterial.uuid ].keepAlive = outlineParameters.keepAlive;
+
+		} else {
+
+			material.transparent = originalMaterial.transparent;
+			material.visible = originalMaterial.visible;
+
+		}
+
+		if ( originalMaterial.wireframe === true || originalMaterial.depthTest === false ) material.visible = false;
+
+	}
+
+	function cleanupCache() {
+
+		var keys;
+
+		// clear originialMaterials
+		keys = Object.keys( originalMaterials );
+
+		for ( var i = 0, il = keys.length; i < il; i ++ ) {
+
+			originalMaterials[ keys[ i ] ] = undefined;
+
+		}
+
+		// clear originalOnBeforeRenders
+		keys = Object.keys( originalOnBeforeRenders );
+
+		for ( var i = 0, il = keys.length; i < il; i ++ ) {
+
+			originalOnBeforeRenders[ keys[ i ] ] = undefined;
+
+		}
+
+		// remove unused outlineMaterial from cache
+		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;
+
+			}
+
+		}
+
+	}
+
+	this.render = function ( scene, camera ) {
+
+		var renderTarget;
+		var forceClear = false;
+
+		if ( arguments[ 2 ] !== undefined ) {
+
+			console.warn( 'THREE.OutlineEffect.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' );
+			renderTarget = arguments[ 2 ];
+
+		}
+
+		if ( arguments[ 3 ] !== undefined ) {
+
+			console.warn( 'THREE.OutlineEffect.render(): the forceClear argument has been removed. Use .clear() instead.' );
+			forceClear = arguments[ 3 ];
+
+		}
+
+		if ( renderTarget !== undefined ) renderer.setRenderTarget( renderTarget );
+
+		if ( forceClear ) renderer.clear();
+
+		if ( this.enabled === false ) {
+
+			renderer.render( scene, camera );
+			return;
+
+		}
+
+		var currentAutoClear = renderer.autoClear;
+		renderer.autoClear = this.autoClear;
+
+		renderer.render( scene, camera );
+
+		renderer.autoClear = currentAutoClear;
+
+		this.renderOutline( scene, camera );
+
+	};
+
+	this.renderOutline = function ( scene, camera ) {
+
+		var currentAutoClear = renderer.autoClear;
+		var currentSceneAutoUpdate = scene.autoUpdate;
+		var currentSceneBackground = scene.background;
+		var currentShadowMapEnabled = renderer.shadowMap.enabled;
+
+		scene.autoUpdate = false;
+		scene.background = null;
+		renderer.autoClear = false;
+		renderer.shadowMap.enabled = false;
+
+		scene.traverse( setOutlineMaterial );
+
+		renderer.render( scene, camera );
+
+		scene.traverse( restoreOriginalMaterial );
+
+		cleanupCache();
+
+		scene.autoUpdate = currentSceneAutoUpdate;
+		scene.background = currentSceneBackground;
+		renderer.autoClear = currentAutoClear;
+		renderer.shadowMap.enabled = currentShadowMapEnabled;
+
+	};
+
+	/*
+	 * See #9918
+	 *
+	 * The following property copies and wrapper methods enable
+	 * OutlineEffect to be called from other *Effect, like
+	 *
+	 * effect = new StereoEffect( new OutlineEffect( renderer ) );
+	 *
+	 * function render () {
+	 *
+ 	 * 	effect.render( scene, camera );
+	 *
+	 * }
+	 */
+	this.autoClear = renderer.autoClear;
+	this.domElement = renderer.domElement;
+	this.shadowMap = renderer.shadowMap;
+
+	this.clear = function ( color, depth, stencil ) {
+
+		renderer.clear( color, depth, stencil );
+
+	};
+
+	this.getPixelRatio = function () {
+
+		return renderer.getPixelRatio();
+
+	};
+
+	this.setPixelRatio = function ( value ) {
+
+		renderer.setPixelRatio( value );
+
+	};
+
+	this.getSize = function ( target ) {
+
+		return renderer.getSize( target );
+
+	};
+
+	this.setSize = function ( width, height, updateStyle ) {
+
+		renderer.setSize( width, height, updateStyle );
+
+	};
+
+	this.setViewport = function ( x, y, width, height ) {
+
+		renderer.setViewport( x, y, width, height );
+
+	};
+
+	this.setScissor = function ( x, y, width, height ) {
+
+		renderer.setScissor( x, y, width, height );
+
+	};
+
+	this.setScissorTest = function ( boolean ) {
+
+		renderer.setScissorTest( boolean );
+
+	};
+
+	this.setRenderTarget = function ( renderTarget ) {
+
+		renderer.setRenderTarget( renderTarget );
+
+	};
+
+};
+
+export { OutlineEffect };

+ 12 - 0
examples/jsm/effects/ParallaxBarrierEffect.d.ts

@@ -0,0 +1,12 @@
+import {
+  Camera,
+  Scene,
+  WebGLRenderer
+} from '../../../src/Three';
+
+export class ParallaxBarrierEffect {
+  constructor(renderer: WebGLRenderer);
+
+  render(scene: Scene, camera: Camera): void;
+  setSize(width: number, height: number): void;
+}

+ 118 - 0
examples/jsm/effects/ParallaxBarrierEffect.js

@@ -0,0 +1,118 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author marklundin / http://mark-lundin.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+import {
+	LinearFilter,
+	Mesh,
+	NearestFilter,
+	OrthographicCamera,
+	PlaneBufferGeometry,
+	RGBAFormat,
+	Scene,
+	ShaderMaterial,
+	StereoCamera,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+
+var ParallaxBarrierEffect = function ( renderer ) {
+
+	var _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+
+	var _scene = new Scene();
+
+	var _stereo = new StereoCamera();
+
+	var _params = { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat };
+
+	var _renderTargetL = new WebGLRenderTarget( 512, 512, _params );
+	var _renderTargetR = new WebGLRenderTarget( 512, 512, _params );
+
+	var _material = new ShaderMaterial( {
+
+		uniforms: {
+
+			"mapLeft": { value: _renderTargetL.texture },
+			"mapRight": { value: _renderTargetR.texture }
+
+		},
+
+		vertexShader: [
+
+			"varying vec2 vUv;",
+
+			"void main() {",
+
+			"	vUv = vec2( uv.x, uv.y );",
+			"	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"uniform sampler2D mapLeft;",
+			"uniform sampler2D mapRight;",
+			"varying vec2 vUv;",
+
+			"void main() {",
+
+			"	vec2 uv = vUv;",
+
+			"	if ( ( mod( gl_FragCoord.y, 2.0 ) ) > 1.00 ) {",
+
+			"		gl_FragColor = texture2D( mapLeft, uv );",
+
+			"	} else {",
+
+			"		gl_FragColor = texture2D( mapRight, uv );",
+
+			"	}",
+
+			"}"
+
+		].join( "\n" )
+
+	} );
+
+	var mesh = new Mesh( new PlaneBufferGeometry( 2, 2 ), _material );
+	_scene.add( mesh );
+
+	this.setSize = function ( width, height ) {
+
+		renderer.setSize( width, height );
+
+		var pixelRatio = renderer.getPixelRatio();
+
+		_renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
+		_renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		scene.updateMatrixWorld();
+
+		if ( camera.parent === null ) camera.updateMatrixWorld();
+
+		_stereo.update( camera );
+
+		renderer.setRenderTarget( _renderTargetL );
+		renderer.clear();
+		renderer.render( scene, _stereo.cameraL );
+
+		renderer.setRenderTarget( _renderTargetR );
+		renderer.clear();
+		renderer.render( scene, _stereo.cameraR );
+
+		renderer.setRenderTarget( null );
+		renderer.render( _scene, _camera );
+
+	};
+
+};
+
+export { ParallaxBarrierEffect };

+ 14 - 0
examples/jsm/effects/PeppersGhostEffect.d.ts

@@ -0,0 +1,14 @@
+import {
+  Camera,
+  Scene,
+  WebGLRenderer
+} from '../../../src/Three';
+
+export class PeppersGhostEffect {
+  constructor(renderer: WebGLRenderer);
+  cameraDistance: number;
+  reflectFromAbove: boolean;
+
+  render(scene: Scene, camera: Camera): void;
+  setSize(width: number, height: number): void;
+}

+ 151 - 0
examples/jsm/effects/PeppersGhostEffect.js

@@ -0,0 +1,151 @@
+/**
+ * Created by tpowellmeto on 29/10/2015.
+ *
+ * peppers ghost effect based on http://www.instructables.com/id/Reflective-Prism/?ALLSTEPS
+ */
+
+import {
+	PerspectiveCamera,
+	Quaternion,
+	Vector3
+} from "../../../build/three.module.js";
+
+var PeppersGhostEffect = function ( renderer ) {
+
+	var scope = this;
+
+	scope.cameraDistance = 15;
+	scope.reflectFromAbove = false;
+
+	// Internals
+	var _halfWidth, _width, _height;
+
+	var _cameraF = new PerspectiveCamera(); //front
+	var _cameraB = new PerspectiveCamera(); //back
+	var _cameraL = new PerspectiveCamera(); //left
+	var _cameraR = new PerspectiveCamera(); //right
+
+	var _position = new Vector3();
+	var _quaternion = new Quaternion();
+	var _scale = new Vector3();
+
+	// Initialization
+	renderer.autoClear = false;
+
+	this.setSize = function ( width, height ) {
+
+		_halfWidth = width / 2;
+		if ( width < height ) {
+
+			_width = width / 3;
+			_height = width / 3;
+
+		} else {
+
+			_width = height / 3;
+			_height = height / 3;
+
+		}
+		renderer.setSize( width, height );
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		scene.updateMatrixWorld();
+
+		if ( camera.parent === null ) camera.updateMatrixWorld();
+
+		camera.matrixWorld.decompose( _position, _quaternion, _scale );
+
+		// front
+		_cameraF.position.copy( _position );
+		_cameraF.quaternion.copy( _quaternion );
+		_cameraF.translateZ( scope.cameraDistance );
+		_cameraF.lookAt( scene.position );
+
+		// back
+		_cameraB.position.copy( _position );
+		_cameraB.quaternion.copy( _quaternion );
+		_cameraB.translateZ( - ( scope.cameraDistance ) );
+		_cameraB.lookAt( scene.position );
+		_cameraB.rotation.z += 180 * ( Math.PI / 180 );
+
+		// left
+		_cameraL.position.copy( _position );
+		_cameraL.quaternion.copy( _quaternion );
+		_cameraL.translateX( - ( scope.cameraDistance ) );
+		_cameraL.lookAt( scene.position );
+		_cameraL.rotation.x += 90 * ( Math.PI / 180 );
+
+		// right
+		_cameraR.position.copy( _position );
+		_cameraR.quaternion.copy( _quaternion );
+		_cameraR.translateX( scope.cameraDistance );
+		_cameraR.lookAt( scene.position );
+		_cameraR.rotation.x += 90 * ( Math.PI / 180 );
+
+
+		renderer.clear();
+		renderer.setScissorTest( true );
+
+		renderer.setScissor( _halfWidth - ( _width / 2 ), ( _height * 2 ), _width, _height );
+		renderer.setViewport( _halfWidth - ( _width / 2 ), ( _height * 2 ), _width, _height );
+
+		if ( scope.reflectFromAbove ) {
+
+			renderer.render( scene, _cameraB );
+
+		} else {
+
+			renderer.render( scene, _cameraF );
+
+		}
+
+		renderer.setScissor( _halfWidth - ( _width / 2 ), 0, _width, _height );
+		renderer.setViewport( _halfWidth - ( _width / 2 ), 0, _width, _height );
+
+		if ( scope.reflectFromAbove ) {
+
+			renderer.render( scene, _cameraF );
+
+		} else {
+
+			renderer.render( scene, _cameraB );
+
+		}
+
+		renderer.setScissor( _halfWidth - ( _width / 2 ) - _width, _height, _width, _height );
+		renderer.setViewport( _halfWidth - ( _width / 2 ) - _width, _height, _width, _height );
+
+		if ( scope.reflectFromAbove ) {
+
+			renderer.render( scene, _cameraR );
+
+		} else {
+
+			renderer.render( scene, _cameraL );
+
+		}
+
+		renderer.setScissor( _halfWidth + ( _width / 2 ), _height, _width, _height );
+		renderer.setViewport( _halfWidth + ( _width / 2 ), _height, _width, _height );
+
+		if ( scope.reflectFromAbove ) {
+
+			renderer.render( scene, _cameraL );
+
+		} else {
+
+			renderer.render( scene, _cameraR );
+
+		}
+
+		renderer.setScissorTest( false );
+
+	};
+
+
+};
+
+export { PeppersGhostEffect };

+ 13 - 0
examples/jsm/effects/StereoEffect.d.ts

@@ -0,0 +1,13 @@
+import {
+  Camera,
+  Scene,
+  WebGLRenderer
+} from '../../../src/Three';
+
+export class StereoEffect {
+  constructor(renderer: WebGLRenderer);
+
+  setEyeSeparation(eyeSep: number): void;
+  render(scene: Scene, camera: Camera): void;
+  setSize(width: number, height: number): void;
+}

+ 58 - 0
examples/jsm/effects/StereoEffect.js

@@ -0,0 +1,58 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @authod mrdoob / http://mrdoob.com/
+ * @authod arodic / http://aleksandarrodic.com/
+ * @authod fonserbc / http://fonserbc.github.io/
+*/
+
+import {
+	StereoCamera,
+	Vector2
+} from "../../../build/three.module.js";
+
+var StereoEffect = function ( renderer ) {
+
+	var _stereo = new StereoCamera();
+	_stereo.aspect = 0.5;
+	var size = new Vector2();
+
+	this.setEyeSeparation = function ( eyeSep ) {
+
+		_stereo.eyeSep = eyeSep;
+
+	};
+
+	this.setSize = function ( width, height ) {
+
+		renderer.setSize( width, height );
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		scene.updateMatrixWorld();
+
+		if ( camera.parent === null ) camera.updateMatrixWorld();
+
+		_stereo.update( camera );
+
+		renderer.getSize( size );
+
+		if ( renderer.autoClear ) renderer.clear();
+		renderer.setScissorTest( true );
+
+		renderer.setScissor( 0, 0, size.width / 2, size.height );
+		renderer.setViewport( 0, 0, size.width / 2, size.height );
+		renderer.render( scene, _stereo.cameraL );
+
+		renderer.setScissor( size.width / 2, 0, size.width / 2, size.height );
+		renderer.setViewport( size.width / 2, 0, size.width / 2, size.height );
+		renderer.render( scene, _stereo.cameraR );
+
+		renderer.setScissorTest( false );
+
+	};
+
+};
+
+export { StereoEffect };

+ 7 - 0
utils/modularize.js

@@ -28,6 +28,13 @@ var files = [
 	{ path: 'curves/NURBSSurface.js', dependencies: [ { name: 'NURBSUtils', path: 'curves/NURBSUtils.js' } ], ignoreList: [] },
 	{ path: 'curves/NURBSUtils.js', dependencies: [], ignoreList: [] },
 
+	{ path: 'effects/AnaglyphEffect.js', dependencies: [], ignoreList: [] },
+	{ path: 'effects/AsciiEffect.js', dependencies: [], ignoreList: [] },
+	{ path: 'effects/OutlineEffect.js', dependencies: [], ignoreList: [] },
+	{ path: 'effects/ParallaxBarrierEffect.js', dependencies: [], ignoreList: [] },
+	{ path: 'effects/PeppersGhostEffect.js', dependencies: [], ignoreList: [] },
+	{ path: 'effects/StereoEffect.js', dependencies: [], ignoreList: [] },
+
 	{ path: 'exporters/GLTFExporter.js', dependencies: [], ignoreList: [ 'AnimationClip', 'Camera', 'Geometry', 'Material', 'Mesh', 'Object3D', 'RGBFormat', 'Scenes', 'ShaderMaterial', 'VertexColors' ] },
 	{ path: 'exporters/MMDExporter.js', dependencies: [], ignoreList: [] },
 	{ path: 'exporters/OBJExporter.js', dependencies: [], ignoreList: [] },