PerspectiveCamera.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import { Camera } from './Camera.js';
  2. import { Object3D } from '../core/Object3D.js';
  3. import { _Math } from '../math/Math.js';
  4. /**
  5. * @author mrdoob / http://mrdoob.com/
  6. * @author greggman / http://games.greggman.com/
  7. * @author zz85 / http://www.lab4games.net/zz85/blog
  8. * @author tschw
  9. */
  10. function PerspectiveCamera( fov, aspect, near, far ) {
  11. Camera.call( this );
  12. this.type = 'PerspectiveCamera';
  13. this.fov = fov !== undefined ? fov : 50;
  14. this.zoom = 1;
  15. this.near = near !== undefined ? near : 0.1;
  16. this.far = far !== undefined ? far : 2000;
  17. this.focus = 10;
  18. this.aspect = aspect !== undefined ? aspect : 1;
  19. this.view = null;
  20. this.filmGauge = 35; // width of the film (default in millimeters)
  21. this.filmOffset = 0; // horizontal film offset (same unit as gauge)
  22. this.updateProjectionMatrix();
  23. }
  24. PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), {
  25. constructor: PerspectiveCamera,
  26. isPerspectiveCamera: true,
  27. copy: function ( source, recursive ) {
  28. Camera.prototype.copy.call( this, source, recursive );
  29. this.fov = source.fov;
  30. this.zoom = source.zoom;
  31. this.near = source.near;
  32. this.far = source.far;
  33. this.focus = source.focus;
  34. this.aspect = source.aspect;
  35. this.view = source.view === null ? null : Object.assign( {}, source.view );
  36. this.filmGauge = source.filmGauge;
  37. this.filmOffset = source.filmOffset;
  38. return this;
  39. },
  40. /**
  41. * Sets the FOV by focal length in respect to the current .filmGauge.
  42. *
  43. * The default film gauge is 35, so that the focal length can be specified for
  44. * a 35mm (full frame) camera.
  45. *
  46. * Values for focal length and film gauge must have the same unit.
  47. */
  48. setFocalLength: function ( focalLength ) {
  49. // see http://www.bobatkins.com/photography/technical/field_of_view.html
  50. var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;
  51. this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope );
  52. this.updateProjectionMatrix();
  53. },
  54. /**
  55. * Calculates the focal length from the current .fov and .filmGauge.
  56. */
  57. getFocalLength: function () {
  58. var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov );
  59. return 0.5 * this.getFilmHeight() / vExtentSlope;
  60. },
  61. getEffectiveFOV: function () {
  62. return _Math.RAD2DEG * 2 * Math.atan(
  63. Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom );
  64. },
  65. getFilmWidth: function () {
  66. // film not completely covered in portrait format (aspect < 1)
  67. return this.filmGauge * Math.min( this.aspect, 1 );
  68. },
  69. getFilmHeight: function () {
  70. // film not completely covered in landscape format (aspect > 1)
  71. return this.filmGauge / Math.max( this.aspect, 1 );
  72. },
  73. /**
  74. * Sets an offset in a larger frustum. This is useful for multi-window or
  75. * multi-monitor/multi-machine setups.
  76. *
  77. * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
  78. * the monitors are in grid like this
  79. *
  80. * +---+---+---+
  81. * | A | B | C |
  82. * +---+---+---+
  83. * | D | E | F |
  84. * +---+---+---+
  85. *
  86. * then for each monitor you would call it like this
  87. *
  88. * var w = 1920;
  89. * var h = 1080;
  90. * var fullWidth = w * 3;
  91. * var fullHeight = h * 2;
  92. *
  93. * --A--
  94. * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
  95. * --B--
  96. * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
  97. * --C--
  98. * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
  99. * --D--
  100. * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
  101. * --E--
  102. * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
  103. * --F--
  104. * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
  105. *
  106. * Note there is no reason monitors have to be the same size or in a grid.
  107. */
  108. setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {
  109. this.aspect = fullWidth / fullHeight;
  110. if ( this.view === null ) {
  111. this.view = {
  112. fullWidth: 1,
  113. fullHeight: 1,
  114. offsetX: 0,
  115. offsetY: 0,
  116. width: 1,
  117. height: 1
  118. };
  119. }
  120. this.view.fullWidth = fullWidth;
  121. this.view.fullHeight = fullHeight;
  122. this.view.offsetX = x;
  123. this.view.offsetY = y;
  124. this.view.width = width;
  125. this.view.height = height;
  126. this.updateProjectionMatrix();
  127. },
  128. clearViewOffset: function () {
  129. this.view = null;
  130. this.updateProjectionMatrix();
  131. },
  132. updateProjectionMatrix: function () {
  133. var near = this.near,
  134. top = near * Math.tan(
  135. _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom,
  136. height = 2 * top,
  137. width = this.aspect * height,
  138. left = - 0.5 * width,
  139. view = this.view;
  140. if ( view !== null ) {
  141. var fullWidth = view.fullWidth,
  142. fullHeight = view.fullHeight;
  143. left += view.offsetX * width / fullWidth;
  144. top -= view.offsetY * height / fullHeight;
  145. width *= view.width / fullWidth;
  146. height *= view.height / fullHeight;
  147. }
  148. var skew = this.filmOffset;
  149. if ( skew !== 0 ) left += near * skew / this.getFilmWidth();
  150. this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far );
  151. },
  152. toJSON: function ( meta ) {
  153. var data = Object3D.prototype.toJSON.call( this, meta );
  154. data.object.fov = this.fov;
  155. data.object.zoom = this.zoom;
  156. data.object.near = this.near;
  157. data.object.far = this.far;
  158. data.object.focus = this.focus;
  159. data.object.aspect = this.aspect;
  160. if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );
  161. data.object.filmGauge = this.filmGauge;
  162. data.object.filmOffset = this.filmOffset;
  163. return data;
  164. }
  165. } );
  166. export { PerspectiveCamera };