WebXRSpaceWarp.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import { WebXRFeatureName, WebXRFeaturesManager } from "../webXRFeaturesManager.js";
  2. import { WebXRAbstractFeature } from "./WebXRAbstractFeature.js";
  3. import { Matrix } from "../../Maths/math.vector.js";
  4. import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture.js";
  5. import { ShaderMaterial } from "../../Materials/shaderMaterial.js";
  6. import "../../Shaders/velocity.fragment.js";
  7. import "../../Shaders/velocity.vertex.js";
  8. /**
  9. * Used for Space Warp render process
  10. */
  11. export class XRSpaceWarpRenderTarget extends RenderTargetTexture {
  12. /**
  13. * Creates a Space Warp render target
  14. * @param motionVectorTexture WebGLTexture provided by WebGLSubImage
  15. * @param depthStencilTexture WebGLTexture provided by WebGLSubImage
  16. * @param scene scene used with the render target
  17. * @param size the size of the render target (used for each view)
  18. */
  19. constructor(motionVectorTexture, depthStencilTexture, scene, size = 512) {
  20. super("spacewarp rtt", size, scene, false, true, 2, false, undefined, false, false, true, undefined, true);
  21. this._originalPairing = [];
  22. this._previousWorldMatrices = [];
  23. this._previousTransforms = [Matrix.Identity(), Matrix.Identity()];
  24. this._renderTarget = this.getScene().getEngine().createMultiviewRenderTargetTexture(this.getRenderWidth(), this.getRenderHeight(), motionVectorTexture, depthStencilTexture);
  25. this._renderTarget._disposeOnlyFramebuffers = true;
  26. this._texture = this._renderTarget.texture;
  27. this._texture.isMultiview = true;
  28. this._texture.format = 5;
  29. if (scene) {
  30. this._velocityMaterial = new ShaderMaterial("velocity shader material", scene, {
  31. vertex: "velocity",
  32. fragment: "velocity",
  33. }, {
  34. uniforms: ["world", "previousWorld", "viewProjection", "viewProjectionR", "previousViewProjection", "previousViewProjectionR"],
  35. });
  36. this._velocityMaterial._materialHelperNeedsPreviousMatrices = true;
  37. this._velocityMaterial.onBindObservable.add((mesh) => {
  38. // mesh. getWorldMatrix can be incorrect under rare conditions (e.g. when using a effective mesh in the render function).
  39. // If the case arise that will require changing it we will need to change the bind process in the material class to also provide the world matrix as a parameter
  40. this._previousWorldMatrices[mesh.uniqueId] = this._previousWorldMatrices[mesh.uniqueId] || mesh.getWorldMatrix();
  41. this._velocityMaterial.getEffect().setMatrix("previousWorld", this._previousWorldMatrices[mesh.uniqueId]);
  42. this._previousWorldMatrices[mesh.uniqueId] = mesh.getWorldMatrix();
  43. // now set the scene's previous matrix
  44. this._velocityMaterial.getEffect().setMatrix("previousViewProjection", this._previousTransforms[0]);
  45. // multiview for sure
  46. this._velocityMaterial.getEffect().setMatrix("previousViewProjectionR", this._previousTransforms[1]);
  47. // store the previous (current, to be exact) transforms
  48. this._previousTransforms[0].copyFrom(scene.getTransformMatrix());
  49. this._previousTransforms[1].copyFrom(scene._transformMatrixR);
  50. });
  51. this._velocityMaterial.freeze();
  52. }
  53. }
  54. render(useCameraPostProcess = false, dumpForDebug = false) {
  55. // Swap to use velocity material
  56. this._originalPairing.length = 0;
  57. const scene = this.getScene();
  58. // set the velocity material to render the velocity RTT
  59. if (scene && this._velocityMaterial) {
  60. scene.getActiveMeshes().forEach((mesh) => {
  61. this._originalPairing.push([mesh, mesh.material]);
  62. mesh.material = this._velocityMaterial;
  63. });
  64. }
  65. super.render(useCameraPostProcess, dumpForDebug);
  66. // Restore original materials
  67. this._originalPairing.forEach((tuple) => {
  68. tuple[0].material = tuple[1];
  69. });
  70. }
  71. /**
  72. * @internal
  73. */
  74. _bindFrameBuffer() {
  75. if (!this._renderTarget) {
  76. return;
  77. }
  78. this.getScene().getEngine().bindSpaceWarpFramebuffer(this._renderTarget);
  79. }
  80. /**
  81. * Gets the number of views the corresponding to the texture (eg. a SpaceWarpRenderTarget will have > 1)
  82. * @returns the view count
  83. */
  84. getViewCount() {
  85. return 2;
  86. }
  87. dispose() {
  88. super.dispose();
  89. this._velocityMaterial.dispose();
  90. this._previousTransforms.length = 0;
  91. this._previousWorldMatrices.length = 0;
  92. this._originalPairing.length = 0;
  93. }
  94. }
  95. /**
  96. * WebXR Space Warp Render Target Texture Provider
  97. */
  98. export class WebXRSpaceWarpRenderTargetTextureProvider {
  99. constructor(_scene, _xrSessionManager, _xrWebGLBinding) {
  100. this._scene = _scene;
  101. this._xrSessionManager = _xrSessionManager;
  102. this._xrWebGLBinding = _xrWebGLBinding;
  103. this._lastSubImages = new Map();
  104. this._renderTargetTextures = new Map();
  105. this._engine = _scene.getEngine();
  106. }
  107. _getSubImageForView(view) {
  108. const layerWrapper = this._xrSessionManager._getBaseLayerWrapper();
  109. if (!layerWrapper) {
  110. throw new Error("For Space Warp, the base layer should be a WebXR Projection Layer.");
  111. }
  112. if (layerWrapper.layerType !== "XRProjectionLayer") {
  113. throw new Error('For Space Warp, the base layer type should "XRProjectionLayer".');
  114. }
  115. const layer = layerWrapper.layer;
  116. return this._xrWebGLBinding.getViewSubImage(layer, view);
  117. }
  118. _setViewportForSubImage(viewport, subImage) {
  119. viewport.x = 0;
  120. viewport.y = 0;
  121. viewport.width = subImage.motionVectorTextureWidth;
  122. viewport.height = subImage.motionVectorTextureHeight;
  123. }
  124. _createRenderTargetTexture(width, height, framebuffer, motionVectorTexture, depthStencilTexture) {
  125. if (!this._engine) {
  126. throw new Error("Engine is disposed");
  127. }
  128. const textureSize = { width, height };
  129. // Create render target texture from the internal texture
  130. const renderTargetTexture = new XRSpaceWarpRenderTarget(motionVectorTexture, depthStencilTexture, this._scene, textureSize);
  131. const renderTargetWrapper = renderTargetTexture.renderTarget;
  132. if (framebuffer) {
  133. renderTargetWrapper._framebuffer = framebuffer;
  134. }
  135. // Create internal texture
  136. renderTargetWrapper._colorTextureArray = motionVectorTexture;
  137. renderTargetWrapper._depthStencilTextureArray = depthStencilTexture;
  138. renderTargetTexture.disableRescaling();
  139. renderTargetTexture.renderListPredicate = () => true;
  140. return renderTargetTexture;
  141. }
  142. _getRenderTargetForSubImage(subImage, view) {
  143. const lastSubImage = this._lastSubImages.get(view);
  144. let renderTargetTexture = this._renderTargetTextures.get(view.eye);
  145. const width = subImage.motionVectorTextureWidth;
  146. const height = subImage.motionVectorTextureHeight;
  147. if (!renderTargetTexture || lastSubImage?.textureWidth !== width || lastSubImage?.textureHeight != height) {
  148. renderTargetTexture = this._createRenderTargetTexture(width, height, null, subImage.motionVectorTexture, subImage.depthStencilTexture);
  149. this._renderTargetTextures.set(view.eye, renderTargetTexture);
  150. this._framebufferDimensions = {
  151. framebufferWidth: width,
  152. framebufferHeight: height,
  153. };
  154. }
  155. this._lastSubImages.set(view, subImage);
  156. return renderTargetTexture;
  157. }
  158. trySetViewportForView(viewport, view) {
  159. const subImage = this._lastSubImages.get(view) || this._getSubImageForView(view);
  160. if (subImage) {
  161. this._setViewportForSubImage(viewport, subImage);
  162. return true;
  163. }
  164. return false;
  165. }
  166. /**
  167. * Access the motion vector (which will turn on Space Warp)
  168. * @param view the view to access the motion vector texture for
  169. */
  170. accessMotionVector(view) {
  171. const subImage = this._getSubImageForView(view);
  172. if (subImage) {
  173. // Meta Quest Browser uses accessing these textures as a sign for turning on Space Warp
  174. subImage.motionVectorTexture;
  175. subImage.depthStencilTexture;
  176. }
  177. }
  178. getRenderTargetTextureForEye(_eye) {
  179. return null;
  180. }
  181. getRenderTargetTextureForView(view) {
  182. const subImage = this._getSubImageForView(view);
  183. if (subImage) {
  184. return this._getRenderTargetForSubImage(subImage, view);
  185. }
  186. return null;
  187. }
  188. dispose() {
  189. this._renderTargetTextures.forEach((rtt) => rtt.dispose());
  190. this._renderTargetTextures.clear();
  191. }
  192. }
  193. /**
  194. * the WebXR Space Warp feature.
  195. */
  196. export class WebXRSpaceWarp extends WebXRAbstractFeature {
  197. /**
  198. * constructor for the space warp feature
  199. * @param _xrSessionManager the xr session manager for this feature
  200. */
  201. constructor(_xrSessionManager) {
  202. super(_xrSessionManager);
  203. this._onAfterRenderObserver = null;
  204. this.dependsOn = [WebXRFeatureName.LAYERS];
  205. this.xrNativeFeatureName = "space-warp";
  206. this._xrSessionManager.scene.needsPreviousWorldMatrices = true;
  207. }
  208. /**
  209. * Attach this feature.
  210. * Will usually be called by the features manager.
  211. *
  212. * @returns true if successful.
  213. */
  214. attach() {
  215. if (!super.attach()) {
  216. return false;
  217. }
  218. const engine = this._xrSessionManager.scene.getEngine();
  219. this._glContext = engine._gl;
  220. this._xrWebGLBinding = new XRWebGLBinding(this._xrSessionManager.session, this._glContext);
  221. this.spaceWarpRTTProvider = new WebXRSpaceWarpRenderTargetTextureProvider(this._xrSessionManager.scene, this._xrSessionManager, this._xrWebGLBinding);
  222. this._onAfterRenderObserver = this._xrSessionManager.scene.onAfterRenderObservable.add(() => this._onAfterRender());
  223. return true;
  224. }
  225. detach() {
  226. this._xrSessionManager.scene.onAfterRenderObservable.remove(this._onAfterRenderObserver);
  227. return super.detach();
  228. }
  229. _onAfterRender() {
  230. if (this.attached && this._renderTargetTexture) {
  231. this._renderTargetTexture.render(false, false);
  232. }
  233. }
  234. isCompatible() {
  235. return this._xrSessionManager.scene.getEngine().getCaps().colorBufferHalfFloat || false;
  236. }
  237. dispose() {
  238. super.dispose();
  239. }
  240. _onXRFrame(_xrFrame) {
  241. const pose = _xrFrame.getViewerPose(this._xrSessionManager.referenceSpace);
  242. if (!pose) {
  243. return;
  244. }
  245. // get the first view to which we will create a texture (or update it)
  246. const view = pose.views[0];
  247. this._renderTargetTexture = this._renderTargetTexture || this.spaceWarpRTTProvider.getRenderTargetTextureForView(view);
  248. this.spaceWarpRTTProvider.accessMotionVector(view);
  249. }
  250. }
  251. /**
  252. * The module's name
  253. */
  254. WebXRSpaceWarp.Name = WebXRFeatureName.SPACE_WARP;
  255. /**
  256. * The (Babylon) version of this module.
  257. * This is an integer representing the implementation version.
  258. * This number does not correspond to the WebXR specs version
  259. */
  260. WebXRSpaceWarp.Version = 1;
  261. //register the plugin
  262. WebXRFeaturesManager.AddWebXRFeature(WebXRSpaceWarp.Name, (xrSessionManager) => {
  263. return () => new WebXRSpaceWarp(xrSessionManager);
  264. }, WebXRSpaceWarp.Version, false);
  265. //# sourceMappingURL=WebXRSpaceWarp.js.map