Ver Fonte

Merge pull request #5612 from Verold/adaptiveToneMappingHDR

Adding adaptive tone-mapping postprocessing pass and example
Mr.doob há 10 anos atrás
pai
commit
164d40fe99

+ 1 - 0
examples/index.html

@@ -102,6 +102,7 @@
 
 		var files = {
 			"webgl": [
+				"webgl_adaptive_tonemapping",
 				"webgl_animation_cloth",
 				"webgl_animation_skinning_blending",
 				"webgl_animation_skinning_morph",

+ 264 - 0
examples/js/postprocessing/AdaptiveToneMappingPass.js

@@ -0,0 +1,264 @@
+/**
+ * @author miibond
+ * Generate a texture that represents the luminosity of the current scene, adapted over time
+ * to simulate the optic nerve responding to the amount of light it is receiving.
+ * Based on a GDC2007 presentation by Wolfgang Engel titled "Post-Processing Pipeline"
+ *
+ * Full-screen tone-mapping shader based on http://www.graphics.cornell.edu/~jaf/publications/sig02_paper.pdf
+ */
+
+THREE.AdaptiveToneMappingPass = function ( adaptive, resolution ) {
+
+	this.resolution = ( resolution !== undefined ) ? resolution : 256;
+	this.needsInit = true;
+	this.adaptive = adaptive !== undefined? !!adaptive : true;
+
+	this.luminanceRT = null;
+	this.previousLuminanceRT = null;
+	this.currentLuminanceRT = null;
+	
+	if ( THREE.CopyShader === undefined )
+		console.error( "THREE.AdaptiveToneMappingPass relies on THREE.CopyShader" );
+
+	var copyShader = THREE.CopyShader;
+
+	this.copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
+
+	this.materialCopy = new THREE.ShaderMaterial( {
+
+		uniforms: this.copyUniforms,
+		vertexShader: copyShader.vertexShader,
+		fragmentShader: copyShader.fragmentShader,
+		blending: THREE.NoBlending,
+		depthTest: false
+
+	} );
+
+	if ( THREE.LuminosityShader === undefined )
+		console.error( "THREE.AdaptiveToneMappingPass relies on THREE.LuminosityShader" );
+
+	this.materialLuminance = new THREE.ShaderMaterial( {
+
+		uniforms: THREE.LuminosityShader.uniforms,
+		vertexShader: THREE.LuminosityShader.vertexShader,
+		fragmentShader: THREE.LuminosityShader.fragmentShader,
+		blending: THREE.NoBlending,
+	} );
+
+	this.adaptLuminanceShader = {
+		defines: {
+			"MIP_LEVEL_1X1" : Math.log2( this.resolution ).toFixed(1),
+		},
+		uniforms: {
+			"lastLum": { type: "t", value: null },
+			"currentLum": { type: "t", value: null },
+			"delta": { type: 'f', value: 0.016 },
+			"tau": { type: 'f', value: 1.0 }
+		},
+		vertexShader: [
+			"varying vec2 vUv;",
+
+			"void main() {",
+
+				"vUv = uv;",
+				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+		].join('\n'),
+		fragmentShader: [
+			"varying vec2 vUv;",
+
+			"uniform sampler2D lastLum;",
+			"uniform sampler2D currentLum;",
+			"uniform float delta;",
+			"uniform float tau;",
+			
+			"void main() {",
+
+				"vec4 lastLum = texture2D( lastLum, vUv, MIP_LEVEL_1X1 );",
+				"vec4 currentLum = texture2D( currentLum, vUv, MIP_LEVEL_1X1 );",
+				
+				"float fLastLum = lastLum.r;",
+				"float fCurrentLum = currentLum.r;",
+				
+				//The adaption seems to work better in extreme lighting differences
+				//if the input luminance is squared.
+				"fCurrentLum *= fCurrentLum;",
+
+				// Adapt the luminance using Pattanaik's technique
+				"float fAdaptedLum = fLastLum + (fCurrentLum - fLastLum) * (1.0 - exp(-delta * tau));",
+				// "fAdaptedLum = sqrt(fAdaptedLum);",
+				"gl_FragColor = vec4( vec3( fAdaptedLum ), 1.0 );",
+			"}",
+		].join('\n')
+	};
+
+	this.materialAdaptiveLum = new THREE.ShaderMaterial( {
+
+		uniforms: this.adaptLuminanceShader.uniforms,
+		vertexShader: this.adaptLuminanceShader.vertexShader,
+		fragmentShader: this.adaptLuminanceShader.fragmentShader,
+		defines: this.adaptLuminanceShader.defines,
+		blending: THREE.NoBlending
+	} );
+
+	if ( THREE.ToneMapShader === undefined )
+		console.error( "THREE.AdaptiveToneMappingPass relies on THREE.ToneMapShader" );
+
+	this.materialToneMap = new THREE.ShaderMaterial( {
+
+		uniforms: THREE.ToneMapShader.uniforms,
+		vertexShader: THREE.ToneMapShader.vertexShader,
+		fragmentShader: THREE.ToneMapShader.fragmentShader,
+		blending: THREE.NoBlending
+	} );
+
+	this.enabled = true;
+	this.needsSwap = true;
+	this.clear = false;
+
+	this.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
+	this.scene  = new THREE.Scene();
+
+	this.quad = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), null );
+	this.scene.add( this.quad );
+
+};
+
+THREE.AdaptiveToneMappingPass.prototype = {
+
+	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+
+		if ( this.needsInit ) {
+			this.reset( renderer );
+			this.luminanceRT.type = readBuffer.type;
+			this.previousLuminanceRT.type = readBuffer.type;
+			this.currentLuminanceRT.type = readBuffer.type;
+			this.needsInit = false;
+		}
+
+		if ( this.adaptive ) {
+			//Render the luminance of the current scene into a render target with mipmapping enabled
+			this.quad.material = this.materialLuminance;
+			this.materialLuminance.uniforms.tDiffuse.value = readBuffer;
+			renderer.render( this.scene, this.camera, this.currentLuminanceRT );
+
+			//Use the new luminance values, the previous luminance and the frame delta to
+			//adapt the luminance over time.
+			this.quad.material = this.materialAdaptiveLum;
+			this.materialAdaptiveLum.uniforms.delta.value = delta;
+			this.materialAdaptiveLum.uniforms.lastLum.value = this.previousLuminanceRT;
+			this.materialAdaptiveLum.uniforms.currentLum.value = this.currentLuminanceRT;
+			renderer.render( this.scene, this.camera, this.luminanceRT );
+
+			//Copy the new adapted luminance value so that it can be used by the next frame.
+			this.quad.material = this.materialCopy;
+			this.copyUniforms.tDiffuse.value = this.luminanceRT;
+			renderer.render( this.scene, this.camera, this.previousLuminanceRT );
+		}
+
+		this.quad.material = this.materialToneMap;
+		this.materialToneMap.uniforms.tDiffuse.value = readBuffer;
+		renderer.render( this.scene, this.camera, writeBuffer, this.clear );
+		
+	},
+
+	reset: function( renderer ) {
+		// render targets
+		if ( this.luminanceRT ) {
+			this.luminanceRT.dispose();
+		}
+		if ( this.currentLuminanceRT ) {
+			this.currentLuminanceRT.dispose();
+		}
+		if ( this.previousLuminanceRT ) {
+			this.previousLuminanceRT.dispose();
+		}
+		var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat };
+
+		this.luminanceRT = new THREE.WebGLRenderTarget( this.resolution, this.resolution, pars );
+		this.luminanceRT.generateMipmaps = false;
+		this.previousLuminanceRT = new THREE.WebGLRenderTarget( this.resolution, this.resolution, pars );
+		this.previousLuminanceRT.generateMipmaps = false;
+
+		//We only need mipmapping for the current luminosity because we want a down-sampled version to sample in our adaptive shader
+		pars.minFilter = THREE.LinearMipMapLinearFilter;
+		this.currentLuminanceRT = new THREE.WebGLRenderTarget( this.resolution, this.resolution, pars );
+		
+		if ( this.adaptive ) {
+			this.materialToneMap.defines["ADAPTED_LUMINANCE"] = "";
+			this.materialToneMap.uniforms.luminanceMap.value = this.luminanceRT;
+		}
+		//Put something in the adaptive luminance texture so that the scene can render initially
+		this.quad.material = new THREE.MeshBasicMaterial( {color: 0x777777 });
+		this.materialLuminance.needsUpdate = true;
+		this.materialAdaptiveLum.needsUpdate = true;
+		this.materialToneMap.needsUpdate = true;
+		// renderer.render( this.scene, this.camera, this.luminanceRT );
+		// renderer.render( this.scene, this.camera, this.previousLuminanceRT );
+		// renderer.render( this.scene, this.camera, this.currentLuminanceRT );
+	},
+
+	setAdaptive: function( adaptive ) {
+		if ( adaptive ) {
+			this.adaptive = true;
+			this.materialToneMap.defines["ADAPTED_LUMINANCE"] = "";
+			this.materialToneMap.uniforms.luminanceMap.value = this.luminanceRT;
+		}
+		else {
+			this.adaptive = false;
+			delete this.materialToneMap.defines["ADAPTED_LUMINANCE"];
+			this.materialToneMap.uniforms.luminanceMap.value = undefined;
+		}
+		this.materialToneMap.needsUpdate = true;
+	},
+
+	setAdaptionRate: function( rate ) {
+		if ( rate ) {
+			this.materialAdaptiveLum.uniforms.tau.value = Math.abs( rate );
+		}
+	},
+
+	setMaxLuminance: function( maxLum ) {
+		if ( maxLum ) {
+			this.materialToneMap.uniforms.maxLuminance.value = maxLum;
+		}
+	},
+
+	setAverageLuminance: function( avgLum ) {
+		if ( avgLum ) {
+			this.materialToneMap.uniforms.averageLuminance.value = avgLum;
+		}
+	},
+
+	setMiddleGrey: function( middleGrey ) {
+		if ( middleGrey ) {
+			this.materialToneMap.uniforms.middleGrey.value = middleGrey;
+		}
+	},
+
+	dispose: function() {
+		if ( this.luminanceRT ) {
+			this.luminanceRT.dispose();
+		}
+		if ( this.previousLuminanceRT ) {
+			this.previousLuminanceRT.dispose();
+		}
+		if ( this.currentLuminanceRT ) {
+			this.currentLuminanceRT.dispose();
+		}
+		if ( this.materialLuminance ) {
+			this.materialLuminance.dispose();
+		}
+		if ( this.materialAdaptiveLum ) {
+			this.materialAdaptiveLum.dispose();
+		}
+		if ( this.materialCopy ) {
+			this.materialCopy.dispose();
+		}
+		if ( this.materialToneMap ) {
+			this.materialToneMap.dispose();
+		}
+	}
+
+};

+ 77 - 0
examples/js/shaders/ToneMapShader.js

@@ -0,0 +1,77 @@
+/**
+ * @author miibond
+ *
+ * Full-screen tone-mapping shader based on http://www.graphics.cornell.edu/~jaf/publications/sig02_paper.pdf
+ */
+
+THREE.ToneMapShader = {
+
+	uniforms: {
+
+		"tDiffuse": { type: "t", value: null },
+		"averageLuminance":  { type: "f", value: 1.0 },
+		"luminanceMap":  { type: "t", value: null },
+		"maxLuminance":  { type: "f", value: 16.0 },
+		"middleGrey":  { type: "f", value: 0.6 }
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+
+		"varying vec2 vUv;",
+
+		"uniform float middleGrey;",
+		"uniform float maxLuminance;",
+		"#ifdef ADAPTED_LUMINANCE",
+			"uniform sampler2D luminanceMap;",
+		"#else",
+			"uniform float averageLuminance;",
+		"#endif",
+		
+		"const vec3 LUM_CONVERT = vec3(0.299, 0.587, 0.114);",
+
+		"vec3 ToneMap( vec3 vColor ) {",
+			"#ifdef ADAPTED_LUMINANCE",
+				// Get the calculated average luminance 
+				"float fLumAvg = texture2D(luminanceMap, vec2(0.5, 0.5)).r;",
+			"#else",
+				"float fLumAvg = averageLuminance;",
+			"#endif",
+			
+			// Calculate the luminance of the current pixel
+			"float fLumPixel = dot(vColor, LUM_CONVERT);",
+
+			// Apply the modified operator (Eq. 4)
+			"float fLumScaled = (fLumPixel * middleGrey) / fLumAvg;",
+
+			"float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (maxLuminance * maxLuminance)))) / (1.0 + fLumScaled);",
+			"return fLumCompressed * vColor;",
+		"}",
+
+		"void main() {",
+
+			"vec4 texel = texture2D( tDiffuse, vUv );",
+			
+			"gl_FragColor = vec4( ToneMap( texel.xyz ), texel.w );",
+			//Gamma 2.0
+			"gl_FragColor.xyz = sqrt( gl_FragColor.xyz );",
+
+		"}"
+
+	].join("\n")
+
+};

BIN
examples/textures/cube/MilkyWay/dark-s_nx.jpg


BIN
examples/textures/cube/MilkyWay/dark-s_ny.jpg


BIN
examples/textures/cube/MilkyWay/dark-s_nz.jpg


BIN
examples/textures/cube/MilkyWay/dark-s_px.jpg


BIN
examples/textures/cube/MilkyWay/dark-s_py.jpg


BIN
examples/textures/cube/MilkyWay/dark-s_pz.jpg


BIN
examples/textures/planets/earth_atmos_4096.jpg


BIN
examples/textures/planets/earth_clouds_2048.png


BIN
examples/textures/planets/earth_lights_2048.png


+ 471 - 0
examples/webgl_adaptive_tonemapping.html

@@ -0,0 +1,471 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - adaptive tone-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:#000;
+				color:#fff;
+				padding:0;
+				margin:0;
+				overflow:hidden;
+				font-family:georgia;
+				text-align:center;
+			}
+			h1 { }
+			a { color:skyblue; text-decoration:none }
+			canvas { pointer-events:none; z-index:0; position:relative; }
+			.label { background-color: black; position: absolute; z-index: 100; padding: 5px }
+
+		</style>
+	</head>
+
+	<body>
+		<div id="d">
+			<div id="info">
+				<a href="http://threejs.org" target="_blank">three.js</a> webgl demo :
+				Earth diffuse and city lights by <a href="http://seanward.org" target="_blank">Sean Ward</a> :
+
+			</div>
+			<div class="label" style="position: absolute;left: 12%;bottom: 10%;">Low Dynamic Range</div>
+			<div class="label" style="position: absolute;left: 12%;bottom: 5%;">Static Tone Mapping</div>
+			<div class="label" style="position: absolute;left: 45%;bottom: 10%;">High Dynamic Range</div>
+			<div class="label" style="position: absolute;left: 45%;bottom: 5%;">Static Tone Mapping</div>
+			<div class="label" style="position: absolute;left: 80%;bottom: 10%;">High Dynamic Range</div>
+			<div class="label" style="position: absolute;left: 80%;bottom: 5%;">Adaptive Tone Mapping</div>
+		</div>
+
+		<script src="../build/three.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/LuminosityShader.js"></script>
+		<script src="js/shaders/ConvolutionShader.js"></script>
+		<script src="js/shaders/ToneMapShader.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/postprocessing/BloomPass.js"></script>
+		<script src="js/postprocessing/AdaptiveToneMappingPass.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var STATS_ENABLED = false;
+
+			var container, stats;
+
+			var bloomPass, adaptToneMappingPass;
+			var params;
+
+			var camera, scene, renderer, dynamicHdrEffectComposer, hdrEffectComposer, ldrEffectComposer;
+			var cameraCube, sceneCube;
+			var cameraBG, debugScene;
+			var adaptiveLuminanceMat, currentLuminanceRT, previousLuminanceMat;
+
+			var directionalLight;
+
+
+			var orbitControls;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+			var windowThirdX = window.innerWidth / 3;
+			var windowThirdY = window.innerHeight / 3;
+
+			init();
+			animate();
+
+			function init() {
+
+				params = {
+					"Average Luminosity": 0.7,
+					middleGrey: 0.04,
+					maxLuminance: 16,
+					bloomAmount: 1.0,
+					adaptionRate: 2.0,
+					sunLight: 4.0,
+				};
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				// CAMERAS
+
+				camera = new THREE.PerspectiveCamera( 70, windowThirdX / window.innerHeight, 0.1, 100000 );
+				camera.position.x = 700;
+				camera.position.y = 400;
+				camera.position.z = 800;
+				cameraCube = new THREE.PerspectiveCamera( 70, windowThirdX / window.innerHeight, 1, 100000 );
+
+				cameraBG = new THREE.OrthographicCamera( -windowHalfX, windowHalfX, windowHalfY, -windowHalfY, -10000, 10000 );
+				cameraBG.position.z = 100;
+
+				orbitControls = new THREE.OrbitControls(camera);
+				orbitControls.autoRotate = true;
+				orbitControls.autoRotateSpeed = 1;
+
+				// SCENE
+
+				scene = new THREE.Scene();
+				sceneCube = new THREE.Scene();
+				debugScene = new THREE.Scene();
+
+				// LIGHTS
+
+				var ambient = new THREE.AmbientLight( 0x050505 );
+				scene.add( ambient );
+
+				directionalLight = new THREE.DirectionalLight( 0xffffff, params.sunLight );
+				directionalLight.position.set( 2, 0, 10 ).normalize();
+				scene.add( directionalLight );
+
+				var atmoShader = {
+					side: THREE.BackSide,
+					// blending: THREE.AdditiveBlending,
+					transparent: true,
+					lights: true,
+					uniforms: THREE.UniformsUtils.merge( [
+
+						THREE.UniformsLib[ "common" ],
+						THREE.UniformsLib[ "lights" ],
+					] ),
+					vertexShader: [
+						"varying vec3 vViewPosition;",
+						"varying vec3 vNormal;",
+						THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
+						"void main() {",
+				   		THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+							"	vNormal = normalize( transformedNormal );",
+							"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+							"vViewPosition = -mvPosition.xyz;",
+			      	"gl_Position = projectionMatrix * mvPosition;",
+			    	"}"
+
+				  ].join("\n"),
+
+				  fragmentShader: [
+
+				    THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
+				    
+				    "void main() {",
+				      "vec3 normal = normalize( -vNormal );",
+			        "vec3 viewPosition = normalize( vViewPosition );",
+			        "#if MAX_DIR_LIGHTS > 0",
+
+								"vec3 dirDiffuse = vec3( 0.0 );",
+
+								"for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",
+
+									"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
+									"vec3 dirVector = normalize( lDirection.xyz );",
+									"float dotProduct = dot( viewPosition, dirVector );",
+									"dotProduct = 1.0 * max( dotProduct, 0.0 ) + (1.0 - max( -dot( normal, dirVector ), 0.0 ));",
+									"dotProduct *= dotProduct;",
+									"dirDiffuse += max( 0.5 * dotProduct, 0.0 ) * directionalLightColor[ i ];",
+								"}",
+							"#endif",
+
+							//Fade out atmosphere at edge
+							"float viewDot = abs(dot( normal, viewPosition ));",
+							"viewDot = clamp( pow( viewDot + 0.6, 10.0 ), 0.0, 1.0);",
+							
+							"vec3 colour = vec3( 0.05, 0.09, 0.13 ) * dirDiffuse;",
+				      "gl_FragColor = vec4( colour, viewDot );",
+
+				    "}"
+
+				  ].join("\n"),
+				};
+
+				var earthAtmoMat = new THREE.ShaderMaterial( atmoShader );
+
+				var earthMat = new THREE.MeshPhongMaterial( { color: 0xffffff, shininess: 200 } );
+				
+				var earthDiffuse = THREE.ImageUtils.loadTexture( 'textures/planets/earth_atmos_4096.jpg', undefined, function( tex ) {
+					earthMat.map = tex;
+					earthMat.needsUpdate = true;
+				} );
+				var earthSpecular = THREE.ImageUtils.loadTexture( 'textures/planets/earth_specular_2048.jpg', undefined, function( tex ) {
+					earthMat.specularMap = tex;
+					earthMat.needsUpdate = true;
+				} );
+				// var earthNormal = THREE.ImageUtils.loadTexture( 'textures/planets/earth-new-normal-2048.jpg', undefined, function( tex ) {
+				// 	earthMat.normalMap = tex;
+				// 	earthMat.needsUpdate = true;
+				// } );
+
+				var earthLightsMat = new THREE.MeshBasicMaterial( { color: 0xffffff, blending: THREE.AdditiveBlending, transparent: true, depthTest: false } );
+				var earthLights = THREE.ImageUtils.loadTexture( 'textures/planets/earth_lights_2048.png', undefined, function( tex ) {
+					earthLightsMat.map = tex;
+					earthLightsMat.needsUpdate = true;
+				} );
+
+				var earthCloudsMat = new THREE.MeshLambertMaterial( { color: 0xffffff, blending: THREE.NormalBlending, transparent: true, depthTest: false } );
+				var earthClouds = THREE.ImageUtils.loadTexture( 'textures/planets/earth_clouds_2048.png', undefined, function( tex ) {
+					earthCloudsMat.map = tex;
+					earthCloudsMat.needsUpdate = true;
+				} );
+
+				var earthGeo = new THREE.SphereGeometry( 600, 24, 24 );
+				var sphereMesh = new THREE.Mesh( earthGeo, earthMat );
+				scene.add( sphereMesh );
+
+				var sphereLightsMesh = new THREE.Mesh( earthGeo, earthLightsMat );
+				scene.add( sphereLightsMesh );
+
+				var sphereCloudsMesh = new THREE.Mesh( earthGeo, earthCloudsMat );
+				scene.add( sphereCloudsMesh );
+
+				var sphereAtmoMesh = new THREE.Mesh( earthGeo, earthAtmoMat );
+				sphereAtmoMesh.scale.set( 1.05, 1.05, 1.05 );
+				scene.add( sphereAtmoMesh );
+
+				var vBGShader = [
+					// "attribute vec2 uv;",
+					"varying vec2 vUv;",
+			   	"void main() {",
+			   		"vUv = uv;",
+		      	"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+		    	"}"
+
+			  ].join("\n");
+
+			  var pBGShader = [
+
+			    "uniform sampler2D map;",
+			    "varying vec2 vUv;",
+			    
+			    "void main() {",
+			      
+		        "vec2 sampleUV = vUv;",
+		        "vec4 colour = texture2D( map, sampleUV, 0.0 );",
+		    
+			      "gl_FragColor = vec4( colour.xyz, 1.0 );",
+
+			    "}"
+
+			  ].join("\n");
+
+				// Skybox
+				adaptiveLuminanceMat = new THREE.ShaderMaterial( {
+					uniforms: {
+						map: { type: 't', value: null }
+					},
+					vertexShader: vBGShader,
+					fragmentShader: pBGShader,
+					depthTest: false,
+					// color: 0xffffff
+					blending: THREE.NoBlending
+				} );
+
+				currentLuminanceMat = new THREE.ShaderMaterial( {
+					uniforms: {
+						map: { type: 't', value: null }
+					},
+					vertexShader: vBGShader,
+					fragmentShader: pBGShader,
+					depthTest: false,
+					// color: 0xffffff
+					// blending: THREE.NoBlending
+				} );
+				
+				var quadBG = new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.1, 0.1 ), currentLuminanceMat );
+				quadBG.position.z = -500;
+				quadBG.position.x = -window.innerWidth * 0.5 + window.innerWidth * 0.05;
+				quadBG.scale.set( window.innerWidth, window.innerHeight, 1 );
+				debugScene.add( quadBG );
+
+				quadBG = new THREE.Mesh( new THREE.PlaneBufferGeometry( 0.1, 0.1 ), adaptiveLuminanceMat );
+				quadBG.position.z = -500;
+				quadBG.position.x = -window.innerWidth * 0.5 + window.innerWidth * 0.15;
+				quadBG.scale.set( window.innerWidth, window.innerHeight, 1 );
+				debugScene.add( quadBG );
+
+				var r = "textures/cube/MilkyWay/";
+				var urls = [ r + "dark-s_px.jpg", r + "dark-s_nx.jpg",
+							 r + "dark-s_py.jpg", r + "dark-s_ny.jpg",
+							 r + "dark-s_pz.jpg", r + "dark-s_nz.jpg" ];
+
+				var textureCube = THREE.ImageUtils.loadTextureCube( urls );
+				textureCube.format = THREE.RGBFormat;
+				var skyboxShader = THREE.ShaderLib[ "cube" ];
+				skyboxShader.uniforms[ "tCube" ].value = textureCube;
+
+				var skyboxMaterial = new THREE.ShaderMaterial( {
+
+					fragmentShader: skyboxShader.fragmentShader,
+					vertexShader: skyboxShader.vertexShader,
+					uniforms: skyboxShader.uniforms,
+					depthWrite: false,
+					side: THREE.BackSide
+
+				} ),
+
+				mesh = new THREE.Mesh( new THREE.BoxGeometry( 100, 100, 100 ), skyboxMaterial );
+				sceneCube.add( mesh );
+
+				
+				renderer = new THREE.WebGLRenderer();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setFaceCulling( THREE.CullFaceNone );
+				renderer.autoClear = false;
+				
+				renderer.gammaInput = true;
+				renderer.gammaOutput = false;
+
+				container.appendChild( renderer.domElement );
+
+				var extensions = new THREE.WebGLExtensions( renderer.getContext() );
+
+				// var width = window.innerWidth || 1;
+				var height = window.innerHeight || 1;
+
+				var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, stencilBuffer: false };
+				var regularRenderTarget = new THREE.WebGLRenderTarget( windowThirdX, height, parameters );
+				ldrEffectComposer = new THREE.EffectComposer( renderer, regularRenderTarget );
+				
+				if ( extensions.get('OES_texture_half_float_linear') ) {
+					parameters.type = THREE.FloatType;
+				}
+				var hdrRenderTarget = new THREE.WebGLRenderTarget( windowThirdX, height, parameters );
+				dynamicHdrEffectComposer = new THREE.EffectComposer( renderer, hdrRenderTarget );
+  			dynamicHdrEffectComposer.setSize( window.innerWidth, window.innerHeight );
+  			hdrEffectComposer = new THREE.EffectComposer( renderer, hdrRenderTarget );
+
+  			var debugPass = new THREE.RenderPass( debugScene, cameraBG );
+				debugPass.clear = false;
+				var scenePass = new THREE.RenderPass( scene, camera, undefined, undefined, undefined );
+				var skyboxPass = new THREE.RenderPass( sceneCube, cameraCube );
+  			scenePass.clear = false;
+
+  			adaptToneMappingPass = new THREE.AdaptiveToneMappingPass( true, 256 );
+  			adaptToneMappingPass.needsSwap = true;
+  			ldrToneMappingPass = new THREE.AdaptiveToneMappingPass( false, 256 );
+  			hdrToneMappingPass = new THREE.AdaptiveToneMappingPass( false, 256 );
+  			bloomPass = new THREE.BloomPass();
+  			var copyPass = new THREE.ShaderPass( THREE.CopyShader );
+  			copyPass.renderToScreen = true;
+  			
+  			dynamicHdrEffectComposer.addPass( skyboxPass );
+  			dynamicHdrEffectComposer.addPass( scenePass );
+  			dynamicHdrEffectComposer.addPass( adaptToneMappingPass );
+  			// dynamicHdrEffectComposer.addPass( debugPass );
+  			dynamicHdrEffectComposer.addPass( bloomPass );
+  			dynamicHdrEffectComposer.addPass( copyPass );
+
+  			hdrEffectComposer.addPass( skyboxPass );
+  			hdrEffectComposer.addPass( scenePass );
+  			hdrEffectComposer.addPass( hdrToneMappingPass );
+  			hdrEffectComposer.addPass( bloomPass );
+  			hdrEffectComposer.addPass( copyPass );
+
+  			ldrEffectComposer.addPass( skyboxPass );
+  			ldrEffectComposer.addPass( scenePass );
+  			ldrEffectComposer.addPass( ldrToneMappingPass );
+  			ldrEffectComposer.addPass( bloomPass );
+  			ldrEffectComposer.addPass( copyPass );
+
+  			// var gammaPass = new THREE.ShaderPass( GammaShader );
+  			// gammaPass.renderToScreen = true;
+  			// ldrEffectComposer.addPass( gammaPass );
+
+
+				var dynamicHdrGui = new dat.GUI();
+
+				// dynamicHdrGui.add( params, 'projection', { 'From cam to mesh': 'camera', 'Normal to mesh': 'normal' } );
+				dynamicHdrGui.add( params, 'middleGrey', 0, 12 );
+				dynamicHdrGui.add( params, 'maxLuminance', 1, 30 );
+				dynamicHdrGui.add( params, 'adaptionRate', 0.0, 10.0 );
+				dynamicHdrGui.add( params, 'bloomAmount', 0.0, 10.0 );
+				dynamicHdrGui.add( params, 'sunLight', 0.1, 12.0 );
+				// dynamicHdrGui.add( params, 'clear' );
+				dynamicHdrGui.open();
+
+				var ldrGui = new dat.GUI();
+				ldrGui.domElement.style.position = 'absolute';
+				ldrGui.add( params, 'Average Luminosity', 0.001, 2.0 );
+				ldrGui.open();
+
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+				windowThirdX = window.innerWidth / 3;
+				windowThirdY = window.innerHeight / 3;
+
+				camera.aspect = windowThirdX / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				cameraCube.aspect = windowThirdX / window.innerHeight;
+				cameraCube.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+				if ( bloomPass ) {
+					bloomPass.copyUniforms[ "opacity" ].value = params.bloomAmount;
+				}
+
+				if ( adaptToneMappingPass ) {
+					adaptToneMappingPass.setAdaptionRate( params.adaptionRate );
+					adaptiveLuminanceMat.uniforms.map.value = adaptToneMappingPass.luminanceRT;
+					currentLuminanceMat.uniforms.map.value = adaptToneMappingPass.currentLuminanceRT;
+					if ( adaptToneMappingPass.setAverageLuminance ) {
+						adaptToneMappingPass.setAverageLuminance( params["Average Luminosity"] );
+					}
+					adaptToneMappingPass.setMaxLuminance( params.maxLuminance );
+					adaptToneMappingPass.setMiddleGrey( params.middleGrey );
+				}
+				directionalLight.intensity = params.sunLight;
+
+				orbitControls.update();
+
+				render();
+
+			}
+
+			function render() {
+
+				camera.lookAt( scene.position );
+				cameraCube.rotation.copy( camera.rotation );
+
+				renderer.setViewport( 0, 0, windowThirdX, window.innerHeight );
+				ldrEffectComposer.render( 0.017 );
+
+				renderer.setViewport( windowThirdX, 0, windowThirdX, window.innerHeight );
+				hdrEffectComposer.render( 0.017 );
+
+				renderer.setViewport( windowThirdX * 2, 0, windowThirdX, window.innerHeight );
+				dynamicHdrEffectComposer.render( 0.017 );
+
+				
+				
+				// if ( STATS_ENABLED ) stats.update();
+
+			}
+
+		</script>
+
+	</body>
+</html>