WebGLMultiview.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /**
  2. * @author fernandojsg / http://fernandojsg.com
  3. */
  4. import { WebGLRenderTarget } from '../WebGLRenderTarget.js';
  5. import { Matrix3 } from '../../math/Matrix3.js';
  6. import { Matrix4 } from '../../math/Matrix4.js';
  7. function WebGLMultiview( requested, gl, canvas, extensions, capabilities, properties ) {
  8. this.isAvailable = function () {
  9. return capabilities.multiview;
  10. };
  11. this.getNumViews = function () {
  12. return numViews;
  13. };
  14. this.getMaxViews = function () {
  15. return capabilities.maxMultiviewViews;
  16. };
  17. this.isEnabled = function () {
  18. return requested && this.isAvailable();
  19. };
  20. if ( requested && ! this.isAvailable() ) {
  21. console.warn( 'WebGLRenderer: Multiview requested but not supported by the browser' );
  22. } else if ( requested !== false && this.isAvailable() ) {
  23. console.info( 'WebGLRenderer: Multiview enabled' );
  24. }
  25. var numViews = 2;
  26. var framebuffer; // multiview framebuffer.
  27. var viewFramebuffer; // single views inside the multiview framebuffer.
  28. var framebufferWidth = 0;
  29. var framebufferHeight = 0;
  30. var texture = {
  31. color: null,
  32. depthStencil: null
  33. };
  34. this.computeCameraMatrices = function ( camera ) {
  35. if ( ! camera.projectionMatrices ) {
  36. camera.projectionMatrices = new Array( numViews );
  37. camera.viewMatrices = new Array( numViews );
  38. for ( var i = 0; i < numViews; i ++ ) {
  39. camera.projectionMatrices[ i ] = new Matrix4();
  40. camera.viewMatrices[ i ] = new Matrix4();
  41. }
  42. }
  43. if ( camera.isArrayCamera ) {
  44. for ( var i = 0; i < numViews; i ++ ) {
  45. camera.projectionMatrices[ i ].copy( camera.cameras[ i ].projectionMatrix );
  46. camera.viewMatrices[ i ].copy( camera.cameras[ i ].matrixWorldInverse );
  47. }
  48. } else {
  49. for ( var i = 0; i < numViews; i ++ ) {
  50. camera.projectionMatrices[ i ].copy( camera.projectionMatrix );
  51. camera.viewMatrices[ i ].copy( camera.matrixWorldInverse );
  52. }
  53. }
  54. };
  55. this.computeObjectMatrices = function ( object, camera ) {
  56. if ( ! object.modelViewMatrices ) {
  57. object.modelViewMatrices = new Array( numViews );
  58. object.normalMatrices = new Array( numViews );
  59. for ( var i = 0; i < numViews; i ++ ) {
  60. object.modelViewMatrices[ i ] = new Matrix4();
  61. object.normalMatrices[ i ] = new Matrix3();
  62. }
  63. }
  64. if ( camera.isArrayCamera ) {
  65. for ( var i = 0; i < numViews; i ++ ) {
  66. object.modelViewMatrices[ i ].multiplyMatrices( camera.cameras[ i ].matrixWorldInverse, object.matrixWorld );
  67. object.normalMatrices[ i ].getNormalMatrix( object.modelViewMatrices[ i ] );
  68. }
  69. } else {
  70. // In this case we still need to provide an array of matrices but just the first one will be used
  71. object.modelViewMatrices[ 0 ].multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
  72. object.normalMatrices[ 0 ].getNormalMatrix( object.modelViewMatrices[ 0 ] );
  73. for ( var i = 1; i < numViews; i ++ ) {
  74. object.modelViewMatrices[ i ].copy( object.modelViewMatrices[ 0 ] );
  75. object.normalMatrices[ i ].copy( object.normalMatrices[ 0 ] );
  76. }
  77. }
  78. };
  79. // @todo Get ArrayCamera
  80. this.createMultiviewRenderTargetTexture = function () {
  81. var halfWidth = Math.floor( canvas.width * 0.5 );
  82. framebuffer = gl.createFramebuffer();
  83. gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer );
  84. var ext = extensions.get( 'OVR_multiview2' );
  85. texture.color = gl.createTexture();
  86. gl.bindTexture( gl.TEXTURE_2D_ARRAY, texture.color );
  87. gl.texParameteri( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  88. gl.texParameteri( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
  89. gl.texImage3D( gl.TEXTURE_2D_ARRAY, 0, gl.RGBA8, halfWidth, canvas.height, numViews, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
  90. ext.framebufferTextureMultiviewOVR( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture.color, 0, 0, numViews );
  91. texture.depthStencil = gl.createTexture();
  92. gl.bindTexture( gl.TEXTURE_2D_ARRAY, texture.depthStencil );
  93. gl.texParameteri( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  94. gl.texParameteri( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
  95. gl.texImage3D( gl.TEXTURE_2D_ARRAY, 0, gl.DEPTH24_STENCIL8, halfWidth, canvas.height, numViews, 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, null );
  96. ext.framebufferTextureMultiviewOVR( gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, texture.depthStencil, 0, 0, numViews );
  97. viewFramebuffer = new Array( numViews );
  98. for ( var viewIndex = 0; viewIndex < numViews; ++ viewIndex ) {
  99. viewFramebuffer[ viewIndex ] = gl.createFramebuffer();
  100. gl.bindFramebuffer( gl.FRAMEBUFFER, viewFramebuffer[ viewIndex ] );
  101. gl.framebufferTextureLayer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture.color, 0, viewIndex );
  102. }
  103. framebufferWidth = halfWidth;
  104. framebufferHeight = canvas.height;
  105. this.renderTarget = new WebGLRenderTarget( framebufferWidth, framebufferHeight );
  106. // @hack This should be done in WebGLTextures?
  107. properties.get( this.renderTarget ).__webglFramebuffer = framebuffer;
  108. };
  109. this.bindFramebuffer = function ( camera ) {
  110. var width = canvas.width;
  111. var height = canvas.height;
  112. if ( camera.isArrayCamera ) {
  113. // Every camera must have the same size, so we just get the size from the first one
  114. var bounds = camera.cameras[ 0 ].bounds;
  115. width *= bounds.z;
  116. height *= bounds.w;
  117. }
  118. if ( framebufferWidth < width || framebufferHeight < height ) {
  119. console.log( 'WebGLMultiview: Updating multiview FBO with dimensions: ', width, height );
  120. gl.bindTexture( gl.TEXTURE_2D_ARRAY, texture.color );
  121. gl.texImage3D( gl.TEXTURE_2D_ARRAY, 0, gl.RGBA8, width, height, numViews, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
  122. gl.bindTexture( gl.TEXTURE_2D_ARRAY, texture.depthStencil );
  123. gl.texImage3D( gl.TEXTURE_2D_ARRAY, 0, gl.DEPTH24_STENCIL8, width, height, numViews, 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, null );
  124. framebufferWidth = width;
  125. framebufferHeight = height;
  126. this.renderTarget.setSize( width, height );
  127. }
  128. gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, framebuffer );
  129. };
  130. this.unbindFramebuffer = function ( camera ) {
  131. gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null );
  132. if ( camera.isArrayCamera ) {
  133. for ( var i = 0; i < camera.cameras.length; i ++ ) {
  134. var bounds = camera.cameras[ i ].bounds;
  135. var x = bounds.x * canvas.width;
  136. var y = bounds.y * canvas.height;
  137. var width = bounds.z * canvas.width;
  138. var height = bounds.w * canvas.height;
  139. gl.bindFramebuffer( gl.READ_FRAMEBUFFER, viewFramebuffer[ i ] );
  140. gl.blitFramebuffer( 0, 0, width, height, x, y, x + width, y + height, gl.COLOR_BUFFER_BIT, gl.NEAREST );
  141. }
  142. } else {
  143. gl.bindFramebuffer( gl.READ_FRAMEBUFFER, viewFramebuffer[ 0 ] );
  144. gl.blitFramebuffer( 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height, gl.COLOR_BUFFER_BIT, gl.NEAREST );
  145. }
  146. };
  147. if ( this.isEnabled() ) {
  148. this.createMultiviewRenderTargetTexture();
  149. }
  150. }
  151. export { WebGLMultiview };