@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
266 lines (265 loc) • 11.7 kB
JavaScript
import { WebXRFeatureName, WebXRFeaturesManager } from "../webXRFeaturesManager.js";
import { WebXRAbstractFeature } from "./WebXRAbstractFeature.js";
import { Matrix } from "../../Maths/math.vector.js";
import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture.js";
import { ShaderMaterial } from "../../Materials/shaderMaterial.js";
import "../../Shaders/velocity.fragment.js";
import "../../Shaders/velocity.vertex.js";
/**
* Used for Space Warp render process
*/
export class XRSpaceWarpRenderTarget extends RenderTargetTexture {
/**
* Creates a Space Warp render target
* @param motionVectorTexture WebGLTexture provided by WebGLSubImage
* @param depthStencilTexture WebGLTexture provided by WebGLSubImage
* @param scene scene used with the render target
* @param size the size of the render target (used for each view)
*/
constructor(motionVectorTexture, depthStencilTexture, scene, size = 512) {
super("spacewarp rtt", size, scene, false, true, 2, false, undefined, false, false, true, undefined, true);
this._originalPairing = [];
this._previousWorldMatrices = [];
this._previousTransforms = [Matrix.Identity(), Matrix.Identity()];
this._renderTarget = this.getScene().getEngine().createMultiviewRenderTargetTexture(this.getRenderWidth(), this.getRenderHeight(), motionVectorTexture, depthStencilTexture);
this._renderTarget._disposeOnlyFramebuffers = true;
this._texture = this._renderTarget.texture;
this._texture.isMultiview = true;
this._texture.format = 5;
if (scene) {
this._velocityMaterial = new ShaderMaterial("velocity shader material", scene, {
vertex: "velocity",
fragment: "velocity",
}, {
uniforms: ["world", "previousWorld", "viewProjection", "viewProjectionR", "previousViewProjection", "previousViewProjectionR"],
});
this._velocityMaterial._materialHelperNeedsPreviousMatrices = true;
this._velocityMaterial.onBindObservable.add((mesh) => {
// mesh. getWorldMatrix can be incorrect under rare conditions (e.g. when using a effective mesh in the render function).
// 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
this._previousWorldMatrices[mesh.uniqueId] = this._previousWorldMatrices[mesh.uniqueId] || mesh.getWorldMatrix();
this._velocityMaterial.getEffect().setMatrix("previousWorld", this._previousWorldMatrices[mesh.uniqueId]);
this._previousWorldMatrices[mesh.uniqueId] = mesh.getWorldMatrix();
// now set the scene's previous matrix
this._velocityMaterial.getEffect().setMatrix("previousViewProjection", this._previousTransforms[0]);
// multiview for sure
this._velocityMaterial.getEffect().setMatrix("previousViewProjectionR", this._previousTransforms[1]);
// store the previous (current, to be exact) transforms
this._previousTransforms[0].copyFrom(scene.getTransformMatrix());
this._previousTransforms[1].copyFrom(scene._transformMatrixR);
});
this._velocityMaterial.freeze();
}
}
render(useCameraPostProcess = false, dumpForDebug = false) {
// Swap to use velocity material
this._originalPairing.length = 0;
const scene = this.getScene();
// set the velocity material to render the velocity RTT
if (scene && this._velocityMaterial) {
scene.getActiveMeshes().forEach((mesh) => {
this._originalPairing.push([mesh, mesh.material]);
mesh.material = this._velocityMaterial;
});
}
super.render(useCameraPostProcess, dumpForDebug);
// Restore original materials
for (const tuple of this._originalPairing) {
tuple[0].material = tuple[1];
}
}
/**
* @internal
*/
_bindFrameBuffer() {
if (!this._renderTarget) {
return;
}
this.getScene().getEngine().bindSpaceWarpFramebuffer(this._renderTarget);
}
/**
* Gets the number of views the corresponding to the texture (eg. a SpaceWarpRenderTarget will have > 1)
* @returns the view count
*/
getViewCount() {
return 2;
}
dispose() {
super.dispose();
this._velocityMaterial.dispose();
this._previousTransforms.length = 0;
this._previousWorldMatrices.length = 0;
this._originalPairing.length = 0;
}
}
/**
* WebXR Space Warp Render Target Texture Provider
*/
export class WebXRSpaceWarpRenderTargetTextureProvider {
constructor(_scene, _xrSessionManager, _xrWebGLBinding) {
this._scene = _scene;
this._xrSessionManager = _xrSessionManager;
this._xrWebGLBinding = _xrWebGLBinding;
this._lastSubImages = new Map();
this._renderTargetTextures = new Map();
this._engine = _scene.getEngine();
}
_getSubImageForView(view) {
const layerWrapper = this._xrSessionManager._getBaseLayerWrapper();
if (!layerWrapper) {
throw new Error("For Space Warp, the base layer should be a WebXR Projection Layer.");
}
if (layerWrapper.layerType !== "XRProjectionLayer") {
throw new Error('For Space Warp, the base layer type should "XRProjectionLayer".');
}
const layer = layerWrapper.layer;
return this._xrWebGLBinding.getViewSubImage(layer, view);
}
_setViewportForSubImage(viewport, subImage) {
viewport.x = 0;
viewport.y = 0;
viewport.width = subImage.motionVectorTextureWidth;
viewport.height = subImage.motionVectorTextureHeight;
}
_createRenderTargetTexture(width, height, framebuffer, motionVectorTexture, depthStencilTexture) {
if (!this._engine) {
throw new Error("Engine is disposed");
}
const textureSize = { width, height };
// Create render target texture from the internal texture
const renderTargetTexture = new XRSpaceWarpRenderTarget(motionVectorTexture, depthStencilTexture, this._scene, textureSize);
const renderTargetWrapper = renderTargetTexture.renderTarget;
if (framebuffer) {
renderTargetWrapper._framebuffer = framebuffer;
}
// Create internal texture
renderTargetWrapper._colorTextureArray = motionVectorTexture;
renderTargetWrapper._depthStencilTextureArray = depthStencilTexture;
renderTargetTexture.disableRescaling();
renderTargetTexture.renderListPredicate = () => true;
return renderTargetTexture;
}
_getRenderTargetForSubImage(subImage, view) {
const lastSubImage = this._lastSubImages.get(view);
let renderTargetTexture = this._renderTargetTextures.get(view.eye);
const width = subImage.motionVectorTextureWidth;
const height = subImage.motionVectorTextureHeight;
if (!renderTargetTexture || lastSubImage?.textureWidth !== width || lastSubImage?.textureHeight != height) {
renderTargetTexture = this._createRenderTargetTexture(width, height, null, subImage.motionVectorTexture, subImage.depthStencilTexture);
this._renderTargetTextures.set(view.eye, renderTargetTexture);
this._framebufferDimensions = {
framebufferWidth: width,
framebufferHeight: height,
};
}
this._lastSubImages.set(view, subImage);
return renderTargetTexture;
}
trySetViewportForView(viewport, view) {
const subImage = this._lastSubImages.get(view) || this._getSubImageForView(view);
if (subImage) {
this._setViewportForSubImage(viewport, subImage);
return true;
}
return false;
}
/**
* Access the motion vector (which will turn on Space Warp)
* @param view the view to access the motion vector texture for
*/
accessMotionVector(view) {
const subImage = this._getSubImageForView(view);
if (subImage) {
// Meta Quest Browser uses accessing these textures as a sign for turning on Space Warp
subImage.motionVectorTexture;
subImage.depthStencilTexture;
}
}
getRenderTargetTextureForEye(_eye) {
return null;
}
getRenderTargetTextureForView(view) {
const subImage = this._getSubImageForView(view);
if (subImage) {
return this._getRenderTargetForSubImage(subImage, view);
}
return null;
}
dispose() {
this._renderTargetTextures.forEach((rtt) => rtt.dispose());
this._renderTargetTextures.clear();
}
}
/**
* the WebXR Space Warp feature.
*/
export class WebXRSpaceWarp extends WebXRAbstractFeature {
/**
* constructor for the space warp feature
* @param _xrSessionManager the xr session manager for this feature
*/
constructor(_xrSessionManager) {
super(_xrSessionManager);
this._onAfterRenderObserver = null;
this.dependsOn = [WebXRFeatureName.LAYERS];
this.xrNativeFeatureName = "space-warp";
this._xrSessionManager.scene.needsPreviousWorldMatrices = true;
}
/**
* Attach this feature.
* Will usually be called by the features manager.
*
* @returns true if successful.
*/
attach() {
if (!super.attach()) {
return false;
}
const engine = this._xrSessionManager.scene.getEngine();
this._glContext = engine._gl;
this._xrWebGLBinding = new XRWebGLBinding(this._xrSessionManager.session, this._glContext);
this.spaceWarpRTTProvider = new WebXRSpaceWarpRenderTargetTextureProvider(this._xrSessionManager.scene, this._xrSessionManager, this._xrWebGLBinding);
this._onAfterRenderObserver = this._xrSessionManager.scene.onAfterRenderObservable.add(() => this._onAfterRender());
return true;
}
detach() {
this._xrSessionManager.scene.onAfterRenderObservable.remove(this._onAfterRenderObserver);
return super.detach();
}
_onAfterRender() {
if (this.attached && this._renderTargetTexture) {
this._renderTargetTexture.render(false, false);
}
}
isCompatible() {
return this._xrSessionManager.scene.getEngine().getCaps().colorBufferHalfFloat || false;
}
dispose() {
super.dispose();
}
_onXRFrame(_xrFrame) {
const pose = _xrFrame.getViewerPose(this._xrSessionManager.referenceSpace);
if (!pose) {
return;
}
// get the first view to which we will create a texture (or update it)
const view = pose.views[0];
this._renderTargetTexture = this._renderTargetTexture || this.spaceWarpRTTProvider.getRenderTargetTextureForView(view);
this.spaceWarpRTTProvider.accessMotionVector(view);
}
}
/**
* The module's name
*/
WebXRSpaceWarp.Name = WebXRFeatureName.SPACE_WARP;
/**
* The (Babylon) version of this module.
* This is an integer representing the implementation version.
* This number does not correspond to the WebXR specs version
*/
WebXRSpaceWarp.Version = 1;
//register the plugin
WebXRFeaturesManager.AddWebXRFeature(WebXRSpaceWarp.Name, (xrSessionManager) => {
return () => new WebXRSpaceWarp(xrSessionManager);
}, WebXRSpaceWarp.Version, false);
//# sourceMappingURL=WebXRSpaceWarp.js.map