diff --git a/examples/files.js b/examples/files.js index 5df19e5e179a69a012bf2455288d4d2863c9536f..d68b7eeef9865cb5aa807a3c6004a1547c82a2a2 100644 --- a/examples/files.js +++ b/examples/files.js @@ -325,6 +325,7 @@ var files = { "webvr_ballshooter", "webvr_cubes", "webvr_dragging", + "webvr_frustum", "webvr_lorenzattractor", "webvr_panorama", "webvr_paint", diff --git a/examples/webvr_frustum.html b/examples/webvr_frustum.html new file mode 100644 index 0000000000000000000000000000000000000000..6960b76e50168e09e178252f5cf79dfb47cd875a --- /dev/null +++ b/examples/webvr_frustum.html @@ -0,0 +1,129 @@ + + + + three.js webvr - frustum + + + + + + + + + + + + + + + + + + + diff --git a/src/cameras/ArrayCamera.js b/src/cameras/ArrayCamera.js index fc98224bf79fd89c9956dccc5e084d4008aef669..0de9131c194b736d0230c2fa1c69fe3986de66db 100644 --- a/src/cameras/ArrayCamera.js +++ b/src/cameras/ArrayCamera.js @@ -3,6 +3,7 @@ */ import { PerspectiveCamera } from './PerspectiveCamera.js'; +import { Vector3 } from '../math/Vector3.js'; function ArrayCamera( array ) { @@ -16,7 +17,66 @@ ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototyp constructor: ArrayCamera, - isArrayCamera: true + isArrayCamera: true, + + /** + * Assumes 2 cameras that are perpendicular and share an X-axis, and that + * the cameras' projection and world matrices have already been set. + * And that near and far planes are identical for both cameras. + */ + setProjectionFromUnion: function () { + var cameraLPos = new Vector3(); + var cameraRPos = new Vector3(); + + return function () { + cameraLPos.setFromMatrixPosition( this.cameras[ 0 ].matrixWorld ); + cameraRPos.setFromMatrixPosition( this.cameras[ 1 ].matrixWorld ); + + var ipd = cameraLPos.distanceTo( cameraRPos ); + + var projL = this.cameras[ 0 ].projectionMatrix; + var projR = this.cameras[ 1 ].projectionMatrix; + + // VR systems will have identical far and near planes, and + // most likely identical top and bottom frustum extents. + // via: https://computergraphics.stackexchange.com/a/4765 + var near = projL[ 14 ] / ( projL[ 10 ] - 1 ); + var far = projL[ 14 ] / ( projL[ 10 ] + 1 ); + + var leftFovL = ( projL[ 8 ] - 1 ) / projL[ 0 ]; + var rightFovR = ( projR[ 8 ] + 1 ) / projR[ 0 ]; + var leftL = leftFovL * near; + var rightR = rightFovR * near; + var topL = near * ( projL[ 9 ] + 1 ) / projL[ 5 ]; + var topR = near * ( projR[ 9 ] + 1 ) / projR[ 5 ]; + var bottomL = near * ( projL[ 9 ] - 1 ) / projL[ 5 ]; + var bottomR = near * ( projR[ 9 ] - 1 ) / projR[ 5 ]; + + // Calculate the new camera's position offset from the + // left camera. + var zOffset = ipd / (leftFovL + rightFovR); + var xOffset = zOffset * leftFovL; + + // TODO: Better way to apply this offset? + this.cameras[ 0 ].matrixWorld.decompose( this.position, this.quaternion, this.scale ); + this.translateX(xOffset); + this.translateZ(-zOffset); + this.matrixWorld.compose( this.position, this.quaternion, this.scale ); + this.matrixWorldInverse.getInverse(this.matrixWorld); + + // Find the union of the frustum values of the cameras and scale + // the values so that the near plane's position does not change in world space, + // although must now be relative to the new union camera. + var near2 = near + zOffset; + var far2 = far + zOffset; + var left = leftL - xOffset; + var right = rightR + (ipd - xOffset) + var top = Math.max( topL, topR ); + var bottom = Math.min( bottomL, bottomR ); + + this.projectionMatrix.makePerspective( left, right, top, bottom, near2, far2 ); + } + }(), } ); diff --git a/src/renderers/webvr/WebVRManager.js b/src/renderers/webvr/WebVRManager.js index bbf9d31554e1139f8039f67b305e10de4208520d..d978dc4cc93273a0a270c0d06a0dcbee4750ddfd 100644 --- a/src/renderers/webvr/WebVRManager.js +++ b/src/renderers/webvr/WebVRManager.js @@ -290,9 +290,6 @@ function WebVRManager( renderer ) { cameraL.far = camera.far; cameraR.far = camera.far; - cameraVR.matrixWorld.copy( camera.matrixWorld ); - cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse ); - cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix ); cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix ); @@ -326,10 +323,7 @@ function WebVRManager( renderer ) { cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix ); cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix ); - // HACK (mrdoob) - // https://github.com/w3c/webvr/issues/203 - - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); + cameraVR.setProjectionFromUnion(); // diff --git a/src/renderers/webvr/WebXRManager.js b/src/renderers/webvr/WebXRManager.js index 0adc0e7751e47c2dd364686e428d5cf6b3184166..1d8f6242dd22b5dcd3392516edc38f972f1bc4e5 100644 --- a/src/renderers/webvr/WebXRManager.js +++ b/src/renderers/webvr/WebXRManager.js @@ -161,8 +161,6 @@ function WebXRManager( renderer ) { var parent = camera.parent; var cameras = cameraVR.cameras; - // apply camera.parent to cameraVR - updateCamera( cameraVR, parent ); for ( var i = 0; i < cameras.length; i ++ ) { @@ -183,6 +181,8 @@ function WebXRManager( renderer ) { } + cameraVR.setProjectionFromUnion(); + return cameraVR; } @@ -221,11 +221,6 @@ function WebXRManager( renderer ) { cameraVR.matrix.copy( camera.matrix ); - // HACK (mrdoob) - // https://github.com/w3c/webvr/issues/203 - - cameraVR.projectionMatrix.copy( camera.projectionMatrix ); - } }