소스 검색

Merge pull request #16537 from Mugen87/dev30

JSM: More modules and TS files for FX-passes.
Michael Herzog 6 년 전
부모
커밋
7bf34a8121

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

@@ -170,10 +170,16 @@
 						<li>GlitchPass</li>
 						<li>HalftonePass</li>
 						<li>MaskPass</li>
+						<li>OutlinePass</li>
 						<li>RenderPass</li>
+						<li>SAOPass</li>
 						<li>SavePass</li>
 						<li>ShaderPass</li>
+						<li>SMAAPass</li>
+						<li>SSAARenderPass</li>
+						<li>TAARenderPass</li>
 						<li>TexturePass</li>
+						<li>UnrealBloomPass</li>
 					</ul>
 				</li>
 				<li>renderers
@@ -223,6 +229,7 @@
 						<li>RGBShiftShader</li>
 						<li>SAOShader</li>
 						<li>SepiaShader</li>
+						<li>SMAAShader</li>
 						<li>SobelOperatorShader</li>
 						<li>SSAOShader</li>
 						<li>TechnicolorShader</li>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 19 - 14
examples/js/postprocessing/SMAAPass.js


+ 2 - 2
examples/js/postprocessing/TAARenderPass.js

@@ -12,7 +12,7 @@
  *
  */
 
-THREE.TAARenderPass = function ( scene, camera, params ) {
+THREE.TAARenderPass = function ( scene, camera, clearColor, clearAlpha ) {
 
 	if ( THREE.SSAARenderPass === undefined ) {
 
@@ -20,7 +20,7 @@ THREE.TAARenderPass = function ( scene, camera, params ) {
 
 	}
 
-	THREE.SSAARenderPass.call( this, scene, camera, params );
+	THREE.SSAARenderPass.call( this, scene, camera, clearColor, clearAlpha );
 
 	this.sampleLevel = 0;
 	this.accumulate = false;

+ 2 - 1
examples/js/postprocessing/UnrealBloomPass.js

@@ -4,6 +4,7 @@
  * Inspired from Unreal Engine
  * https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
  */
+
 THREE.UnrealBloomPass = function ( resolution, strength, radius, threshold ) {
 
 	THREE.Pass.call( this );
@@ -106,7 +107,7 @@ THREE.UnrealBloomPass = function ( resolution, strength, radius, threshold ) {
 	// copy material
 	if ( THREE.CopyShader === undefined ) {
 
-		console.error( "THREE.BloomPass relies on THREE.CopyShader" );
+		console.error( "THREE.UnrealBloomPass relies on THREE.CopyShader" );
 
 	}
 

+ 27 - 23
examples/js/shaders/SMAAShader.js

@@ -6,7 +6,7 @@
  * https://github.com/iryoku/smaa/releases/tag/v2.8
  */
 
-THREE.SMAAShader = [ {
+THREE.SMAAEdgesShader = {
 
 	defines: {
 
@@ -16,8 +16,8 @@ THREE.SMAAShader = [ {
 
 	uniforms: {
 
-		"tDiffuse":		{ value: null },
-		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
+		"tDiffuse": { value: null },
+		"resolution": { value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
 
 	},
 
@@ -44,7 +44,7 @@ THREE.SMAAShader = [ {
 
 		"}"
 
-	].join("\n"),
+	].join( "\n" ),
 
 	fragmentShader: [
 
@@ -111,25 +111,27 @@ THREE.SMAAShader = [ {
 
 		"}"
 
-	].join("\n")
+	].join( "\n" )
 
-}, {
+};
+
+THREE.SMAAWeightsShader = {
 
 	defines: {
 
-		"SMAA_MAX_SEARCH_STEPS":		"8",
-		"SMAA_AREATEX_MAX_DISTANCE":	"16",
-		"SMAA_AREATEX_PIXEL_SIZE":		"( 1.0 / vec2( 160.0, 560.0 ) )",
-		"SMAA_AREATEX_SUBTEX_SIZE":		"( 1.0 / 7.0 )"
+		"SMAA_MAX_SEARCH_STEPS": "8",
+		"SMAA_AREATEX_MAX_DISTANCE": "16",
+		"SMAA_AREATEX_PIXEL_SIZE": "( 1.0 / vec2( 160.0, 560.0 ) )",
+		"SMAA_AREATEX_SUBTEX_SIZE": "( 1.0 / 7.0 )"
 
 	},
 
 	uniforms: {
 
-		"tDiffuse":		{ value: null },
-		"tArea":		{ value: null },
-		"tSearch":		{ value: null },
-		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
+		"tDiffuse": { value: null },
+		"tArea": { value: null },
+		"tSearch": { value: null },
+		"resolution": { value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
 
 	},
 
@@ -163,7 +165,7 @@ THREE.SMAAShader = [ {
 
 		"}"
 
-	].join("\n"),
+	].join( "\n" ),
 
 	fragmentShader: [
 
@@ -365,15 +367,17 @@ THREE.SMAAShader = [ {
 
 		"}"
 
-	].join("\n")
+	].join( "\n" )
+
+};
 
-}, {
+THREE.SMAABlendShader = {
 
 	uniforms: {
 
-		"tDiffuse":		{ value: null },
-		"tColor":		{ value: null },
-		"resolution":	{ value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
+		"tDiffuse": { value: null },
+		"tColor": { value: null },
+		"resolution": { value: new THREE.Vector2( 1 / 1024, 1 / 512 ) }
 
 	},
 
@@ -399,7 +403,7 @@ THREE.SMAAShader = [ {
 
 		"}"
 
-	].join("\n"),
+	].join( "\n" ),
 
 	fragmentShader: [
 
@@ -457,6 +461,6 @@ THREE.SMAAShader = [ {
 
 		"}"
 
-	].join("\n")
+	].join( "\n" )
 
-} ];
+};

+ 62 - 0
examples/jsm/postprocessing/OutlinePass.d.ts

@@ -0,0 +1,62 @@
+import {
+  Vector2,
+  Scene,
+  Camera,
+  Object3D,
+  Color,
+  Matrix4,
+  MeshBasicMaterial,
+  MeshDepthMaterial,
+  ShaderMaterial,
+  WebGLRenderTarget
+} from '../../../src/Three';
+
+import { Pass } from './Pass';
+
+export class OutlinePass extends Pass {
+  constructor(resolution: Vector2, scene: Scene, camera: Camera, selectedObjects?: Object3D[]);
+  renderScene: Scene;
+  renderCamera: Camera;
+  selectedObjects: Object3D[];
+  visibleEdgeColor: Color;
+  hiddenEdgeColor: Color;
+  edgeGlow: number;
+  usePatternTexture: boolean;
+  edgeThickness: number;
+  edgeStrength: number;
+  downSampleRatio: number;
+  pulsePeriod: number;
+  resolution: Vector2;
+
+  maskBufferMaterial: MeshBasicMaterial;
+  renderTargetMaskBuffer: WebGLRenderTarget;
+  depthMaterial: MeshDepthMaterial;
+  prepareMaskMaterial: ShaderMaterial;
+  renderTargetDepthBuffer: WebGLRenderTarget;
+  renderTargetMaskDownSampleBuffer: WebGLRenderTarget;
+  renderTargetBlurBuffer1: WebGLRenderTarget;
+  renderTargetBlurBuffer2: WebGLRenderTarget;
+  edgeDetectionMaterial: ShaderMaterial;
+  renderTargetEdgeBuffer1: WebGLRenderTarget;
+  renderTargetEdgeBuffer2: WebGLRenderTarget;
+  separableBlurMaterial1: ShaderMaterial;
+  separableBlurMaterial2: ShaderMaterial;
+  overlayMaterial: ShaderMaterial;
+  copyUniforms: object;
+  materialCopy: ShaderMaterial;
+  oldClearColor: Color;
+  oldClearAlpha: number;
+  fsQuad: object;
+  tempPulseColor1: Color;
+  tempPulseColor2: Color;
+  textureMatrix: Matrix4;
+
+  dispose(): void;
+  changeVisibilityOfSelectedObjects(bVisible: boolean): void;
+  changeVisibilityOfNonSelectedObjects(bVisible: boolean): void;
+  updateTextureMatrix(): void;
+  getPrepareMaskMaterial(): ShaderMaterial;
+  getEdgeDetectionMaterial(): ShaderMaterial;
+  getSeperableBlurMaterial(): ShaderMaterial;
+  getOverlayMaterial(): ShaderMaterial;
+}

+ 606 - 0
examples/jsm/postprocessing/OutlinePass.js

@@ -0,0 +1,606 @@
+/**
+ * @author spidersharma / http://eduperiment.com/
+ */
+
+import {
+	AdditiveBlending,
+	Color,
+	DoubleSide,
+	LinearFilter,
+	Matrix4,
+	MeshBasicMaterial,
+	MeshDepthMaterial,
+	NoBlending,
+	RGBADepthPacking,
+	RGBAFormat,
+	ShaderMaterial,
+	UniformsUtils,
+	Vector2,
+	Vector3,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+import { Pass } from "../postprocessing/Pass.js";
+import { CopyShader } from "../shaders/CopyShader.js";
+
+var OutlinePass = function ( resolution, scene, camera, selectedObjects ) {
+
+	this.renderScene = scene;
+	this.renderCamera = camera;
+	this.selectedObjects = selectedObjects !== undefined ? selectedObjects : [];
+	this.visibleEdgeColor = new Color( 1, 1, 1 );
+	this.hiddenEdgeColor = new Color( 0.1, 0.04, 0.02 );
+	this.edgeGlow = 0.0;
+	this.usePatternTexture = false;
+	this.edgeThickness = 1.0;
+	this.edgeStrength = 3.0;
+	this.downSampleRatio = 2;
+	this.pulsePeriod = 0;
+
+	Pass.call( this );
+
+	this.resolution = ( resolution !== undefined ) ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 );
+
+	var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat };
+
+	var resx = Math.round( this.resolution.x / this.downSampleRatio );
+	var resy = Math.round( this.resolution.y / this.downSampleRatio );
+
+	this.maskBufferMaterial = new MeshBasicMaterial( { color: 0xffffff } );
+	this.maskBufferMaterial.side = DoubleSide;
+	this.renderTargetMaskBuffer = new WebGLRenderTarget( this.resolution.x, this.resolution.y, pars );
+	this.renderTargetMaskBuffer.texture.name = "OutlinePass.mask";
+	this.renderTargetMaskBuffer.texture.generateMipmaps = false;
+
+	this.depthMaterial = new MeshDepthMaterial();
+	this.depthMaterial.side = DoubleSide;
+	this.depthMaterial.depthPacking = RGBADepthPacking;
+	this.depthMaterial.blending = NoBlending;
+
+	this.prepareMaskMaterial = this.getPrepareMaskMaterial();
+	this.prepareMaskMaterial.side = DoubleSide;
+	this.prepareMaskMaterial.fragmentShader = replaceDepthToViewZ( this.prepareMaskMaterial.fragmentShader, this.renderCamera );
+
+	this.renderTargetDepthBuffer = new WebGLRenderTarget( this.resolution.x, this.resolution.y, pars );
+	this.renderTargetDepthBuffer.texture.name = "OutlinePass.depth";
+	this.renderTargetDepthBuffer.texture.generateMipmaps = false;
+
+	this.renderTargetMaskDownSampleBuffer = new WebGLRenderTarget( resx, resy, pars );
+	this.renderTargetMaskDownSampleBuffer.texture.name = "OutlinePass.depthDownSample";
+	this.renderTargetMaskDownSampleBuffer.texture.generateMipmaps = false;
+
+	this.renderTargetBlurBuffer1 = new WebGLRenderTarget( resx, resy, pars );
+	this.renderTargetBlurBuffer1.texture.name = "OutlinePass.blur1";
+	this.renderTargetBlurBuffer1.texture.generateMipmaps = false;
+	this.renderTargetBlurBuffer2 = new WebGLRenderTarget( Math.round( resx / 2 ), Math.round( resy / 2 ), pars );
+	this.renderTargetBlurBuffer2.texture.name = "OutlinePass.blur2";
+	this.renderTargetBlurBuffer2.texture.generateMipmaps = false;
+
+	this.edgeDetectionMaterial = this.getEdgeDetectionMaterial();
+	this.renderTargetEdgeBuffer1 = new WebGLRenderTarget( resx, resy, pars );
+	this.renderTargetEdgeBuffer1.texture.name = "OutlinePass.edge1";
+	this.renderTargetEdgeBuffer1.texture.generateMipmaps = false;
+	this.renderTargetEdgeBuffer2 = new WebGLRenderTarget( Math.round( resx / 2 ), Math.round( resy / 2 ), pars );
+	this.renderTargetEdgeBuffer2.texture.name = "OutlinePass.edge2";
+	this.renderTargetEdgeBuffer2.texture.generateMipmaps = false;
+
+	var MAX_EDGE_THICKNESS = 4;
+	var MAX_EDGE_GLOW = 4;
+
+	this.separableBlurMaterial1 = this.getSeperableBlurMaterial( MAX_EDGE_THICKNESS );
+	this.separableBlurMaterial1.uniforms[ "texSize" ].value = new Vector2( resx, resy );
+	this.separableBlurMaterial1.uniforms[ "kernelRadius" ].value = 1;
+	this.separableBlurMaterial2 = this.getSeperableBlurMaterial( MAX_EDGE_GLOW );
+	this.separableBlurMaterial2.uniforms[ "texSize" ].value = new Vector2( Math.round( resx / 2 ), Math.round( resy / 2 ) );
+	this.separableBlurMaterial2.uniforms[ "kernelRadius" ].value = MAX_EDGE_GLOW;
+
+	// Overlay material
+	this.overlayMaterial = this.getOverlayMaterial();
+
+	// copy material
+	if ( CopyShader === undefined )
+		console.error( "OutlinePass relies on CopyShader" );
+
+	var copyShader = CopyShader;
+
+	this.copyUniforms = UniformsUtils.clone( copyShader.uniforms );
+	this.copyUniforms[ "opacity" ].value = 1.0;
+
+	this.materialCopy = new ShaderMaterial( {
+		uniforms: this.copyUniforms,
+		vertexShader: copyShader.vertexShader,
+		fragmentShader: copyShader.fragmentShader,
+		blending: NoBlending,
+		depthTest: false,
+		depthWrite: false,
+		transparent: true
+	} );
+
+	this.enabled = true;
+	this.needsSwap = false;
+
+	this.oldClearColor = new Color();
+	this.oldClearAlpha = 1;
+
+	this.fsQuad = new Pass.FullScreenQuad( null );
+
+	this.tempPulseColor1 = new Color();
+	this.tempPulseColor2 = new Color();
+	this.textureMatrix = new Matrix4();
+
+	function replaceDepthToViewZ( string, camera ) {
+
+		var type = camera.isPerspectiveCamera ? 'perspective' : 'orthographic';
+
+		return string.replace( /DEPTH_TO_VIEW_Z/g, type + 'DepthToViewZ' );
+
+	}
+
+};
+
+OutlinePass.prototype = Object.assign( Object.create( Pass.prototype ), {
+
+	constructor: OutlinePass,
+
+	dispose: function () {
+
+		this.renderTargetMaskBuffer.dispose();
+		this.renderTargetDepthBuffer.dispose();
+		this.renderTargetMaskDownSampleBuffer.dispose();
+		this.renderTargetBlurBuffer1.dispose();
+		this.renderTargetBlurBuffer2.dispose();
+		this.renderTargetEdgeBuffer1.dispose();
+		this.renderTargetEdgeBuffer2.dispose();
+
+	},
+
+	setSize: function ( width, height ) {
+
+		this.renderTargetMaskBuffer.setSize( width, height );
+
+		var resx = Math.round( width / this.downSampleRatio );
+		var resy = Math.round( height / this.downSampleRatio );
+		this.renderTargetMaskDownSampleBuffer.setSize( resx, resy );
+		this.renderTargetBlurBuffer1.setSize( resx, resy );
+		this.renderTargetEdgeBuffer1.setSize( resx, resy );
+		this.separableBlurMaterial1.uniforms[ "texSize" ].value = new Vector2( resx, resy );
+
+		resx = Math.round( resx / 2 );
+		resy = Math.round( resy / 2 );
+
+		this.renderTargetBlurBuffer2.setSize( resx, resy );
+		this.renderTargetEdgeBuffer2.setSize( resx, resy );
+
+		this.separableBlurMaterial2.uniforms[ "texSize" ].value = new Vector2( resx, resy );
+
+	},
+
+	changeVisibilityOfSelectedObjects: function ( bVisible ) {
+
+		function gatherSelectedMeshesCallBack( object ) {
+
+			if ( object.isMesh ) {
+
+				if ( bVisible ) {
+
+					object.visible = object.userData.oldVisible;
+					delete object.userData.oldVisible;
+
+				} else {
+
+					object.userData.oldVisible = object.visible;
+					object.visible = bVisible;
+
+				}
+
+			}
+
+		}
+
+		for ( var i = 0; i < this.selectedObjects.length; i ++ ) {
+
+			var selectedObject = this.selectedObjects[ i ];
+			selectedObject.traverse( gatherSelectedMeshesCallBack );
+
+		}
+
+	},
+
+	changeVisibilityOfNonSelectedObjects: function ( bVisible ) {
+
+		var selectedMeshes = [];
+
+		function gatherSelectedMeshesCallBack( object ) {
+
+			if ( object.isMesh ) selectedMeshes.push( object );
+
+		}
+
+		for ( var i = 0; i < this.selectedObjects.length; i ++ ) {
+
+			var selectedObject = this.selectedObjects[ i ];
+			selectedObject.traverse( gatherSelectedMeshesCallBack );
+
+		}
+
+		function VisibilityChangeCallBack( object ) {
+
+			if ( object.isMesh || object.isLine || object.isSprite ) {
+
+				var bFound = false;
+
+				for ( var i = 0; i < selectedMeshes.length; i ++ ) {
+
+					var selectedObjectId = selectedMeshes[ i ].id;
+
+					if ( selectedObjectId === object.id ) {
+
+						bFound = true;
+						break;
+
+					}
+
+				}
+
+				if ( ! bFound ) {
+
+					var visibility = object.visible;
+
+					if ( ! bVisible || object.bVisible ) object.visible = bVisible;
+
+					object.bVisible = visibility;
+
+				}
+
+			}
+
+		}
+
+		this.renderScene.traverse( VisibilityChangeCallBack );
+
+	},
+
+	updateTextureMatrix: function () {
+
+		this.textureMatrix.set( 0.5, 0.0, 0.0, 0.5,
+			0.0, 0.5, 0.0, 0.5,
+			0.0, 0.0, 0.5, 0.5,
+			0.0, 0.0, 0.0, 1.0 );
+		this.textureMatrix.multiply( this.renderCamera.projectionMatrix );
+		this.textureMatrix.multiply( this.renderCamera.matrixWorldInverse );
+
+	},
+
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
+
+		if ( this.selectedObjects.length > 0 ) {
+
+			this.oldClearColor.copy( renderer.getClearColor() );
+			this.oldClearAlpha = renderer.getClearAlpha();
+			var oldAutoClear = renderer.autoClear;
+
+			renderer.autoClear = false;
+
+			if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST );
+
+			renderer.setClearColor( 0xffffff, 1 );
+
+			// Make selected objects invisible
+			this.changeVisibilityOfSelectedObjects( false );
+
+			var currentBackground = this.renderScene.background;
+			this.renderScene.background = null;
+
+			// 1. Draw Non Selected objects in the depth buffer
+			this.renderScene.overrideMaterial = this.depthMaterial;
+			renderer.setRenderTarget( this.renderTargetDepthBuffer );
+			renderer.clear();
+			renderer.render( this.renderScene, this.renderCamera );
+
+			// Make selected objects visible
+			this.changeVisibilityOfSelectedObjects( true );
+
+			// Update Texture Matrix for Depth compare
+			this.updateTextureMatrix();
+
+			// Make non selected objects invisible, and draw only the selected objects, by comparing the depth buffer of non selected objects
+			this.changeVisibilityOfNonSelectedObjects( false );
+			this.renderScene.overrideMaterial = this.prepareMaskMaterial;
+			this.prepareMaskMaterial.uniforms[ "cameraNearFar" ].value = new Vector2( this.renderCamera.near, this.renderCamera.far );
+			this.prepareMaskMaterial.uniforms[ "depthTexture" ].value = this.renderTargetDepthBuffer.texture;
+			this.prepareMaskMaterial.uniforms[ "textureMatrix" ].value = this.textureMatrix;
+			renderer.setRenderTarget( this.renderTargetMaskBuffer );
+			renderer.clear();
+			renderer.render( this.renderScene, this.renderCamera );
+			this.renderScene.overrideMaterial = null;
+			this.changeVisibilityOfNonSelectedObjects( true );
+
+			this.renderScene.background = currentBackground;
+
+			// 2. Downsample to Half resolution
+			this.fsQuad.material = this.materialCopy;
+			this.copyUniforms[ "tDiffuse" ].value = this.renderTargetMaskBuffer.texture;
+			renderer.setRenderTarget( this.renderTargetMaskDownSampleBuffer );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+
+			this.tempPulseColor1.copy( this.visibleEdgeColor );
+			this.tempPulseColor2.copy( this.hiddenEdgeColor );
+
+			if ( this.pulsePeriod > 0 ) {
+
+				var scalar = ( 1 + 0.25 ) / 2 + Math.cos( performance.now() * 0.01 / this.pulsePeriod ) * ( 1.0 - 0.25 ) / 2;
+				this.tempPulseColor1.multiplyScalar( scalar );
+				this.tempPulseColor2.multiplyScalar( scalar );
+
+			}
+
+			// 3. Apply Edge Detection Pass
+			this.fsQuad.material = this.edgeDetectionMaterial;
+			this.edgeDetectionMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskDownSampleBuffer.texture;
+			this.edgeDetectionMaterial.uniforms[ "texSize" ].value = new Vector2( this.renderTargetMaskDownSampleBuffer.width, this.renderTargetMaskDownSampleBuffer.height );
+			this.edgeDetectionMaterial.uniforms[ "visibleEdgeColor" ].value = this.tempPulseColor1;
+			this.edgeDetectionMaterial.uniforms[ "hiddenEdgeColor" ].value = this.tempPulseColor2;
+			renderer.setRenderTarget( this.renderTargetEdgeBuffer1 );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+
+			// 4. Apply Blur on Half res
+			this.fsQuad.material = this.separableBlurMaterial1;
+			this.separableBlurMaterial1.uniforms[ "colorTexture" ].value = this.renderTargetEdgeBuffer1.texture;
+			this.separableBlurMaterial1.uniforms[ "direction" ].value = OutlinePass.BlurDirectionX;
+			this.separableBlurMaterial1.uniforms[ "kernelRadius" ].value = this.edgeThickness;
+			renderer.setRenderTarget( this.renderTargetBlurBuffer1 );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+			this.separableBlurMaterial1.uniforms[ "colorTexture" ].value = this.renderTargetBlurBuffer1.texture;
+			this.separableBlurMaterial1.uniforms[ "direction" ].value = OutlinePass.BlurDirectionY;
+			renderer.setRenderTarget( this.renderTargetEdgeBuffer1 );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+
+			// Apply Blur on quarter res
+			this.fsQuad.material = this.separableBlurMaterial2;
+			this.separableBlurMaterial2.uniforms[ "colorTexture" ].value = this.renderTargetEdgeBuffer1.texture;
+			this.separableBlurMaterial2.uniforms[ "direction" ].value = OutlinePass.BlurDirectionX;
+			renderer.setRenderTarget( this.renderTargetBlurBuffer2 );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+			this.separableBlurMaterial2.uniforms[ "colorTexture" ].value = this.renderTargetBlurBuffer2.texture;
+			this.separableBlurMaterial2.uniforms[ "direction" ].value = OutlinePass.BlurDirectionY;
+			renderer.setRenderTarget( this.renderTargetEdgeBuffer2 );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+
+			// Blend it additively over the input texture
+			this.fsQuad.material = this.overlayMaterial;
+			this.overlayMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskBuffer.texture;
+			this.overlayMaterial.uniforms[ "edgeTexture1" ].value = this.renderTargetEdgeBuffer1.texture;
+			this.overlayMaterial.uniforms[ "edgeTexture2" ].value = this.renderTargetEdgeBuffer2.texture;
+			this.overlayMaterial.uniforms[ "patternTexture" ].value = this.patternTexture;
+			this.overlayMaterial.uniforms[ "edgeStrength" ].value = this.edgeStrength;
+			this.overlayMaterial.uniforms[ "edgeGlow" ].value = this.edgeGlow;
+			this.overlayMaterial.uniforms[ "usePatternTexture" ].value = this.usePatternTexture;
+
+
+			if ( maskActive ) renderer.context.enable( renderer.context.STENCIL_TEST );
+
+			renderer.setRenderTarget( readBuffer );
+			this.fsQuad.render( renderer );
+
+			renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
+			renderer.autoClear = oldAutoClear;
+
+		}
+
+		if ( this.renderToScreen ) {
+
+			this.fsQuad.material = this.materialCopy;
+			this.copyUniforms[ "tDiffuse" ].value = readBuffer.texture;
+			renderer.setRenderTarget( null );
+			this.fsQuad.render( renderer );
+
+		}
+
+	},
+
+	getPrepareMaskMaterial: function () {
+
+		return new ShaderMaterial( {
+
+			uniforms: {
+				"depthTexture": { value: null },
+				"cameraNearFar": { value: new Vector2( 0.5, 0.5 ) },
+				"textureMatrix": { value: new Matrix4() }
+			},
+
+			vertexShader: [
+				'varying vec4 projTexCoord;',
+				'varying vec4 vPosition;',
+				'uniform mat4 textureMatrix;',
+
+				'void main() {',
+
+				'	vPosition = modelViewMatrix * vec4( position, 1.0 );',
+				'	vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
+				'	projTexCoord = textureMatrix * worldPosition;',
+				'	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
+
+				'}'
+			].join( '\n' ),
+
+			fragmentShader: [
+				'#include <packing>',
+				'varying vec4 vPosition;',
+				'varying vec4 projTexCoord;',
+				'uniform sampler2D depthTexture;',
+				'uniform vec2 cameraNearFar;',
+
+				'void main() {',
+
+				'	float depth = unpackRGBAToDepth(texture2DProj( depthTexture, projTexCoord ));',
+				'	float viewZ = - DEPTH_TO_VIEW_Z( depth, cameraNearFar.x, cameraNearFar.y );',
+				'	float depthTest = (-vPosition.z > viewZ) ? 1.0 : 0.0;',
+				'	gl_FragColor = vec4(0.0, depthTest, 1.0, 1.0);',
+
+				'}'
+			].join( '\n' )
+
+		} );
+
+	},
+
+	getEdgeDetectionMaterial: function () {
+
+		return new ShaderMaterial( {
+
+			uniforms: {
+				"maskTexture": { value: null },
+				"texSize": { value: new Vector2( 0.5, 0.5 ) },
+				"visibleEdgeColor": { value: new Vector3( 1.0, 1.0, 1.0 ) },
+				"hiddenEdgeColor": { value: new Vector3( 1.0, 1.0, 1.0 ) },
+			},
+
+			vertexShader:
+				"varying vec2 vUv;\n\
+				void main() {\n\
+					vUv = uv;\n\
+					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
+				}",
+
+			fragmentShader:
+				"varying vec2 vUv;\
+				uniform sampler2D maskTexture;\
+				uniform vec2 texSize;\
+				uniform vec3 visibleEdgeColor;\
+				uniform vec3 hiddenEdgeColor;\
+				\
+				void main() {\n\
+					vec2 invSize = 1.0 / texSize;\
+					vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);\
+					vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);\
+					vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);\
+					vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);\
+					vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);\
+					float diff1 = (c1.r - c2.r)*0.5;\
+					float diff2 = (c3.r - c4.r)*0.5;\
+					float d = length( vec2(diff1, diff2) );\
+					float a1 = min(c1.g, c2.g);\
+					float a2 = min(c3.g, c4.g);\
+					float visibilityFactor = min(a1, a2);\
+					vec3 edgeColor = 1.0 - visibilityFactor > 0.001 ? visibleEdgeColor : hiddenEdgeColor;\
+					gl_FragColor = vec4(edgeColor, 1.0) * vec4(d);\
+				}"
+		} );
+
+	},
+
+	getSeperableBlurMaterial: function ( maxRadius ) {
+
+		return new ShaderMaterial( {
+
+			defines: {
+				"MAX_RADIUS": maxRadius,
+			},
+
+			uniforms: {
+				"colorTexture": { value: null },
+				"texSize": { value: new Vector2( 0.5, 0.5 ) },
+				"direction": { value: new Vector2( 0.5, 0.5 ) },
+				"kernelRadius": { value: 1.0 }
+			},
+
+			vertexShader:
+				"varying vec2 vUv;\n\
+				void main() {\n\
+					vUv = uv;\n\
+					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
+				}",
+
+			fragmentShader:
+				"#include <common>\
+				varying vec2 vUv;\
+				uniform sampler2D colorTexture;\
+				uniform vec2 texSize;\
+				uniform vec2 direction;\
+				uniform float kernelRadius;\
+				\
+				float gaussianPdf(in float x, in float sigma) {\
+					return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\
+				}\
+				void main() {\
+					vec2 invSize = 1.0 / texSize;\
+					float weightSum = gaussianPdf(0.0, kernelRadius);\
+					vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\
+					vec2 delta = direction * invSize * kernelRadius/float(MAX_RADIUS);\
+					vec2 uvOffset = delta;\
+					for( int i = 1; i <= MAX_RADIUS; i ++ ) {\
+						float w = gaussianPdf(uvOffset.x, kernelRadius);\
+						vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;\
+						vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;\
+						diffuseSum += ((sample1 + sample2) * w);\
+						weightSum += (2.0 * w);\
+						uvOffset += delta;\
+					}\
+					gl_FragColor = vec4(diffuseSum/weightSum, 1.0);\
+				}"
+		} );
+
+	},
+
+	getOverlayMaterial: function () {
+
+		return new ShaderMaterial( {
+
+			uniforms: {
+				"maskTexture": { value: null },
+				"edgeTexture1": { value: null },
+				"edgeTexture2": { value: null },
+				"patternTexture": { value: null },
+				"edgeStrength": { value: 1.0 },
+				"edgeGlow": { value: 1.0 },
+				"usePatternTexture": { value: 0.0 }
+			},
+
+			vertexShader:
+				"varying vec2 vUv;\n\
+				void main() {\n\
+					vUv = uv;\n\
+					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
+				}",
+
+			fragmentShader:
+				"varying vec2 vUv;\
+				uniform sampler2D maskTexture;\
+				uniform sampler2D edgeTexture1;\
+				uniform sampler2D edgeTexture2;\
+				uniform sampler2D patternTexture;\
+				uniform float edgeStrength;\
+				uniform float edgeGlow;\
+				uniform bool usePatternTexture;\
+				\
+				void main() {\
+					vec4 edgeValue1 = texture2D(edgeTexture1, vUv);\
+					vec4 edgeValue2 = texture2D(edgeTexture2, vUv);\
+					vec4 maskColor = texture2D(maskTexture, vUv);\
+					vec4 patternColor = texture2D(patternTexture, 6.0 * vUv);\
+					float visibilityFactor = 1.0 - maskColor.g > 0.0 ? 1.0 : 0.5;\
+					vec4 edgeValue = edgeValue1 + edgeValue2 * edgeGlow;\
+					vec4 finalColor = edgeStrength * maskColor.r * edgeValue;\
+					if(usePatternTexture)\
+						finalColor += + visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r);\
+					gl_FragColor = finalColor;\
+				}",
+			blending: AdditiveBlending,
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+	}
+
+} );
+
+OutlinePass.BlurDirectionX = new Vector2( 1.0, 0.0 );
+OutlinePass.BlurDirectionY = new Vector2( 0.0, 1.0 );
+
+export { OutlinePass };

+ 50 - 0
examples/jsm/postprocessing/SAOPass.d.ts

@@ -0,0 +1,50 @@
+import {
+  Scene,
+  Camera,
+  Material,
+  MeshDepthMaterial,
+  MeshNormalMaterial,
+  ShaderMaterial,
+  Color,
+  Vector2,
+  WebGLRenderer,
+  WebGLRenderTarget
+} from '../../../src/Three';
+
+import { Pass } from './Pass';
+
+export class SAOPass extends Pass {
+  constructor(scene: Scene, camera: Camera, depthTexture?: boolean, useNormals?: boolean, resolution?: Vector2);
+  scene: Scene;
+  camera: Camera;
+  supportsDepthTextureExtension: boolean;
+  supportsNormalTexture: boolean;
+  originalClearColor: Color;
+  oldClearColor: Color;
+  oldClearAlpha: number;
+  resolution: Vector2;
+  saoRenderTarget: WebGLRenderTarget;
+  blurIntermediateRenderTarget: WebGLRenderTarget;
+  beautyRenderTarget: WebGLRenderTarget;
+  normalRenderTarget: WebGLRenderTarget;
+  depthRenderTarget: WebGLRenderTarget;
+  depthMaterial: MeshDepthMaterial;
+  normalMaterial: MeshNormalMaterial;
+  saoMaterial: ShaderMaterial;
+  vBlurMaterial: ShaderMaterial;
+  hBlurMaterial: ShaderMaterial;
+  materialCopy: ShaderMaterial;
+  depthCopy: ShaderMaterial;
+  fsQuad: object;
+
+  static OUTPUT: {
+    Default: number;
+    Beauty: number;
+    SAO: number;
+    Depth: number;
+    Normal: number;
+  };
+
+  renderPass(renderer: WebGLRenderer, passMaterial: Material, renderTarget: WebGLRenderer, clearColor: Color, clearAlpha: number): void;
+  renderOverride(renderer: WebGLRenderer, overrideMaterial: Material, renderTarget: WebGLRenderer, clearColor: Color, clearAlpha: number): void;
+}

+ 422 - 0
examples/jsm/postprocessing/SAOPass.js

@@ -0,0 +1,422 @@
+/**
+ * @author ludobaka / ludobaka.github.io
+ * SAO implementation inspired from bhouston previous SAO work
+ */
+
+import {
+	AddEquation,
+	Color,
+	CustomBlending,
+	DepthTexture,
+	DstAlphaFactor,
+	DstColorFactor,
+	LinearFilter,
+	MeshDepthMaterial,
+	MeshNormalMaterial,
+	NearestFilter,
+	NoBlending,
+	RGBADepthPacking,
+	RGBAFormat,
+	ShaderMaterial,
+	UniformsUtils,
+	UnsignedShortType,
+	Vector2,
+	WebGLRenderTarget,
+	ZeroFactor
+} from "../../../build/three.module.js";
+import { Pass } from "../postprocessing/Pass.js";
+import { SAOShader } from "../shaders/SAOShader.js";
+import { DepthLimitedBlurShader } from "../shaders/DepthLimitedBlurShader.js";
+import { BlurShaderUtils } from "../shaders/DepthLimitedBlurShader.js";
+import { CopyShader } from "../shaders/CopyShader.js";
+import { UnpackDepthRGBAShader } from "../shaders/UnpackDepthRGBAShader.js";
+
+var SAOPass = function ( scene, camera, depthTexture, useNormals, resolution ) {
+
+	Pass.call( this );
+
+	this.scene = scene;
+	this.camera = camera;
+
+	this.clear = true;
+	this.needsSwap = false;
+
+	this.supportsDepthTextureExtension = ( depthTexture !== undefined ) ? depthTexture : false;
+	this.supportsNormalTexture = ( useNormals !== undefined ) ? useNormals : false;
+
+	this.originalClearColor = new Color();
+	this.oldClearColor = new Color();
+	this.oldClearAlpha = 1;
+
+	this.params = {
+		output: 0,
+		saoBias: 0.5,
+		saoIntensity: 0.18,
+		saoScale: 1,
+		saoKernelRadius: 100,
+		saoMinResolution: 0,
+		saoBlur: true,
+		saoBlurRadius: 8,
+		saoBlurStdDev: 4,
+		saoBlurDepthCutoff: 0.01
+	};
+
+	this.resolution = ( resolution !== undefined ) ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 );
+
+	this.saoRenderTarget = new WebGLRenderTarget( this.resolution.x, this.resolution.y, {
+		minFilter: LinearFilter,
+		magFilter: LinearFilter,
+		format: RGBAFormat
+	} );
+	this.blurIntermediateRenderTarget = this.saoRenderTarget.clone();
+	this.beautyRenderTarget = this.saoRenderTarget.clone();
+
+	this.normalRenderTarget = new WebGLRenderTarget( this.resolution.x, this.resolution.y, {
+		minFilter: NearestFilter,
+		magFilter: NearestFilter,
+		format: RGBAFormat
+	} );
+	this.depthRenderTarget = this.normalRenderTarget.clone();
+
+	if ( this.supportsDepthTextureExtension ) {
+
+		var depthTexture = new DepthTexture();
+		depthTexture.type = UnsignedShortType;
+		depthTexture.minFilter = NearestFilter;
+		depthTexture.maxFilter = NearestFilter;
+
+		this.beautyRenderTarget.depthTexture = depthTexture;
+		this.beautyRenderTarget.depthBuffer = true;
+
+	}
+
+	this.depthMaterial = new MeshDepthMaterial();
+	this.depthMaterial.depthPacking = RGBADepthPacking;
+	this.depthMaterial.blending = NoBlending;
+
+	this.normalMaterial = new MeshNormalMaterial();
+	this.normalMaterial.blending = NoBlending;
+
+	if ( SAOShader === undefined ) {
+
+		console.error( 'THREE.SAOPass relies on SAOShader' );
+
+	}
+
+	this.saoMaterial = new ShaderMaterial( {
+		defines: Object.assign( {}, SAOShader.defines ),
+		fragmentShader: SAOShader.fragmentShader,
+		vertexShader: SAOShader.vertexShader,
+		uniforms: UniformsUtils.clone( SAOShader.uniforms )
+	} );
+	this.saoMaterial.extensions.derivatives = true;
+	this.saoMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1;
+	this.saoMaterial.defines[ 'NORMAL_TEXTURE' ] = this.supportsNormalTexture ? 1 : 0;
+	this.saoMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0;
+	this.saoMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture;
+	this.saoMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture;
+	this.saoMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y );
+	this.saoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix );
+	this.saoMaterial.uniforms[ 'cameraProjectionMatrix' ].value = this.camera.projectionMatrix;
+	this.saoMaterial.blending = NoBlending;
+
+	if ( DepthLimitedBlurShader === undefined ) {
+
+		console.error( 'THREE.SAOPass relies on DepthLimitedBlurShader' );
+
+	}
+
+	this.vBlurMaterial = new ShaderMaterial( {
+		uniforms: UniformsUtils.clone( DepthLimitedBlurShader.uniforms ),
+		defines: Object.assign( {}, DepthLimitedBlurShader.defines ),
+		vertexShader: DepthLimitedBlurShader.vertexShader,
+		fragmentShader: DepthLimitedBlurShader.fragmentShader
+	} );
+	this.vBlurMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1;
+	this.vBlurMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0;
+	this.vBlurMaterial.uniforms[ 'tDiffuse' ].value = this.saoRenderTarget.texture;
+	this.vBlurMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture;
+	this.vBlurMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y );
+	this.vBlurMaterial.blending = NoBlending;
+
+	this.hBlurMaterial = new ShaderMaterial( {
+		uniforms: UniformsUtils.clone( DepthLimitedBlurShader.uniforms ),
+		defines: Object.assign( {}, DepthLimitedBlurShader.defines ),
+		vertexShader: DepthLimitedBlurShader.vertexShader,
+		fragmentShader: DepthLimitedBlurShader.fragmentShader
+	} );
+	this.hBlurMaterial.defines[ 'DEPTH_PACKING' ] = this.supportsDepthTextureExtension ? 0 : 1;
+	this.hBlurMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0;
+	this.hBlurMaterial.uniforms[ 'tDiffuse' ].value = this.blurIntermediateRenderTarget.texture;
+	this.hBlurMaterial.uniforms[ 'tDepth' ].value = ( this.supportsDepthTextureExtension ) ? depthTexture : this.depthRenderTarget.texture;
+	this.hBlurMaterial.uniforms[ 'size' ].value.set( this.resolution.x, this.resolution.y );
+	this.hBlurMaterial.blending = NoBlending;
+
+	if ( CopyShader === undefined ) {
+
+		console.error( 'THREE.SAOPass relies on CopyShader' );
+
+	}
+
+	this.materialCopy = new ShaderMaterial( {
+		uniforms: UniformsUtils.clone( CopyShader.uniforms ),
+		vertexShader: CopyShader.vertexShader,
+		fragmentShader: CopyShader.fragmentShader,
+		blending: NoBlending
+	} );
+	this.materialCopy.transparent = true;
+	this.materialCopy.depthTest = false;
+	this.materialCopy.depthWrite = false;
+	this.materialCopy.blending = CustomBlending;
+	this.materialCopy.blendSrc = DstColorFactor;
+	this.materialCopy.blendDst = ZeroFactor;
+	this.materialCopy.blendEquation = AddEquation;
+	this.materialCopy.blendSrcAlpha = DstAlphaFactor;
+	this.materialCopy.blendDstAlpha = ZeroFactor;
+	this.materialCopy.blendEquationAlpha = AddEquation;
+
+	if ( UnpackDepthRGBAShader === undefined ) {
+
+		console.error( 'THREE.SAOPass relies on UnpackDepthRGBAShader' );
+
+	}
+
+	this.depthCopy = new ShaderMaterial( {
+		uniforms: UniformsUtils.clone( UnpackDepthRGBAShader.uniforms ),
+		vertexShader: UnpackDepthRGBAShader.vertexShader,
+		fragmentShader: UnpackDepthRGBAShader.fragmentShader,
+		blending: NoBlending
+	} );
+
+	this.fsQuad = new Pass.FullScreenQuad( null );
+
+};
+
+SAOPass.OUTPUT = {
+	'Beauty': 1,
+	'Default': 0,
+	'SAO': 2,
+	'Depth': 3,
+	'Normal': 4
+};
+
+SAOPass.prototype = Object.assign( Object.create( Pass.prototype ), {
+	constructor: SAOPass,
+
+	render: function ( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) {
+
+		// Rendering readBuffer first when rendering to screen
+		if ( this.renderToScreen ) {
+
+			this.materialCopy.blending = NoBlending;
+			this.materialCopy.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
+			this.materialCopy.needsUpdate = true;
+			this.renderPass( renderer, this.materialCopy, null );
+
+		}
+
+		if ( this.params.output === 1 ) {
+
+			return;
+
+		}
+
+		this.oldClearColor.copy( renderer.getClearColor() );
+		this.oldClearAlpha = renderer.getClearAlpha();
+		var oldAutoClear = renderer.autoClear;
+		renderer.autoClear = false;
+
+		renderer.setRenderTarget( this.depthRenderTarget );
+		renderer.clear();
+
+		this.saoMaterial.uniforms[ 'bias' ].value = this.params.saoBias;
+		this.saoMaterial.uniforms[ 'intensity' ].value = this.params.saoIntensity;
+		this.saoMaterial.uniforms[ 'scale' ].value = this.params.saoScale;
+		this.saoMaterial.uniforms[ 'kernelRadius' ].value = this.params.saoKernelRadius;
+		this.saoMaterial.uniforms[ 'minResolution' ].value = this.params.saoMinResolution;
+		this.saoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
+		this.saoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
+		// this.saoMaterial.uniforms['randomSeed'].value = Math.random();
+
+		var depthCutoff = this.params.saoBlurDepthCutoff * ( this.camera.far - this.camera.near );
+		this.vBlurMaterial.uniforms[ 'depthCutoff' ].value = depthCutoff;
+		this.hBlurMaterial.uniforms[ 'depthCutoff' ].value = depthCutoff;
+
+		this.vBlurMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
+		this.vBlurMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
+		this.hBlurMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
+		this.hBlurMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
+
+		this.params.saoBlurRadius = Math.floor( this.params.saoBlurRadius );
+		if ( ( this.prevStdDev !== this.params.saoBlurStdDev ) || ( this.prevNumSamples !== this.params.saoBlurRadius ) ) {
+
+			BlurShaderUtils.configure( this.vBlurMaterial, this.params.saoBlurRadius, this.params.saoBlurStdDev, new Vector2( 0, 1 ) );
+			BlurShaderUtils.configure( this.hBlurMaterial, this.params.saoBlurRadius, this.params.saoBlurStdDev, new Vector2( 1, 0 ) );
+			this.prevStdDev = this.params.saoBlurStdDev;
+			this.prevNumSamples = this.params.saoBlurRadius;
+
+		}
+
+		// Rendering scene to depth texture
+		renderer.setClearColor( 0x000000 );
+		renderer.setRenderTarget( this.beautyRenderTarget );
+		renderer.clear();
+		renderer.render( this.scene, this.camera );
+
+		// Re-render scene if depth texture extension is not supported
+		if ( ! this.supportsDepthTextureExtension ) {
+
+			// Clear rule : far clipping plane in both RGBA and Basic encoding
+			this.renderOverride( renderer, this.depthMaterial, this.depthRenderTarget, 0x000000, 1.0 );
+
+		}
+
+		if ( this.supportsNormalTexture ) {
+
+			// Clear rule : default normal is facing the camera
+			this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0x7777ff, 1.0 );
+
+		}
+
+		// Rendering SAO texture
+		this.renderPass( renderer, this.saoMaterial, this.saoRenderTarget, 0xffffff, 1.0 );
+
+		// Blurring SAO texture
+		if ( this.params.saoBlur ) {
+
+			this.renderPass( renderer, this.vBlurMaterial, this.blurIntermediateRenderTarget, 0xffffff, 1.0 );
+			this.renderPass( renderer, this.hBlurMaterial, this.saoRenderTarget, 0xffffff, 1.0 );
+
+		}
+
+		var outputMaterial = this.materialCopy;
+		// Setting up SAO rendering
+		if ( this.params.output === 3 ) {
+
+			if ( this.supportsDepthTextureExtension ) {
+
+				this.materialCopy.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.depthTexture;
+				this.materialCopy.needsUpdate = true;
+
+			} else {
+
+				this.depthCopy.uniforms[ 'tDiffuse' ].value = this.depthRenderTarget.texture;
+				this.depthCopy.needsUpdate = true;
+				outputMaterial = this.depthCopy;
+
+			}
+
+		} else if ( this.params.output === 4 ) {
+
+			this.materialCopy.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture;
+			this.materialCopy.needsUpdate = true;
+
+		} else {
+
+			this.materialCopy.uniforms[ 'tDiffuse' ].value = this.saoRenderTarget.texture;
+			this.materialCopy.needsUpdate = true;
+
+		}
+
+		// Blending depends on output, only want a CustomBlending when showing SAO
+		if ( this.params.output === 0 ) {
+
+			outputMaterial.blending = CustomBlending;
+
+		} else {
+
+			outputMaterial.blending = NoBlending;
+
+		}
+
+		// Rendering SAOPass result on top of previous pass
+		this.renderPass( renderer, outputMaterial, this.renderToScreen ? null : readBuffer );
+
+		renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
+		renderer.autoClear = oldAutoClear;
+
+	},
+
+	renderPass: function ( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) {
+
+		// save original state
+		this.originalClearColor.copy( renderer.getClearColor() );
+		var originalClearAlpha = renderer.getClearAlpha();
+		var originalAutoClear = renderer.autoClear;
+
+		renderer.setRenderTarget( renderTarget );
+
+		// setup pass state
+		renderer.autoClear = false;
+		if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
+
+			renderer.setClearColor( clearColor );
+			renderer.setClearAlpha( clearAlpha || 0.0 );
+			renderer.clear();
+
+		}
+
+		this.fsQuad.material = passMaterial;
+		this.fsQuad.render( renderer );
+
+		// restore original state
+		renderer.autoClear = originalAutoClear;
+		renderer.setClearColor( this.originalClearColor );
+		renderer.setClearAlpha( originalClearAlpha );
+
+	},
+
+	renderOverride: function ( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
+
+		this.originalClearColor.copy( renderer.getClearColor() );
+		var originalClearAlpha = renderer.getClearAlpha();
+		var originalAutoClear = renderer.autoClear;
+
+		renderer.setRenderTarget( renderTarget );
+		renderer.autoClear = false;
+
+		clearColor = overrideMaterial.clearColor || clearColor;
+		clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
+		if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
+
+			renderer.setClearColor( clearColor );
+			renderer.setClearAlpha( clearAlpha || 0.0 );
+			renderer.clear();
+
+		}
+
+		this.scene.overrideMaterial = overrideMaterial;
+		renderer.render( this.scene, this.camera );
+		this.scene.overrideMaterial = null;
+
+		// restore original state
+		renderer.autoClear = originalAutoClear;
+		renderer.setClearColor( this.originalClearColor );
+		renderer.setClearAlpha( originalClearAlpha );
+
+	},
+
+	setSize: function ( width, height ) {
+
+		this.beautyRenderTarget.setSize( width, height );
+		this.saoRenderTarget.setSize( width, height );
+		this.blurIntermediateRenderTarget.setSize( width, height );
+		this.normalRenderTarget.setSize( width, height );
+		this.depthRenderTarget.setSize( width, height );
+
+		this.saoMaterial.uniforms[ 'size' ].value.set( width, height );
+		this.saoMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.getInverse( this.camera.projectionMatrix );
+		this.saoMaterial.uniforms[ 'cameraProjectionMatrix' ].value = this.camera.projectionMatrix;
+		this.saoMaterial.needsUpdate = true;
+
+		this.vBlurMaterial.uniforms[ 'size' ].value.set( width, height );
+		this.vBlurMaterial.needsUpdate = true;
+
+		this.hBlurMaterial.uniforms[ 'size' ].value.set( width, height );
+		this.hBlurMaterial.needsUpdate = true;
+
+	}
+
+} );
+
+export { SAOPass };

+ 25 - 0
examples/jsm/postprocessing/SMAAPass.d.ts

@@ -0,0 +1,25 @@
+import {
+  ShaderMaterial,
+  Texture,
+  WebGLRenderTarget
+} from '../../../src/Three';
+
+import { Pass } from './Pass';
+
+export class SMAAPass extends Pass {
+  constructor(width: number, height: number);
+  edgesRT: WebGLRenderTarget;
+  weightsRT: WebGLRenderTarget;
+  areaTexture: Texture;
+  searchTexture: Texture;
+  uniformsEdges: object;
+  materialEdges: ShaderMaterial;
+  uniformsWeights: object;
+  materialWeights: ShaderMaterial;
+  uniformsBlend: object;
+  materialBlend: ShaderMaterial;
+  fsQuad: object;
+
+  getAreaTexture(): string;
+  getSearchTexture(): string;
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 192 - 0
examples/jsm/postprocessing/SMAAPass.js


+ 21 - 0
examples/jsm/postprocessing/SSAARenderPass.d.ts

@@ -0,0 +1,21 @@
+import {
+  Scene,
+  Camera,
+  Color,
+  ShaderMaterial
+} from '../../../src/Three';
+
+import { Pass } from './Pass';
+
+export class SSAARenderPass extends Pass {
+  constructor(scene: Scene, camera: Camera, clearColor: Color | string | number, clearAlpha: number);
+  scene: Scene;
+  camera: Camera;
+  sampleLevel: number;
+  unbiased: boolean;
+  clearColor: Color | string | number;
+  clearAlpha: number;
+  copyUniforms: object;
+  copyMaterial: ShaderMaterial;
+  fsQuad: object;
+}

+ 194 - 0
examples/jsm/postprocessing/SSAARenderPass.js

@@ -0,0 +1,194 @@
+/**
+*
+* Supersample Anti-Aliasing Render Pass
+*
+* @author bhouston / http://clara.io/
+*
+* This manual approach to SSAA re-renders the scene ones for each sample with camera jitter and accumulates the results.
+*
+* References: https://en.wikipedia.org/wiki/Supersampling
+*
+*/
+
+import {
+	AdditiveBlending,
+	LinearFilter,
+	RGBAFormat,
+	ShaderMaterial,
+	UniformsUtils,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+import { Pass } from "../postprocessing/Pass.js";
+import { CopyShader } from "../shaders/CopyShader.js";
+
+var SSAARenderPass = function ( scene, camera, clearColor, clearAlpha ) {
+
+	Pass.call( this );
+
+	this.scene = scene;
+	this.camera = camera;
+
+	this.sampleLevel = 4; // specified as n, where the number of samples is 2^n, so sampleLevel = 4, is 2^4 samples, 16.
+	this.unbiased = true;
+
+	// as we need to clear the buffer in this pass, clearColor must be set to something, defaults to black.
+	this.clearColor = ( clearColor !== undefined ) ? clearColor : 0x000000;
+	this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0;
+
+	if ( CopyShader === undefined ) console.error( "SSAARenderPass relies on CopyShader" );
+
+	var copyShader = CopyShader;
+	this.copyUniforms = UniformsUtils.clone( copyShader.uniforms );
+
+	this.copyMaterial = new ShaderMaterial(	{
+		uniforms: this.copyUniforms,
+		vertexShader: copyShader.vertexShader,
+		fragmentShader: copyShader.fragmentShader,
+		premultipliedAlpha: true,
+		transparent: true,
+		blending: AdditiveBlending,
+		depthTest: false,
+		depthWrite: false
+	} );
+
+	this.fsQuad = new Pass.FullScreenQuad( this.copyMaterial );
+
+};
+
+SSAARenderPass.prototype = Object.assign( Object.create( Pass.prototype ), {
+
+	constructor: SSAARenderPass,
+
+	dispose: function () {
+
+		if ( this.sampleRenderTarget ) {
+
+			this.sampleRenderTarget.dispose();
+			this.sampleRenderTarget = null;
+
+		}
+
+	},
+
+	setSize: function ( width, height ) {
+
+		if ( this.sampleRenderTarget )	this.sampleRenderTarget.setSize( width, height );
+
+	},
+
+	render: function ( renderer, writeBuffer, readBuffer ) {
+
+		if ( ! this.sampleRenderTarget ) {
+
+			this.sampleRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat } );
+			this.sampleRenderTarget.texture.name = "SSAARenderPass.sample";
+
+		}
+
+		var jitterOffsets = SSAARenderPass.JitterVectors[ Math.max( 0, Math.min( this.sampleLevel, 5 ) ) ];
+
+		var autoClear = renderer.autoClear;
+		renderer.autoClear = false;
+
+		var oldClearColor = renderer.getClearColor().getHex();
+		var oldClearAlpha = renderer.getClearAlpha();
+
+		var baseSampleWeight = 1.0 / jitterOffsets.length;
+		var roundingRange = 1 / 32;
+		this.copyUniforms[ "tDiffuse" ].value = this.sampleRenderTarget.texture;
+
+		var width = readBuffer.width, height = readBuffer.height;
+
+		// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
+		for ( var i = 0; i < jitterOffsets.length; i ++ ) {
+
+			var jitterOffset = jitterOffsets[ i ];
+
+			if ( this.camera.setViewOffset ) {
+
+				this.camera.setViewOffset( width, height,
+					jitterOffset[ 0 ] * 0.0625, jitterOffset[ 1 ] * 0.0625, // 0.0625 = 1 / 16
+					width, height );
+
+			}
+
+			var sampleWeight = baseSampleWeight;
+
+			if ( this.unbiased ) {
+
+				// the theory is that equal weights for each sample lead to an accumulation of rounding errors.
+				// The following equation varies the sampleWeight per sample so that it is uniformly distributed
+				// across a range of values whose rounding errors cancel each other out.
+
+				var uniformCenteredDistribution = ( - 0.5 + ( i + 0.5 ) / jitterOffsets.length );
+				sampleWeight += roundingRange * uniformCenteredDistribution;
+
+			}
+
+			this.copyUniforms[ "opacity" ].value = sampleWeight;
+			renderer.setClearColor( this.clearColor, this.clearAlpha );
+			renderer.setRenderTarget( this.sampleRenderTarget );
+			renderer.clear();
+			renderer.render( this.scene, this.camera );
+
+			renderer.setRenderTarget( this.renderToScreen ? null : writeBuffer );
+
+			if ( i === 0 ) {
+
+				renderer.setClearColor( 0x000000, 0.0 );
+				renderer.clear();
+
+			}
+
+			this.fsQuad.render( renderer );
+
+		}
+
+		if ( this.camera.clearViewOffset ) this.camera.clearViewOffset();
+
+		renderer.autoClear = autoClear;
+		renderer.setClearColor( oldClearColor, oldClearAlpha );
+
+	}
+
+} );
+
+
+// These jitter vectors are specified in integers because it is easier.
+// I am assuming a [-8,8) integer grid, but it needs to be mapped onto [-0.5,0.5)
+// before being used, thus these integers need to be scaled by 1/16.
+//
+// Sample patterns reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476218%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
+SSAARenderPass.JitterVectors = [
+	[
+		[ 0, 0 ]
+	],
+	[
+		[ 4, 4 ], [ - 4, - 4 ]
+	],
+	[
+		[ - 2, - 6 ], [ 6, - 2 ], [ - 6, 2 ], [ 2, 6 ]
+	],
+	[
+		[ 1, - 3 ], [ - 1, 3 ], [ 5, 1 ], [ - 3, - 5 ],
+		[ - 5, 5 ], [ - 7, - 1 ], [ 3, 7 ], [ 7, - 7 ]
+	],
+	[
+		[ 1, 1 ], [ - 1, - 3 ], [ - 3, 2 ], [ 4, - 1 ],
+		[ - 5, - 2 ], [ 2, 5 ], [ 5, 3 ], [ 3, - 5 ],
+		[ - 2, 6 ], [ 0, - 7 ], [ - 4, - 6 ], [ - 6, 4 ],
+		[ - 8, 0 ], [ 7, - 4 ], [ 6, 7 ], [ - 7, - 8 ]
+	],
+	[
+		[ - 4, - 7 ], [ - 7, - 5 ], [ - 3, - 5 ], [ - 5, - 4 ],
+		[ - 1, - 4 ], [ - 2, - 2 ], [ - 6, - 1 ], [ - 4, 0 ],
+		[ - 7, 1 ], [ - 1, 2 ], [ - 6, 3 ], [ - 3, 3 ],
+		[ - 7, 6 ], [ - 3, 6 ], [ - 5, 7 ], [ - 1, 7 ],
+		[ 5, - 7 ], [ 1, - 6 ], [ 6, - 5 ], [ 4, - 4 ],
+		[ 2, - 3 ], [ 7, - 2 ], [ 1, - 1 ], [ 4, - 1 ],
+		[ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],
+		[ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]
+	]
+];
+
+export { SSAARenderPass };

+ 12 - 0
examples/jsm/postprocessing/TAARenderPass.d.ts

@@ -0,0 +1,12 @@
+import {
+  Scene,
+  Camera,
+  Color
+} from '../../../src/Three';
+
+import { SSAARenderPass } from './SSAARenderPass';
+
+export class TAARenderPass extends SSAARenderPass {
+  constructor(scene: Scene, camera: Camera, clearColor: Color | string | number, clearAlpha: number);
+  accumulate: boolean;
+}

+ 147 - 0
examples/jsm/postprocessing/TAARenderPass.js

@@ -0,0 +1,147 @@
+/**
+ *
+ * Temporal Anti-Aliasing Render Pass
+ *
+ * @author bhouston / http://clara.io/
+ *
+ * When there is no motion in the scene, the TAA render pass accumulates jittered camera samples across frames to create a high quality anti-aliased result.
+ *
+ * References:
+ *
+ * TODO: Add support for motion vector pas so that accumulation of samples across frames can occur on dynamics scenes.
+ *
+ */
+
+import {
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+import { SSAARenderPass } from "../postprocessing/SSAARenderPass.js";
+
+var TAARenderPass = function ( scene, camera, clearColor, clearAlpha ) {
+
+	if ( SSAARenderPass === undefined ) {
+
+		console.error( "TAARenderPass relies on SSAARenderPass" );
+
+	}
+
+	SSAARenderPass.call( this, scene, camera, clearColor, clearAlpha );
+
+	this.sampleLevel = 0;
+	this.accumulate = false;
+
+};
+
+TAARenderPass.JitterVectors = SSAARenderPass.JitterVectors;
+
+TAARenderPass.prototype = Object.assign( Object.create( SSAARenderPass.prototype ), {
+
+	constructor: TAARenderPass,
+
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime ) {
+
+		if ( ! this.accumulate ) {
+
+			SSAARenderPass.prototype.render.call( this, renderer, writeBuffer, readBuffer, deltaTime );
+
+			this.accumulateIndex = - 1;
+			return;
+
+		}
+
+		var jitterOffsets = TAARenderPass.JitterVectors[ 5 ];
+
+		if ( ! this.sampleRenderTarget ) {
+
+			this.sampleRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, this.params );
+			this.sampleRenderTarget.texture.name = "TAARenderPass.sample";
+
+		}
+
+		if ( ! this.holdRenderTarget ) {
+
+			this.holdRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, this.params );
+			this.holdRenderTarget.texture.name = "TAARenderPass.hold";
+
+		}
+
+		if ( this.accumulate && this.accumulateIndex === - 1 ) {
+
+			SSAARenderPass.prototype.render.call( this, renderer, this.holdRenderTarget, readBuffer, deltaTime );
+
+			this.accumulateIndex = 0;
+
+		}
+
+		var autoClear = renderer.autoClear;
+		renderer.autoClear = false;
+
+		var sampleWeight = 1.0 / ( jitterOffsets.length );
+
+		if ( this.accumulateIndex >= 0 && this.accumulateIndex < jitterOffsets.length ) {
+
+			this.copyUniforms[ "opacity" ].value = sampleWeight;
+			this.copyUniforms[ "tDiffuse" ].value = writeBuffer.texture;
+
+			// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
+			var numSamplesPerFrame = Math.pow( 2, this.sampleLevel );
+			for ( var i = 0; i < numSamplesPerFrame; i ++ ) {
+
+				var j = this.accumulateIndex;
+				var jitterOffset = jitterOffsets[ j ];
+
+				if ( this.camera.setViewOffset ) {
+
+					this.camera.setViewOffset( readBuffer.width, readBuffer.height,
+						jitterOffset[ 0 ] * 0.0625, jitterOffset[ 1 ] * 0.0625, // 0.0625 = 1 / 16
+						readBuffer.width, readBuffer.height );
+
+				}
+
+				renderer.setRenderTarget( writeBuffer );
+				renderer.clear();
+				renderer.render( this.scene, this.camera );
+
+				renderer.setRenderTarget( this.sampleRenderTarget );
+				if ( this.accumulateIndex === 0 ) renderer.clear();
+				this.fsQuad.render( renderer );
+
+				this.accumulateIndex ++;
+
+				if ( this.accumulateIndex >= jitterOffsets.length ) break;
+
+			}
+
+			if ( this.camera.clearViewOffset ) this.camera.clearViewOffset();
+
+		}
+
+		var accumulationWeight = this.accumulateIndex * sampleWeight;
+
+		if ( accumulationWeight > 0 ) {
+
+			this.copyUniforms[ "opacity" ].value = 1.0;
+			this.copyUniforms[ "tDiffuse" ].value = this.sampleRenderTarget.texture;
+			renderer.setRenderTarget( writeBuffer );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+
+		}
+
+		if ( accumulationWeight < 1.0 ) {
+
+			this.copyUniforms[ "opacity" ].value = 1.0 - accumulationWeight;
+			this.copyUniforms[ "tDiffuse" ].value = this.holdRenderTarget.texture;
+			renderer.setRenderTarget( writeBuffer );
+			if ( accumulationWeight === 0 ) renderer.clear();
+			this.fsQuad.render( renderer );
+
+		}
+
+		renderer.autoClear = autoClear;
+
+	}
+
+} );
+
+export { TAARenderPass };

+ 38 - 0
examples/jsm/postprocessing/UnrealBloomPass.d.ts

@@ -0,0 +1,38 @@
+import {
+  Color,
+  MeshBasicMaterial,
+  ShaderMaterial,
+  Vector2,
+  Vector3,
+  WebGLRenderTarget
+} from '../../../src/Three';
+
+import { Pass } from './Pass';
+
+export class UnrealBloomPass extends Pass {
+  constructor(resolution: Vector2, strength: number, radius: number, threshold: number);
+  resolution: Vector2;
+  strength: number;
+  radius: number;
+  threshold: number;
+  clearColor: Color;
+  renderTargetsHorizontal: WebGLRenderTarget[];
+  renderTargetsVertical: WebGLRenderTarget[];
+  nMips: number;
+  renderTargetBright: WebGLRenderTarget;
+  highPassUniforms: object;
+  materialHighPassFilter: ShaderMaterial;
+  separableBlurMaterials: ShaderMaterial[];
+  compositeMaterial: ShaderMaterial;
+  bloomTintColors: Vector3[];
+  copyUniforms: object;
+  materialCopy: ShaderMaterial;
+  oldClearColor: Color;
+  oldClearAlpha: number;
+  basic: MeshBasicMaterial;
+  fsQuad: object;
+
+  dispose(): void;
+  getSeperableBlurMaterial(): ShaderMaterial;
+  getCompositeMaterial(): ShaderMaterial;
+}

+ 410 - 0
examples/jsm/postprocessing/UnrealBloomPass.js

@@ -0,0 +1,410 @@
+/**
+ * @author spidersharma / http://eduperiment.com/
+ *
+ * Inspired from Unreal Engine
+ * https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
+ */
+
+import {
+	AdditiveBlending,
+	Color,
+	LinearFilter,
+	MeshBasicMaterial,
+	RGBAFormat,
+	ShaderMaterial,
+	UniformsUtils,
+	Vector2,
+	Vector3,
+	WebGLRenderTarget
+} from "../../../build/three.module.js";
+import { Pass } from "../postprocessing/Pass.js";
+import { CopyShader } from "../shaders/CopyShader.js";
+import { LuminosityHighPassShader } from "../shaders/LuminosityHighPassShader.js";
+
+var UnrealBloomPass = function ( resolution, strength, radius, threshold ) {
+
+	Pass.call( this );
+
+	this.strength = ( strength !== undefined ) ? strength : 1;
+	this.radius = radius;
+	this.threshold = threshold;
+	this.resolution = ( resolution !== undefined ) ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 );
+
+	// create color only once here, reuse it later inside the render function
+	this.clearColor = new Color( 0, 0, 0 );
+
+	// render targets
+	var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat };
+	this.renderTargetsHorizontal = [];
+	this.renderTargetsVertical = [];
+	this.nMips = 5;
+	var resx = Math.round( this.resolution.x / 2 );
+	var resy = Math.round( this.resolution.y / 2 );
+
+	this.renderTargetBright = new WebGLRenderTarget( resx, resy, pars );
+	this.renderTargetBright.texture.name = "UnrealBloomPass.bright";
+	this.renderTargetBright.texture.generateMipmaps = false;
+
+	for ( var i = 0; i < this.nMips; i ++ ) {
+
+		var renderTargetHorizonal = new WebGLRenderTarget( resx, resy, pars );
+
+		renderTargetHorizonal.texture.name = "UnrealBloomPass.h" + i;
+		renderTargetHorizonal.texture.generateMipmaps = false;
+
+		this.renderTargetsHorizontal.push( renderTargetHorizonal );
+
+		var renderTargetVertical = new WebGLRenderTarget( resx, resy, pars );
+
+		renderTargetVertical.texture.name = "UnrealBloomPass.v" + i;
+		renderTargetVertical.texture.generateMipmaps = false;
+
+		this.renderTargetsVertical.push( renderTargetVertical );
+
+		resx = Math.round( resx / 2 );
+
+		resy = Math.round( resy / 2 );
+
+	}
+
+	// luminosity high pass material
+
+	if ( LuminosityHighPassShader === undefined )
+		console.error( "UnrealBloomPass relies on LuminosityHighPassShader" );
+
+	var highPassShader = LuminosityHighPassShader;
+	this.highPassUniforms = UniformsUtils.clone( highPassShader.uniforms );
+
+	this.highPassUniforms[ "luminosityThreshold" ].value = threshold;
+	this.highPassUniforms[ "smoothWidth" ].value = 0.01;
+
+	this.materialHighPassFilter = new ShaderMaterial( {
+		uniforms: this.highPassUniforms,
+		vertexShader: highPassShader.vertexShader,
+		fragmentShader: highPassShader.fragmentShader,
+		defines: {}
+	} );
+
+	// Gaussian Blur Materials
+	this.separableBlurMaterials = [];
+	var kernelSizeArray = [ 3, 5, 7, 9, 11 ];
+	var resx = Math.round( this.resolution.x / 2 );
+	var resy = Math.round( this.resolution.y / 2 );
+
+	for ( var i = 0; i < this.nMips; i ++ ) {
+
+		this.separableBlurMaterials.push( this.getSeperableBlurMaterial( kernelSizeArray[ i ] ) );
+
+		this.separableBlurMaterials[ i ].uniforms[ "texSize" ].value = new Vector2( resx, resy );
+
+		resx = Math.round( resx / 2 );
+
+		resy = Math.round( resy / 2 );
+
+	}
+
+	// Composite material
+	this.compositeMaterial = this.getCompositeMaterial( this.nMips );
+	this.compositeMaterial.uniforms[ "blurTexture1" ].value = this.renderTargetsVertical[ 0 ].texture;
+	this.compositeMaterial.uniforms[ "blurTexture2" ].value = this.renderTargetsVertical[ 1 ].texture;
+	this.compositeMaterial.uniforms[ "blurTexture3" ].value = this.renderTargetsVertical[ 2 ].texture;
+	this.compositeMaterial.uniforms[ "blurTexture4" ].value = this.renderTargetsVertical[ 3 ].texture;
+	this.compositeMaterial.uniforms[ "blurTexture5" ].value = this.renderTargetsVertical[ 4 ].texture;
+	this.compositeMaterial.uniforms[ "bloomStrength" ].value = strength;
+	this.compositeMaterial.uniforms[ "bloomRadius" ].value = 0.1;
+	this.compositeMaterial.needsUpdate = true;
+
+	var bloomFactors = [ 1.0, 0.8, 0.6, 0.4, 0.2 ];
+	this.compositeMaterial.uniforms[ "bloomFactors" ].value = bloomFactors;
+	this.bloomTintColors = [ new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ),
+							 new Vector3( 1, 1, 1 ), new Vector3( 1, 1, 1 ) ];
+	this.compositeMaterial.uniforms[ "bloomTintColors" ].value = this.bloomTintColors;
+
+	// copy material
+	if ( CopyShader === undefined ) {
+
+		console.error( "UnrealBloomPass relies on CopyShader" );
+
+	}
+
+	var copyShader = CopyShader;
+
+	this.copyUniforms = UniformsUtils.clone( copyShader.uniforms );
+	this.copyUniforms[ "opacity" ].value = 1.0;
+
+	this.materialCopy = new ShaderMaterial( {
+		uniforms: this.copyUniforms,
+		vertexShader: copyShader.vertexShader,
+		fragmentShader: copyShader.fragmentShader,
+		blending: AdditiveBlending,
+		depthTest: false,
+		depthWrite: false,
+		transparent: true
+	} );
+
+	this.enabled = true;
+	this.needsSwap = false;
+
+	this.oldClearColor = new Color();
+	this.oldClearAlpha = 1;
+
+	this.basic = new MeshBasicMaterial();
+
+	this.fsQuad = new Pass.FullScreenQuad( null );
+
+};
+
+UnrealBloomPass.prototype = Object.assign( Object.create( Pass.prototype ), {
+
+	constructor: UnrealBloomPass,
+
+	dispose: function () {
+
+		for ( var i = 0; i < this.renderTargetsHorizontal.length; i ++ ) {
+
+			this.renderTargetsHorizontal[ i ].dispose();
+
+		}
+
+		for ( var i = 0; i < this.renderTargetsVertical.length; i ++ ) {
+
+			this.renderTargetsVertical[ i ].dispose();
+
+		}
+
+		this.renderTargetBright.dispose();
+
+	},
+
+	setSize: function ( width, height ) {
+
+		var resx = Math.round( width / 2 );
+		var resy = Math.round( height / 2 );
+
+		this.renderTargetBright.setSize( resx, resy );
+
+		for ( var i = 0; i < this.nMips; i ++ ) {
+
+			this.renderTargetsHorizontal[ i ].setSize( resx, resy );
+			this.renderTargetsVertical[ i ].setSize( resx, resy );
+
+			this.separableBlurMaterials[ i ].uniforms[ "texSize" ].value = new Vector2( resx, resy );
+
+			resx = Math.round( resx / 2 );
+			resy = Math.round( resy / 2 );
+
+		}
+
+	},
+
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
+
+		this.oldClearColor.copy( renderer.getClearColor() );
+		this.oldClearAlpha = renderer.getClearAlpha();
+		var oldAutoClear = renderer.autoClear;
+		renderer.autoClear = false;
+
+		renderer.setClearColor( this.clearColor, 0 );
+
+		if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST );
+
+		// Render input to screen
+
+		if ( this.renderToScreen ) {
+
+			this.fsQuad.material = this.basic;
+			this.basic.map = readBuffer.texture;
+
+			renderer.setRenderTarget( null );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+
+		}
+
+		// 1. Extract Bright Areas
+
+		this.highPassUniforms[ "tDiffuse" ].value = readBuffer.texture;
+		this.highPassUniforms[ "luminosityThreshold" ].value = this.threshold;
+		this.fsQuad.material = this.materialHighPassFilter;
+
+		renderer.setRenderTarget( this.renderTargetBright );
+		renderer.clear();
+		this.fsQuad.render( renderer );
+
+		// 2. Blur All the mips progressively
+
+		var inputRenderTarget = this.renderTargetBright;
+
+		for ( var i = 0; i < this.nMips; i ++ ) {
+
+			this.fsQuad.material = this.separableBlurMaterials[ i ];
+
+			this.separableBlurMaterials[ i ].uniforms[ "colorTexture" ].value = inputRenderTarget.texture;
+			this.separableBlurMaterials[ i ].uniforms[ "direction" ].value = UnrealBloomPass.BlurDirectionX;
+			renderer.setRenderTarget( this.renderTargetsHorizontal[ i ] );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+
+			this.separableBlurMaterials[ i ].uniforms[ "colorTexture" ].value = this.renderTargetsHorizontal[ i ].texture;
+			this.separableBlurMaterials[ i ].uniforms[ "direction" ].value = UnrealBloomPass.BlurDirectionY;
+			renderer.setRenderTarget( this.renderTargetsVertical[ i ] );
+			renderer.clear();
+			this.fsQuad.render( renderer );
+
+			inputRenderTarget = this.renderTargetsVertical[ i ];
+
+		}
+
+		// Composite All the mips
+
+		this.fsQuad.material = this.compositeMaterial;
+		this.compositeMaterial.uniforms[ "bloomStrength" ].value = this.strength;
+		this.compositeMaterial.uniforms[ "bloomRadius" ].value = this.radius;
+		this.compositeMaterial.uniforms[ "bloomTintColors" ].value = this.bloomTintColors;
+
+		renderer.setRenderTarget( this.renderTargetsHorizontal[ 0 ] );
+		renderer.clear();
+		this.fsQuad.render( renderer );
+
+		// Blend it additively over the input texture
+
+		this.fsQuad.material = this.materialCopy;
+		this.copyUniforms[ "tDiffuse" ].value = this.renderTargetsHorizontal[ 0 ].texture;
+
+		if ( maskActive ) renderer.context.enable( renderer.context.STENCIL_TEST );
+
+
+		if ( this.renderToScreen ) {
+
+			renderer.setRenderTarget( null );
+			this.fsQuad.render( renderer );
+
+		} else {
+
+			renderer.setRenderTarget( readBuffer );
+			this.fsQuad.render( renderer );
+
+		}
+
+		// Restore renderer settings
+
+		renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
+		renderer.autoClear = oldAutoClear;
+
+	},
+
+	getSeperableBlurMaterial: function ( kernelRadius ) {
+
+		return new ShaderMaterial( {
+
+			defines: {
+				"KERNEL_RADIUS": kernelRadius,
+				"SIGMA": kernelRadius
+			},
+
+			uniforms: {
+				"colorTexture": { value: null },
+				"texSize": { value: new Vector2( 0.5, 0.5 ) },
+				"direction": { value: new Vector2( 0.5, 0.5 ) }
+			},
+
+			vertexShader:
+				"varying vec2 vUv;\n\
+				void main() {\n\
+					vUv = uv;\n\
+					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
+				}",
+
+			fragmentShader:
+				"#include <common>\
+				varying vec2 vUv;\n\
+				uniform sampler2D colorTexture;\n\
+				uniform vec2 texSize;\
+				uniform vec2 direction;\
+				\
+				float gaussianPdf(in float x, in float sigma) {\
+					return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\
+				}\
+				void main() {\n\
+					vec2 invSize = 1.0 / texSize;\
+					float fSigma = float(SIGMA);\
+					float weightSum = gaussianPdf(0.0, fSigma);\
+					vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\
+					for( int i = 1; i < KERNEL_RADIUS; i ++ ) {\
+						float x = float(i);\
+						float w = gaussianPdf(x, fSigma);\
+						vec2 uvOffset = direction * invSize * x;\
+						vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;\
+						vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;\
+						diffuseSum += (sample1 + sample2) * w;\
+						weightSum += 2.0 * w;\
+					}\
+					gl_FragColor = vec4(diffuseSum/weightSum, 1.0);\n\
+				}"
+		} );
+
+	},
+
+	getCompositeMaterial: function ( nMips ) {
+
+		return new ShaderMaterial( {
+
+			defines: {
+				"NUM_MIPS": nMips
+			},
+
+			uniforms: {
+				"blurTexture1": { value: null },
+				"blurTexture2": { value: null },
+				"blurTexture3": { value: null },
+				"blurTexture4": { value: null },
+				"blurTexture5": { value: null },
+				"dirtTexture": { value: null },
+				"bloomStrength": { value: 1.0 },
+				"bloomFactors": { value: null },
+				"bloomTintColors": { value: null },
+				"bloomRadius": { value: 0.0 }
+			},
+
+			vertexShader:
+				"varying vec2 vUv;\n\
+				void main() {\n\
+					vUv = uv;\n\
+					gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
+				}",
+
+			fragmentShader:
+				"varying vec2 vUv;\
+				uniform sampler2D blurTexture1;\
+				uniform sampler2D blurTexture2;\
+				uniform sampler2D blurTexture3;\
+				uniform sampler2D blurTexture4;\
+				uniform sampler2D blurTexture5;\
+				uniform sampler2D dirtTexture;\
+				uniform float bloomStrength;\
+				uniform float bloomRadius;\
+				uniform float bloomFactors[NUM_MIPS];\
+				uniform vec3 bloomTintColors[NUM_MIPS];\
+				\
+				float lerpBloomFactor(const in float factor) { \
+					float mirrorFactor = 1.2 - factor;\
+					return mix(factor, mirrorFactor, bloomRadius);\
+				}\
+				\
+				void main() {\
+					gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + \
+													 lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + \
+													 lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + \
+													 lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + \
+													 lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );\
+				}"
+		} );
+
+	}
+
+} );
+
+UnrealBloomPass.BlurDirectionX = new Vector2( 1.0, 0.0 );
+UnrealBloomPass.BlurDirectionY = new Vector2( 0.0, 1.0 );
+
+export { UnrealBloomPass };

+ 42 - 0
examples/jsm/shaders/SMAAShader.d.ts

@@ -0,0 +1,42 @@
+import {
+  Uniform
+} from '../../../src/Three';
+
+export interface SMAAEdgesShader {
+  defines: {
+    SMAA_THRESHOLD: string;
+  },
+  uniforms: {
+    tDiffuse: Uniform;
+    resolution: Uniform;
+  };
+  vertexShader: string;
+  fragmentShader: string;
+}
+
+export interface SMAAWeightsShader {
+  defines: {
+    SMAA_MAX_SEARCH_STEPS: string;
+    SMAA_AREATEX_MAX_DISTANCE: string;
+    SMAA_AREATEX_PIXEL_SIZE: string;
+    SMAA_AREATEX_SUBTEX_SIZE: string;
+  },
+  uniforms: {
+    tDiffuse: Uniform;
+    tArea: Uniform;
+    tSearch: Uniform;
+    resolution: Uniform;
+  };
+  vertexShader: string;
+  fragmentShader: string;
+}
+
+export interface SMAAWeightsShader {
+  uniforms: {
+    tDiffuse: Uniform;
+    tColor: Uniform;
+    resolution: Uniform;
+  };
+  vertexShader: string;
+  fragmentShader: string;
+}

+ 472 - 0
examples/jsm/shaders/SMAAShader.js

@@ -0,0 +1,472 @@
+/**
+ * @author mpk / http://polko.me/
+ *
+ * WebGL port of Subpixel Morphological Antialiasing (SMAA) v2.8
+ * Preset: SMAA 1x Medium (with color edge detection)
+ * https://github.com/iryoku/smaa/releases/tag/v2.8
+ */
+
+import {
+	Vector2
+} from "../../../build/three.module.js";
+
+var SMAAEdgesShader = {
+
+	defines: {
+
+		"SMAA_THRESHOLD": "0.1"
+
+	},
+
+	uniforms: {
+
+		"tDiffuse": { value: null },
+		"resolution": { value: new Vector2( 1 / 1024, 1 / 512 ) }
+
+	},
+
+	vertexShader: [
+
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 3 ];",
+
+		"void SMAAEdgeDetectionVS( vec2 texcoord ) {",
+			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -1.0, 0.0, 0.0,  1.0 );", // WebGL port note: Changed sign in W component
+			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4(  1.0, 0.0, 0.0, -1.0 );", // WebGL port note: Changed sign in W component
+			"vOffset[ 2 ] = texcoord.xyxy + resolution.xyxy * vec4( -2.0, 0.0, 0.0,  2.0 );", // WebGL port note: Changed sign in W component
+		"}",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"SMAAEdgeDetectionVS( vUv );",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 3 ];",
+
+		"vec4 SMAAColorEdgeDetectionPS( vec2 texcoord, vec4 offset[3], sampler2D colorTex ) {",
+			"vec2 threshold = vec2( SMAA_THRESHOLD, SMAA_THRESHOLD );",
+
+			// Calculate color deltas:
+			"vec4 delta;",
+			"vec3 C = texture2D( colorTex, texcoord ).rgb;",
+
+			"vec3 Cleft = texture2D( colorTex, offset[0].xy ).rgb;",
+			"vec3 t = abs( C - Cleft );",
+			"delta.x = max( max( t.r, t.g ), t.b );",
+
+			"vec3 Ctop = texture2D( colorTex, offset[0].zw ).rgb;",
+			"t = abs( C - Ctop );",
+			"delta.y = max( max( t.r, t.g ), t.b );",
+
+			// We do the usual threshold:
+			"vec2 edges = step( threshold, delta.xy );",
+
+			// Then discard if there is no edge:
+			"if ( dot( edges, vec2( 1.0, 1.0 ) ) == 0.0 )",
+				"discard;",
+
+			// Calculate right and bottom deltas:
+			"vec3 Cright = texture2D( colorTex, offset[1].xy ).rgb;",
+			"t = abs( C - Cright );",
+			"delta.z = max( max( t.r, t.g ), t.b );",
+
+			"vec3 Cbottom  = texture2D( colorTex, offset[1].zw ).rgb;",
+			"t = abs( C - Cbottom );",
+			"delta.w = max( max( t.r, t.g ), t.b );",
+
+			// Calculate the maximum delta in the direct neighborhood:
+			"float maxDelta = max( max( max( delta.x, delta.y ), delta.z ), delta.w );",
+
+			// Calculate left-left and top-top deltas:
+			"vec3 Cleftleft  = texture2D( colorTex, offset[2].xy ).rgb;",
+			"t = abs( C - Cleftleft );",
+			"delta.z = max( max( t.r, t.g ), t.b );",
+
+			"vec3 Ctoptop = texture2D( colorTex, offset[2].zw ).rgb;",
+			"t = abs( C - Ctoptop );",
+			"delta.w = max( max( t.r, t.g ), t.b );",
+
+			// Calculate the final maximum delta:
+			"maxDelta = max( max( maxDelta, delta.z ), delta.w );",
+
+			// Local contrast adaptation in action:
+			"edges.xy *= step( 0.5 * maxDelta, delta.xy );",
+
+			"return vec4( edges, 0.0, 0.0 );",
+		"}",
+
+		"void main() {",
+
+			"gl_FragColor = SMAAColorEdgeDetectionPS( vUv, vOffset, tDiffuse );",
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+var SMAAWeightsShader = {
+
+	defines: {
+
+		"SMAA_MAX_SEARCH_STEPS": "8",
+		"SMAA_AREATEX_MAX_DISTANCE": "16",
+		"SMAA_AREATEX_PIXEL_SIZE": "( 1.0 / vec2( 160.0, 560.0 ) )",
+		"SMAA_AREATEX_SUBTEX_SIZE": "( 1.0 / 7.0 )"
+
+	},
+
+	uniforms: {
+
+		"tDiffuse": { value: null },
+		"tArea": { value: null },
+		"tSearch": { value: null },
+		"resolution": { value: new Vector2( 1 / 1024, 1 / 512 ) }
+
+	},
+
+	vertexShader: [
+
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 3 ];",
+		"varying vec2 vPixcoord;",
+
+		"void SMAABlendingWeightCalculationVS( vec2 texcoord ) {",
+			"vPixcoord = texcoord / resolution;",
+
+			// We will use these offsets for the searches later on (see @PSEUDO_GATHER4):
+			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -0.25, 0.125, 1.25, 0.125 );", // WebGL port note: Changed sign in Y and W components
+			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4( -0.125, 0.25, -0.125, -1.25 );", // WebGL port note: Changed sign in Y and W components
+
+			// And these for the searches, they indicate the ends of the loops:
+			"vOffset[ 2 ] = vec4( vOffset[ 0 ].xz, vOffset[ 1 ].yw ) + vec4( -2.0, 2.0, -2.0, 2.0 ) * resolution.xxyy * float( SMAA_MAX_SEARCH_STEPS );",
+
+		"}",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"SMAABlendingWeightCalculationVS( vUv );",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"#define SMAASampleLevelZeroOffset( tex, coord, offset ) texture2D( tex, coord + float( offset ) * resolution, 0.0 )",
+
+		"uniform sampler2D tDiffuse;",
+		"uniform sampler2D tArea;",
+		"uniform sampler2D tSearch;",
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[3];",
+		"varying vec2 vPixcoord;",
+
+		"vec2 round( vec2 x ) {",
+			"return sign( x ) * floor( abs( x ) + 0.5 );",
+		"}",
+
+		"float SMAASearchLength( sampler2D searchTex, vec2 e, float bias, float scale ) {",
+			// Not required if searchTex accesses are set to point:
+			// float2 SEARCH_TEX_PIXEL_SIZE = 1.0 / float2(66.0, 33.0);
+			// e = float2(bias, 0.0) + 0.5 * SEARCH_TEX_PIXEL_SIZE +
+			//     e * float2(scale, 1.0) * float2(64.0, 32.0) * SEARCH_TEX_PIXEL_SIZE;
+			"e.r = bias + e.r * scale;",
+			"return 255.0 * texture2D( searchTex, e, 0.0 ).r;",
+		"}",
+
+		"float SMAASearchXLeft( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
+			/**
+			* @PSEUDO_GATHER4
+			* This texcoord has been offset by (-0.25, -0.125) in the vertex shader to
+			* sample between edge, thus fetching four edges in a row.
+			* Sampling with different offsets in each direction allows to disambiguate
+			* which edges are active from the four fetched ones.
+			*/
+			"vec2 e = vec2( 0.0, 1.0 );",
+
+			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
+				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
+				"texcoord -= vec2( 2.0, 0.0 ) * resolution;",
+				"if ( ! ( texcoord.x > end && e.g > 0.8281 && e.r == 0.0 ) ) break;",
+			"}",
+
+			// We correct the previous (-0.25, -0.125) offset we applied:
+			"texcoord.x += 0.25 * resolution.x;",
+
+			// The searches are bias by 1, so adjust the coords accordingly:
+			"texcoord.x += resolution.x;",
+
+			// Disambiguate the length added by the last step:
+			"texcoord.x += 2.0 * resolution.x;", // Undo last step
+			"texcoord.x -= resolution.x * SMAASearchLength(searchTex, e, 0.0, 0.5);",
+
+			"return texcoord.x;",
+		"}",
+
+		"float SMAASearchXRight( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
+			"vec2 e = vec2( 0.0, 1.0 );",
+
+			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
+				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
+				"texcoord += vec2( 2.0, 0.0 ) * resolution;",
+				"if ( ! ( texcoord.x < end && e.g > 0.8281 && e.r == 0.0 ) ) break;",
+			"}",
+
+			"texcoord.x -= 0.25 * resolution.x;",
+			"texcoord.x -= resolution.x;",
+			"texcoord.x -= 2.0 * resolution.x;",
+			"texcoord.x += resolution.x * SMAASearchLength( searchTex, e, 0.5, 0.5 );",
+
+			"return texcoord.x;",
+		"}",
+
+		"float SMAASearchYUp( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
+			"vec2 e = vec2( 1.0, 0.0 );",
+
+			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
+				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
+				"texcoord += vec2( 0.0, 2.0 ) * resolution;", // WebGL port note: Changed sign
+				"if ( ! ( texcoord.y > end && e.r > 0.8281 && e.g == 0.0 ) ) break;",
+			"}",
+
+			"texcoord.y -= 0.25 * resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y -= resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y -= 2.0 * resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y += resolution.y * SMAASearchLength( searchTex, e.gr, 0.0, 0.5 );", // WebGL port note: Changed sign
+
+			"return texcoord.y;",
+		"}",
+
+		"float SMAASearchYDown( sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end ) {",
+			"vec2 e = vec2( 1.0, 0.0 );",
+
+			"for ( int i = 0; i < SMAA_MAX_SEARCH_STEPS; i ++ ) {", // WebGL port note: Changed while to for
+				"e = texture2D( edgesTex, texcoord, 0.0 ).rg;",
+				"texcoord -= vec2( 0.0, 2.0 ) * resolution;", // WebGL port note: Changed sign
+				"if ( ! ( texcoord.y < end && e.r > 0.8281 && e.g == 0.0 ) ) break;",
+			"}",
+
+			"texcoord.y += 0.25 * resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y += resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y += 2.0 * resolution.y;", // WebGL port note: Changed sign
+			"texcoord.y -= resolution.y * SMAASearchLength( searchTex, e.gr, 0.5, 0.5 );", // WebGL port note: Changed sign
+
+			"return texcoord.y;",
+		"}",
+
+		"vec2 SMAAArea( sampler2D areaTex, vec2 dist, float e1, float e2, float offset ) {",
+			// Rounding prevents precision errors of bilinear filtering:
+			"vec2 texcoord = float( SMAA_AREATEX_MAX_DISTANCE ) * round( 4.0 * vec2( e1, e2 ) ) + dist;",
+
+			// We do a scale and bias for mapping to texel space:
+			"texcoord = SMAA_AREATEX_PIXEL_SIZE * texcoord + ( 0.5 * SMAA_AREATEX_PIXEL_SIZE );",
+
+			// Move to proper place, according to the subpixel offset:
+			"texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset;",
+
+			"return texture2D( areaTex, texcoord, 0.0 ).rg;",
+		"}",
+
+		"vec4 SMAABlendingWeightCalculationPS( vec2 texcoord, vec2 pixcoord, vec4 offset[ 3 ], sampler2D edgesTex, sampler2D areaTex, sampler2D searchTex, ivec4 subsampleIndices ) {",
+			"vec4 weights = vec4( 0.0, 0.0, 0.0, 0.0 );",
+
+			"vec2 e = texture2D( edgesTex, texcoord ).rg;",
+
+			"if ( e.g > 0.0 ) {", // Edge at north
+				"vec2 d;",
+
+				// Find the distance to the left:
+				"vec2 coords;",
+				"coords.x = SMAASearchXLeft( edgesTex, searchTex, offset[ 0 ].xy, offset[ 2 ].x );",
+				"coords.y = offset[ 1 ].y;", // offset[1].y = texcoord.y - 0.25 * resolution.y (@CROSSING_OFFSET)
+				"d.x = coords.x;",
+
+				// Now fetch the left crossing edges, two at a time using bilinear
+				// filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to
+				// discern what value each edge has:
+				"float e1 = texture2D( edgesTex, coords, 0.0 ).r;",
+
+				// Find the distance to the right:
+				"coords.x = SMAASearchXRight( edgesTex, searchTex, offset[ 0 ].zw, offset[ 2 ].y );",
+				"d.y = coords.x;",
+
+				// We want the distances to be in pixel units (doing this here allow to
+				// better interleave arithmetic and memory accesses):
+				"d = d / resolution.x - pixcoord.x;",
+
+				// SMAAArea below needs a sqrt, as the areas texture is compressed
+				// quadratically:
+				"vec2 sqrt_d = sqrt( abs( d ) );",
+
+				// Fetch the right crossing edges:
+				"coords.y -= 1.0 * resolution.y;", // WebGL port note: Added
+				"float e2 = SMAASampleLevelZeroOffset( edgesTex, coords, ivec2( 1, 0 ) ).r;",
+
+				// Ok, we know how this pattern looks like, now it is time for getting
+				// the actual area:
+				"weights.rg = SMAAArea( areaTex, sqrt_d, e1, e2, float( subsampleIndices.y ) );",
+			"}",
+
+			"if ( e.r > 0.0 ) {", // Edge at west
+				"vec2 d;",
+
+				// Find the distance to the top:
+				"vec2 coords;",
+
+				"coords.y = SMAASearchYUp( edgesTex, searchTex, offset[ 1 ].xy, offset[ 2 ].z );",
+				"coords.x = offset[ 0 ].x;", // offset[1].x = texcoord.x - 0.25 * resolution.x;
+				"d.x = coords.y;",
+
+				// Fetch the top crossing edges:
+				"float e1 = texture2D( edgesTex, coords, 0.0 ).g;",
+
+				// Find the distance to the bottom:
+				"coords.y = SMAASearchYDown( edgesTex, searchTex, offset[ 1 ].zw, offset[ 2 ].w );",
+				"d.y = coords.y;",
+
+				// We want the distances to be in pixel units:
+				"d = d / resolution.y - pixcoord.y;",
+
+				// SMAAArea below needs a sqrt, as the areas texture is compressed
+				// quadratically:
+				"vec2 sqrt_d = sqrt( abs( d ) );",
+
+				// Fetch the bottom crossing edges:
+				"coords.y -= 1.0 * resolution.y;", // WebGL port note: Added
+				"float e2 = SMAASampleLevelZeroOffset( edgesTex, coords, ivec2( 0, 1 ) ).g;",
+
+				// Get the area for this direction:
+				"weights.ba = SMAAArea( areaTex, sqrt_d, e1, e2, float( subsampleIndices.x ) );",
+			"}",
+
+			"return weights;",
+		"}",
+
+		"void main() {",
+
+			"gl_FragColor = SMAABlendingWeightCalculationPS( vUv, vPixcoord, vOffset, tDiffuse, tArea, tSearch, ivec4( 0.0 ) );",
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+var SMAABlendShader = {
+
+	uniforms: {
+
+		"tDiffuse": { value: null },
+		"tColor": { value: null },
+		"resolution": { value: new Vector2( 1 / 1024, 1 / 512 ) }
+
+	},
+
+	vertexShader: [
+
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 2 ];",
+
+		"void SMAANeighborhoodBlendingVS( vec2 texcoord ) {",
+			"vOffset[ 0 ] = texcoord.xyxy + resolution.xyxy * vec4( -1.0, 0.0, 0.0, 1.0 );", // WebGL port note: Changed sign in W component
+			"vOffset[ 1 ] = texcoord.xyxy + resolution.xyxy * vec4( 1.0, 0.0, 0.0, -1.0 );", // WebGL port note: Changed sign in W component
+		"}",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"SMAANeighborhoodBlendingVS( vUv );",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+		"uniform sampler2D tColor;",
+		"uniform vec2 resolution;",
+
+		"varying vec2 vUv;",
+		"varying vec4 vOffset[ 2 ];",
+
+		"vec4 SMAANeighborhoodBlendingPS( vec2 texcoord, vec4 offset[ 2 ], sampler2D colorTex, sampler2D blendTex ) {",
+			// Fetch the blending weights for current pixel:
+			"vec4 a;",
+			"a.xz = texture2D( blendTex, texcoord ).xz;",
+			"a.y = texture2D( blendTex, offset[ 1 ].zw ).g;",
+			"a.w = texture2D( blendTex, offset[ 1 ].xy ).a;",
+
+			// Is there any blending weight with a value greater than 0.0?
+			"if ( dot(a, vec4( 1.0, 1.0, 1.0, 1.0 )) < 1e-5 ) {",
+				"return texture2D( colorTex, texcoord, 0.0 );",
+			"} else {",
+				// Up to 4 lines can be crossing a pixel (one through each edge). We
+				// favor blending by choosing the line with the maximum weight for each
+				// direction:
+				"vec2 offset;",
+				"offset.x = a.a > a.b ? a.a : -a.b;", // left vs. right
+				"offset.y = a.g > a.r ? -a.g : a.r;", // top vs. bottom // WebGL port note: Changed signs
+
+				// Then we go in the direction that has the maximum weight:
+				"if ( abs( offset.x ) > abs( offset.y )) {", // horizontal vs. vertical
+					"offset.y = 0.0;",
+				"} else {",
+					"offset.x = 0.0;",
+				"}",
+
+				// Fetch the opposite color and lerp by hand:
+				"vec4 C = texture2D( colorTex, texcoord, 0.0 );",
+				"texcoord += sign( offset ) * resolution;",
+				"vec4 Cop = texture2D( colorTex, texcoord, 0.0 );",
+				"float s = abs( offset.x ) > abs( offset.y ) ? abs( offset.x ) : abs( offset.y );",
+
+				// WebGL port note: Added gamma correction
+				"C.xyz = pow(C.xyz, vec3(2.2));",
+				"Cop.xyz = pow(Cop.xyz, vec3(2.2));",
+				"vec4 mixed = mix(C, Cop, s);",
+				"mixed.xyz = pow(mixed.xyz, vec3(1.0 / 2.2));",
+
+				"return mixed;",
+			"}",
+		"}",
+
+		"void main() {",
+
+			"gl_FragColor = SMAANeighborhoodBlendingPS( vUv, vOffset, tColor, tDiffuse );",
+
+		"}"
+
+	].join( "\n" )
+
+};
+
+export { SMAAEdgesShader, SMAAWeightsShader, SMAABlendShader };

+ 8 - 0
utils/modularize.js

@@ -1,5 +1,6 @@
 /**
  * @author mrdoob / http://mrdoob.com/
+ * @author Mugen87 / https://github.com/Mugen87
  */
 
 var fs = require( 'fs' );
@@ -79,10 +80,16 @@ var files = [
 	{ path: 'postprocessing/GlitchPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'DigitalGlitch', path: 'shaders/DigitalGlitch.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/HalftonePass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'HalftoneShader', path: 'shaders/HalftoneShader.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/MaskPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' } ], ignoreList: [] },
+	{ path: 'postprocessing/OutlinePass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/RenderPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' } ], ignoreList: [] },
+	{ path: 'postprocessing/SAOPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'SAOShader', path: 'shaders/SAOShader.js' }, { name: 'DepthLimitedBlurShader', path: 'shaders/DepthLimitedBlurShader.js' }, { name: 'BlurShaderUtils', path: 'shaders/DepthLimitedBlurShader.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' }, { name: 'UnpackDepthRGBAShader', path: 'shaders/UnpackDepthRGBAShader.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/SavePass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/ShaderPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' } ], ignoreList: [] },
+	{ path: 'postprocessing/SMAAPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'SMAAEdgesShader', path: 'shaders/SMAAShader.js' }, { name: 'SMAAWeightsShader', path: 'shaders/SMAAShader.js' }, { name: 'SMAABlendShader', path: 'shaders/SMAAShader.js' } ], ignoreList: [] },
+	{ path: 'postprocessing/SSAARenderPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' } ], ignoreList: [] },
+	{ path: 'postprocessing/TAARenderPass.js', dependencies: [ { name: 'SSAARenderPass', path: 'postprocessing/SSAARenderPass.js' } ], ignoreList: [] },
 	{ path: 'postprocessing/TexturePass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' } ], ignoreList: [] },
+	{ path: 'postprocessing/UnrealBloomPass.js', dependencies: [ { name: 'Pass', path: 'postprocessing/Pass.js' }, { name: 'CopyShader', path: 'shaders/CopyShader.js' }, { name: 'LuminosityHighPassShader', path: 'shaders/LuminosityHighPassShader.js' } ], ignoreList: [] },
 
 	{ path: 'renderers/CSS2DRenderer.js', dependencies: [], ignoreList: [] },
 	{ path: 'renderers/CSS3DRenderer.js', dependencies: [], ignoreList: [] },
@@ -126,6 +133,7 @@ var files = [
 	{ path: 'shaders/RGBShiftShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/SAOShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/SepiaShader.js', dependencies: [], ignoreList: [] },
+	{ path: 'shaders/SMAAShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/SobelOperatorShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/SSAOShader.js', dependencies: [], ignoreList: [] },
 	{ path: 'shaders/TechnicolorShader.js', dependencies: [], ignoreList: [] },

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.