PeppersGhostEffect.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import {
  2. PerspectiveCamera,
  3. Quaternion,
  4. Vector3
  5. } from 'three';
  6. /**
  7. * A class that implements a peppers ghost effect.
  8. *
  9. * Reference: [Reflective Prism]{@link http://www.instructables.com/id/Reflective-Prism/?ALLSTEPS}
  10. *
  11. * @three_import import { PeppersGhostEffect } from 'three/addons/effects/PeppersGhostEffect.js';
  12. */
  13. class PeppersGhostEffect {
  14. /**
  15. * Constructs a new peppers ghost effect.
  16. *
  17. * @param {(WebGPURenderer|WebGLRenderer)} renderer - The renderer.
  18. */
  19. constructor( renderer ) {
  20. const scope = this;
  21. scope.cameraDistance = 15;
  22. scope.reflectFromAbove = false;
  23. // Internals
  24. let _halfWidth, _width, _height;
  25. const _cameraF = new PerspectiveCamera(); //front
  26. const _cameraB = new PerspectiveCamera(); //back
  27. const _cameraL = new PerspectiveCamera(); //left
  28. const _cameraR = new PerspectiveCamera(); //right
  29. const _position = new Vector3();
  30. const _quaternion = new Quaternion();
  31. const _scale = new Vector3();
  32. // Initialization
  33. renderer.autoClear = false;
  34. /**
  35. * Resizes the effect.
  36. *
  37. * @param {number} width - The width of the effect in logical pixels.
  38. * @param {number} height - The height of the effect in logical pixels.
  39. */
  40. this.setSize = function ( width, height ) {
  41. _halfWidth = width / 2;
  42. if ( width < height ) {
  43. _width = width / 3;
  44. _height = width / 3;
  45. } else {
  46. _width = height / 3;
  47. _height = height / 3;
  48. }
  49. renderer.setSize( width, height );
  50. };
  51. /**
  52. * When using this effect, this method should be called instead of the
  53. * default {@link WebGLRenderer#render}.
  54. *
  55. * @param {Object3D} scene - The scene to render.
  56. * @param {Camera} camera - The camera.
  57. */
  58. this.render = function ( scene, camera ) {
  59. if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
  60. if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
  61. camera.matrixWorld.decompose( _position, _quaternion, _scale );
  62. // front
  63. _cameraF.position.copy( _position );
  64. _cameraF.quaternion.copy( _quaternion );
  65. _cameraF.translateZ( scope.cameraDistance );
  66. _cameraF.lookAt( scene.position );
  67. // back
  68. _cameraB.position.copy( _position );
  69. _cameraB.quaternion.copy( _quaternion );
  70. _cameraB.translateZ( - ( scope.cameraDistance ) );
  71. _cameraB.lookAt( scene.position );
  72. _cameraB.rotation.z += 180 * ( Math.PI / 180 );
  73. // left
  74. _cameraL.position.copy( _position );
  75. _cameraL.quaternion.copy( _quaternion );
  76. _cameraL.translateX( - ( scope.cameraDistance ) );
  77. _cameraL.lookAt( scene.position );
  78. _cameraL.rotation.x += 90 * ( Math.PI / 180 );
  79. // right
  80. _cameraR.position.copy( _position );
  81. _cameraR.quaternion.copy( _quaternion );
  82. _cameraR.translateX( scope.cameraDistance );
  83. _cameraR.lookAt( scene.position );
  84. _cameraR.rotation.x += 90 * ( Math.PI / 180 );
  85. renderer.clear();
  86. renderer.setScissorTest( true );
  87. renderer.setScissor( _halfWidth - ( _width / 2 ), ( _height * 2 ), _width, _height );
  88. renderer.setViewport( _halfWidth - ( _width / 2 ), ( _height * 2 ), _width, _height );
  89. if ( scope.reflectFromAbove ) {
  90. renderer.render( scene, _cameraB );
  91. } else {
  92. renderer.render( scene, _cameraF );
  93. }
  94. renderer.setScissor( _halfWidth - ( _width / 2 ), 0, _width, _height );
  95. renderer.setViewport( _halfWidth - ( _width / 2 ), 0, _width, _height );
  96. if ( scope.reflectFromAbove ) {
  97. renderer.render( scene, _cameraF );
  98. } else {
  99. renderer.render( scene, _cameraB );
  100. }
  101. renderer.setScissor( _halfWidth - ( _width / 2 ) - _width, _height, _width, _height );
  102. renderer.setViewport( _halfWidth - ( _width / 2 ) - _width, _height, _width, _height );
  103. if ( scope.reflectFromAbove ) {
  104. renderer.render( scene, _cameraR );
  105. } else {
  106. renderer.render( scene, _cameraL );
  107. }
  108. renderer.setScissor( _halfWidth + ( _width / 2 ), _height, _width, _height );
  109. renderer.setViewport( _halfWidth + ( _width / 2 ), _height, _width, _height );
  110. if ( scope.reflectFromAbove ) {
  111. renderer.render( scene, _cameraL );
  112. } else {
  113. renderer.render( scene, _cameraR );
  114. }
  115. renderer.setScissorTest( false );
  116. };
  117. }
  118. }
  119. export { PeppersGhostEffect };