Ver código fonte

Merge pull request #2142 from CraigFeldspar/ssao

[WIP] MultiRenderTargets, geometryRenderer and SSAO V2
David Catuhe 8 anos atrás
pai
commit
648e92890b

+ 1 - 0
Playground/scripts/scripts.txt

@@ -22,6 +22,7 @@ proceduralTexture
 basic sounds
 sound on mesh
 ssao rendering pipeline
+ssao 2
 volumetric Light Scattering
 hdr Rendering Pipeline
 refraction and Reflection

+ 61 - 0
Playground/scripts/ssao 2.js

@@ -0,0 +1,61 @@
+var createScene = function () {
+    // Create scene
+    var scene = new BABYLON.Scene(engine);
+    scene.clearColor = BABYLON.Color3.Black();
+
+    // Create camera
+    var camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(29, 13, 23), scene);
+    camera.setTarget(new BABYLON.Vector3(0, 0, 0));
+    camera.attachControl(canvas);
+
+    // Create some boxes and deactivate lighting (specular color and back faces)
+    var boxMaterial = new BABYLON.StandardMaterial("boxMaterail", scene);
+    boxMaterial.diffuseTexture = new BABYLON.Texture("textures/ground.jpg", scene);
+    boxMaterial.specularColor = BABYLON.Color3.Black();
+    boxMaterial.emissiveColor = BABYLON.Color3.White();
+
+    for (var i = 0; i < 10; i++) {
+        for (var j = 0; j < 10; j++) {
+            var box = BABYLON.Mesh.CreateBox("box" + i + " - " + j, 5, scene);
+            box.position = new BABYLON.Vector3(i * 5, 2.5, j * 5);
+            box.rotation = new BABYLON.Vector3(i, i * j, j);
+            box.material = boxMaterial;
+        }
+    }
+
+    // Create SSAO and configure all properties (for the example)
+    var ssaoRatio = {
+        ssaoRatio: 0.5, // Ratio of the SSAO post-process, in a lower resolution
+        blurRatio: 0.5// Ratio of the combine post-process (combines the SSAO and the scene)
+    };
+
+    var ssao = new BABYLON.SSAO2RenderingPipeline("ssao", scene, ssaoRatio);
+    ssao.radius = 3.5;
+    ssao.totalStrength = 1.3;
+    ssao.expensiveBlur = true;
+    ssao.samples = 16;
+    ssao.maxZ = 250;
+
+    // Attach camera to the SSAO render pipeline
+    scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+
+    // Manage SSAO
+    window.addEventListener("keydown", function (evt) {
+        // draw SSAO with scene when pressed "1"
+        if (evt.keyCode === 49) {
+            scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+            scene.postProcessRenderPipelineManager.enableEffectInPipeline("ssao", ssao.SSAOCombineRenderEffect, camera);
+        }
+            // draw without SSAO when pressed "2"
+        else if (evt.keyCode === 50) {
+            scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline("ssao", camera);
+        }
+            // draw only SSAO when pressed "2"
+        else if (evt.keyCode === 51) {
+            scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+            scene.postProcessRenderPipelineManager.disableEffectInPipeline("ssao", ssao.SSAOCombineRenderEffect, camera);
+        }
+    });
+
+    return scene;
+}

+ 24 - 2
Tools/Gulp/config.json

@@ -16,7 +16,7 @@
                 "pointLight", "directionalLight", "spotLight", "animations", "actions", "sprites", "picking", "collisions",
                 "particles", "solidParticles", "additionalMeshes", "meshBuilder", "audio", "additionalTextures", "shadows",
                 "loader", "userData", "offline", "fresnel", "multiMaterial", "touchCamera", "procedural", "gamepad",
-                "additionalCameras", "postProcesses", "renderingPipeline", "additionalRenderingPipeline", "depthRenderer", "additionalPostProcesses",
+                "additionalCameras", "postProcesses", "renderingPipeline", "additionalRenderingPipeline", "depthRenderer", "geometryBufferRenderer", "additionalPostProcesses",
                 "bones", "hdr", "polygonMesh", "csg", "lensFlares", "physics", "textureFormats", "debug", "morphTargets",
                 "colorCurves", "octrees", "simd", "vr", "virtualJoystick", "optimizations", "highlights", "assetsManager",
                 "mapTexture", "dynamicFloatArray", "serialization", "probes", "layer"
@@ -382,6 +382,7 @@
             "files": [
                 "../../src/Materials/Textures/babylon.cubeTexture.js",
                 "../../src/Materials/Textures/babylon.renderTargetTexture.js",
+                "../../src/Materials/Textures/babylon.multiRenderTarget.js",
                 "../../src/Materials/Textures/babylon.mirrorTexture.js",
                 "../../src/Materials/Textures/babylon.refractionTexture.js",
                 "../../src/Materials/Textures/babylon.dynamicTexture.js",
@@ -528,7 +529,26 @@
                 "instancesVertex",
                 "bonesVertex"
             ]
-        },      
+        },
+        "geometryBufferRenderer" : 
+        {
+            "files": [
+                "../../src/Rendering/babylon.geometryBufferRenderer.js"
+            ],
+            "dependUpon" : [
+                "core"
+            ], 
+            "shaders" : [
+                "geometry.vertex",
+                "geometry.fragment"
+            ],
+            "shaderIncludes": [
+                "bones300Declaration",
+                "instances300Declaration",
+                "instancesVertex",
+                "bonesVertex"
+            ]
+        },            
         "postProcesses" : 
         {
             "files": [
@@ -590,6 +610,7 @@
         {
             "files": [
                 "../../src/PostProcess/babylon.ssaoRenderingPipeline.js",
+                "../../src/PostProcess/babylon.ssao2RenderingPipeline.js",
                 "../../src/PostProcess/babylon.lensRenderingPipeline.js",
                 "../../src/PostProcess/babylon.hdrRenderingPipeline.js",
                 "../../src/PostProcess/babylon.standardRenderingPipeline.js"
@@ -600,6 +621,7 @@
             ], 
             "shaders" : [
                 "ssao.fragment",
+                "ssao2.fragment",
                 "ssaoCombine.fragment",
                 "chromaticAberration.fragment",
                 "lensHighlights.fragment",

Diferenças do arquivo suprimidas por serem muito extensas
+ 8251 - 8083
dist/preview release/babylon.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 40 - 39
dist/preview release/babylon.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 749 - 8
dist/preview release/babylon.max.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 8251 - 8083
dist/preview release/babylon.module.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 43 - 42
dist/preview release/babylon.worker.js


+ 14 - 14
dist/preview release/inspector/babylon.inspector.bundle.js

@@ -43,15 +43,15 @@ var INSPECTOR =
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ function(module, exports, __webpack_require__) {
 
 	__webpack_require__(1);
 	module.exports = __webpack_require__(5);
 
 
-/***/ }),
+/***/ },
 /* 1 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ function(module, exports, __webpack_require__) {
 
 	// style-loader: Adds some css to the DOM by adding a <style> tag
 
@@ -75,9 +75,9 @@ var INSPECTOR =
 		module.hot.dispose(function() { update(); });
 	}
 
-/***/ }),
+/***/ },
 /* 2 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ function(module, exports, __webpack_require__) {
 
 	exports = module.exports = __webpack_require__(3)();
 	// imports
@@ -90,9 +90,9 @@ var INSPECTOR =
 	// exports
 
 
-/***/ }),
+/***/ },
 /* 3 */
-/***/ (function(module, exports) {
+/***/ function(module, exports) {
 
 	/*
 		MIT License http://www.opensource.org/licenses/mit-license.php
@@ -146,9 +146,9 @@ var INSPECTOR =
 	};
 
 
-/***/ }),
+/***/ },
 /* 4 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ function(module, exports, __webpack_require__) {
 
 	/*
 		MIT License http://www.opensource.org/licenses/mit-license.php
@@ -398,9 +398,9 @@ var INSPECTOR =
 	}
 
 
-/***/ }),
+/***/ },
 /* 5 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ function(module, exports, __webpack_require__) {
 
 	/*** IMPORTS FROM imports-loader ***/
 	var Split = __webpack_require__(6);
@@ -413,9 +413,9 @@ var INSPECTOR =
 	module.exports = INSPECTOR;
 
 
-/***/ }),
+/***/ },
 /* 6 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ function(module, exports, __webpack_require__) {
 
 	// The programming goals of Split.js are to deliver readable, understandable and
 	// maintainable code, while at the same time manually optimizing for tiny minified file size,
@@ -987,5 +987,5 @@ var INSPECTOR =
 	}).call(window);
 
 
-/***/ })
+/***/ }
 /******/ ]);

+ 134 - 0
src/Materials/Textures/babylon.multiRenderTarget.ts

@@ -0,0 +1,134 @@
+module BABYLON {
+    export interface IMultiRenderTargetOptions {
+        generateMipMaps: boolean,
+        types: number[],
+        samplingModes: number[],
+        generateDepthBuffer: boolean,
+        generateStencilBuffer: boolean,
+        generateDepthTexture: boolean,
+        textureCount: number
+    };
+    export class MultiRenderTarget extends RenderTargetTexture {
+
+        private _webGLTextures: WebGLTexture[];
+        private _textures: Texture[];
+        private _count: number;
+
+        public get isSupported(): boolean {
+            var engine = this.getScene().getEngine();
+            return engine.webGLVersion > 1 || engine.getCaps().drawBuffersExtension;
+        }
+
+        private _multiRenderTargetOptions: IMultiRenderTargetOptions;
+
+        public get textures(): Texture[] {
+            return this._textures;
+        }
+
+        public get depthTexture(): Texture {
+            return this._textures[this._textures.length - 1];
+        }
+
+        constructor(name: string, size: any, count: number, scene: Scene, options?: any) {
+            options = options || {};
+
+            var generateMipMaps = options.generateMipMaps ? options.generateMipMaps : false;
+            var generateDepthTexture = options.generateDepthTexture ? options.generateDepthTexture : false;
+            var doNotChangeAspectRatio = options.doNotChangeAspectRatio === undefined ? true : options.doNotChangeAspectRatio;
+
+            super(name, size, scene, generateMipMaps, doNotChangeAspectRatio);
+
+            if (!this.isSupported) {
+                this.dispose();
+                return;
+            }
+
+            var types = [];
+            var samplingModes = [];
+
+            for (var i = 0; i < count; i++) {
+                if (options.types && options.types[i]) {
+                    types.push(options.types[i]);
+                } else {
+                    types.push(Engine.TEXTURETYPE_FLOAT);
+                }
+
+                if (options.samplingModes && options.samplingModes[i]) {
+                    samplingModes.push(options.samplingModes[i]);
+                } else {
+                    samplingModes.push(Texture.BILINEAR_SAMPLINGMODE);
+                }
+            }
+
+            var generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
+            var generateStencilBuffer = options.generateStencilBuffer === undefined ? false : options.generateStencilBuffer;
+
+            this._count = count;
+            this._size = size;
+            this._multiRenderTargetOptions = {
+                samplingModes: samplingModes,
+                generateMipMaps: generateMipMaps,
+                generateDepthBuffer: generateDepthBuffer,
+                generateStencilBuffer: generateStencilBuffer,
+                generateDepthTexture: generateDepthTexture,
+                types: types,
+                textureCount: count
+            };
+
+            this._webGLTextures = scene.getEngine().createMultipleRenderTarget(size, this._multiRenderTargetOptions);
+
+            this._createInternalTextures();
+        }
+
+        private _createInternalTextures(): void {
+            this._textures = [];
+            for (var i = 0; i < this._webGLTextures.length; i++) {
+                var texture = new BABYLON.Texture(null, this.getScene());
+                texture._texture = this._webGLTextures[i];
+                this._textures.push(texture);
+            }
+
+            // Keeps references to frame buffer and stencil/depth buffer
+            this._texture = this._webGLTextures[0];
+        }
+
+        public get samples(): number {
+            return this._samples;
+        }
+
+        public set samples(value: number) {
+            if (this._samples === value) {
+                return;
+            }
+            
+            for (var i = 0 ; i < this._webGLTextures.length; i++) {
+                this._samples = this.getScene().getEngine().updateRenderTargetTextureSampleCount(this._webGLTextures[i], value);
+            }
+        }
+
+        public resize(size: any) {
+            this.releaseInternalTextures();
+            this._webGLTextures = this.getScene().getEngine().createMultipleRenderTarget(size, this._multiRenderTargetOptions);
+            this._createInternalTextures();
+        }
+
+        public dispose(): void {
+            this.releaseInternalTextures();
+
+            super.dispose();
+        }
+
+        public releaseInternalTextures(): void {
+            if (!this._webGLTextures) {
+                return;
+            }
+
+            for (var i = this._webGLTextures.length - 1; i >= 0; i--) {
+                if (this._webGLTextures[i] !== undefined) {
+                    this.getScene().getEngine().releaseInternalTexture(this._webGLTextures[i]);
+                    this._webGLTextures.splice(i, 1);
+                }
+            }
+        }
+    }
+}

+ 19 - 10
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -99,21 +99,21 @@
             this._onClearObserver = this.onClearObservable.add(callback);
         }
 
-        private _size: number;
+        protected _size: number;
         public _generateMipMaps: boolean;
-        private _renderingManager: RenderingManager;
+        protected _renderingManager: RenderingManager;
         public _waitingRenderList: string[];
-        private _doNotChangeAspectRatio: boolean;
-        private _currentRefreshId = -1;
-        private _refreshRate = 1;
-        private _textureMatrix: Matrix;
-        private _samples = 1;
+        protected _doNotChangeAspectRatio: boolean;
+        protected _currentRefreshId = -1;
+        protected _refreshRate = 1;
+        protected _textureMatrix: Matrix;
+        protected _samples = 1;
         protected _renderTargetOptions: IRenderTargetOptions;
         public get renderTargetOptions(): IRenderTargetOptions {
             return this._renderTargetOptions;
         }
 
-        constructor(name: string, size: any, scene: Scene, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Engine.TEXTURETYPE_UNSIGNED_INT, public isCube = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, generateDepthBuffer = true, generateStencilBuffer = false) {
+        constructor(name: string, size: any, scene: Scene, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Engine.TEXTURETYPE_UNSIGNED_INT, public isCube = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, generateDepthBuffer = true, generateStencilBuffer = false, isMulti = false) {
             super(null, scene, !generateMipMaps);
 
             this.name = name;
@@ -122,6 +122,13 @@
             this._generateMipMaps = generateMipMaps;
             this._doNotChangeAspectRatio = doNotChangeAspectRatio;
 
+            // Rendering groups
+            this._renderingManager = new RenderingManager(scene);
+
+            if (isMulti) {
+                return;
+            }
+
             this._renderTargetOptions = {
                 generateMipMaps: generateMipMaps,
                 type: type,
@@ -143,8 +150,6 @@
                 this._texture = scene.getEngine().createRenderTargetTexture(size, this._renderTargetOptions);
             }
 
-            // Rendering groups
-            this._renderingManager = new RenderingManager(scene);
         }
 
         public get samples(): number {
@@ -469,5 +474,9 @@
 
             return serializationObject;
         }
+
+        public dispose(): void {
+            super.dispose();
+        }
     }
 }

+ 1 - 1
src/Materials/babylon.effect.ts

@@ -352,7 +352,7 @@
 
             // Already converted
             if (preparedSourceCode.indexOf("#version 3") !== -1) {
-                callback(preparedSourceCode);
+                callback(preparedSourceCode.replace("#version 300 es", ""));
                 return;
             }
             

+ 3 - 3
src/PostProcess/babylon.postProcess.ts

@@ -131,11 +131,11 @@
             this.updateEffect(defines);
         }
         
-        public updateEffect(defines?: string) {
+        public updateEffect(defines?: string, uniforms?: string[], samplers?: string[]) {
             this._effect = this._engine.createEffect({ vertex: this._vertexUrl, fragment: this._fragmentUrl },
                 ["position"],
-                this._parameters,
-                this._samplers, defines !== undefined ? defines : "");
+                uniforms || this._parameters,
+                samplers || this._samplers, defines !== undefined ? defines : "");
         }
 
         public isReusable(): boolean {

+ 373 - 0
src/PostProcess/babylon.ssao2RenderingPipeline.ts

@@ -0,0 +1,373 @@
+module BABYLON {
+    export class SSAO2RenderingPipeline extends PostProcessRenderPipeline {
+        // Members
+
+        /**
+        * The PassPostProcess id in the pipeline that contains the original scene color
+        * @type {string}
+        */
+        public SSAOOriginalSceneColorEffect: string = "SSAOOriginalSceneColorEffect";
+        /**
+        * The SSAO PostProcess id in the pipeline
+        * @type {string}
+        */
+        public SSAORenderEffect: string = "SSAORenderEffect";
+        /**
+        * The horizontal blur PostProcess id in the pipeline
+        * @type {string}
+        */
+        public SSAOBlurHRenderEffect: string = "SSAOBlurHRenderEffect";
+        /**
+        * The vertical blur PostProcess id in the pipeline
+        * @type {string}
+        */
+        public SSAOBlurVRenderEffect: string = "SSAOBlurVRenderEffect";
+        /**
+        * The PostProcess id in the pipeline that combines the SSAO-Blur output with the original scene color (SSAOOriginalSceneColorEffect)
+        * @type {string}
+        */
+        public SSAOCombineRenderEffect: string = "SSAOCombineRenderEffect";
+
+        /**
+        * The output strength of the SSAO post-process. Default value is 1.0.
+        * @type {number}
+        */
+        @serialize()
+        public totalStrength: number = 1.0;
+
+        /**
+        * Maximum depth value to still render AO. A smooth falloff makes the dimming more natural, so there will be no abrupt shading change.
+        * @type {number}
+        */
+        @serialize()
+        public maxZ: number = 100.0;
+
+        /**
+        * In order to save performances, SSAO radius is clamped on close geometry. This ratio changes by how much
+        * @type {number}
+        */
+        @serialize()
+        public minZAspect: number = 0.2;
+
+        /**
+        * Number of samples used for the SSAO calculations. Default value is 8
+        * @type {number}
+        */
+        @serialize("samples")
+        private _samples: number = 8;
+
+        /**
+        * Dynamically generated sphere sampler.
+        * @type {number[]}
+        */
+        private _sampleSphere: number[];
+
+        /**
+        * Blur filter offsets
+        * @type {number[]}
+        */
+        private _samplerOffsets: number[];
+
+        public set samples(n: number) {
+            this._ssaoPostProcess.updateEffect("#define SAMPLES " + n + "\n#define SSAO");
+            this._samples = n;
+            this._sampleSphere = this._generateHemisphere();
+
+            this._firstUpdate = true;
+        }
+
+        public get samples(): number {
+            return this._samples;
+        }
+
+        /**
+        * Are we using bilateral blur ?
+        * @type {boolean}
+        */
+        @serialize("expensiveBlur")
+        private _expensiveBlur: boolean = true;
+        public set expensiveBlur(b: boolean) {
+            this._blurHPostProcess.updateEffect("#define BILATERAL_BLUR\n#define BILATERAL_BLUR_H\n#define SAMPLES 16\n#define EXPENSIVE " + (b ? "1" : "0") + "\n",
+                                                null, ["textureSampler", "depthSampler"]);
+            this._blurVPostProcess.updateEffect("#define BILATERAL_BLUR\n#define SAMPLES 16\n#define EXPENSIVE " + (b ? "1" : "0") + "\n",
+                                                null, ["textureSampler", "depthSampler"]);
+            this._expensiveBlur = b;
+            this._firstUpdate = true;
+        }
+
+        public get expensiveBlur(): boolean {
+            return this._expensiveBlur;
+        }
+
+        /**
+        * The radius around the analyzed pixel used by the SSAO post-process. Default value is 2.0
+        * @type {number}
+        */
+        @serialize()
+        public radius: number = 2.0;
+
+        /**
+        * The base color of the SSAO post-process
+        * The final result is "base + ssao" between [0, 1]
+        * @type {number}
+        */
+        @serialize()
+        public base: number = 0.1;
+
+        /**
+        *  Support test.
+        * @type {boolean}
+        */
+        public get isSupported(): boolean {
+            var engine = this._scene.getEngine();
+            return engine.webGLVersion > 1;
+        }
+
+        private _scene: Scene;
+        private _depthTexture: Texture;
+        private _normalTexture: Texture;
+        private _randomTexture: DynamicTexture;
+
+        private _originalColorPostProcess: PassPostProcess;
+        private _ssaoPostProcess: PostProcess;
+        private _blurHPostProcess: PostProcess;
+        private _blurVPostProcess: PostProcess;
+        private _ssaoCombinePostProcess: PostProcess;
+
+        private _firstUpdate: boolean = true;
+
+        @serialize()
+        private _ratio: any;
+
+        /**
+         * @constructor
+         * @param {string} name - The rendering pipeline name
+         * @param {BABYLON.Scene} scene - The scene linked to this pipeline
+         * @param {any} ratio - The size of the postprocesses. Can be a number shared between passes or an object for more precision: { ssaoRatio: 0.5, blurRatio: 1.0 }
+         * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
+         */
+        constructor(name: string, scene: Scene, ratio: any, cameras?: Camera[]) {
+            super(scene.getEngine(), name);
+
+            this._scene = scene;
+
+            if (!this.isSupported) {
+                Tools.Error("SSAO 2 needs WebGL 2 support.");
+                return;
+            }
+
+            var ssaoRatio = ratio.ssaoRatio || ratio;
+            var blurRatio = ratio.blurRatio || ratio;
+            this._ratio = {
+                ssaoRatio: ssaoRatio,
+                blurRatio: blurRatio
+            };
+
+            // Set up assets
+            this._createRandomTexture();
+            this._depthTexture = scene.enableGeometryBufferRenderer().getGBuffer().textures[0]; 
+            this._normalTexture = scene.enableGeometryBufferRenderer().getGBuffer().textures[1];
+
+            this._originalColorPostProcess = new PassPostProcess("SSAOOriginalSceneColor", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this._createSSAOPostProcess(1.0);
+            this._createBlurPostProcess(ssaoRatio, blurRatio);
+            this._createSSAOCombinePostProcess(blurRatio);
+
+            // Set up pipeline
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAOOriginalSceneColorEffect, () => { return this._originalColorPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAORenderEffect, () => { return this._ssaoPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAOBlurHRenderEffect, () => { return this._blurHPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAOBlurVRenderEffect, () => { return this._blurVPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.SSAOCombineRenderEffect, () => { return this._ssaoCombinePostProcess; }, true));
+
+            // Finish
+            scene.postProcessRenderPipelineManager.addPipeline(this);
+            if (cameras)
+                scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
+
+        }
+
+        // Public Methods
+
+        /**
+         * Removes the internal pipeline assets and detatches the pipeline from the scene cameras
+         */
+        public dispose(disableGeometryBufferRenderer: boolean = false): void {
+            for (var i = 0; i < this._scene.cameras.length; i++) {
+                var camera = this._scene.cameras[i];
+
+                this._originalColorPostProcess.dispose(camera);
+                this._ssaoPostProcess.dispose(camera);
+                this._blurHPostProcess.dispose(camera);
+                this._blurVPostProcess.dispose(camera);
+                this._ssaoCombinePostProcess.dispose(camera);
+            }
+
+            this._randomTexture.dispose();
+
+            if (disableGeometryBufferRenderer)
+                this._scene.disableGeometryBufferRenderer();
+
+            this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
+
+            super.dispose();
+        }
+
+        // Private Methods
+        private _createBlurPostProcess(ssaoRatio: number, blurRatio: number): void {
+            var samples = 16;
+            this._samplerOffsets = [];
+            var expensive = this.expensiveBlur;
+
+            for (var i = -8; i < 8; i++) {
+                this._samplerOffsets.push(i * 2 + 0.5);
+            }
+
+            this._blurHPostProcess = new PostProcess("BlurH", "ssao2", ["outSize", "samplerOffsets", "near", "far", "radius"], ["depthSampler"], ssaoRatio, null, Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define BILATERAL_BLUR\n#define BILATERAL_BLUR_H\n#define SAMPLES 16\n#define EXPENSIVE " + (expensive ? "1" : "0") + "\n");
+            this._blurHPostProcess.onApply = (effect: Effect) => {
+                effect.setFloat("outSize", this._ssaoCombinePostProcess.width);
+                effect.setFloat("near", this._scene.activeCamera.minZ);
+                effect.setFloat("far", this._scene.activeCamera.maxZ);
+                effect.setFloat("radius", this.radius);
+                effect.setTexture("depthSampler", this._depthTexture);
+
+                if (this._firstUpdate) {
+                    effect.setArray("samplerOffsets", this._samplerOffsets);
+                }
+            };
+
+            this._blurVPostProcess = new PostProcess("BlurV", "ssao2", ["outSize", "samplerOffsets", "near", "far", "radius"], ["depthSampler"], blurRatio, null, Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define BILATERAL_BLUR\n#define BILATERAL_BLUR_V\n#define SAMPLES 16\n#define EXPENSIVE " + (expensive ? "1" : "0") + "\n");
+            this._blurVPostProcess.onApply = (effect: Effect) => {
+                effect.setFloat("outSize", this._ssaoCombinePostProcess.height);
+                effect.setFloat("near", this._scene.activeCamera.minZ);
+                effect.setFloat("far", this._scene.activeCamera.maxZ);
+                effect.setFloat("radius", this.radius);
+                effect.setTexture("depthSampler", this._depthTexture);
+
+                if (this._firstUpdate) {
+                    effect.setArray("samplerOffsets", this._samplerOffsets);
+                    this._firstUpdate = false;
+                }
+            };
+        }
+
+        private _generateHemisphere(): number[] {
+            var numSamples = this.samples;
+            var result = [];
+            var vector, scale;
+
+            var rand = (min, max) => {
+                return Math.random() * (max - min) + min;
+            }
+
+            var lerp = (start, end, percent) => {
+                return (start + percent*(end - start));
+            }
+
+            var i = 0;
+            var normal = new BABYLON.Vector3(0, 0, 1);
+            while (i < numSamples) {
+               vector = new BABYLON.Vector3(
+                   rand(-1.0, 1.0),
+                   rand(-1.0, 1.0),
+                   rand(0.30, 1.0));
+               vector.normalize();
+               scale = i / numSamples;
+               scale = lerp(0.1, 1.0, scale*scale);
+               vector.scaleInPlace(scale);
+
+
+               result.push(vector.x, vector.y, vector.z);
+               i++;
+            }
+
+            return result;
+        }
+
+        private _createSSAOPostProcess(ratio: number): void {
+            var numSamples = this.samples;
+
+            this._sampleSphere = this._generateHemisphere();
+
+            this._ssaoPostProcess = new PostProcess("ssao2", "ssao2",
+                                                    [
+                                                        "sampleSphere", "samplesFactor", "randTextureTiles", "totalStrength", "radius",
+                                                        "base", "range", "projection", "near", "far", "texelSize",
+                                                        "xViewport", "yViewport", "maxZ", "minZAspect"
+                                                    ],
+                                                    ["randomSampler", "normalSampler"],
+                                                    ratio, null, Texture.BILINEAR_SAMPLINGMODE,
+                                                    this._scene.getEngine(), false,
+                                                    "#define SAMPLES " + numSamples + "\n#define SSAO");
+
+            this._ssaoPostProcess.onApply = (effect: Effect) => {
+                if (this._firstUpdate) {
+                    effect.setArray3("sampleSphere", this._sampleSphere);
+                    effect.setFloat("randTextureTiles", 4.0);
+                }
+
+                effect.setFloat("samplesFactor", 1 / this.samples);
+                effect.setFloat("totalStrength", this.totalStrength);
+                effect.setFloat2("texelSize", 1 / this._ssaoPostProcess.width, 1 / this._ssaoPostProcess.height);
+                effect.setFloat("radius", this.radius);
+                effect.setFloat("maxZ", this.maxZ);
+                effect.setFloat("minZAspect", this.minZAspect);
+                effect.setFloat("base", this.base);
+                effect.setFloat("near", this._scene.activeCamera.minZ);
+                effect.setFloat("far", this._scene.activeCamera.maxZ);
+                effect.setFloat("xViewport", Math.tan(this._scene.activeCamera.fov / 2) * this._scene.activeCamera.minZ * this._scene.getEngine().getAspectRatio(this._scene.activeCamera, true));
+                effect.setFloat("yViewport", Math.tan(this._scene.activeCamera.fov / 2) * this._scene.activeCamera.minZ );
+                effect.setMatrix("projection", this._scene.getProjectionMatrix());
+
+                effect.setTexture("textureSampler", this._depthTexture);
+                effect.setTexture("normalSampler", this._normalTexture);
+                effect.setTexture("randomSampler", this._randomTexture);
+            };
+        }
+
+        private _createSSAOCombinePostProcess(ratio: number): void {
+            this._ssaoCombinePostProcess = new PostProcess("ssaoCombine", "ssaoCombine", [], ["originalColor"],
+                                                           ratio, null, Texture.BILINEAR_SAMPLINGMODE,
+                                                           this._scene.getEngine(), false);
+
+            this._ssaoCombinePostProcess.onApply = (effect: Effect) => {
+                effect.setTextureFromPostProcess("originalColor", this._originalColorPostProcess);
+            };
+        }
+
+        private _createRandomTexture(): void {
+            var size = 512;
+
+            this._randomTexture = new DynamicTexture("SSAORandomTexture", size, this._scene, false, Texture.TRILINEAR_SAMPLINGMODE);
+            this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
+            this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
+
+            var context = this._randomTexture.getContext();
+
+            var rand = (min, max) => {
+                return Math.random() * (max - min) + min;
+            }
+
+            var randVector = Vector3.Zero();
+
+            for (var x = 0; x < size; x++) {
+                for (var y = 0; y < size; y++) {
+                    randVector.x = rand(0.0, 1.0);
+                    randVector.y = rand(0.0, 1.0);
+                    randVector.z = 0.0;
+
+                    randVector.normalize();
+
+                    randVector.scaleInPlace(255);
+                    randVector.x = Math.floor(randVector.x);
+                    randVector.y = Math.floor(randVector.y);
+
+                    context.fillStyle = 'rgb(' + randVector.x + ', ' + randVector.y + ', ' + randVector.z + ')';
+                    context.fillRect(x, y, 1, 1);
+                }
+            }
+
+            this._randomTexture.update(false);
+        }
+    }
+}

+ 173 - 0
src/Rendering/babylon.geometryBufferRenderer.ts

@@ -0,0 +1,173 @@
+module BABYLON {
+    export class GeometryBufferRenderer {
+        private _scene: Scene;
+        private _multiRenderTarget: MultiRenderTarget;
+        private _effect: Effect;
+
+        private _viewMatrix = Matrix.Zero();
+        private _projectionMatrix = Matrix.Zero();
+        private _transformMatrix = Matrix.Zero();
+        private _worldViewProjection = Matrix.Zero();
+
+        private _cachedDefines: string;
+
+        public set renderList(meshes: Mesh[]) {
+            this._multiRenderTarget.renderList = meshes;
+        }
+
+        public get isSupported(): boolean {
+            return this._multiRenderTarget.isSupported;
+        }
+
+        constructor(scene: Scene, ratio: number = 1) {
+            this._scene = scene;
+            var engine = scene.getEngine();
+
+            // Render target
+            this._multiRenderTarget = new MultiRenderTarget("gBuffer", { width: engine.getRenderWidth() * ratio, height: engine.getRenderHeight() * ratio }, 2, this._scene, { generateMipMaps : false, generateDepthTexture: true });
+            if (!this.isSupported) {
+                return null;
+            }
+            this._multiRenderTarget.wrapU = Texture.CLAMP_ADDRESSMODE;
+            this._multiRenderTarget.wrapV = Texture.CLAMP_ADDRESSMODE;
+            this._multiRenderTarget.refreshRate = 1;
+            this._multiRenderTarget.renderParticles = false;
+            this._multiRenderTarget.renderList = null;
+
+            
+            // set default depth value to 1.0 (far away)
+            this._multiRenderTarget.onClearObservable.add((engine: Engine) => {
+                engine.clear(new Color4(0.0, 0.0, 0.0, 1.0), true, true, true);
+            });
+
+            // Custom render function
+            var renderSubMesh = (subMesh: SubMesh): void => {
+                var mesh = subMesh.getRenderingMesh();
+                var scene = this._scene;
+                var engine = scene.getEngine();
+
+                // Culling
+                engine.setState(subMesh.getMaterial().backFaceCulling);
+
+                // Managing instances
+                var batch = mesh._getInstancesRenderList(subMesh._id);
+
+                if (batch.mustReturn) {
+                    return;
+                }
+
+                var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances[subMesh._id] !== null);
+
+                if (this.isReady(subMesh, hardwareInstancedRendering)) {
+                    engine.enableEffect(this._effect);
+                    mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
+                    var material = subMesh.getMaterial();
+
+                    this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
+                    this._effect.setMatrix("view", scene.getViewMatrix());
+
+                    // Alpha test
+                    if (material && material.needAlphaTesting()) {
+                        var alphaTexture = material.getAlphaTestTexture();
+                        this._effect.setTexture("diffuseSampler", alphaTexture);
+                        this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
+                    }
+
+                    // Bones
+                    if (mesh.useBones && mesh.computeBonesUsingShaders) {
+                        this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
+                    }
+
+                    // Draw
+                    mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
+                        (isInstance, world) => this._effect.setMatrix("world", world));
+                }
+            };
+
+            this._multiRenderTarget.customRenderFunction = (opaqueSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>): void => {
+                var index;
+
+                for (index = 0; index < opaqueSubMeshes.length; index++) {
+                    renderSubMesh(opaqueSubMeshes.data[index]);
+                }
+
+                for (index = 0; index < alphaTestSubMeshes.length; index++) {
+                    renderSubMesh(alphaTestSubMeshes.data[index]);
+                }
+            };
+
+        }
+
+        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
+            var material: any = subMesh.getMaterial();
+
+            if (material && material.disableDepthWrite) {
+                return false;
+            }
+
+            var defines = [];
+
+            var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
+
+            var mesh = subMesh.getMesh();
+            var scene = mesh.getScene();
+
+            // Alpha test
+            if (material && material.needAlphaTesting()) {
+                defines.push("#define ALPHATEST");
+                if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
+                    attribs.push(VertexBuffer.UVKind);
+                    defines.push("#define UV1");
+                }
+                if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
+                    attribs.push(VertexBuffer.UV2Kind);
+                    defines.push("#define UV2");
+                }
+            }
+
+            // Bones
+            if (mesh.useBones && mesh.computeBonesUsingShaders) {
+                attribs.push(VertexBuffer.MatricesIndicesKind);
+                attribs.push(VertexBuffer.MatricesWeightsKind);
+                if (mesh.numBoneInfluencers > 4) {
+                    attribs.push(VertexBuffer.MatricesIndicesExtraKind);
+                    attribs.push(VertexBuffer.MatricesWeightsExtraKind);
+                }
+                defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
+                defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
+            } else {
+                defines.push("#define NUM_BONE_INFLUENCERS 0");
+            }
+
+            // Instances
+            if (useInstances) {
+                defines.push("#define INSTANCES");
+                attribs.push("world0");
+                attribs.push("world1");
+                attribs.push("world2");
+                attribs.push("world3");
+            }
+
+            // Get correct effect      
+            var join = defines.join("\n");
+            if (this._cachedDefines !== join) {
+                this._cachedDefines = join;
+                this._effect = this._scene.getEngine().createEffect("geometry",
+                    attribs,
+                    ["world", "mBones", "viewProjection", "diffuseMatrix", "view"],
+                    ["diffuseSampler"], join);
+            }
+
+            return this._effect.isReady();
+        }
+
+        public getGBuffer(): MultiRenderTarget {
+            return this._multiRenderTarget;
+        }
+
+        // Methods
+        public dispose(): void {
+            this.getGBuffer().dispose();
+        }
+    }
+} 

+ 10 - 0
src/Shaders/ShadersInclude/bones300Declaration.fx

@@ -0,0 +1,10 @@
+#if NUM_BONE_INFLUENCERS > 0
+	uniform mat4 mBones[BonesPerMesh];
+
+	in vec4 matricesIndices;
+	in vec4 matricesWeights;
+	#if NUM_BONE_INFLUENCERS > 4
+		in vec4 matricesIndicesExtra;
+		in vec4 matricesWeightsExtra;
+	#endif
+#endif

+ 8 - 0
src/Shaders/ShadersInclude/instances300Declaration.fx

@@ -0,0 +1,8 @@
+#ifdef INSTANCES
+	in vec4 world0;
+	in vec4 world1;
+	in vec4 world2;
+	in vec4 world3;
+#else
+	uniform mat4 world;
+#endif

+ 26 - 0
src/Shaders/geometry.fragment.fx

@@ -0,0 +1,26 @@
+#version 300 es
+
+precision highp float;
+precision highp int;
+
+in vec3 vNormalV;
+in vec4 vViewPos;
+
+#ifdef ALPHATEST
+in vec2 vUV;
+uniform sampler2D diffuseSampler;
+#endif
+
+layout(location = 0) out vec4 color0;
+layout(location = 1) out vec4 color1;
+
+void main() {
+#ifdef ALPHATEST
+	if (texture(diffuseSampler, vUV).a < 0.4)
+		discard;
+#endif
+
+    color0 = vec4(vViewPos.z / vViewPos.w, 0.0, 0.0, 0.0);
+    color1 = vec4(normalize(vNormalV), 1.0);
+    //color2 = vec4(vPositionV, 1.0);
+}

+ 48 - 0
src/Shaders/geometry.vertex.fx

@@ -0,0 +1,48 @@
+#version 300 es
+
+precision highp float;
+precision highp int;
+
+#include<bones300Declaration>
+#include<instances300Declaration>
+
+in vec3 position;
+in vec3 normal;
+
+#if defined(ALPHATEST) || defined(NEED_UV)
+out vec2 vUV;
+uniform mat4 diffuseMatrix;
+#ifdef UV1
+in vec2 uv;
+#endif
+#ifdef UV2
+in vec2 uv2;
+#endif
+#endif
+
+// Uniform
+uniform mat4 viewProjection;
+uniform mat4 view;
+
+out vec3 vNormalV;
+out vec4 vViewPos;
+
+void main(void)
+{
+#include<instancesVertex>
+
+#include<bonesVertex>
+
+	vNormalV = normalize(vec3((view * finalWorld) * vec4(normal, 0.0)));
+	vViewPos = view * finalWorld * vec4(position, 1.0);
+	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
+
+#if defined(ALPHATEST) || defined(BASIC_RENDER)
+#ifdef UV1
+	vUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
+#endif
+#ifdef UV2
+	vUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
+#endif
+#endif
+}

+ 217 - 0
src/Shaders/ssao2.fragment.fx

@@ -0,0 +1,217 @@
+// SSAO 2 Shader
+precision highp float;
+uniform sampler2D textureSampler;
+uniform float near;
+uniform float far;
+uniform float radius;
+
+varying vec2 vUV;
+
+float perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {
+	return ( near * far ) / ( ( far - near ) * invClipZ - far );
+}
+
+float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {
+	return ( near * far / viewZ + far) / ( far - near );
+}
+
+float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {
+	return ( viewZ + near ) / ( near - far );
+}
+
+#ifdef SSAO
+uniform sampler2D randomSampler;
+uniform sampler2D normalSampler;
+
+uniform float randTextureTiles;
+uniform float samplesFactor;
+uniform vec3 sampleSphere[SAMPLES];
+
+uniform float totalStrength;
+uniform float base;
+uniform float xViewport;
+uniform float yViewport;
+uniform float maxZ;
+uniform float minZAspect;
+uniform vec2 texelSize;
+
+uniform mat4 projection;
+
+void main()
+{
+	vec3 random = texture2D(randomSampler, vUV * randTextureTiles).rgb;
+	float depth = abs(texture(textureSampler, vUV).r);
+	vec3 normal = texture2D(normalSampler, vUV).rgb; 
+	float occlusion = 0.0;
+	float correctedRadius = min(radius, minZAspect * depth / near);
+
+	vec3 vViewRay = vec3((vUV.x * 2.0 - 1.0)*xViewport, (vUV.y * 2.0 - 1.0)*yViewport, 1.0);
+	vec3 origin = vViewRay * depth;
+	vec3 rvec = random * 2.0 - 1.0;
+	rvec.z = 0.0;
+	vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
+	vec3 bitangent = cross(normal, tangent);
+	mat3 tbn = mat3(tangent, bitangent, normal);
+
+	float difference;
+
+	if (depth > maxZ) {
+		gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
+		return;
+	}
+
+	for (int i = 0; i < SAMPLES; ++i) {
+		// get sample position:
+	   vec3 samplePosition = tbn * sampleSphere[i];
+	   samplePosition = samplePosition * correctedRadius + origin;
+	  
+		// project sample position:
+	   vec4 offset = vec4(samplePosition, 1.0);
+	   offset = projection * offset;
+	   offset.xyz /= offset.w;
+	   offset.xy = offset.xy * 0.5 + 0.5;
+
+	   if (offset.x < 0.0 || offset.y < 0.0 || offset.x > 1.0 || offset.y > 1.0) {
+	     continue;
+	   }
+	  
+		// get sample linearDepth:
+	   float sampleDepth = abs(texture(textureSampler, offset.xy).r);
+		// range check & accumulate:
+	   float rangeCheck = abs(depth - sampleDepth) < correctedRadius ? 1.0 : 0.0;
+	   difference = samplePosition.z - sampleDepth;
+	  //occlusion += step(fallOff, difference) * (1.0 - smoothstep(fallOff, area, difference)) * rangeCheck;
+	   occlusion += (difference >= 1e-5 ? 1.0 : 0.0) * rangeCheck;
+	}
+
+
+	// float screenEdgeFactor = clamp(vUV.x * 10.0, 0.0, 1.0) * clamp(vUV.y * 10.0, 0.0, 1.0) * clamp((1.0 - vUV.x) * 10.0, 0.0, 1.0) * clamp((1.0 - vUV.y) * 10.0, 0.0, 1.0);
+
+	float ao = 1.0 - totalStrength * occlusion * samplesFactor;
+	float result = clamp(ao + base, 0.0, 1.0);
+	gl_FragColor = vec4(vec3(result), 1.0);
+}
+#endif
+
+#ifdef BILATERAL_BLUR
+uniform sampler2D depthSampler;
+uniform float outSize;
+uniform float samplerOffsets[SAMPLES];
+
+vec4 blur9(sampler2D image, vec2 uv, float resolution, vec2 direction) {
+  vec4 color = vec4(0.0);
+  vec2 off1 = vec2(1.3846153846) * direction;
+  vec2 off2 = vec2(3.2307692308) * direction;
+  color += texture2D(image, uv) * 0.2270270270;
+  color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162;
+  color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162;
+  color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703;
+  color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703;
+  return color;
+}
+
+vec4 blur13(sampler2D image, vec2 uv, float resolution, vec2 direction) {
+  vec4 color = vec4(0.0);
+  vec2 off1 = vec2(1.411764705882353) * direction;
+  vec2 off2 = vec2(3.2941176470588234) * direction;
+  vec2 off3 = vec2(5.176470588235294) * direction;
+  color += texture2D(image, uv) * 0.1964825501511404;
+  color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344;
+  color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344;
+  color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732;
+  color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732;
+  color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057;
+  color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057;
+  return color;
+}
+
+vec4 blur13Bilateral(sampler2D image, vec2 uv, float resolution, vec2 direction) {
+  vec4 color = vec4(0.0);
+  vec2 off1 = vec2(1.411764705882353) * direction;
+  vec2 off2 = vec2(3.2941176470588234) * direction;
+  vec2 off3 = vec2(5.176470588235294) * direction;
+
+  float compareDepth = abs(texture2D(depthSampler, uv).r);
+  float sampleDepth;
+  float weight;
+  float weightSum = 30.0;
+
+  color += texture2D(image, uv) * 30.0;
+
+  sampleDepth = abs(texture2D(depthSampler, uv + (off1 / resolution)).r);
+  weight = clamp(1.0 / ( 0.003 + abs(compareDepth - sampleDepth)), 0.0, 30.0);
+  weightSum +=  weight;
+  color += texture2D(image, uv + (off1 / resolution)) * weight;
+
+  sampleDepth = abs(texture2D(depthSampler, uv - (off1 / resolution)).r);
+  weight = clamp(1.0 / ( 0.003 + abs(compareDepth - sampleDepth)), 0.0, 30.0);
+  weightSum +=  weight;
+  color += texture2D(image, uv - (off1 / resolution)) * weight;
+
+  sampleDepth = abs(texture2D(depthSampler, uv + (off2 / resolution)).r);
+  weight = clamp(1.0 / ( 0.003 + abs(compareDepth - sampleDepth)), 0.0, 30.0);
+  weightSum += weight;
+  color += texture2D(image, uv + (off2 / resolution)) * weight;
+
+  sampleDepth = abs(texture2D(depthSampler, uv - (off2 / resolution)).r);
+  weight = clamp(1.0 / ( 0.003 + abs(compareDepth - sampleDepth)), 0.0, 30.0);
+  weightSum += weight;
+  color += texture2D(image, uv - (off2 / resolution)) * weight;
+
+  sampleDepth = abs(texture2D(depthSampler, uv + (off3 / resolution)).r);
+  weight = clamp(1.0 / ( 0.003 + abs(compareDepth - sampleDepth)), 0.0, 30.0);
+  weightSum += weight;
+  color += texture2D(image, uv + (off3 / resolution)) * weight;
+
+  sampleDepth = abs(texture2D(depthSampler, uv - (off3 / resolution)).r);
+  weight = clamp(1.0 / ( 0.003 + abs(compareDepth - sampleDepth)), 0.0, 30.0);
+  weightSum += weight;
+  color += texture2D(image, uv - (off3 / resolution)) * weight;
+
+  return color / weightSum;
+}
+
+void main()
+{
+	#if EXPENSIVE
+	float compareDepth = abs(texture2D(depthSampler, vUV).r);
+	float texelsize = 1.0 / outSize;
+	float result = 0.0;
+	float weightSum = 0.0;
+
+	for (int i = 0; i < SAMPLES; ++i)
+	{
+		#ifdef BILATERAL_BLUR_H
+		vec2 direction = vec2(1.0, 0.0);
+		vec2 sampleOffset = vec2(texelsize * samplerOffsets[i], 0.0);
+		#else
+		vec2 direction = vec2(0.0, 1.0);
+		vec2 sampleOffset = vec2(0.0, texelsize * samplerOffsets[i]);
+		#endif
+		vec2 samplePos = vUV + sampleOffset;
+
+		float sampleDepth = abs(texture2D(depthSampler, samplePos).r);
+		float weight = clamp(1.0 / ( 0.003 + abs(compareDepth - sampleDepth)), 0.0, 30000.0);
+
+		result += texture2D(textureSampler, samplePos).r * weight;
+		weightSum += weight;
+	}
+
+	result /= weightSum;
+	gl_FragColor.rgb = vec3(result);
+	gl_FragColor.a = 1.0;
+	#else
+	vec4 color;
+	#ifdef BILATERAL_BLUR_H
+	vec2 direction = vec2(1.0, 0.0);
+	color = blur13Bilateral(textureSampler, vUV, outSize, direction);
+	#else
+	vec2 direction = vec2(0.0, 1.0);
+	color = blur13Bilateral(textureSampler, vUV, outSize, direction);
+	#endif
+
+	gl_FragColor.rgb = vec3(color.r);
+	gl_FragColor.a = 1.0;
+	#endif
+}
+#endif

+ 166 - 0
src/babylon.engine.ts

@@ -2705,6 +2705,172 @@
             return texture;
         }
 
+        public createMultipleRenderTarget(size: any, options): WebGLTexture[] {
+            var generateMipMaps = false;
+            var generateDepthBuffer = true;
+            var generateStencilBuffer = false;
+            var generateDepthTexture = false;
+            var textureCount = 1;
+
+            var defaultType = Engine.TEXTURETYPE_UNSIGNED_INT;
+            var defaultSamplingMode = Texture.TRILINEAR_SAMPLINGMODE;
+
+            var types = [], samplingModes = [];
+
+            if (options !== undefined) {
+                generateMipMaps = options.generateMipMaps;
+                generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
+                generateStencilBuffer = options.generateStencilBuffer;
+                generateDepthTexture = options.generateDepthTexture;
+                textureCount = options.textureCount || 1;
+
+                if (options.types) {
+                    types = options.types;
+                }
+                if (options.samplingModes) {
+                    samplingModes = options.samplingModes;
+                }
+
+            }
+            var gl = this._gl;
+            // Create the framebuffer
+            var framebuffer = gl.createFramebuffer();
+            this.bindUnboundFramebuffer(framebuffer);
+
+            var colorRenderbuffer = gl.createRenderbuffer();
+            gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
+            gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, width, height);
+            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
+            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, colorRenderbuffer);
+            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, colorRenderbuffer);
+
+            var width = size.width || size;
+            var height = size.height || size;
+            
+            var textures = [];
+            var attachments = []
+
+            var depthStencilBuffer = this._setupFramebufferDepthAttachments(generateStencilBuffer, generateDepthBuffer, width, height);
+
+            for (var i = 0; i < textureCount; i++) {
+                var samplingMode = samplingModes[i] || defaultSamplingMode;
+                var type = types[i] || defaultType;
+
+                if (type === Engine.TEXTURETYPE_FLOAT && !this._caps.textureFloatLinearFiltering) {
+                    // if floating point linear (gl.FLOAT) then force to NEAREST_SAMPLINGMODE
+                    samplingMode = Texture.NEAREST_SAMPLINGMODE;
+                }
+                else if (type === Engine.TEXTURETYPE_HALF_FLOAT && !this._caps.textureHalfFloatLinearFiltering) {
+                    // if floating point linear (HALF_FLOAT) then force to NEAREST_SAMPLINGMODE
+                    samplingMode = Texture.NEAREST_SAMPLINGMODE;
+                }
+
+                var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
+                if (type === Engine.TEXTURETYPE_FLOAT && !this._caps.textureFloat) {
+                    type = Engine.TEXTURETYPE_UNSIGNED_INT;
+                    Tools.Warn("Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type");
+                }
+
+                var texture = gl.createTexture();
+                var attachment = gl["COLOR_ATTACHMENT" + i];
+                textures.push(texture);
+                attachments.push(attachment);
+
+                gl.activeTexture(gl["TEXTURE" + i]);
+                gl.bindTexture(gl.TEXTURE_2D, texture);
+
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filters.min);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+                gl.texImage2D(gl.TEXTURE_2D, 0, this._getRGBABufferInternalSizedFormat(type), width, height, 0, gl.RGBA, this._getWebGLTextureType(type), null);
+            
+                gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, attachment, gl.TEXTURE_2D, texture, 0);
+
+
+                if (generateMipMaps) {
+                    this._gl.generateMipmap(this._gl.TEXTURE_2D);
+                }
+
+                // Unbind
+                this._bindTextureDirectly(gl.TEXTURE_2D, null);
+
+                texture._framebuffer = framebuffer;
+                texture._depthStencilBuffer = depthStencilBuffer;
+                texture._baseWidth = width;
+                texture._baseHeight = height;
+                texture._width = width;
+                texture._height = height;
+                texture.isReady = true;
+                texture.samples = 1;
+                texture.generateMipMaps = generateMipMaps;
+                texture.references = 1;
+                texture.samplingMode = samplingMode;
+                texture.type = type;
+                texture._generateDepthBuffer = generateDepthBuffer;
+                texture._generateStencilBuffer = generateStencilBuffer;
+
+                this._loadedTexturesCache.push(texture);
+            }
+
+            if (generateDepthTexture) {
+                // Depth texture
+                var depthTexture = gl.createTexture();
+
+                gl.activeTexture(gl.TEXTURE0);
+                gl.bindTexture(gl.TEXTURE_2D, depthTexture);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+                gl.texImage2D(
+                    gl.TEXTURE_2D,
+                    0,
+                    gl.DEPTH_COMPONENT16,
+                    width,
+                    height,
+                    0,
+                    gl.DEPTH_COMPONENT,
+                    gl.UNSIGNED_SHORT,
+                    null
+                );
+
+                gl.framebufferTexture2D(
+                    gl.FRAMEBUFFER,
+                    gl.DEPTH_ATTACHMENT,
+                    gl.TEXTURE_2D,
+                    depthTexture,
+                    0
+                );
+
+                depthTexture._framebuffer = framebuffer;
+                depthTexture._baseWidth = width;
+                depthTexture._baseHeight = height;
+                depthTexture._width = width;
+                depthTexture._height = height;
+                depthTexture.isReady = true;
+                depthTexture.samples = 1;
+                depthTexture.generateMipMaps = generateMipMaps;
+                depthTexture.references = 1;
+                depthTexture.samplingMode = gl.NEAREST;
+                depthTexture._generateDepthBuffer = generateDepthBuffer;
+                depthTexture._generateStencilBuffer = generateStencilBuffer;
+
+                textures.push(depthTexture)
+                this._loadedTexturesCache.push(depthTexture);
+            }
+
+            gl.drawBuffers(attachments);
+            gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+            this.bindUnboundFramebuffer(null);
+
+            this.resetTextureCache();
+
+            return textures;
+        }
+
         private _setupFramebufferDepthAttachments(generateStencilBuffer: boolean, generateDepthBuffer: boolean, width: number, height: number, samples = 1): WebGLRenderbuffer {
             var depthStencilBuffer: WebGLRenderbuffer = null;
             var gl = this._gl;

+ 9 - 0
src/babylon.mixins.ts

@@ -50,6 +50,15 @@ interface WebGLRenderingContext {
     DRAW_FRAMEBUFFER: number;
     UNIFORM_BUFFER: number;
 
+    /* Multiple Render Targets */
+    drawBuffers(buffers: number[]): void;
+    readBuffer(src: number): void;
+
+
+    readonly COLOR_ATTACHMENT0: number;                             // 0x8CE1
+    readonly COLOR_ATTACHMENT1: number;                             // 0x8CE2
+    readonly COLOR_ATTACHMENT2: number;                             // 0x8CE3
+    readonly COLOR_ATTACHMENT3: number;                             // 0x8CE4
 }
 
 interface HTMLURL {

+ 28 - 0
src/babylon.scene.ts

@@ -711,6 +711,7 @@
         private _debugLayer: DebugLayer;
 
         private _depthRenderer: DepthRenderer;
+        private _geometryBufferRenderer: GeometryBufferRenderer;
 
         private _uniqueIdCounter = 0;
 
@@ -2939,6 +2940,11 @@
                 this._renderTargets.push(this._depthRenderer.getDepthMap());
             }
 
+            // Geometry renderer
+            if (this._geometryBufferRenderer) {
+                this._renderTargets.push(this._geometryBufferRenderer.getGBuffer());
+            }
+
             // RenderPipeline
             if (this._postProcessRenderPipelineManager) {
                 this._postProcessRenderPipelineManager.update();
@@ -3133,6 +3139,28 @@
             this._depthRenderer = null;
         }
 
+        public enableGeometryBufferRenderer(ratio: number = 1): GeometryBufferRenderer {
+            if (this._geometryBufferRenderer) {
+                return this._geometryBufferRenderer;
+            }
+
+            this._geometryBufferRenderer = new GeometryBufferRenderer(this, ratio);
+            if (!this._geometryBufferRenderer.isSupported) {
+                this._geometryBufferRenderer = null;
+            }
+
+            return this._geometryBufferRenderer;
+        }
+
+        public disableGeometryBufferRenderer(): void {
+            if (!this._geometryBufferRenderer) {
+                return;
+            }
+
+            this._geometryBufferRenderer.dispose();
+            this._geometryBufferRenderer = null;
+        }
+
         public freezeMaterials(): void {
             for (var i = 0; i < this.materials.length; i++) {
                 this.materials[i].freeze();