OculusRiftEffect.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /**
  2. * @author troffmo5 / http://github.com/troffmo5
  3. *
  4. * Effect to render the scene in stereo 3d side by side with lens distortion.
  5. * It is written to be used with the Oculus Rift (http://www.oculusvr.com/) but
  6. * it works also with other HMD using the same technology
  7. */
  8. THREE.OculusRiftEffect = function ( renderer, options ) {
  9. // worldFactor indicates how many units is 1 meter
  10. var worldFactor = (options && options.worldFactor) ? options.worldFactor: 1.0;
  11. // Specific HMD parameters
  12. var HMD = (options && options.HMD) ? options.HMD: {
  13. // Parameters from the Oculus Rift DK1
  14. hResolution: 1280,
  15. vResolution: 800,
  16. hScreenSize: 0.14976,
  17. vScreenSize: 0.0936,
  18. interpupillaryDistance: 0.064,
  19. lensSeparationDistance: 0.064,
  20. eyeToScreenDistance: 0.041,
  21. distortionK : [1.0, 0.22, 0.24, 0.0]
  22. };
  23. // Perspective camera
  24. var pCamera = new THREE.PerspectiveCamera();
  25. pCamera.matrixAutoUpdate = false;
  26. pCamera.target = new THREE.Vector3();
  27. // Orthographic camera
  28. var oCamera = new THREE.OrthographicCamera( -1, 1, 1, -1, 1, 1000 );
  29. oCamera.position.z = 1;
  30. // pre-render hooks
  31. this.preLeftRender = function() {};
  32. this.preRightRender = function() {};
  33. renderer.autoClear = false;
  34. var emptyColor = new THREE.Color("black");
  35. // Render target
  36. var RTParams = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat };
  37. var renderTarget = new THREE.WebGLRenderTarget( 640, 800, RTParams );
  38. var RTMaterial = new THREE.ShaderMaterial( {
  39. uniforms: {
  40. "texid": { type: "t", value: renderTarget },
  41. "scale": { type: "v2", value: new THREE.Vector2(1.0,1.0) },
  42. "scaleIn": { type: "v2", value: new THREE.Vector2(1.0,1.0) },
  43. "lensCenter": { type: "v2", value: new THREE.Vector2(0.0,0.0) },
  44. "hmdWarpParam": { type: "v4", value: new THREE.Vector4(1.0,0.0,0.0,0.0) }
  45. },
  46. vertexShader: [
  47. "varying vec2 vUv;",
  48. "void main() {",
  49. " vUv = uv;",
  50. " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
  51. "}"
  52. ].join("\n"),
  53. fragmentShader: [
  54. "uniform vec2 scale;",
  55. "uniform vec2 scaleIn;",
  56. "uniform vec2 lensCenter;",
  57. "uniform vec4 hmdWarpParam;",
  58. "uniform sampler2D texid;",
  59. "varying vec2 vUv;",
  60. "void main()",
  61. "{",
  62. " vec2 uv = (vUv*2.0)-1.0;", // range from [0,1] to [-1,1]
  63. " vec2 theta = (uv-lensCenter)*scaleIn;",
  64. " float rSq = theta.x*theta.x + theta.y*theta.y;",
  65. " vec2 rvector = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq);",
  66. " vec2 tc = (lensCenter + scale * rvector);",
  67. " tc = (tc+1.0)/2.0;", // range from [-1,1] to [0,1]
  68. " if (any(bvec2(clamp(tc, vec2(0.0,0.0), vec2(1.0,1.0))-tc)))",
  69. " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);",
  70. " else",
  71. " gl_FragColor = texture2D(texid, tc);",
  72. "}"
  73. ].join("\n")
  74. } );
  75. var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), RTMaterial );
  76. // Final scene
  77. var finalScene = new THREE.Scene();
  78. finalScene.add( oCamera );
  79. finalScene.add( mesh );
  80. var left = {}, right = {};
  81. var distScale = 1.0;
  82. this.setHMD = function(v) {
  83. HMD = v;
  84. // Compute aspect ratio and FOV
  85. var aspect = HMD.hResolution / (2*HMD.vResolution);
  86. // Fov is normally computed with:
  87. // THREE.Math.radToDeg( 2*Math.atan2(HMD.vScreenSize,2*HMD.eyeToScreenDistance) );
  88. // But with lens distortion it is increased (see Oculus SDK Documentation)
  89. var r = -1.0 - (4 * (HMD.hScreenSize/4 - HMD.lensSeparationDistance/2) / HMD.hScreenSize);
  90. 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));
  91. var fov = THREE.Math.radToDeg(2*Math.atan2(HMD.vScreenSize*distScale, 2*HMD.eyeToScreenDistance));
  92. // Compute camera projection matrices
  93. var proj = (new THREE.Matrix4()).makePerspective( fov, aspect, 0.3, 10000 );
  94. var h = 4 * (HMD.hScreenSize/4 - HMD.interpupillaryDistance/2) / HMD.hScreenSize;
  95. left.proj = ((new THREE.Matrix4()).makeTranslation( h, 0.0, 0.0 )).multiply(proj);
  96. right.proj = ((new THREE.Matrix4()).makeTranslation( -h, 0.0, 0.0 )).multiply(proj);
  97. // Compute camera transformation matrices
  98. left.tranform = (new THREE.Matrix4()).makeTranslation( -worldFactor * HMD.interpupillaryDistance/2, 0.0, 0.0 );
  99. right.tranform = (new THREE.Matrix4()).makeTranslation( worldFactor * HMD.interpupillaryDistance/2, 0.0, 0.0 );
  100. // Compute Viewport
  101. left.viewport = [0, 0, HMD.hResolution/2, HMD.vResolution];
  102. right.viewport = [HMD.hResolution/2, 0, HMD.hResolution/2, HMD.vResolution];
  103. // Distortion shader parameters
  104. var lensShift = 4 * (HMD.hScreenSize/4 - HMD.lensSeparationDistance/2) / HMD.hScreenSize;
  105. left.lensCenter = new THREE.Vector2(lensShift, 0.0);
  106. right.lensCenter = new THREE.Vector2(-lensShift, 0.0);
  107. RTMaterial.uniforms['hmdWarpParam'].value = new THREE.Vector4(HMD.distortionK[0], HMD.distortionK[1], HMD.distortionK[2], HMD.distortionK[3]);
  108. RTMaterial.uniforms['scaleIn'].value = new THREE.Vector2(1.0,1.0/aspect);
  109. RTMaterial.uniforms['scale'].value = new THREE.Vector2(1.0/distScale, 1.0*aspect/distScale);
  110. // Create render target
  111. renderTarget = new THREE.WebGLRenderTarget( HMD.hResolution*distScale/2, HMD.vResolution*distScale, RTParams );
  112. RTMaterial.uniforms[ "texid" ].value = renderTarget;
  113. }
  114. this.getHMD = function() {return HMD};
  115. this.setHMD(HMD);
  116. this.setSize = function ( width, height ) {
  117. left.viewport = [width/2 - HMD.hResolution/2, height/2 - HMD.vResolution/2, HMD.hResolution/2, HMD.vResolution];
  118. right.viewport = [width/2, height/2 - HMD.vResolution/2, HMD.hResolution/2, HMD.vResolution];
  119. renderer.setSize( width, height );
  120. };
  121. this.render = function ( scene, camera ) {
  122. var cc = renderer.getClearColor().clone();
  123. // Clear
  124. renderer.setClearColor(emptyColor);
  125. renderer.clear();
  126. renderer.setClearColor(cc);
  127. // camera parameters
  128. if (camera.matrixAutoUpdate) camera.updateMatrix();
  129. // Render left
  130. this.preLeftRender();
  131. pCamera.projectionMatrix.copy(left.proj);
  132. pCamera.matrix.copy(camera.matrix).multiply(left.tranform);
  133. pCamera.matrixWorldNeedsUpdate = true;
  134. renderer.setViewport(left.viewport[0], left.viewport[1], left.viewport[2], left.viewport[3]);
  135. RTMaterial.uniforms['lensCenter'].value = left.lensCenter;
  136. renderer.render( scene, pCamera, renderTarget, true );
  137. renderer.render( finalScene, oCamera );
  138. // Render right
  139. this.preRightRender();
  140. pCamera.projectionMatrix.copy(right.proj);
  141. pCamera.matrix.copy(camera.matrix).multiply(right.tranform);
  142. pCamera.matrixWorldNeedsUpdate = true;
  143. renderer.setViewport(right.viewport[0], right.viewport[1], right.viewport[2], right.viewport[3]);
  144. RTMaterial.uniforms['lensCenter'].value = right.lensCenter;
  145. renderer.render( scene, pCamera, renderTarget, true );
  146. renderer.render( finalScene, oCamera );
  147. };
  148. };