Browse Source

Merge remote-tracking branch 'troffmo5/oculus' into dev

Mr.doob 12 năm trước cách đây
mục cha
commit
834439421d

+ 127 - 66
examples/js/effects/OculusRiftEffect.js

@@ -6,40 +6,49 @@
  * it works also with other HMD using the same technology
  */
 
-THREE.OculusRiftEffect = function ( renderer ) {
-
-	// Configuration
-	this.separation = 10;
-	this.distortion = 0.1;
-	this.aspectFactor = 1;
-	this.fov = null; // use original camera FOV
-
-	// initialization
-	var _width, _height;
-
-	var _pCamera = new THREE.PerspectiveCamera();
-	_pCamera.matrixAutoUpdate = false;
-
-	var _scene = new THREE.Scene();
+THREE.OculusRiftEffect = function ( renderer, options ) {
+	// worldFactor indicates how many units is 1 meter
+	var worldFactor = (options && options.worldFactor) ? options.worldFactor: 1.0;
+
+	// Specific HMD parameters
+	var HMD = (options && options.HMD) ? options.HMD: {
+		// Parameters from the Oculus Rift DK1
+		hResolution: 1280,
+		vResolution: 800,
+		hScreenSize: 0.14976,
+		vScreenSize: 0.0936,
+		interpupillaryDistance: 0.064,
+		lensSeparationDistance: 0.064,
+		eyeToScreenDistance: 0.041,
+		distortionK : [1.0, 0.22, 0.24, 0.0]
+	};
 
-	var _oCamera = new THREE.OrthographicCamera( -1, 1, 1, -1, 1, 1000 );
-	_oCamera.position.z = 1;
-	_scene.add( _oCamera );
+	// Perspective camera
+	var pCamera = new THREE.PerspectiveCamera();
+	pCamera.matrixAutoUpdate = false;
+	pCamera.target = new THREE.Vector3();
 
-	var _offset = new THREE.Matrix4();
+	// Orthographic camera
+	var oCamera = new THREE.OrthographicCamera( -1, 1, 1, -1, 1, 1000 );
+	oCamera.position.z = 1;
 
 	// pre-render hooks
 	this.preLeftRender = function() {};
 	this.preRightRender = function() {};
 
 	renderer.autoClear = false;
+	var emptyColor = new THREE.Color("black");
 
-	var _params = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat };
-	var _renderTarget = new THREE.WebGLRenderTarget( 800, 600, _params );
-	var _material = new THREE.ShaderMaterial( {
+	// Render target
+	var RTParams = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat };
+	var renderTarget = new THREE.WebGLRenderTarget( 640, 800, RTParams );
+	var RTMaterial = new THREE.ShaderMaterial( {
 		uniforms: {
-			"tex": { type: "t", value: _renderTarget },
-			"c": { type: "f", value: this.distortion }
+			"texid": { type: "t", value: renderTarget },
+			"scale": { type: "v2", value: new THREE.Vector2(1.0,1.0) },
+			"scaleIn": { type: "v2", value: new THREE.Vector2(1.0,1.0) },
+			"lensCenter": { type: "v2", value: new THREE.Vector2(0.0,0.0) },
+			"hmdWarpParam": { type: "v4", value: new THREE.Vector4(1.0,0.0,0.0,0.0) }
 		},
 		vertexShader: [
 			"varying vec2 vUv;",
@@ -49,76 +58,128 @@ THREE.OculusRiftEffect = function ( renderer ) {
 			"}"
 		].join("\n"),
 
-		// Formula used from the paper: "Applying and removing lens distortion in post production"
-		// by Gergely Vass , Tamás Perlaki
-		// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.136.3745
 		fragmentShader: [
-			"uniform float c;",
-			"uniform sampler2D tex;",
+		    "uniform vec2 scale;",
+		    "uniform vec2 scaleIn;",
+		    "uniform vec2 lensCenter;",
+		    "uniform vec4 hmdWarpParam;",
+			"uniform sampler2D texid;",
 			"varying vec2 vUv;",
 			"void main()",
 			"{",
-			"	vec2 uv = vUv;",
-			"	vec2 vector = uv * 2.0 - 1.0;",
-			"   float factor = 1.0/(1.0+c);",
-			"   float vectorLen = length(vector);",
-			"   vec2 direction = vector / vectorLen;",
-			"   float newLen = vectorLen + c * pow(vectorLen,3.0);",
-			"   vec2 newVector = direction * newLen * factor;",
-			"	newVector = (newVector + 1.0) / 2.0;",
-			"	if (newVector.x < 0.0 || newVector.x > 1.0 || newVector.y < 0.0 || newVector.y > 1.0)",
-			"		gl_FragColor = vec4(0.0,0.0,0.0,1.0);",
-			"	else",
-			"   	gl_FragColor = texture2D(tex, newVector);",
+			"  vec2 uv = (vUv*2.0)-1.0;", // range from [0,1] to [-1,1]
+			"  vec2 theta = (uv-lensCenter)*scaleIn;",
+			"  float rSq = theta.x*theta.x + theta.y*theta.y;",
+			"  vec2 rvector = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq);",
+			"  vec2 tc = (lensCenter + scale * rvector);",
+			"  tc = (tc+1.0)/2.0;", // range from [-1,1] to [0,1]
+			"  if (any(bvec2(clamp(tc, vec2(0.0,0.0), vec2(1.0,1.0))-tc)))",
+			"    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);",
+			"  else",
+			"    gl_FragColor = texture2D(texid, tc);",
 			"}"
 		].join("\n")
 	} );
-	var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), _material );
-	_scene.add( mesh );
+
+	var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), RTMaterial );
+
+	// Final scene
+	var finalScene = new THREE.Scene();
+	finalScene.add( oCamera );
+	finalScene.add( mesh );
+
+    var left = {}, right = {};
+    var distScale = 1.0;
+	this.setHMD = function(v) {
+		HMD = v;
+		// Compute aspect ratio and FOV
+		var aspect = HMD.hResolution / (2*HMD.vResolution);
+
+		// Fov is normally computed with:
+		//   THREE.Math.radToDeg( 2*Math.atan2(HMD.vScreenSize,2*HMD.eyeToScreenDistance) );
+		// But with lens distortion it is increased (see Oculus SDK Documentation)
+		var r = -1.0 - (4 * (HMD.hScreenSize/4 - HMD.lensSeparationDistance/2) / HMD.hScreenSize);
+		distScale = (HMD.distortionK[0] + HMD.distortionK[1] * Math.pow(r,2) + HMD.distortionK[2] * Math.pow(r,4) + HMD.distortionK[3] * Math.pow(r,6));
+		var fov = THREE.Math.radToDeg(2*Math.atan2(HMD.vScreenSize*distScale, 2*HMD.eyeToScreenDistance));
+
+		// Compute camera projection matrices
+		var proj = (new THREE.Matrix4()).makePerspective( fov, aspect, 0.3, 10000 );
+		var h = 4 * (HMD.hScreenSize/4 - HMD.interpupillaryDistance/2) / HMD.hScreenSize;
+		left.proj = ((new THREE.Matrix4()).makeTranslation( h, 0.0, 0.0 )).multiply(proj);
+		right.proj = ((new THREE.Matrix4()).makeTranslation( -h, 0.0, 0.0 )).multiply(proj);
+
+		// Compute camera transformation matrices
+		left.tranform = (new THREE.Matrix4()).makeTranslation( -worldFactor * HMD.interpupillaryDistance/2, 0.0, 0.0 );
+		right.tranform = (new THREE.Matrix4()).makeTranslation( worldFactor * HMD.interpupillaryDistance/2, 0.0, 0.0 );
+
+		// Compute Viewport
+		left.viewport = [0, 0, HMD.hResolution/2, HMD.vResolution];
+		right.viewport = [HMD.hResolution/2, 0, HMD.hResolution/2, HMD.vResolution];
+
+		// Distortion shader parameters
+		var lensShift = 4 * (HMD.hScreenSize/4 - HMD.lensSeparationDistance/2) / HMD.hScreenSize;
+		left.lensCenter = new THREE.Vector2(lensShift, 0.0);
+		right.lensCenter = new THREE.Vector2(-lensShift, 0.0);    
+
+		RTMaterial.uniforms['hmdWarpParam'].value = new THREE.Vector4(HMD.distortionK[0], HMD.distortionK[1], HMD.distortionK[2], HMD.distortionK[3]);
+		RTMaterial.uniforms['scaleIn'].value = new THREE.Vector2(1.0,1.0/aspect);
+		RTMaterial.uniforms['scale'].value = new THREE.Vector2(1.0/distScale, 1.0*aspect/distScale);
+
+		// Create render target
+		renderTarget = new THREE.WebGLRenderTarget( HMD.hResolution*distScale/2, HMD.vResolution*distScale, RTParams );
+		RTMaterial.uniforms[ "texid" ].value = renderTarget;
+
+	}	
+	this.getHMD = function() {return HMD};
+
+	this.setHMD(HMD);	
 
 	this.setSize = function ( width, height ) {
-		_width = width / 2;
-		_height = height;
-		_renderTarget = new THREE.WebGLRenderTarget( width, height, _params );
-		_material.uniforms[ "tex" ].value = _renderTarget;
-		renderer.setSize( width, height );
+		left.viewport = [width/2 - HMD.hResolution/2, height/2 - HMD.vResolution/2, HMD.hResolution/2, HMD.vResolution];
+		right.viewport = [width/2, height/2 - HMD.vResolution/2, HMD.hResolution/2, HMD.vResolution];
 
+		renderer.setSize( width, height );
 	};
 
 	this.render = function ( scene, camera ) {
+		var cc = renderer.getClearColor().clone();
+
+		// Clear
+		renderer.setClearColor(emptyColor);
 		renderer.clear();
-		_material.uniforms['c'].value = this.distortion;
+		renderer.setClearColor(cc);
 
 		// camera parameters
 		if (camera.matrixAutoUpdate) camera.updateMatrix();
-		_pCamera.fov = this.fov ? this.fov : camera.fov;
-		_pCamera.aspect = camera.aspect / (2*this.aspectFactor);
-		_pCamera.near = camera.near;
-		_pCamera.far = camera.far;
-		_pCamera.updateProjectionMatrix();
 
 		// Render left
 		this.preLeftRender();
 
-		_offset.elements[12] = -this.separation; // set x offset
-		_pCamera.matrix.copy(camera.matrix).multiply(_offset);
-		_pCamera.matrixWorldNeedsUpdate = true;
+		pCamera.matrix.copy(camera.matrix).multiply(left.tranform);
+		pCamera.matrixWorldNeedsUpdate = true;
+
+		renderer.setViewport(left.viewport[0], left.viewport[1], left.viewport[2], left.viewport[3]);
+
+		RTMaterial.uniforms['lensCenter'].value = left.lensCenter;
+		renderer.render( scene, pCamera, renderTarget, true );
 
-		renderer.setViewport( 0, 0, _width, _height );
-		renderer.render( scene, _pCamera, _renderTarget, true );
-		renderer.render( _scene, _oCamera );
+		renderer.render( finalScene, oCamera );
 
 		// Render right
 		this.preRightRender();
 
-		_offset.elements[12] = this.separation; // set x offset
-		_pCamera.matrix.copy(camera.matrix).multiply(_offset);
-		_pCamera.matrixWorldNeedsUpdate = true;
+		pCamera.projectionMatrix.copy(right.proj);		
+
+		pCamera.matrix.copy(camera.matrix).multiply(right.tranform);
+		pCamera.matrixWorldNeedsUpdate = true;
+
+		renderer.setViewport(right.viewport[0], right.viewport[1], right.viewport[2], right.viewport[3]);
+
+		RTMaterial.uniforms['lensCenter'].value = right.lensCenter;
 
-		renderer.setViewport( _width, 0, _width, _height );
-		renderer.render( scene, _pCamera, _renderTarget, true );
+		renderer.render( scene, pCamera, renderTarget, true );
+		renderer.render( finalScene, oCamera );
 
-		renderer.render( _scene, _oCamera );
 	};
 
 };

+ 13 - 23
examples/webgl_geometry_minecraft_oculusrift.html

@@ -11,7 +11,7 @@
 				font-size:13px;
 				text-align:center;
 
-				background-color: #bfd1e5;
+				background-color: #000000;
 				margin: 0px;
 				overflow: hidden;
 			}
@@ -39,7 +39,7 @@
 	<body>
 
 		<div id="container"><br /><br /><br /><br /><br />Generating world...</div>
-		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - <a href="http://www.minecraft.net/" target="_blank">minecraft</a> demo [oculus rift]. featuring <a href="http://painterlypack.net/" target="_blank">painterly pack</a><br />(left click: forward, right click: backward, o,p: distortion [<span id='distVal'>0</span>], k,l; eye separation [<span id='sepVal'>0</span>], n,m; fov [<span id='fovVal'>0</span>])</div>
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - <a href="http://www.minecraft.net/" target="_blank">minecraft</a> demo for oculus rift. featuring <a href="http://painterlypack.net/" target="_blank">painterly pack</a><br />(left click: forward, right click: backward, h: hide text)</div>
 
 		<script src="../build/three.min.js"></script>
 
@@ -121,7 +121,6 @@
 				nzGeometry.faceVertexUvs[ 0 ][ 0 ][ 3 ].y = 0.5;
 				nzGeometry.applyMatrix( matrix.makeRotationY( Math.PI ) );
 				nzGeometry.applyMatrix( matrix.makeTranslation( 0, 0, -50 ) );
-
 				//
 
 				var geometry = new THREE.Geometry();
@@ -193,19 +192,12 @@
 
 				renderer = new THREE.WebGLRenderer();
 				renderer.setSize( window.innerWidth, window.innerHeight );
-				effect = new THREE.OculusRiftEffect( renderer );
-				effect.setSize( window.innerWidth, window.innerHeight );
-
-				// Right Oculus Parameters are yet to be determined
-				effect.separation = 20;
-				effect.distortion = 0.1;
-				effect.fov = 110;
-
-
-				document.getElementById( 'distVal' ).innerHTML = effect.distortion.toFixed(2);
-				document.getElementById( 'sepVal' ).innerHTML = effect.separation;
-				document.getElementById( 'fovVal' ).innerHTML = effect.fov;
+				renderer.setClearColor(new THREE.Color(0xbfd1e5));
 
+				// Here is the effect for the Oculus Rift
+				// worldScale 100 means that 100 Units == 1m
+				effect = new THREE.OculusRiftEffect( renderer, {worldScale: 100} );
+				effect.setSize( window.innerWidth, window.innerHeight );
 
 				container.innerHTML = "";
 
@@ -220,6 +212,8 @@
 				window.addEventListener( 'resize', onWindowResize, false );
 				document.addEventListener( 'keydown', keyPressed, false );
 
+				guiVisible = true;
+
 			}
 
 			function onWindowResize() {
@@ -234,14 +228,10 @@
 			}
 
 			function keyPressed (event) {
-				switch ( event.keyCode ) {
-					case 79: /*O*/ effect.distortion -= 0.01; document.getElementById( 'distVal' ).innerHTML = effect.distortion.toFixed(2); break;
-					case 80: /*P*/ effect.distortion += 0.01; document.getElementById( 'distVal' ).innerHTML = effect.distortion.toFixed(2); break;
-					case 75: /*K*/ effect.separation -= 1; document.getElementById( 'sepVal' ).innerHTML = effect.separation; break;
-					case 76: /*L*/ effect.separation += 1; document.getElementById( 'sepVal' ).innerHTML = effect.separation; break;
-					case 78: /*N*/ effect.fov -= 1; document.getElementById( 'fovVal' ).innerHTML = effect.fov; break;
-					case 77: /*M*/ effect.fov += 1; document.getElementById( 'fovVal' ).innerHTML = effect.fov; break;
-
+				if (event.keyCode === 72) { // H
+					guiVisible = !guiVisible;
+					document.getElementById('info').style.display = guiVisible ? "block" : "none";
+					stats.domElement.style.display = guiVisible ? "block" : "none";
 				}
 			}