Jump to content

Is there a way to set the default render target a MultiRenderTarget?


Recommended Posts

If we use post process (such as SSAO), we might want to render directly the depth and the normal when doing the main pass. Then we can avoid doing another pass by GeometryBufferRenderer. 

I know that we don't support the feature with built-in shaders. My question is if I use my own shaders. Is it feasible in the current Babylon code base? I can overwrite Babylon's built-in classes if needed.


Link to comment
Share on other sites

I found this (https://hacks.mozilla.org/2014/01/webgl-deferred-shading/). But it seems that this works only with WebGL1.0 + extension. How can it work with WebGL2.0?


Is it possible to overwrite the below function so that I can inject the "MRT stuff"? 



Another way I am thinking is that if I write a class which inherit PostProcess and I override the activate function. Instead of creating a RenderTargetTexture, I can create a MultiRenderTargetTexutre. Is it feasible though?



I really need this because performance is the first priority in my project.


Thanks you in advance.

Link to comment
Share on other sites

9 hours ago, Deltakosh said:

So you can have a look at how SSAO2 works: https://github.com/BabylonJS/Babylon.js/blob/master/src/PostProcess/RenderPipeline/Pipelines/babylon.ssao2RenderingPipeline.ts

It is using MRT and the GeometryBuffer to do multiple render at once.


If you want to create your own postprocess, this is the code you should get inspiration from

I don’t want to create any Post process. I just want to have color depth and normal in one pass instead of two.

Link to comment
Share on other sites

Yes but to do that you have to change the material shader (And thus you have to use your own one  as the Standard and PRB material won't support it)

Link to comment
Share on other sites

On 8/6/2018 at 11:59 PM, Deltakosh said:

Yes but to do that you have to change the material shader (And thus you have to use your own one  as the Standard and PRB material won't support it)

Yes, I know.

So I modified the shader as below.

    gl_FragData[0] = finalColor;

#if defined(ID_BUFFER)
    #if defined(PACK_ID)
        gl_FragData[1] = dbId_modelId;
        gl_FragData[1] = vec4(dbId, modelId, 0.0, 1.0);

        gl_FragData[DEPTH_INDEX] = vec4(vDepthMetric, log2(vFragmentDepth) * logarithmicDepthConstant * 0.5, vViewPos.z, 1.0);
        gl_FragData[DEPTH_INDEX] = vec4(vDepthMetric, gl_FragCoord.z, vViewPos.z, 1.0);

    gl_FragData[DEPTH_INDEX + 1] = vec4(normalize(vNormalV), 1.0);

And I wrote a MRTPostProcess

import * as BABYLON from 'babylonjs';
export class MRTPostProcess extends BABYLON.PostProcess {

    private _textureCount: number;
    private _externalTextures: BABYLON.Texture[];

    constructor(name: string, textureCount: number, fragmentUrl: string, parameters: BABYLON.Nullable<string[]>, samplers: BABYLON.Nullable<string[]>, options: number | BABYLON.PostProcessOptions, camera: BABYLON.Nullable<BABYLON.Camera>,
        samplingMode: number = BABYLON.Texture.NEAREST_SAMPLINGMODE, engine?: BABYLON.Engine, reusable?: boolean, defines: BABYLON.Nullable<string> = null, textureType: number = BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT, vertexUrl: string = "postprocess", indexParameters?: any, blockCompilation = false) {

        super(name, fragmentUrl, parameters, samplers, options, camera, samplingMode, engine, reusable, defines, textureType, vertexUrl, indexParameters, blockCompilation);
        this._textureCount = textureCount;

        this.onAfterRenderObservable.add(() => {

    get textures(): BABYLON.SmartArray<BABYLON.InternalTexture> {
        return this._textures;

    get externalTextures(): BABYLON.Texture[] {
        return this._externalTextures;

    private _createExternalTextures(scene: BABYLON.Scene): void {
        this._externalTextures = [];
        for (var i = 0; i < this._textureCount; i++) {
            var texture = new BABYLON.Texture(null, scene, false, false, this.renderTargetSamplingMode);
            texture._texture = this._textures.data[i];

    activate(camera: BABYLON.Nullable<BABYLON.Camera>, sourceTexture: BABYLON.Nullable<BABYLON.InternalTexture> = null, forceDepthStencil?: boolean): BABYLON.InternalTexture {
        camera = camera || this._camera;

        var scene = camera.getScene();
        var engine = scene.getEngine();
        var maxSize = engine.getCaps().maxTextureSize;

        var requiredWidth = ((sourceTexture ? sourceTexture.width : this._engine.getRenderWidth(true)) * <number>this._options) | 0;
        var requiredHeight = ((sourceTexture ? sourceTexture.height : this._engine.getRenderHeight(true)) * <number>this._options) | 0;

        // If rendering to a webvr camera's left or right eye only half the width should be used to avoid resize when rendered to screen
        var webVRCamera = (<BABYLON.WebVRFreeCamera>camera.parent);
        if (webVRCamera && (webVRCamera.leftCamera == camera || webVRCamera.rightCamera == camera)) {
            requiredWidth /= 2;

        var desiredWidth = ((<BABYLON.PostProcessOptions>this._options).width || requiredWidth);
        var desiredHeight = (<BABYLON.PostProcessOptions>this._options).height || requiredHeight;

        if (!this._shareOutputWithPostProcess && !this._forcedOutputTexture) {

            if (this.adaptScaleToCurrentViewport) {
                let currentViewport = engine.currentViewport;

                if (currentViewport) {
                    desiredWidth *= currentViewport.width;
                    desiredHeight *= currentViewport.height;

            if (this.renderTargetSamplingMode === BABYLON.Texture.TRILINEAR_SAMPLINGMODE || this.alwaysForcePOT) {
                if (!(<BABYLON.PostProcessOptions>this._options).width) {
                    desiredWidth = engine.needPOTTextures ? BABYLON.Tools.GetExponentOfTwo(desiredWidth, maxSize, this.scaleMode) : desiredWidth;

                if (!(<BABYLON.PostProcessOptions>this._options).height) {
                    desiredHeight = engine.needPOTTextures ? BABYLON.Tools.GetExponentOfTwo(desiredHeight, maxSize, this.scaleMode) : desiredHeight;

            if (this.width !== desiredWidth || this.height !== desiredHeight) {
                if (this._textures.length > 0) {
                    for (var i = 0; i < this._textures.length; i++) {
                this.width = desiredWidth;
                this.height = desiredHeight;

                let textureSize = { width: this.width, height: this.height };
                var types = [];
                var samplingModes = [];
                for (var i = 0; i < this._textureCount; i++) {
                let textureOptions = {
                    generateMipMaps: false,
                    generateDepthBuffer: forceDepthStencil || camera._postProcesses.indexOf(this) === 0,
                    generateStencilBuffer: (forceDepthStencil || camera._postProcesses.indexOf(this) === 0) && this._engine.isStencilEnable,
                    samplingModes: samplingModes,
                    types: types,
                    textureCount: this._textureCount

                this._textures.concat(this._engine.createMultipleRenderTarget(textureSize, textureOptions));

                if (this._reusable) {
                    this._textures.concat(this._engine.createMultipleRenderTarget(textureSize, textureOptions));

                this._texelSize.copyFromFloats(1.0 / this.width, 1.0 / this.height);


            this._textures.forEach(texture => {
                if (texture.samples !== this.samples) {
                    this._engine.updateRenderTargetTextureSampleCount(texture, this.samples);

        var target: BABYLON.InternalTexture;

        if (this._shareOutputWithPostProcess) {
            target = this._shareOutputWithPostProcess.inputTexture;
        } else if (this._forcedOutputTexture) {
            target = this._forcedOutputTexture;

            this.width = this._forcedOutputTexture.width;
            this.height = this._forcedOutputTexture.height;
        } else {
            target = this.inputTexture;

        // Bind the input of this post process to be used as the output of the previous post process.
        if (this.enablePixelPerfectMode) {
            this._scaleRatio.copyFromFloats(requiredWidth / desiredWidth, requiredHeight / desiredHeight);
            this._engine.bindFramebuffer(target, 0, requiredWidth, requiredHeight, true);
        else {
            this._scaleRatio.copyFromFloats(1, 1);
            this._engine.bindFramebuffer(target, 0, undefined, undefined, true);


        // Clear
        if (this.autoClear && this.alphaMode === BABYLON.Engine.ALPHA_DISABLE) {
            this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);

        if (this._reusable) {
            this._currentRenderTextureInd = (this._currentRenderTextureInd + this._textureCount) % (this._textureCount + 1);
        return target;

    unbindFrameBuffer(engine: BABYLON.Engine): void {
        let internalTextures: BABYLON.InternalTexture[] = [];
        for (var i = 0; i < this._textureCount; i++) {
        engine.unBindMultiColorAttachmentFramebuffer(internalTextures, false, () => { });


It seems to work so far...


Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...