Browse Source

Bloom Pass - Inspired from Unreal Engine (#9351)

* Bloom Pass Inspired from Unreal
* Extract Bright Pass
* Mip Map blur Pass
* Combine Pass

* resize function in Bloom

resize function in Bloom

* Bug Fixes and a few Changes

* Resize bug fix
* changed blur filter. Need to reinvestigate, the older blur sometime
* Changed kernel size. was too big.
spidersharma03 9 years ago
parent
commit
22a4c76306

+ 1 - 0
examples/files.js

@@ -205,6 +205,7 @@ var files = {
 		"webgl_postprocessing_smaa",
 		"webgl_postprocessing_smaa",
 		"webgl_postprocessing_ssao",
 		"webgl_postprocessing_ssao",
 		"webgl_postprocessing_taa",
 		"webgl_postprocessing_taa",
+		"webgl_postprocessing_unreal_bloom",
 		"webgl_raycast_texture",
 		"webgl_raycast_texture",
 		"webgl_read_float_buffer",
 		"webgl_read_float_buffer",
 		"webgl_rtt",
 		"webgl_rtt",

+ 329 - 0
examples/js/postprocessing/UnrealBloomPass.js

@@ -0,0 +1,329 @@
+/**
+ * @author spidersharma / http://eduperiment.com/
+ 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 );
+
+	this.strength = ( strength !== undefined ) ? strength : 1;
+	this.radius = radius;
+	this.threshold = threshold;
+	this.resolution = ( resolution !== undefined ) ? new THREE.Vector2(resolution.x, resolution.y) : new THREE.Vector2(256, 256);
+
+	// render targets
+	var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.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 THREE.WebGLRenderTarget( resx, resy, pars );
+	this.renderTargetBright.texture.generateMipmaps = false;
+
+	for( var i=0; i<this.nMips; i++) {
+
+		var renderTarget = new THREE.WebGLRenderTarget( resx, resy, pars );
+
+		renderTarget.texture.generateMipmaps = false;
+
+		this.renderTargetsHorizontal.push(renderTarget);
+
+		var renderTarget = new THREE.WebGLRenderTarget( resx, resy, pars );
+
+		renderTarget.texture.generateMipmaps = false;
+
+		this.renderTargetsVertical.push(renderTarget);
+
+		resx = Math.round(resx/2);
+
+		resy = Math.round(resy/2);
+	}
+
+	// luminosity high pass material
+
+	if ( THREE.LuminosityHighPassShader === undefined )
+		console.error( "THREE.UnrealBloomPass relies on THREE.LuminosityHighPassShader" );
+
+	var highPassShader = THREE.LuminosityHighPassShader;
+	this.highPassUniforms = THREE.UniformsUtils.clone( highPassShader.uniforms );
+
+	this.highPassUniforms[ "luminosityThreshold" ].value = threshold;
+	this.highPassUniforms[ "smoothWidth" ].value = 0.01;
+
+	this.materialHighPassFilter = new THREE.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 THREE.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 THREE.Vector3(1,1,1), new THREE.Vector3(1,1,1), new THREE.Vector3(1,1,1)
+												,new THREE.Vector3(1,1,1), new THREE.Vector3(1,1,1)];
+	this.compositeMaterial.uniforms["bloomTintColors"].value = this.bloomTintColors;
+
+	// copy material
+	if ( THREE.CopyShader === undefined )
+		console.error( "THREE.BloomPass relies on THREE.CopyShader" );
+
+	var copyShader = THREE.CopyShader;
+
+	this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
+	this.copyUniforms[ "opacity" ].value = 1.0;
+
+	this.materialCopy = new THREE.ShaderMaterial( {
+		uniforms: this.copyUniforms,
+		vertexShader: copyShader.vertexShader,
+		fragmentShader: copyShader.fragmentShader,
+		blending: THREE.AdditiveBlending,
+		depthTest: false,
+		depthWrite: false,
+		transparent: true
+	} );
+
+	this.enabled = true;
+	this.needsSwap = false;
+
+	this.oldClearColor = new THREE.Color();
+	this.oldClearAlpha = 1;
+
+	this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+	this.scene  = new THREE.Scene();
+
+	this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
+	this.scene.add( this.quad );
+
+};
+
+THREE.UnrealBloomPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {
+
+	constructor: THREE.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 THREE.Vector2(resx, resy);
+
+			resx = Math.round(resx/2);
+			resy = Math.round(resy/2);
+		}
+	},
+
+	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+
+		this.oldClearColor.copy( renderer.getClearColor() );
+		this.oldClearAlpha = renderer.getClearAlpha();
+		var oldAutoClear = renderer.autoClear;
+		renderer.autoClear = false;
+
+		renderer.setClearColor( new THREE.Color( 0, 0, 0 ), 0 );
+
+		if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST );
+
+		// 1. Extract Bright Areas
+		this.highPassUniforms[ "tDiffuse" ].value = readBuffer.texture;
+		this.highPassUniforms[ "luminosityThreshold" ].value = this.threshold;
+		this.quad.material = this.materialHighPassFilter;
+		renderer.render( this.scene, this.camera, this.renderTargetBright, true );
+
+		// 2. Blur All the mips progressively
+		var inputRenderTarget = this.renderTargetBright;
+
+		for(var i=0; i<this.nMips; i++) {
+
+			this.quad.material = this.separableBlurMaterials[i];
+
+			this.separableBlurMaterials[i].uniforms[ "colorTexture" ].value = inputRenderTarget.texture;
+
+			this.separableBlurMaterials[i].uniforms[ "direction" ].value = THREE.UnrealBloomPass.BlurDirectionX;
+
+			renderer.render( this.scene, this.camera, this.renderTargetsHorizontal[i], true );
+
+			this.separableBlurMaterials[i].uniforms[ "colorTexture" ].value = this.renderTargetsHorizontal[i].texture;
+
+			this.separableBlurMaterials[i].uniforms[ "direction" ].value = THREE.UnrealBloomPass.BlurDirectionY;
+
+			renderer.render( this.scene, this.camera, this.renderTargetsVertical[i], true );
+
+			inputRenderTarget = this.renderTargetsVertical[i];
+		}
+
+		// Composite All the mips
+		this.quad.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.render( this.scene, this.camera, this.renderTargetsHorizontal[0], true );
+
+		// Blend it additively over the input texture
+		this.quad.material = this.materialCopy;
+		this.copyUniforms[ "tDiffuse" ].value = this.renderTargetsHorizontal[0].texture;
+
+		if ( maskActive ) renderer.context.enable( renderer.context.STENCIL_TEST );
+
+		renderer.render( this.scene, this.camera, readBuffer, false );
+
+		renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
+		renderer.autoClear = oldAutoClear;
+	},
+
+	getSeperableBlurMaterial: function(kernelRadius) {
+
+		return new THREE.ShaderMaterial( {
+
+			defines: {
+				"KERNEL_RADIUS" : kernelRadius,
+				"SIGMA" : kernelRadius
+			},
+
+			uniforms: {
+				"colorTexture": { value: null },
+				"texSize": 				{ value: new THREE.Vector2( 0.5, 0.5 ) },
+				"direction": 				{ value: new THREE.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;\
+				\
+				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 THREE.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) );\
+				}"
+		} );
+	}
+
+} );
+
+THREE.UnrealBloomPass.BlurDirectionX = new THREE.Vector2( 1.0, 0.0 );
+THREE.UnrealBloomPass.BlurDirectionY = new THREE.Vector2( 0.0, 1.0 );

+ 64 - 0
examples/js/shaders/LuminosityHighPassShader.js

@@ -0,0 +1,64 @@
+/**
+ * @author bhouston / http://clara.io/
+ *
+ * Luminosity
+ * http://en.wikipedia.org/wiki/Luminosity
+ */
+
+THREE.LuminosityHighPassShader = {
+
+  shaderID: "luminosityHighPass",
+
+	uniforms: {
+
+		"tDiffuse": { type: "t", value: null },
+		"luminosityThreshold": { type: "f", value: 1.0 },
+		"smoothWidth": { type: "f", value: 1.0 },
+		"defaultColor": { type: "c", value: new THREE.Color( 0x000000 ) },
+		"defaultOpacity":  { type: "f", value: 0.0 },
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+		"uniform vec3 defaultColor;",
+		"uniform float defaultOpacity;",
+		"uniform float luminosityThreshold;",
+		"uniform float smoothWidth;",
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vec4 texel = texture2D( tDiffuse, vUv );",
+
+			"vec3 luma = vec3( 0.299, 0.587, 0.114 );",
+
+			"float v = dot( texel.xyz, luma );",
+
+			"vec4 outputColor = vec4( defaultColor.rgb, defaultOpacity );",
+
+			"float alpha = smoothstep( luminosityThreshold, luminosityThreshold + smoothWidth, v );",
+
+			"gl_FragColor = mix( outputColor, texel, alpha );",
+
+		"}"
+
+	].join("\n")
+
+};

+ 275 - 0
examples/webgl_postprocessing_unreal_bloom.html

@@ -0,0 +1,275 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>threejs webgl - materials - hdr environment mapping</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				background-color: #000000;
+				margin: 0px;
+				overflow: hidden;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				font-weight: bold;
+			}
+			a {
+				color:#00ff78;
+			}
+
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				display:block;
+			}
+			.dg.ac {
+				z-index: 1 !important; /* FIX DAT.GUI */
+			}
+		</style>
+	</head>
+	<body>
+
+		<script src="../build/three.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/loaders/RGBELoader.js"></script>
+		<script src="js/loaders/HDRCubeTextureLoader.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script src="js/Half.js"></script>
+		<script src="js/Encodings.js"></script>
+		<script src="js/pmrem/PMREMGenerator.js"></script>
+		<script src="js/pmrem/PMREMCubeUVPacker.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/postprocessing/EffectComposer.js"></script>
+		<script src="js/postprocessing/RenderPass.js"></script>
+		<script src="js/postprocessing/MaskPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/FXAAShader.js"></script>
+		<script src="js/shaders/ConvolutionShader.js"></script>
+		<script src="js/shaders/LuminosityHighPassShader.js"></script>
+		<script src="js/postprocessing/UnrealBloomPass.js"></script>
+
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - Bloom pass by <a href="http://eduperiment.com" target="_blank">Prashant Sharma</a> and <a href="https://clara.io" target="_blank">Ben Houston</a><br/><br/>
+			This Bloom Pass is inspired by the bloom pass of the Unreal Engine. It creates a mip map chain of bloom textures and blur them <br>
+			with different radii. Because of the weigted combination of mips, and since larger blurs are done on higher mips this bloom <br>
+			is better in quality and performance.<br>
+		</div>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var container, stats;
+			var params = {
+				projection: 'normal',
+				background: false,
+				exposure: 1.0,
+				bloomStrength: 1.5,
+				bloomThreshold: 0.85,
+				bloomRadius: 0.4
+			};
+			var camera, scene, renderer, controls, objects = [];
+			var effectFXAA, bloomPass, renderScene;
+			var hdrCubeMap;
+			var composer;
+			var standardMaterial;
+			var hdrCubeRenderTarget;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.set( 0.0, 35, 35 * 3.5 );
+
+				scene = new THREE.Scene();
+
+				renderer = new THREE.WebGLRenderer( { antialias: false } );
+				renderer.setClearColor( new THREE.Color( 0x111111 ) );
+				renderer.toneMapping = THREE.LinearToneMapping;
+
+				standardMaterial = new THREE.MeshStandardMaterial( {
+					map: null,
+					color: 0xffffff,
+					metalness: 1.0,
+					shading: THREE.SmoothShading
+				} );
+
+				var geometry = new THREE.TorusKnotGeometry( 18, 8, 150, 20 );;
+				var torusMesh1 = new THREE.Mesh( geometry, standardMaterial );
+				torusMesh1.position.x = 0.0;
+				torusMesh1.castShadow = true;
+				torusMesh1.receiveShadow = true;
+				scene.add( torusMesh1 );
+				objects.push( torusMesh1 );
+
+				var textureLoader = new THREE.TextureLoader();
+				textureLoader.load( "./textures/roughness_map.jpg", function( map ) {
+					map.wrapS = THREE.RepeatWrapping;
+					map.wrapT = THREE.RepeatWrapping;
+					map.anisotropy = 4;
+					map.repeat.set( 9, 2 );
+					standardMaterial.roughnessMap = map;
+					standardMaterial.bumpMap = map;
+					standardMaterial.needsUpdate = true;
+				} );
+
+				var genCubeUrls = function( prefix, postfix ) {
+					return [
+						prefix + 'px' + postfix, prefix + 'nx' + postfix,
+						prefix + 'py' + postfix, prefix + 'ny' + postfix,
+						prefix + 'pz' + postfix, prefix + 'nz' + postfix
+					];
+				};
+
+				var hdrUrls = genCubeUrls( "./textures/cube/pisaHDR/", ".hdr" );
+				new THREE.HDRCubeTextureLoader().load( THREE.UnsignedByteType, hdrUrls, function ( hdrCubeMap ) {
+
+					var pmremGenerator = new THREE.PMREMGenerator( hdrCubeMap );
+					pmremGenerator.update( renderer );
+
+					var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
+					pmremCubeUVPacker.update( renderer );
+
+					hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+
+				} );
+				// Lights
+
+				scene.add( new THREE.AmbientLight( 0x222222 ) );
+
+				var spotLight = new THREE.SpotLight( 0xffffff );
+				spotLight.position.set( 50, 100, 50 );
+				spotLight.angle = Math.PI / 7;
+				spotLight.penumbra = 0.8
+				spotLight.castShadow = true;
+				scene.add( spotLight );
+
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.shadowMap.enabled = true;
+				container.appendChild( renderer.domElement );
+
+				renderScene = new THREE.RenderPass(scene, camera);
+
+		    // renderScene.clear = true;
+		    effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
+		    effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight );
+
+				var copyShader = new THREE.ShaderPass(THREE.CopyShader);
+				copyShader.renderToScreen = true;
+
+		    bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);//1.0, 9, 0.5, 512);
+				composer = new THREE.EffectComposer(renderer);
+		    composer.setSize(window.innerWidth, window.innerHeight);
+		    composer.addPass(renderScene);
+				composer.addPass(effectFXAA);
+		    composer.addPass(bloomPass);
+				composer.addPass(copyShader);
+				//renderer.toneMapping = THREE.ReinhardToneMapping;
+				renderer.gammaInput = true;
+				renderer.gammaOutput = true;
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, 0, 0 );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				var gui = new dat.GUI();
+
+				gui.add( params, 'exposure', 0.1, 2 );
+				gui.add( params, 'bloomThreshold', 0.0, 1.0 ).onChange( function(value) {
+		        bloomPass.threshold = Number(value);
+		    });
+				gui.add( params, 'bloomStrength', 0.0, 3.0 ).onChange( function(value) {
+		        bloomPass.strength = Number(value);
+		    });
+				gui.add( params, 'bloomRadius', 0.0, 1.0 ).onChange( function(value) {
+		        bloomPass.radius = Number(value);
+		    });
+				gui.open();
+
+			}
+
+			function onWindowResize() {
+
+				var width = window.innerWidth;
+				var height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+				composer.setSize( width, height );
+				effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight );
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				stats.begin();
+				render();
+				stats.end();
+
+			}
+
+			function render() {
+
+				if ( standardMaterial !== undefined ) {
+
+					standardMaterial.roughness = 1.0;
+					standardMaterial.bumpScale = - 0.05;
+
+					var newEnvMap = standardMaterial.envMap;
+					newEnvMap = hdrCubeRenderTarget ? hdrCubeRenderTarget.texture : null;
+
+					if( newEnvMap !== standardMaterial.envMap ) {
+
+						standardMaterial.envMap = newEnvMap;
+						standardMaterial.needsUpdate = true;
+
+					}
+
+				}
+
+				renderer.toneMappingExposure = Math.pow( params.exposure, 4.0 );
+
+				var timer = Date.now() * 0.00025;
+
+				camera.lookAt( scene.position );
+
+				for ( var i = 0, l = objects.length; i < l; i ++ ) {
+
+					var object = objects[ i ];
+					object.rotation.y += 0.005;
+
+				}
+
+				// renderer.render( scene, camera );
+				composer.render();
+			}
+
+		</script>
+
+	</body>
+</html>

+ 6 - 0
src/renderers/shaders/ShaderChunk/common.glsl

@@ -72,3 +72,9 @@ vec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 poi
 	return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;
 	return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;
 
 
 }
 }
+
+float gaussianPdf(in float x, in float sigma) {
+
+	return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
+
+}