فهرست منبع

Merge pull request #6061 from TrevorDev/multiviewComponent

multiview component
David Catuhe 6 سال پیش
والد
کامیت
3abd30c3ab

+ 1 - 1
dist/preview release/what's new.md

@@ -29,7 +29,7 @@
   - InvalidateRect added to AdvancedDynamicTexture to improve perf for heavily populated GUIs, works with shadows ([TrevorDev](https://github.com/TrevorDev)) **** NEED DEMO or DOC LINK)
 - Migrated the code to modules and deploy [ES6 npm packages](https://doc.babylonjs.com/features/es6_support) ([Sebavan](https://github.com/Sebavan))
 - Added `TrailMesh` class. Credit to furcatomasz ([danjpar](https://github.com/danjpar)) **** NEED DEMO or DOC LINK)
-- Support rendering to a Multiview outputRenderTargetTexture to improve performance for XR scenarios ([TrevorDev](https://github.com/TrevorDev))
+- Support rendering to a Multiview outputRenderTargetTexture with multiview engine component to improve performance for XR scenarios ([TrevorDev](https://github.com/TrevorDev))
 - PBR:
   - Added Inspector Debug Mode ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
   - Added Smith Height Correlated Visibility term to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)

+ 3 - 2
src/Cameras/RigModes/vrRigMode.ts

@@ -1,6 +1,7 @@
 import { Camera } from "../camera";
 import { Matrix, Viewport } from "../../Maths/math";
-import { VRDistortionCorrectionPostProcess, VRMultiviewToSingleview } from "../../PostProcesses/vrDistortionCorrectionPostProcess";
+import { VRDistortionCorrectionPostProcess } from "../../PostProcesses/vrDistortionCorrectionPostProcess";
+import { VRMultiviewToSingleviewPostProcess } from '../../PostProcesses/vrMultiviewToSingleviewPostProcess';
 import { VRCameraMetrics } from "../VR/vrCameraMetrics";
 import { Logger } from '../../Misc/logger';
 
@@ -30,7 +31,7 @@ Camera._setVRRigMode = function(camera: Camera, rigParams: any) {
             metrics.multiviewEnabled = false;
         }else {
             camera._useMultiviewToSingleView = true;
-            camera._rigPostProcess = new VRMultiviewToSingleview("VRMultiviewToSingleview", camera, metrics.postProcessScaleFactor);
+            camera._rigPostProcess = new VRMultiviewToSingleviewPostProcess("VRMultiviewToSingleview", camera, metrics.postProcessScaleFactor);
         }
     }
 

+ 2 - 2
src/Cameras/VR/webVRCamera.ts

@@ -14,7 +14,7 @@ import { AbstractMesh } from "../../Meshes/abstractMesh";
 import { Ray } from "../../Culling/ray";
 import { HemisphericLight } from "../../Lights/hemisphericLight";
 import { Logger } from '../../Misc/logger';
-import { VRMultiviewToSingleview } from '../../PostProcesses/vrDistortionCorrectionPostProcess';
+import { VRMultiviewToSingleviewPostProcess } from '../../PostProcesses/vrMultiviewToSingleviewPostProcess';
 
 // Side effect import to define the stereoscopic mode.
 import "../RigModes/webVRRigMode";
@@ -289,7 +289,7 @@ export class WebVRFreeCamera extends FreeCamera implements PoseControlled {
                 this._useMultiviewToSingleView = false;
             }else {
                 this._useMultiviewToSingleView = true;
-                this._rigPostProcess = new VRMultiviewToSingleview("VRMultiviewToSingleview", this, 1.0);
+                this._rigPostProcess = new VRMultiviewToSingleviewPostProcess("VRMultiviewToSingleview", this, 1.0);
             }
         }
 

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1241 - 1268
src/Cameras/camera.ts


+ 169 - 0
src/Engines/Extensions/engine.multiview.ts

@@ -0,0 +1,169 @@
+import { Camera } from "../../Cameras/camera";
+import { Engine } from "../../Engines/engine";
+import { Scene } from "../../scene";
+import { InternalTexture } from '../../Materials/Textures/internalTexture';
+import { Nullable } from '../../types';
+import { RenderTargetTexture } from '../../Materials/Textures/renderTargetTexture';
+import { Matrix, Tmp, Frustum } from '../../Maths/math';
+import { UniformBuffer } from '../../Materials/uniformBuffer';
+import { MultiviewRenderTarget } from '../../Materials/Textures/MultiviewRenderTarget';
+
+declare module "../../Engines/engine" {
+    export interface Engine {
+        /**
+         * Creates a new multiview render target
+         * @param width defines the width of the texture
+         * @param height defines the height of the texture
+         * @returns the created multiview texture
+         */
+        createMultiviewRenderTargetTexture(width: number, height: number): InternalTexture;
+
+        /**
+         * Binds a multiview framebuffer to be drawn to
+         * @param multiviewTexture texture to bind
+         */
+        bindMultiviewFramebuffer(multiviewTexture: InternalTexture): void;
+    }
+}
+
+Engine.prototype.createMultiviewRenderTargetTexture = function(width: number, height: number) {
+    var gl = this._gl;
+
+    if (!this.getCaps().multiview) {
+        throw "Multiview is not supported";
+    }
+
+    var internalTexture = new InternalTexture(this, InternalTexture.DATASOURCE_UNKNOWN, true);
+    internalTexture.width = width;
+    internalTexture.height = height;
+    internalTexture._framebuffer = gl.createFramebuffer();
+
+    internalTexture._colorTextureArray = gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D_ARRAY, internalTexture._colorTextureArray);
+    (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, 2);
+
+    internalTexture._depthStencilTextureArray = gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D_ARRAY, internalTexture._depthStencilTextureArray);
+    (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, (gl as any).DEPTH32F_STENCIL8, width, height, 2);
+    internalTexture.isReady = true;
+    return internalTexture;
+};
+
+Engine.prototype.bindMultiviewFramebuffer = function(multiviewTexture: InternalTexture) {
+    var gl: any = this._gl;
+    var ext = this.getCaps().multiview;
+
+    this.bindFramebuffer(multiviewTexture, undefined, undefined, undefined, true);
+    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, multiviewTexture._framebuffer);
+    if (multiviewTexture._colorTextureArray && multiviewTexture._depthStencilTextureArray) {
+        ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, multiviewTexture._colorTextureArray, 0, 0, 2);
+        ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, multiviewTexture._depthStencilTextureArray, 0, 0, 2);
+    } else {
+        throw "Invalid multiview frame buffer";
+    }
+};
+
+declare module "../../Cameras/camera" {
+    export interface Camera {
+        /**
+         * @hidden
+         * For cameras that cannot use multiview images to display directly. (e.g. webVR camera will render to multiview texture, then copy to each eye texture and go from there)
+         */
+        _useMultiviewToSingleView: boolean;
+        /**
+         * @hidden
+         * For cameras that cannot use multiview images to display directly. (e.g. webVR camera will render to multiview texture, then copy to each eye texture and go from there)
+         */
+        _multiviewTexture: Nullable<RenderTargetTexture>;
+
+        /**
+         * @hidden
+         * ensures the multiview texture of the camera exists and has the specified width/height
+         * @param width height to set on the multiview texture
+         * @param height width to set on the multiview texture
+         */
+        _resizeOrCreateMultiviewTexture(width: number, height: number): void;
+    }
+}
+
+Camera.prototype._useMultiviewToSingleView = false;
+
+Camera.prototype._multiviewTexture = null;
+
+Camera.prototype._resizeOrCreateMultiviewTexture = function(width: number, height: number) {
+    if (!this._multiviewTexture) {
+        this._multiviewTexture = new MultiviewRenderTarget(this.getScene(), {width: width, height: height});
+    }else if (this._multiviewTexture.getRenderWidth() != width || this._multiviewTexture.getRenderHeight() != height) {
+        this._multiviewTexture.dispose();
+        this._multiviewTexture = new MultiviewRenderTarget(this.getScene(), {width: width, height: height});
+    }
+};
+
+declare module "../../scene" {
+    export interface Scene {
+        /** @hidden */
+        _transformMatrixR: Matrix;
+        /** @hidden */
+        _multiviewSceneUbo: Nullable<UniformBuffer>;
+        /** @hidden */
+        _createMultiviewUbo(): void;
+        /** @hidden */
+        _updateMultiviewUbo(viewR?: Matrix, projectionR?: Matrix): void;
+        /** @hidden */
+        _renderMultiviewToSingleView(camera: Camera): void;
+    }
+}
+
+Scene.prototype._transformMatrixR = Matrix.Zero();
+Scene.prototype._multiviewSceneUbo = null;
+Scene.prototype._createMultiviewUbo = function() {
+    this._multiviewSceneUbo = new UniformBuffer(this.getEngine(), undefined, true);
+    this._multiviewSceneUbo.addUniform("viewProjection", 16);
+    this._multiviewSceneUbo.addUniform("viewProjectionR", 16);
+    this._multiviewSceneUbo.addUniform("view", 16);
+};
+Scene.prototype._updateMultiviewUbo = function(viewR?: Matrix, projectionR?: Matrix) {
+    if (viewR && projectionR) {
+        viewR.multiplyToRef(projectionR, this._transformMatrixR);
+    }
+
+    if (viewR && projectionR) {
+        viewR.multiplyToRef(projectionR, Tmp.Matrix[0]);
+        Frustum.GetRightPlaneToRef(Tmp.Matrix[0], this._frustumPlanes[3]); // Replace right plane by second camera right plane
+    }
+
+    if (this._multiviewSceneUbo) {
+        this._multiviewSceneUbo.updateMatrix("viewProjection", this.getTransformMatrix());
+        this._multiviewSceneUbo.updateMatrix("viewProjectionR", this._transformMatrixR);
+        this._multiviewSceneUbo.updateMatrix("view", this._viewMatrix);
+        this._multiviewSceneUbo.update();
+    }
+};
+Scene.prototype._renderMultiviewToSingleView = function(camera: Camera) {
+    // Multiview is only able to be displayed directly for API's such as webXR
+    // This displays a multiview image by rendering to the multiview image and then
+    // copying the result into the sub cameras instead of rendering them and proceeding as normal from there
+
+    // Render to a multiview texture
+    camera._resizeOrCreateMultiviewTexture(
+        (camera._rigPostProcess && camera._rigPostProcess && camera._rigPostProcess.width > 0) ? camera._rigPostProcess.width / 2 : this.getEngine().getRenderWidth(true) / 2,
+        (camera._rigPostProcess && camera._rigPostProcess && camera._rigPostProcess.height > 0) ? camera._rigPostProcess.height : this.getEngine().getRenderHeight(true)
+    );
+    if (!this._multiviewSceneUbo) {
+        this._createMultiviewUbo();
+    }
+    camera.outputRenderTarget = camera._multiviewTexture;
+    this._renderForCamera(camera);
+    camera.outputRenderTarget = null;
+
+    // Consume the multiview texture through a shader for each eye
+    for (var index = 0; index < camera._rigCameras.length; index++) {
+        var engine = this.getEngine();
+        this._activeCamera = camera._rigCameras[index];
+        engine.setViewport(this._activeCamera.viewport);
+        if (this.postProcessManager) {
+            this.postProcessManager._prepareFrame();
+            this.postProcessManager._finalizeFrame(this._activeCamera.isIntermediate);
+        }
+    }
+};

+ 2 - 1
src/Engines/Extensions/index.ts

@@ -1,2 +1,3 @@
 export * from "./engine.occlusionQuery";
-export * from "./engine.transformFeedback";
+export * from "./engine.transformFeedback";
+export * from "./engine.multiview";

+ 0 - 47
src/Engines/engine.ts

@@ -5614,53 +5614,6 @@ export class Engine {
     }
 
     /**
-     * Creates a new multiview render target
-     * @param width defines the width of the texture
-     * @param height defines the height of the texture
-     * @returns the created multiview texture
-     */
-    public createMultiviewRenderTargetTexture(width: number, height: number) {
-        var gl = this._gl;
-
-        if (!this.getCaps().multiview) {
-            throw "Multiview is not supported";
-        }
-
-        var internalTexture = new InternalTexture(this, InternalTexture.DATASOURCE_UNKNOWN, true);
-        internalTexture.width = width;
-        internalTexture.height = height;
-        internalTexture._framebuffer = gl.createFramebuffer();
-
-        internalTexture._colorTextureArray = gl.createTexture();
-        gl.bindTexture(gl.TEXTURE_2D_ARRAY, internalTexture._colorTextureArray);
-        (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, 2);
-
-        internalTexture._depthStencilTextureArray = gl.createTexture();
-        gl.bindTexture(gl.TEXTURE_2D_ARRAY, internalTexture._depthStencilTextureArray);
-        (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, (gl as any).DEPTH32F_STENCIL8, width, height, 2);
-        internalTexture.isReady = true;
-        return internalTexture;
-    }
-
-    /**
-     * Binds a multiview framebuffer to be drawn to
-     * @param multiviewTexture texture to bind
-     */
-    public bindMultiviewFramebuffer(multiviewTexture: InternalTexture) {
-        var gl: any = this._gl;
-        var ext = this.getCaps().multiview;
-
-        this.bindFramebuffer(multiviewTexture, undefined, undefined, undefined, true);
-        gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, multiviewTexture._framebuffer);
-        if (multiviewTexture._colorTextureArray && multiviewTexture._depthStencilTextureArray) {
-            ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, multiviewTexture._colorTextureArray, 0, 0, 2);
-            ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, multiviewTexture._depthStencilTextureArray, 0, 0, 2);
-        } else {
-            throw "Invalid multiview frame buffer";
-        }
-    }
-
-    /**
      * Creates a new render target cube texture
      * @param size defines the size of the texture
      * @param options defines the options used to create the texture

+ 1 - 7
src/Materials/Background/backgroundMaterial.ts

@@ -677,13 +677,7 @@ export class BackgroundMaterial extends PushMaterial {
         defines._needNormals = true;
 
         // Multiview
-        if (scene.activeCamera) {
-            var previousMultiview = defines.MULTIVIEW;
-            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
-            if (defines.MULTIVIEW != previousMultiview) {
-                defines.markAsUnprocessed();
-            }
-        }
+        MaterialHelper.PrepareDefinesForMultiview(scene, defines);
 
         // Textures
         if (defines._areTexturesDirty) {

+ 1 - 7
src/Materials/PBR/pbrBaseMaterial.ts

@@ -1254,13 +1254,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         defines._needNormals = true;
 
         // Multiview
-        if (scene.activeCamera) {
-            var previousMultiview = defines.MULTIVIEW;
-            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
-            if (defines.MULTIVIEW != previousMultiview) {
-                defines.markAsUnprocessed();
-            }
-        }
+        MaterialHelper.PrepareDefinesForMultiview(scene, defines);
 
         // Textures
         defines.METALLICWORKFLOW = this.isMetallicWorkflow();

+ 40 - 0
src/Materials/Textures/MultiviewRenderTarget.ts

@@ -0,0 +1,40 @@
+import { RenderTargetTexture } from '../Textures/renderTargetTexture';
+import { Scene } from '../../scene';
+import { InternalTexture } from '../Textures/internalTexture';
+
+/**
+ * Renders to multiple views with a single draw call
+ * @see https://www.khronos.org/registry/webgl/extensions/WEBGL_multiview/
+ */
+export class MultiviewRenderTarget extends RenderTargetTexture {
+    /**
+     * Creates a multiview render target
+     * @param scene scene used with the render target
+     * @param size the size of the render target (used for each view)
+     */
+    constructor(scene: Scene, size: number | { width: number, height: number } | { ratio: number } = 512) {
+        super("multiview rtt", size, scene, false, true, InternalTexture.DATASOURCE_UNKNOWN, false, undefined, false, false, true, undefined, true);
+        var internalTexture = scene.getEngine().createMultiviewRenderTargetTexture(this.getRenderWidth(), this.getRenderHeight());
+        internalTexture.isMultiview = true;
+        this._texture = internalTexture;
+    }
+
+    /**
+     * @hidden
+     * @param faceIndex the face index, if its a cube texture
+     */
+    public _bindFrameBuffer(faceIndex: number = 0) {
+        if (!this._texture) {
+            return;
+        }
+        this.getScene()!.getEngine().bindMultiviewFramebuffer(this._texture);
+    }
+
+    /**
+     * Gets the number of views the corresponding to the texture (eg. a MultiviewRenderTarget will have > 1)
+     * @returns the view count
+     */
+    public getViewCount() {
+        return 2;
+    }
+}

+ 1 - 38
src/Materials/Textures/renderTargetTexture.ts

@@ -1001,41 +1001,4 @@ export class RenderTargetTexture extends Texture {
 
 Texture._CreateRenderTargetTexture = (name: string, renderTargetSize: number, scene: Scene, generateMipMaps: boolean) => {
     return new RenderTargetTexture(name, renderTargetSize, scene, generateMipMaps);
-};
-
-/**
- * Renders to multiple views with a single draw call
- * @see https://www.khronos.org/registry/webgl/extensions/WEBGL_multiview/
- */
-export class MultiviewRenderTarget extends RenderTargetTexture {
-    /**
-     * Creates a multiview render target
-     * @param scene scene used with the render target
-     * @param size the size of the render target (used for each view)
-     */
-    constructor(scene: Scene, size: number | { width: number, height: number } | { ratio: number } = 512) {
-        super("multiview rtt", size, scene, false, true, InternalTexture.DATASOURCE_UNKNOWN, false, undefined, false, false, true, undefined, true);
-        var internalTexture = scene.getEngine().createMultiviewRenderTargetTexture(this.getRenderWidth(), this.getRenderHeight());
-        internalTexture.isMultiview = true;
-        this._texture = internalTexture;
-    }
-
-    /**
-     * @hidden
-     * @param faceIndex the face index, if its a cube texture
-     */
-    public _bindFrameBuffer(faceIndex: number = 0) {
-        if (!this._texture) {
-            return;
-        }
-        this.getScene()!.getEngine().bindMultiviewFramebuffer(this._texture);
-    }
-
-    /**
-     * Gets the number of views the corresponding to the texture (eg. a MultiviewRenderTarget will have > 1)
-     * @returns the view count
-     */
-    public getViewCount() {
-        return 2;
-    }
-}
+};

+ 15 - 0
src/Materials/materialHelper.ts

@@ -229,6 +229,21 @@ export class MaterialHelper {
     }
 
     /**
+     * Prepares the defines related to multiview
+     * @param scene The scene we are intending to draw
+     * @param defines The defines to update
+     */
+    public static PrepareDefinesForMultiview(scene: Scene, defines: any) {
+        if (scene.activeCamera) {
+            var previousMultiview = defines.MULTIVIEW;
+            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
+            if (defines.MULTIVIEW != previousMultiview) {
+                defines.markAsUnprocessed();
+            }
+        }
+    }
+
+    /**
      * Prepares the defines related to the light information passed in parameter
      * @param scene The scene we are intending to draw
      * @param mesh The mesh the effect is compiling for

+ 3 - 7
src/Materials/standardMaterial.ts

@@ -793,13 +793,9 @@ export class StandardMaterial extends PushMaterial {
 
         // Lights
         defines._needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, true, this._maxSimultaneousLights, this._disableLighting);
-        if (scene.activeCamera) {
-            var previousMultiview = defines.MULTIVIEW;
-            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
-            if (defines.MULTIVIEW != previousMultiview) {
-                defines.markAsUnprocessed();
-            }
-        }
+
+        // Multiview
+        MaterialHelper.PrepareDefinesForMultiview(scene, defines);
 
         // Textures
         if (defines._areTexturesDirty) {

+ 2 - 1
src/PostProcesses/index.ts

@@ -27,4 +27,5 @@ export * from "./sharpenPostProcess";
 export * from "./stereoscopicInterlacePostProcess";
 export * from "./tonemapPostProcess";
 export * from "./volumetricLightScatteringPostProcess";
-export * from "./vrDistortionCorrectionPostProcess";
+export * from "./vrDistortionCorrectionPostProcess";
+export * from "./vrMultiviewToSingleviewPostProcess";

+ 1 - 29
src/PostProcesses/vrDistortionCorrectionPostProcess.ts

@@ -6,7 +6,6 @@ import { Texture } from "../Materials/Textures/texture";
 import { PostProcess } from "./postProcess";
 
 import "../Shaders/vrDistortionCorrection.fragment";
-import "../Shaders/vrMultiviewToSingleview.fragment";
 
 /**
  * VRDistortionCorrectionPostProcess used for mobile VR
@@ -53,31 +52,4 @@ export class VRDistortionCorrectionPostProcess extends PostProcess {
             effect.setFloat4("HmdWarpParam", this._distortionFactors[0], this._distortionFactors[1], this._distortionFactors[2], this._distortionFactors[3]);
         });
     }
-}
-
-/**
- * VRMultiviewToSingleview used to convert multiview texture arrays to standard textures for scenarios such as webVR
- * This will not be used for webXR as it supports displaying texture arrays directly
- */
-export class VRMultiviewToSingleview extends PostProcess {
-    /**
-     * Initializes a VRMultiviewToSingleview
-     * @param name name of the post process
-     * @param camera camera to be applied to
-     * @param scaleFactor scaling factor to the size of the output texture
-     */
-    constructor(name: string, camera: Camera, scaleFactor: number) {
-        super(name, "vrMultiviewToSingleview", ["imageIndex"], ["multiviewSampler"], scaleFactor, camera, Texture.BILINEAR_SAMPLINGMODE);
-
-        this.onSizeChangedObservable.add(() => {
-        });
-        this.onApplyObservable.add((effect: Effect) => {
-            if (camera._scene.activeCamera && camera._scene.activeCamera.isLeftCamera) {
-                effect.setInt("imageIndex", 0);
-            }else {
-                effect.setInt("imageIndex", 1);
-            }
-            effect.setTexture("multiviewSampler", camera._multiviewTexture);
-        });
-    }
-}
+}

+ 34 - 0
src/PostProcesses/vrMultiviewToSingleviewPostProcess.ts

@@ -0,0 +1,34 @@
+import { Camera } from "../Cameras/camera";
+import { Effect } from "../Materials/effect";
+import { Texture } from "../Materials/Textures/texture";
+import { PostProcess } from "./postProcess";
+
+import "../Shaders/vrMultiviewToSingleview.fragment";
+import "../Engines/Extensions/engine.multiview";
+
+/**
+ * VRMultiviewToSingleview used to convert multiview texture arrays to standard textures for scenarios such as webVR
+ * This will not be used for webXR as it supports displaying texture arrays directly
+ */
+export class VRMultiviewToSingleviewPostProcess extends PostProcess {
+    /**
+     * Initializes a VRMultiviewToSingleview
+     * @param name name of the post process
+     * @param camera camera to be applied to
+     * @param scaleFactor scaling factor to the size of the output texture
+     */
+    constructor(name: string, camera: Camera, scaleFactor: number) {
+        super(name, "vrMultiviewToSingleview", ["imageIndex"], ["multiviewSampler"], scaleFactor, camera, Texture.BILINEAR_SAMPLINGMODE);
+
+        this.onSizeChangedObservable.add(() => {
+        });
+        this.onApplyObservable.add((effect: Effect) => {
+            if (camera._scene.activeCamera && camera._scene.activeCamera.isLeftCamera) {
+                effect.setInt("imageIndex", 0);
+            }else {
+                effect.setInt("imageIndex", 1);
+            }
+            effect.setTexture("multiviewSampler", camera._multiviewTexture);
+        });
+    }
+}

+ 11 - 52
src/scene.ts

@@ -5,7 +5,7 @@ import { Observable, Observer } from "./Misc/observable";
 import { SmartArrayNoDuplicate, SmartArray, ISmartArrayLike } from "./Misc/smartArray";
 import { StringDictionary } from "./Misc/stringDictionary";
 import { Tags } from "./Misc/tags";
-import { Color4, Color3, Plane, Vector2, Vector3, Matrix, Tmp, Frustum } from "./Maths/math";
+import { Color4, Color3, Plane, Vector2, Vector3, Matrix, Frustum } from "./Maths/math";
 import { Geometry } from "./Meshes/geometry";
 import { TransformNode } from "./Meshes/transformNode";
 import { SubMesh } from "./Meshes/subMesh";
@@ -870,7 +870,8 @@ export class Scene extends AbstractScene implements IAnimatable {
     /** All of the active cameras added to this scene. */
     public activeCameras = new Array<Camera>();
 
-    private _activeCamera: Nullable<Camera>;
+    /** @hidden */
+    public _activeCamera: Nullable<Camera>;
     /** Gets or sets the current active camera */
     public get activeCamera(): Nullable<Camera> {
         return this._activeCamera;
@@ -1113,17 +1114,17 @@ export class Scene extends AbstractScene implements IAnimatable {
     public _activeAnimatables = new Array<Animatable>();
 
     private _transformMatrix = Matrix.Zero();
-    private _transformMatrixR = Matrix.Zero();
     private _sceneUbo: UniformBuffer;
-    private _multiviewSceneUbo: UniformBuffer;
 
-    private _viewMatrix: Matrix;
+    /** @hidden */
+    public _viewMatrix: Matrix;
     private _projectionMatrix: Matrix;
     private _wheelEventName = "";
     /** @hidden */
     public _forcedViewPosition: Nullable<Vector3>;
 
-    private _frustumPlanes: Plane[];
+    /** @hidden */
+    public _frustumPlanes: Plane[];
     /**
      * Gets the list of frustum planes (built from the active camera)
      */
@@ -1606,13 +1607,6 @@ export class Scene extends AbstractScene implements IAnimatable {
         this._sceneUbo.addUniform("view", 16);
     }
 
-    private _createMultiviewUbo(): void {
-        this._multiviewSceneUbo = new UniformBuffer(this._engine, undefined, true);
-        this._multiviewSceneUbo.addUniform("viewProjection", 16);
-        this._multiviewSceneUbo.addUniform("viewProjectionR", 16);
-        this._multiviewSceneUbo.addUniform("view", 16);
-    }
-
     // Pointers handling
     private _setRayOnPointerInfo(pointerInfo: PointerInfo) {
         if (pointerInfo.pickInfo && !pointerInfo.pickInfo._pickingUnavailable) {
@@ -2574,9 +2568,6 @@ export class Scene extends AbstractScene implements IAnimatable {
         this._projectionMatrix = projectionL;
 
         this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
-        if (viewR && projectionR) {
-            viewR.multiplyToRef(projectionR, this._transformMatrixR);
-        }
 
         // Update frustum
         if (!this._frustumPlanes) {
@@ -2585,16 +2576,8 @@ export class Scene extends AbstractScene implements IAnimatable {
             Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
         }
 
-        if (viewR && projectionR) {
-            viewR.multiplyToRef(projectionR, Tmp.Matrix[0]);
-            Frustum.GetRightPlaneToRef(Tmp.Matrix[0], this._frustumPlanes[3]); // Replace right plane by second camera right plane
-        }
-
         if (this._multiviewSceneUbo && this._multiviewSceneUbo.useUbo) {
-            this._multiviewSceneUbo.updateMatrix("viewProjection", this._transformMatrix);
-            this._multiviewSceneUbo.updateMatrix("viewProjectionR", this._transformMatrixR);
-            this._multiviewSceneUbo.updateMatrix("view", this._viewMatrix);
-            this._multiviewSceneUbo.update();
+            this._updateMultiviewUbo(viewR, projectionR);
         } else if (this._sceneUbo.useUbo) {
             this._sceneUbo.updateMatrix("viewProjection", this._transformMatrix);
             this._sceneUbo.updateMatrix("view", this._viewMatrix);
@@ -4045,7 +4028,8 @@ export class Scene extends AbstractScene implements IAnimatable {
     }
     /** @hidden */
     public _allowPostProcessClearColor = true;
-    private _renderForCamera(camera: Camera, rigParent?: Camera): void {
+    /** @hidden */
+    public _renderForCamera(camera: Camera, rigParent?: Camera): void {
         if (camera && camera._skipRendering) {
             return;
         }
@@ -4169,32 +4153,7 @@ export class Scene extends AbstractScene implements IAnimatable {
         }
 
         if (camera._useMultiviewToSingleView) {
-            // Multiview is only able to be displayed directly for API's such as webXR
-            // This displays a multiview image by rendering to the multiview image and then
-            // copying the result into the sub cameras instead of rendering them and proceeding as normal from there
-
-            // Render to a multiview texture
-            camera._resizeOrCreateMultiviewTexture(
-                (camera._rigPostProcess && camera._rigPostProcess && camera._rigPostProcess.width > 0) ? camera._rigPostProcess.width / 2 : this.getEngine().getRenderWidth(true) / 2,
-                (camera._rigPostProcess && camera._rigPostProcess && camera._rigPostProcess.height > 0) ? camera._rigPostProcess.height : this.getEngine().getRenderHeight(true)
-            );
-            if (!this._multiviewSceneUbo) {
-                this._createMultiviewUbo();
-            }
-            camera.outputRenderTarget = camera._multiviewTexture;
-            this._renderForCamera(camera);
-            camera.outputRenderTarget = null;
-
-            // Consume the multiview texture through a shader for each eye
-            for (var index = 0; index < camera._rigCameras.length; index++) {
-                var engine = this._engine;
-                this._activeCamera = camera._rigCameras[index];
-                engine.setViewport(this._activeCamera.viewport);
-                if (this.postProcessManager) {
-                    this.postProcessManager._prepareFrame();
-                    this.postProcessManager._finalizeFrame(this._activeCamera.isIntermediate);
-                }
-            }
+            this._renderMultiviewToSingleView(camera);
         } else {
             // rig cameras
             for (var index = 0; index < camera._rigCameras.length; index++) {