Prechádzať zdrojové kódy

create multiview rtt

Trevor Baron 6 rokov pred
rodič
commit
bf62b553fc

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

@@ -104,6 +104,7 @@
 - Added option `multiMultiMaterials` to mesh.mergeMeshes ([danjpar](https://github.com/danjpar))
 - Expose fallback camera distortion metrics option in vrExperienceHelper ([TrevorDev](https://github.com/TrevorDev))
 - Add setColor method to boundingBoxGizmo ([TrevorDev](https://github.com/TrevorDev))
+- Support rendering to a Multiview outputRenderTargetTexture to improve performance for XR scenarios ([TrevorDev](https://github.com/TrevorDev))
 
 ### OBJ Loader
 - Add color vertex support (not part of standard) ([brianzinn](https://github.com/brianzinn))

+ 14 - 4
src/Engines/engine.ts

@@ -173,10 +173,14 @@ export class EngineCapabilities {
     public timerQuery: EXT_disjoint_timer_query;
     /** Defines if timestamp can be used with timer query */
     public canUseTimestampForTimerQuery: boolean;
+    /** Defines if multiview is supported (https://www.khronos.org/registry/webgl/extensions/WEBGL_multiview/) */
+    public multiview: any;
     /** Function used to let the system compiles shaders in background */
     public parallelShaderCompile: {
-        MAX_SHADER_COMPILER_THREADS_KHR: number;
-        maxShaderCompilerThreadsKHR: (thread: number) => void;
+        MAX_SHADER_COMPILER_THREADS_KHR?: number;
+        MAX_SHADER_COMPILER_THREADS?: number;
+        maxShaderCompilerThreadsKHR?: (thread: number) => void;
+        maxShaderCompilerThreads?: (thread: number) => void;
         COMPLETION_STATUS_KHR: number;
     };
 }
@@ -1423,6 +1427,7 @@ export class Engine {
 
         this._caps.textureLOD = (this._webGLVersion > 1 || this._gl.getExtension('EXT_shader_texture_lod')) ? true : false;
 
+        this._caps.multiview = this._gl.getExtension('WEBGL_multiview');
         // Draw buffers
         if (this._webGLVersion > 1) {
             this._caps.drawBuffersExtension = true;
@@ -1445,8 +1450,13 @@ export class Engine {
         // Shader compiler threads
         this._caps.parallelShaderCompile = this._gl.getExtension('KHR_parallel_shader_compile');
         if (this._caps.parallelShaderCompile) {
-            const threads = this._gl.getParameter(this._caps.parallelShaderCompile.MAX_SHADER_COMPILER_THREADS_KHR);
-            this._caps.parallelShaderCompile.maxShaderCompilerThreadsKHR(threads);
+            if (this._caps.parallelShaderCompile.MAX_SHADER_COMPILER_THREADS_KHR && this._caps.parallelShaderCompile.maxShaderCompilerThreadsKHR) {
+                const threads = this._gl.getParameter(this._caps.parallelShaderCompile.MAX_SHADER_COMPILER_THREADS_KHR);
+                this._caps.parallelShaderCompile.maxShaderCompilerThreadsKHR(threads);
+            }else if (this._caps.parallelShaderCompile.MAX_SHADER_COMPILER_THREADS && this._caps.parallelShaderCompile.maxShaderCompilerThreads) {
+                const threads = this._gl.getParameter(this._caps.parallelShaderCompile.MAX_SHADER_COMPILER_THREADS);
+                this._caps.parallelShaderCompile.maxShaderCompilerThreads(threads);
+            }
         }
 
         // Depth Texture

+ 81 - 3
src/Materials/Textures/renderTargetTexture.ts

@@ -727,6 +727,18 @@ export class RenderTargetTexture extends Texture {
         return Math.min(Tools.FloorPOT(renderDimension), curved);
     }
 
+    public _bindFrameBuffer(faceIndex: number = 0) {
+        var scene = this.getScene();
+        if (!scene) {
+            return;
+        }
+
+        var engine = scene.getEngine();
+        if (this._texture) {
+            engine.bindFramebuffer(this._texture, this.isCube ? faceIndex : undefined, undefined, undefined, this.ignoreCameraViewport, this.depthStencilTexture ? this.depthStencilTexture : undefined);
+        }
+    }
+
     protected unbindFrameBuffer(engine: Engine, faceIndex: number): void {
         if (!this._texture) {
             return;
@@ -754,9 +766,7 @@ export class RenderTargetTexture extends Texture {
             this._postProcessManager._prepareFrame(this._texture, this._postProcesses);
         }
         else if (!useCameraPostProcess || !scene.postProcessManager._prepareFrame(this._texture)) {
-            if (this._texture) {
-                engine.bindFramebuffer(this._texture, this.isCube ? faceIndex : undefined, undefined, undefined, this.ignoreCameraViewport, this.depthStencilTexture ? this.depthStencilTexture : undefined);
-            }
+            this._bindFrameBuffer(faceIndex);
         }
 
         this.onBeforeRenderObservable.notifyObservers(faceIndex);
@@ -975,8 +985,76 @@ export class RenderTargetTexture extends Texture {
             this._renderingManager.freeRenderingGroups();
         }
     }
+
+    /**
+     * Gets the number of views the corrisponding to the texture (eg. a MultiviewRenderTarget will have > 1)
+     */
+    public getViewCount() {
+        return 1;
+    }
 }
 
 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 {
+    private _multiviewColorTexture: Nullable<WebGLTexture>;
+    private _multivewDepthStencilTexture: Nullable<WebGLTexture>;
+
+    /**
+     * 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(public 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);
+        if (!this.scene.getEngine().getCaps().multiview) {
+            this.dispose();
+            throw "Multiview is not supported";
+        }
+        var gl = scene.getEngine()._gl;
+
+        this._multiviewColorTexture = gl.createTexture();
+        gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._multiviewColorTexture);
+        (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, this.getRenderWidth(), this.getRenderHeight(), 2);
+
+        this._multivewDepthStencilTexture = gl.createTexture();
+        gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._multivewDepthStencilTexture);
+        (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, (gl as any).DEPTH32F_STENCIL8, this.getRenderWidth(), this.getRenderHeight(), 2);
+
+        var internalTexture = new InternalTexture(scene.getEngine(), InternalTexture.DATASOURCE_UNKNOWN, true);
+        internalTexture.width = this.getRenderWidth();
+        internalTexture.height = this.getRenderHeight();
+        internalTexture._framebuffer = gl.createFramebuffer();
+        this._texture = internalTexture;
+    }
+
+    /**
+     * @hidden
+     * @param faceIndex the face index, if its a cube texture
+     */
+    public _bindFrameBuffer(faceIndex: number = 0) {
+        if (!this._texture) {
+            return;
+        }
+        var gl: any = this.scene.getEngine()._gl;
+        var ext = this.scene.getEngine().getCaps().multiview;
+
+        this.scene.getEngine().bindFramebuffer(this._texture, undefined, undefined, undefined, this.ignoreCameraViewport);
+        gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._texture._framebuffer);
+        ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, this._multiviewColorTexture, 0, 0, 2);
+        ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, this._multivewDepthStencilTexture, 0, 0, 2);
+    }
+
+    /**
+     * Gets the number of views the corrisponding to the texture (eg. a MultiviewRenderTarget will have > 1)
+     */
+    public getViewCount() {
+        return 2;
+    }
+}

+ 7 - 1
src/Materials/effect.ts

@@ -631,7 +631,7 @@ export class Effect {
         // #extension GL_EXT_shader_texture_lod : enable
         // #extension GL_EXT_frag_depth : enable
         // #extension GL_EXT_draw_buffers : require
-        var regex = /#extension.+(GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g;
+        var regex = /#extension.+(GL_OVR_multiview|GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth|GL_EXT_draw_buffers).+(enable|require)/g;
         var result = preparedSourceCode.replace(regex, "");
 
         // Migrate to GLSL v300
@@ -650,6 +650,12 @@ export class Effect {
             result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
         }
 
+        // Add multiview setup to top of file when defined
+        var hasMultiviewExtension = this.defines.indexOf("#define MULTIVIEW\n") !== -1;
+        if (hasMultiviewExtension && !isFragment) {
+            result = "#extension GL_OVR_multiview : require\nlayout (num_views = 2) in;\n" + result;
+        }
+
         callback(result);
     }
 

+ 9 - 0
src/Materials/standardMaterial.ts

@@ -124,6 +124,7 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr
     public SAMPLER3DGREENDEPTH = false;
     public SAMPLER3DBGRMAP = false;
     public IMAGEPROCESSINGPOSTPROCESS = false;
+    public MULTIVIEW = false;
     /**
      * If the reflection texture on this material is in linear color space
      * @hidden
@@ -792,6 +793,10 @@ export class StandardMaterial extends PushMaterial {
 
         // Lights
         defines._needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, true, this._maxSimultaneousLights, this._disableLighting);
+        if (scene.activeCamera) {
+            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
+            defines.markAsUnprocessed();
+        }
 
         // Textures
         if (defines._areTexturesDirty) {
@@ -1080,6 +1085,10 @@ export class StandardMaterial extends PushMaterial {
                 fallbacks.addFallback(4, "FRESNEL");
             }
 
+            if (defines.MULTIVIEW) {
+                fallbacks.addFallback(0, "MULTIVIEW");
+            }
+
             //Attributes
             var attribs = [VertexBuffer.PositionKind];
 

+ 1 - 0
src/Shaders/ShadersInclude/backgroundUboDeclaration.fx

@@ -26,5 +26,6 @@ uniform Material
 
 uniform Scene {
 	mat4 viewProjection;
+	mat4 viewProjectionR;    
 	mat4 view;
 };

+ 2 - 1
src/Shaders/ShadersInclude/defaultUboDeclaration.fx

@@ -39,6 +39,7 @@ uniform Material
 };
 
 uniform Scene {
-	mat4 viewProjection;
+    mat4 viewProjection;
+	mat4 viewProjectionR;
 	mat4 view;
 };

+ 1 - 0
src/Shaders/ShadersInclude/pbrUboDeclaration.fx

@@ -55,5 +55,6 @@ uniform Material
 
 uniform Scene {
     mat4 viewProjection;
+    mat4 viewProjectionR;
     mat4 view;
 };

+ 9 - 0
src/Shaders/default.vertex.fx

@@ -123,7 +123,16 @@ void main(void) {
 #include<instancesVertex>
 #include<bonesVertex>
 
+#ifdef MULTIVIEW
+	if (gl_ViewID_OVR == 0u) {
+		gl_Position = viewProjection * finalWorld * vec4(positionUpdated, 1.0);
+	} else {
+		gl_Position = viewProjectionR * finalWorld * vec4(positionUpdated, 1.0);
+	}
+#else
 	gl_Position = viewProjection * finalWorld * vec4(positionUpdated, 1.0);
+#endif
+	
 
 	vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0);
 	vPositionW = vec3(worldPos);

+ 48 - 20
src/scene.ts

@@ -1090,6 +1090,7 @@ export class Scene extends AbstractScene implements IAnimatable {
     public _activeAnimatables = new Array<Animatable>();
 
     private _transformMatrix = Matrix.Zero();
+    private _transformMatrixR = Matrix.Zero();
     private _sceneUbo: UniformBuffer;
     private _alternateSceneUbo: UniformBuffer;
 
@@ -1581,6 +1582,7 @@ export class Scene extends AbstractScene implements IAnimatable {
     private _createUbo(): void {
         this._sceneUbo = new UniformBuffer(this._engine, undefined, true);
         this._sceneUbo.addUniform("viewProjection", 16);
+        this._sceneUbo.addUniform("viewProjectionR", 16);
         this._sceneUbo.addUniform("view", 16);
     }
 
@@ -2539,16 +2541,23 @@ export class Scene extends AbstractScene implements IAnimatable {
      * @param projection defines the Projection matrix to use
      */
     public setTransformMatrix(view: Matrix, projection: Matrix): void {
-        if (this._viewUpdateFlag === view.updateFlag && this._projectionUpdateFlag === projection.updateFlag) {
+        this._setMultiviewTransformMatrix(view, projection);
+    }
+
+    private _setMultiviewTransformMatrix(viewL: Matrix, projectionL: Matrix, viewR?: Matrix, projectionR?: Matrix): void {
+        if (this._viewUpdateFlag === viewL.updateFlag && this._projectionUpdateFlag === projectionL.updateFlag) {
             return;
         }
 
-        this._viewUpdateFlag = view.updateFlag;
-        this._projectionUpdateFlag = projection.updateFlag;
-        this._viewMatrix = view;
-        this._projectionMatrix = projection;
+        this._viewUpdateFlag = viewL.updateFlag;
+        this._projectionUpdateFlag = projectionL.updateFlag;
+        this._viewMatrix = viewL;
+        this._projectionMatrix = projectionL;
 
         this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
+        if (viewR && projectionR) {
+            viewR.multiplyToRef(projectionR, this._transformMatrixR);
+        }
 
         // Update frustum
         if (!this._frustumPlanes) {
@@ -2565,6 +2574,7 @@ export class Scene extends AbstractScene implements IAnimatable {
 
         if (this._sceneUbo.useUbo) {
             this._sceneUbo.updateMatrix("viewProjection", this._transformMatrix);
+            this._sceneUbo.updateMatrix("viewProjectionR", this._transformMatrixR);
             this._sceneUbo.updateMatrix("view", this._viewMatrix);
             this._sceneUbo.update();
         }
@@ -3980,6 +3990,24 @@ export class Scene extends AbstractScene implements IAnimatable {
     public updateAlternateTransformMatrix(alternateCamera: Camera): void {
         this._setAlternateTransformMatrix(alternateCamera.getViewMatrix(), alternateCamera.getProjectionMatrix());
     }
+
+    private _bindFrameBuffer() {
+        if (this.activeCamera && this.activeCamera.outputRenderTarget) {
+            var useMultiview = this.getEngine().getCaps().multiview && this.activeCamera.outputRenderTarget && this.activeCamera.outputRenderTarget.getViewCount() > 1;
+            if (useMultiview) {
+                this.activeCamera.outputRenderTarget._bindFrameBuffer();
+            }else {
+                var internalTexture = this.activeCamera.outputRenderTarget.getInternalTexture();
+                if (internalTexture) {
+                    this.getEngine().bindFramebuffer(internalTexture);
+                } else {
+                    Logger.Error("Camera contains invalid customDefaultRenderTarget");
+                }
+            }
+        } else {
+            this.getEngine().restoreDefaultFramebuffer(); // Restore back buffer if needed
+        }
+    }
     /** @hidden */
     public _allowPostProcessClearColor = true;
     private _renderForCamera(camera: Camera, rigParent?: Camera): void {
@@ -4001,11 +4029,19 @@ export class Scene extends AbstractScene implements IAnimatable {
         // Camera
         this.resetCachedMaterial();
         this._renderId++;
-        this.updateTransformMatrix();
 
-        if (camera._alternateCamera) {
-            this.updateAlternateTransformMatrix(camera._alternateCamera);
-            this._alternateRendering = true;
+        // Bind outputRenderTarget before setting transform if needed
+        this._bindFrameBuffer();
+        var useMultiview = this.getEngine().getCaps().multiview && camera.outputRenderTarget && camera.outputRenderTarget.getViewCount() > 1;
+        if (useMultiview) {
+            this._setMultiviewTransformMatrix(camera._rigCameras[0].getViewMatrix(), camera._rigCameras[0].getProjectionMatrix(), camera._rigCameras[1].getViewMatrix(), camera._rigCameras[1].getProjectionMatrix());
+        }else {
+            this.updateTransformMatrix();
+
+            if (camera._alternateCamera) {
+                this.updateAlternateTransformMatrix(camera._alternateCamera);
+                this._alternateRendering = true;
+            }
         }
 
         this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
@@ -4060,16 +4096,8 @@ export class Scene extends AbstractScene implements IAnimatable {
 
             this._intermediateRendering = false;
 
-            if (this.activeCamera.outputRenderTarget) {
-                var internalTexture = this.activeCamera.outputRenderTarget.getInternalTexture();
-                if (internalTexture) {
-                    engine.bindFramebuffer(internalTexture);
-                } else {
-                    Logger.Error("Camera contains invalid customDefaultRenderTarget");
-                }
-            } else {
-                engine.restoreDefaultFramebuffer(); // Restore back buffer if needed
-            }
+            // Restore framebuffer after rendering to targets
+            this._bindFrameBuffer();
         }
 
         this.onAfterRenderTargetsRenderObservable.notifyObservers(this);
@@ -4108,7 +4136,7 @@ export class Scene extends AbstractScene implements IAnimatable {
     }
 
     private _processSubCameras(camera: Camera): void {
-        if (camera.cameraRigMode === Camera.RIG_MODE_NONE) {
+        if (camera.cameraRigMode === Camera.RIG_MODE_NONE || (camera.outputRenderTarget && camera.outputRenderTarget.getViewCount() > 1 && this.getEngine().getCaps().multiview)) {
             this._renderForCamera(camera);
             return;
         }