Browse Source

Merge pull request #7664 from kaypiKun/CinematicCamera_2

Cinematic Camera with bokehShader2.
Mr.doob 9 years ago
parent
commit
da3336cf76
2 changed files with 425 additions and 0 deletions
  1. 181 0
      examples/js/cameras/CinematicCamera.js
  2. 244 0
      examples/webgl_camera_cinematic.html

+ 181 - 0
examples/js/cameras/CinematicCamera.js

@@ -0,0 +1,181 @@
+/**
+  * @author mrdoob / http://mrdoob.com/
+  * @author greggman / http://games.greggman.com/
+  * @author zz85 / http://www.lab4games.net/zz85/blog
+  * @author kaypiKun
+  */
+
+//Inheriting PerspectiveCamera
+
+THREE.CinematicCamera = function(fov, aspect, near, far){
+  THREE.PerspectiveCamera.call(this, fov, aspect, near, far);
+  this.type = "CinematicCamera";
+
+  this.postprocessing = { enabled  : true };
+  this.shaderSettings = {
+    rings: 3,
+    samples: 4
+  };
+
+  this.material_depth = new THREE.MeshDepthMaterial();
+  //In case of cinematicCamera, having a default lens set is important
+  this.setLens();
+
+  this.initPostProcessing();
+  
+};
+
+THREE.CinematicCamera.prototype = Object.create(THREE.PerspectiveCamera.prototype);
+THREE.CinematicCamera.prototype.constructor = THREE.CinematicCamera;
+
+
+//providing fnumber and coc(Circle of Confusion) as extra arguments
+THREE.CinematicCamera.prototype.setLens = function ( focalLength, frameHeight, fNumber, coc ) {
+
+  //In case of cinematicCamera, having a default lens set is important
+  if ( focalLength === undefined ) focalLength = 35;
+
+  THREE.PerspectiveCamera.prototype.setLens.call( this, focalLength, frameHeight );
+
+  //if fnumber and coc are not provided, cinematicCamera tries to act as a basic PerspectiveCamera
+  if ( fNumber === undefined ) fNumber = 8;
+  if ( coc === undefined ) coc = 0.019;
+
+
+
+  this.focalLength = focalLength;
+  this.fNumber = fNumber;
+  this.coc = coc;
+
+  //fNumber is focalLength by aperture
+  this.aperture = this.focalLength/this.fNumber;
+
+  //hyperFocal is required to calculate depthOfField when a lens tries to focus at a distance with given fNumber and focalLength
+  this.hyperFocal = (this.focalLength*this.focalLength)/(this.aperture*this.coc);
+
+};
+
+
+
+THREE.CinematicCamera.prototype.linearize = function(depth) {
+	var zfar = this.far;
+	var znear = this.near;
+	return -zfar * znear / (depth * (zfar - znear) - zfar);
+}
+
+
+THREE.CinematicCamera.prototype.smoothstep = function(near, far, depth) {
+	var x = this.saturate( (depth - near) / (far - near));
+	return x * x * (3- 2*x);
+}
+
+
+THREE.CinematicCamera.prototype.saturate = function(x) {
+	return Math.max(0, Math.min(1, x));
+}
+
+
+//function for focusing at a distance from the camera
+THREE.CinematicCamera.prototype.focusAt = function (focusDistance){
+
+  if ( focusDistance === undefined ) focusDistance = 20;
+
+  //distance from the camera (normal to frustrum) to focus on (unused)
+  this.focusDistance = focusDistance;
+
+  //the nearest point from the camera which is in focus (unused)
+  this.nearPoint = (this.hyperFocal*this.focusDistance)/(this.hyperFocal+(this.focusDistance-this.focalLength));
+
+  //the farthest point from the camera which is in focus (unused)
+  this.farPoint = (this.hyperFocal*this.focusDistance)/(this.hyperFocal-(this.focusDistance-this.focalLength));
+
+  //the gap or width of the space in which is everything is in focus (unused)
+  this.depthOfField = this.farPoint - this.nearPoint;
+
+  //Considering minimum distance of focus for a standard lens (unused)
+  if( this.depthOfField < 0) this.depthOfField = 0;
+  
+  this.sdistance = this.smoothstep(this.near, this.far, this.focusDistance);
+
+	this.ldistance = this.linearize(1 -  this.sdistance);
+	
+	this.postprocessing.bokeh_uniforms[ 'focalDepth' ].value = this.ldistance;
+
+};
+
+
+
+THREE.CinematicCamera.prototype.initPostProcessing = function (){
+    if(this.postprocessing.enabled)
+    {
+        this.postprocessing.scene = new THREE.Scene();
+
+        
+        this.postprocessing.camera = new THREE.OrthographicCamera( window.innerWidth / -2, window.innerWidth / 2,  window.innerHeight / 2, window.innerHeight / - 2, -10000, 10000 );
+        
+    
+        this.postprocessing.scene.add( this.postprocessing.camera );
+
+        var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat };
+        this.postprocessing.rtTextureDepth = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, pars );
+        this.postprocessing.rtTextureColor = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, pars );
+
+
+        var bokeh_shader = THREE.BokehShader;
+
+        this.postprocessing.bokeh_uniforms = THREE.UniformsUtils.clone( bokeh_shader.uniforms );
+
+        this.postprocessing.bokeh_uniforms[ "tColor" ].value = this.postprocessing.rtTextureColor;
+        this.postprocessing.bokeh_uniforms[ "tDepth" ].value = this.postprocessing.rtTextureDepth;
+
+        this.postprocessing.bokeh_uniforms[ "manualdof" ].value = 0;
+				this.postprocessing.bokeh_uniforms[ "shaderFocus" ].value = 0;
+
+        this.postprocessing.bokeh_uniforms[ "fstop" ].value = 2.8;
+
+        this.postprocessing.bokeh_uniforms[ "showFocus" ].value = 1;
+
+        this.postprocessing.bokeh_uniforms[ "focalDepth" ].value = 0.1;
+
+        console.log(this.postprocessing.bokeh_uniforms["focalDepth"].value);
+
+				this.postprocessing.bokeh_uniforms[ "znear" ].value = this.near;
+				this.postprocessing.bokeh_uniforms[ "zfar" ].value = this.near;
+
+
+        this.postprocessing.bokeh_uniforms[ "textureWidth" ].value = window.innerWidth;
+
+        this.postprocessing.bokeh_uniforms[ "textureHeight" ].value = window.innerHeight;
+
+        this.postprocessing.materialBokeh = new THREE.ShaderMaterial({uniforms: this.postprocessing.bokeh_uniforms, vertexShader: bokeh_shader.vertexShader, fragmentShader: bokeh_shader.fragmentShader, defines: { RINGS: this.shaderSettings.rings, SAMPLES: this.shaderSettings.samples } } );
+
+        this.postprocessing.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( window.innerWidth, window.innerHeight ), this.postprocessing.materialBokeh );
+        this.postprocessing.quad.position.z = -500;
+        this.postprocessing.scene.add( this.postprocessing.quad );
+    }
+
+}
+
+THREE.CinematicCamera.prototype.renderCinematic = function (scene, renderer ){
+
+    if ( this.postprocessing.enabled ) {
+
+      renderer.clear();
+
+      // Render scene into texture
+
+      scene.overrideMaterial = null;
+      renderer.render( scene, camera, this.postprocessing.rtTextureColor, true );
+
+      // Render depth into texture
+
+      scene.overrideMaterial = this.material_depth;
+      renderer.render( scene, camera, this.postprocessing.rtTextureDepth, true );
+
+      // Render bokeh composite
+
+      renderer.render( this.postprocessing.scene, this.postprocessing.camera );
+
+    }
+
+}

+ 244 - 0
examples/webgl_camera_cinematic.html

@@ -0,0 +1,244 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl-camera cinematic</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 {
+				font-family: Monospace;
+				background-color: #f0f0f0;
+				margin: 0px;
+				overflow: hidden;
+			}
+		</style>
+	</head>
+	<body>
+
+		<script src="../build/three.js"></script>
+
+		<script src="js/shaders/BokehShader2.js"></script>
+
+		<script src="js/cameras/CinematicCamera.js"></script>
+
+		<script src="js/libs/stats.min.js"></script>
+		<script src='js/libs/dat.gui.min.js'></script>
+
+		<script>
+
+			var container, stats;
+			var camera, scene, raycaster, renderer;
+
+			var mouse = new THREE.Vector2(), INTERSECTED;
+			var radius = 100, theta = 0;
+
+
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				var info = document.createElement( 'div' );
+				info.style.position = 'absolute';
+				info.style.top = '10px';
+				info.style.width = '100%';
+				info.style.textAlign = 'center';
+				info.innerHTML = '<a href="http://threejs.org" target="_blank">three.js</a> webgl - interactive cubes';
+				container.appendChild( info );
+				camera = new THREE.CinematicCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.setLens(5);
+        		camera.position.set(2, 1, 500);
+
+
+				scene = new THREE.Scene();
+				scene.add( new THREE.AmbientLight( 0xffffff, 0.3 ) );
+
+				var light = new THREE.DirectionalLight( 0xffffff, 0.35 );
+				light.position.set( 1, 1, 1 ).normalize();
+				scene.add( light );
+
+
+				var geometry = new THREE.BoxGeometry( 20, 20, 20 );
+
+				for ( var i = 0; i < 1500; i ++ ) {
+
+					var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
+
+					object.position.x = Math.random() * 800 - 400;
+					object.position.y = Math.random() * 800 - 400;
+					object.position.z = Math.random() * 800 - 400;
+
+					scene.add( object );
+
+				}
+
+				raycaster = new THREE.Raycaster();
+
+				renderer = new THREE.WebGLRenderer({antialias:true});
+				renderer.setClearColor( 0xf0f0f0 );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.sortObjects = false;
+				container.appendChild(renderer.domElement);
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+
+		
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				var effectController  = {
+
+					focalLength: 15,
+					// jsDepthCalculation: true,
+					// shaderFocus: false,
+					//
+					fstop: 2.8,
+					// maxblur: 1.0,
+					//
+					showFocus: false,
+					focalDepth: 3,
+					// manualdof: false,
+					// vignetting: false,
+					// depthblur: false,
+					//
+					// threshold: 0.5,
+					// gain: 2.0,
+					// bias: 0.5,
+					// fringe: 0.7,
+					//
+					// focalLength: 35,
+					// noise: true,
+					// pentagon: false,
+					//
+					// dithering: 0.0001
+
+				};
+
+				var matChanger = function( ) {
+
+					for (var e in effectController) {
+						if (e in camera.postprocessing.bokeh_uniforms)
+						camera.postprocessing.bokeh_uniforms[ e ].value = effectController[ e ];
+					}
+					camera.postprocessing.bokeh_uniforms[ 'znear' ].value = camera.near;
+					camera.postprocessing.bokeh_uniforms[ 'zfar' ].value = camera.far;
+					camera.setLens(effectController.focalLength, camera.frameHeight ,effectController.fstop, camera.coc);
+					effectController['focalDepth'] = camera.postprocessing.bokeh_uniforms["focalDepth"].value;
+				};
+
+
+				var gui = new dat.GUI();
+
+				gui.add( effectController, "focalLength", 1, 135, 0.01 ).onChange( matChanger );
+				gui.add( effectController, "fstop", 1.8, 22, 0.01 ).onChange( matChanger );
+				gui.add( effectController, "focalDepth", 0.1, 100, 0.001 ).onChange( matChanger );
+				gui.add( effectController, "showFocus", true ).onChange( matChanger );
+
+				matChanger();
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+
+		function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				event.preventDefault();
+
+				mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
+				mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
+
+			}
+
+
+			function animate() {
+
+				requestAnimationFrame( animate, renderer.domElement );
+
+				render();
+				stats.update();
+
+			}
+
+
+			function render() {
+
+				theta += 0.1;
+
+				camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
+				camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
+				camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
+				camera.lookAt( scene.position );
+
+				camera.updateMatrixWorld();
+
+				// find intersections
+
+				raycaster.setFromCamera( mouse, camera );
+
+				var intersects = raycaster.intersectObjects( scene.children );
+
+				if ( intersects.length > 0 ) {
+
+					var targetDistance = intersects[ 0 ].distance;
+					
+					//Using Cinematic camera focusAt method
+					camera.focusAt(targetDistance);
+
+					if ( INTERSECTED != intersects[ 0 ].object ) {
+
+						if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
+
+						INTERSECTED = intersects[ 0 ].object;
+						INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
+						INTERSECTED.material.emissive.setHex( 0xff0000 );
+					}
+
+				} else {
+
+					if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
+
+					INTERSECTED = null;
+
+				}
+
+
+				if(camera.postprocessing.enabled){
+					//rendering Cinematic Camera effects
+					camera.renderCinematic(scene, renderer);
+				}
+
+				 else {
+
+					scene.overrideMaterial = null;
+
+					renderer.clear();
+					renderer.render( scene, camera );
+
+				}
+
+			}
+
+		</script>
+
+	</body>
+</html>