Explorar o código

HDR rendering pipeline

David Catuhe %!s(int64=10) %!d(string=hai) anos
pai
achega
9108bd33e8

+ 2 - 2
Babylon/Animations/babylon.animation.js

@@ -14,7 +14,7 @@ var BABYLON;
             this.dataType = dataType;
             this.loopMode = loopMode === undefined ? Animation.ANIMATIONLOOPMODE_CYCLE : loopMode;
         }
-        Animation.CreateAndStartAnimation = function (name, mesh, tartgetProperty, framePerSecond, totalFrame, from, to, loopMode) {
+        Animation.CreateAndStartAnimation = function (name, mesh, targetProperty, framePerSecond, totalFrame, from, to, loopMode) {
             var dataType = undefined;
             if (!isNaN(parseFloat(from)) && isFinite(from)) {
                 dataType = Animation.ANIMATIONTYPE_FLOAT;
@@ -34,7 +34,7 @@ var BABYLON;
             if (dataType == undefined) {
                 return null;
             }
-            var animation = new Animation(name, tartgetProperty, framePerSecond, dataType, loopMode);
+            var animation = new Animation(name, targetProperty, framePerSecond, dataType, loopMode);
             var keys = [];
             keys.push({ frame: 0, value: from });
             keys.push({ frame: totalFrame, value: to });

+ 2 - 2
Babylon/Animations/babylon.animation.ts

@@ -10,7 +10,7 @@
         public targetPropertyPath: string[];
         public currentFrame: number;
 
-        public static CreateAndStartAnimation(name: string, mesh: AbstractMesh, tartgetProperty: string,
+        public static CreateAndStartAnimation(name: string, mesh: AbstractMesh, targetProperty: string,
             framePerSecond: number, totalFrame: number,
             from: any, to: any, loopMode?: number) {
 
@@ -32,7 +32,7 @@
                 return null;
             }
 
-            var animation = new Animation(name, tartgetProperty, framePerSecond, dataType, loopMode);
+            var animation = new Animation(name, targetProperty, framePerSecond, dataType, loopMode);
 
             var keys = [];
             keys.push({ frame: 0, value: from });

+ 1 - 45
Babylon/Math/babylon.math.ts

@@ -295,13 +295,6 @@
             return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
         }
 
-        public addInPlace(otherVector: Vector2): Vector2 {
-            this.x += otherVector.x;
-            this.y += otherVector.y;
-
-            return this;
-        }
-
         public subtract(otherVector: Vector2): Vector2 {
             return new Vector2(this.x - otherVector.x, this.y - otherVector.y);
         }
@@ -346,10 +339,6 @@
             return this;
         }
 
-        public divideByFloats(x: number, y: number): Vector2 {
-            return new Vector2(this.x / x, this.y / y);
-        }
-
         public negate(): Vector2 {
             return new Vector2(-this.x, -this.y);
         }
@@ -570,18 +559,6 @@
             return this;
         }
 
-        public addFromFloats(x: number, y: number, z: number): Vector3 {
-            return new Vector3(this.x + x, this.y + y, this.z + z);
-        }
-
-        public addFromFloatsToRef(x: number, y: number, z: number, result: Vector3): Vector3 {
-            result.x = this.x + x;
-            result.y = this.y + y;
-            result.z = this.z + z;
-
-            return this;
-        }
-
         public subtractInPlace(otherVector: Vector3): Vector3 {
             this.x -= otherVector.x;
             this.y -= otherVector.y;
@@ -683,10 +660,6 @@
             return this;
         }
 
-        public divideByFloats(x: number, y: number, z: number): Vector3 {
-            return new Vector3(this.x / x, this.y / y, this.z / z);
-        }
-
         public MinimizeInPlace(other: Vector3): Vector3 {
             if (other.x < this.x) this.x = other.x;
             if (other.y < this.y) this.y = other.y;
@@ -1197,19 +1170,6 @@
             return this;
         }
 
-        public addFromFloats(x: number, y: number, z: number, w: number): Vector4 {
-            return new Vector4(this.x + x, this.y + y, this.z + z, this.w + w);
-        }
-
-        public addFromFloatsToRef(x: number, y: number, z: number, w: number, result: Vector4): Vector4 {
-            result.x = this.x + x;
-            result.y = this.y + y;
-            result.z = this.z + z;
-            result.w = this.w + w;
-
-            return this;
-        }
-
         public subtractInPlace(otherVector: Vector4): Vector4 {
             this.x -= otherVector.x;
             this.y -= otherVector.y;
@@ -1323,10 +1283,6 @@
             return this;
         }
 
-        public divideByFloats(x: number, y: number, z: number, w: number): Vector4 {
-            return new Vector4(this.x / x, this.y / y, this.z / z, this.w / w);
-        }
-
         public MinimizeInPlace(other: Vector4): Vector4 {
             if (other.x < this.x) this.x = other.x;
             if (other.y < this.y) this.y = other.y;
@@ -3743,4 +3699,4 @@
             SIMDHelper._isEnabled = true;
         }
     }
-}
+}

+ 355 - 0
Babylon/PostProcess/babylon.hdrRenderingPipeline.js

@@ -0,0 +1,355 @@
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    __.prototype = b.prototype;
+    d.prototype = new __();
+};
+var BABYLON;
+(function (BABYLON) {
+    var HDRRenderingPipeline = (function (_super) {
+        __extends(HDRRenderingPipeline, _super);
+        /**
+         * @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 (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
+         * @param {BABYLON.PostProcess} originalPostProcess - the custom original color post-process. Must be "reusable". Can be null.
+         * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
+         */
+        function HDRRenderingPipeline(name, scene, ratio, originalPostProcess, cameras) {
+            var _this = this;
+            if (originalPostProcess === void 0) { originalPostProcess = null; }
+            _super.call(this, scene.getEngine(), name);
+            /**
+            * Public members
+            */
+            // Gaussian Blur
+            /**
+            * Gaussian blur coefficient
+            * @type {number}
+            */
+            this.gaussCoeff = 0.3;
+            /**
+            * Gaussian blur mean
+            * @type {number}
+            */
+            this.gaussMean = 1.0;
+            /**
+            * Gaussian blur standard derivation
+            * @type {number}
+            */
+            this.gaussStandDev = 0.8;
+            // HDR
+            /**
+            * Exposure, controls the overall intensity of the pipeline
+            * @type {number}
+            */
+            this.exposure = 1.0;
+            /**
+            * Minimum luminance that the post-process can output. Luminance is >= 0
+            * @type {number}
+            */
+            this.minimumLuminance = 1.0;
+            /**
+            * Maximum luminance that the post-process can output. Must be suprerior to minimumLuminance
+            * @type {number}
+            */
+            this.maximumLuminance = 1e20;
+            /**
+            * Increase rate for luminance: eye adaptation speed to bright
+            * @type {number}
+            */
+            this.luminanceIncreaserate = 0.5;
+            /**
+            * Decrease rate for luminance: eye adaptation speed to dark
+            * @type {number}
+            */
+            this.luminanceDecreaseRate = 0.5;
+            // Bright pass
+            /**
+            * Minimum luminance needed to compute HDR
+            * @type {number}
+            */
+            this.brightThreshold = 0.8;
+            // Global
+            this._needUpdate = true;
+            // Bright pass
+            this._createBrightPassPostProcess(scene, ratio);
+            // Down sample X4
+            this._createDownSampleX4PostProcess(scene, ratio);
+            // Create gaussian blur post-processes
+            this._createGaussianBlurPostProcess(scene, ratio);
+            // Texture adder
+            this._createTextureAdderPostProcess(scene, ratio);
+            // Luminance generator
+            this._createLuminanceGeneratorPostProcess(scene);
+            // HDR
+            this._createHDRPostProcess(scene, ratio);
+            // Pass postprocess
+            if (originalPostProcess === null) {
+                this._originalPostProcess = new BABYLON.PassPostProcess("hdr", ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            }
+            else {
+                this._originalPostProcess = originalPostProcess;
+            }
+            // Configure pipeline
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRPassPostProcess", function () { return _this._originalPostProcess; }, true));
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRBrightPass", function () { return _this._brightPassPostProcess; }, true));
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRDownSampleX4", function () { return _this._downSampleX4PostProcess; }, true));
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRGaussianBlurH", function () { return _this._guassianBlurHPostProcess; }, true));
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRGaussianBlurV", function () { return _this._guassianBlurVPostProcess; }, true));
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRTextureAdder", function () { return _this._textureAdderPostProcess; }, true));
+            var addDownSamplerPostProcess = function (id) {
+                _this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDRDownSampler" + id, function () { return _this._downSamplePostProcesses[id]; }, true));
+            };
+            for (var i = HDRRenderingPipeline.LUM_STEPS - 1; i >= 0; i--) {
+                addDownSamplerPostProcess(i);
+            }
+            this.addEffect(new BABYLON.PostProcessRenderEffect(scene.getEngine(), "HDR", function () { return _this._hdrPostProcess; }, true));
+            // Finish
+            scene.postProcessRenderPipelineManager.addPipeline(this);
+            this.update();
+        }
+        /**
+        * Tells the pipeline to update its post-processes
+        */
+        HDRRenderingPipeline.prototype.update = function () {
+            this._needUpdate = true;
+        };
+        /**
+        * Returns the current calculated luminance
+        */
+        HDRRenderingPipeline.prototype.getCurrentLuminance = function () {
+            return this._hdrCurrentLuminance;
+        };
+        /**
+        * Returns the currently drawn luminance
+        */
+        HDRRenderingPipeline.prototype.getOutputLuminance = function () {
+            return this._hdrOutputLuminance;
+        };
+        /**
+        * Creates the HDR post-process and computes the luminance adaptation
+        */
+        HDRRenderingPipeline.prototype._createHDRPostProcess = function (scene, ratio) {
+            var _this = this;
+            var hdrLastLuminance = 0.0;
+            this._hdrOutputLuminance = -1.0;
+            this._hdrCurrentLuminance = 1.0;
+            this._hdrPostProcess = new BABYLON.PostProcess("hdr", "hdr", ["exposure", "avgLuminance"], ["otherSampler"], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define HDR");
+            this._hdrPostProcess.onApply = function (effect) {
+                if (_this._hdrOutputLuminance < 0.0) {
+                    _this._hdrOutputLuminance = _this._hdrCurrentLuminance;
+                }
+                else {
+                    var dt = (hdrLastLuminance - (hdrLastLuminance + scene.getEngine().getDeltaTime())) / 1000.0;
+                    if (_this._hdrCurrentLuminance < _this._hdrOutputLuminance + _this.luminanceDecreaseRate * dt) {
+                        _this._hdrOutputLuminance += _this.luminanceDecreaseRate * dt;
+                    }
+                    else if (_this._hdrCurrentLuminance > _this._hdrOutputLuminance - _this.luminanceIncreaserate * dt) {
+                        _this._hdrOutputLuminance -= _this.luminanceIncreaserate * dt;
+                    }
+                    else {
+                        _this._hdrOutputLuminance = _this._hdrCurrentLuminance;
+                    }
+                }
+                _this._hdrOutputLuminance = BABYLON.Tools.Clamp(_this._hdrOutputLuminance, _this.minimumLuminance, _this.maximumLuminance);
+                hdrLastLuminance += scene.getEngine().getDeltaTime();
+                effect.setTextureFromPostProcess("textureSampler", _this._originalPostProcess);
+                effect.setTextureFromPostProcess("otherSampler", _this._textureAdderPostProcess);
+                effect.setFloat("exposure", _this.exposure);
+                effect.setFloat("avgLuminance", _this._hdrOutputLuminance);
+                _this._needUpdate = false;
+            };
+        };
+        /**
+        * Texture Adder post-process
+        */
+        HDRRenderingPipeline.prototype._createTextureAdderPostProcess = function (scene, ratio) {
+            var _this = this;
+            this._textureAdderPostProcess = new BABYLON.PostProcess("hdr", "hdr", [], ["otherSampler"], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define TEXTURE_ADDER");
+            this._textureAdderPostProcess.onApply = function (effect) {
+                effect.setTextureFromPostProcess("otherSampler", _this._originalPostProcess);
+            };
+        };
+        /**
+        * Down sample X4 post-process
+        */
+        HDRRenderingPipeline.prototype._createDownSampleX4PostProcess = function (scene, ratio) {
+            var _this = this;
+            var downSampleX4Offsets = new Array(32);
+            this._downSampleX4PostProcess = new BABYLON.PostProcess("hdr", "hdr", ["dsOffsets"], [], ratio / 4, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define DOWN_SAMPLE_X4");
+            this._downSampleX4PostProcess.onApply = function (effect) {
+                if (_this._needUpdate) {
+                    var id = 0;
+                    for (var i = -2; i < 2; i++) {
+                        for (var j = -2; j < 2; j++) {
+                            downSampleX4Offsets[id] = (i + 0.5) * (1.0 / _this._downSampleX4PostProcess.width);
+                            downSampleX4Offsets[id + 1] = (j + 0.5) * (1.0 / _this._downSampleX4PostProcess.height);
+                            id += 2;
+                        }
+                    }
+                }
+                effect.setArray2("dsOffsets", downSampleX4Offsets);
+            };
+        };
+        /**
+        * Bright pass post-process
+        */
+        HDRRenderingPipeline.prototype._createBrightPassPostProcess = function (scene, ratio) {
+            var _this = this;
+            var brightOffsets = new Array(8);
+            var brightPassCallback = function (effect) {
+                if (_this._needUpdate) {
+                    var sU = (1.0 / _this._brightPassPostProcess.width);
+                    var sV = (1.0 / _this._brightPassPostProcess.height);
+                    brightOffsets[0] = -0.5 * sU;
+                    brightOffsets[1] = 0.5 * sV;
+                    brightOffsets[2] = 0.5 * sU;
+                    brightOffsets[3] = 0.5 * sV;
+                    brightOffsets[4] = -0.5 * sU;
+                    brightOffsets[5] = -0.5 * sV;
+                    brightOffsets[6] = 0.5 * sU;
+                    brightOffsets[7] = -0.5 * sV;
+                }
+                effect.setArray2("dsOffsets", brightOffsets);
+                effect.setFloat("brightThreshold", _this.brightThreshold);
+            };
+            this._brightPassPostProcess = new BABYLON.PostProcess("hdr", "hdr", ["dsOffsets", "brightThreshold"], [], ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define BRIGHT_PASS");
+            this._brightPassPostProcess.onApply = brightPassCallback;
+        };
+        /**
+        * Luminance generator. Creates the luminance post-process and down sample post-processes
+        */
+        HDRRenderingPipeline.prototype._createLuminanceGeneratorPostProcess = function (scene) {
+            var _this = this;
+            var lumSteps = HDRRenderingPipeline.LUM_STEPS;
+            var luminanceOffsets = new Array(8);
+            var downSampleOffsets = new Array(18);
+            var halfDestPixelSize;
+            this._downSamplePostProcesses = new Array(lumSteps);
+            // Utils for luminance
+            var luminanceUpdateSourceOffsets = function (width, height) {
+                var sU = (1.0 / width);
+                var sV = (1.0 / height);
+                luminanceOffsets[0] = -0.5 * sU;
+                luminanceOffsets[1] = 0.5 * sV;
+                luminanceOffsets[2] = 0.5 * sU;
+                luminanceOffsets[3] = 0.5 * sV;
+                luminanceOffsets[4] = -0.5 * sU;
+                luminanceOffsets[5] = -0.5 * sV;
+                luminanceOffsets[6] = 0.5 * sU;
+                luminanceOffsets[7] = -0.5 * sV;
+            };
+            var luminanceUpdateDestOffsets = function (width, height) {
+                var id = 0;
+                for (var x = -1; x < 2; x++) {
+                    for (var y = -1; y < 2; y++) {
+                        downSampleOffsets[id] = (x) / width;
+                        downSampleOffsets[id + 1] = (y) / height;
+                        id += 2;
+                    }
+                }
+            };
+            // Luminance callback
+            var luminanceCallback = function (effect) {
+                if (_this._needUpdate) {
+                    luminanceUpdateSourceOffsets(_this._textureAdderPostProcess.width, _this._textureAdderPostProcess.height);
+                }
+                effect.setTextureFromPostProcess("textureSampler", _this._textureAdderPostProcess);
+                effect.setArray2("lumOffsets", luminanceOffsets);
+            };
+            // Down sample callbacks
+            var downSampleCallback = function (indice) {
+                var i = indice;
+                return function (effect) {
+                    luminanceUpdateSourceOffsets(_this._downSamplePostProcesses[i].width, _this._downSamplePostProcesses[i].height);
+                    luminanceUpdateDestOffsets(_this._downSamplePostProcesses[i].width, _this._downSamplePostProcesses[i].height);
+                    halfDestPixelSize = 0.5 / _this._downSamplePostProcesses[i].width;
+                    effect.setTextureFromPostProcess("textureSampler", _this._downSamplePostProcesses[i + 1]);
+                    effect.setFloat("halfDestPixelSize", halfDestPixelSize);
+                    effect.setArray2("dsOffsets", downSampleOffsets);
+                };
+            };
+            var downSampleAfterRenderCallback = function (effect) {
+                // Unpack result
+                var pixel = scene.getEngine().readPixels(0, 0, 1, 1);
+                var bit_shift = new BABYLON.Vector4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
+                _this._hdrCurrentLuminance = (pixel[0] * bit_shift.x + pixel[1] * bit_shift.y + pixel[2] * bit_shift.z + pixel[3] * bit_shift.w) / 100.0;
+            };
+            // Create luminance post-process
+            var ratio = { width: Math.pow(3, lumSteps - 1), height: Math.pow(3, lumSteps - 1) };
+            this._downSamplePostProcesses[lumSteps - 1] = new BABYLON.PostProcess("hdr", "hdr", ["lumOffsets"], [], ratio, null, BABYLON.Texture.NEAREST_SAMPLINGMODE, scene.getEngine(), false, "#define LUMINANCE_GENERATOR", BABYLON.Engine.TEXTURETYPE_FLOAT);
+            this._downSamplePostProcesses[lumSteps - 1].onApply = luminanceCallback;
+            // Create down sample post-processes
+            for (var i = lumSteps - 2; i >= 0; i--) {
+                var length = Math.pow(3, i);
+                ratio = { width: length, height: length };
+                var defines = "#define DOWN_SAMPLE\n";
+                if (i === 0) {
+                    defines += "#define FINAL_DOWN_SAMPLE\n"; // To pack the result
+                }
+                this._downSamplePostProcesses[i] = new BABYLON.PostProcess("hdr", "hdr", ["dsOffsets", "halfDestPixelSize"], [], ratio, null, BABYLON.Texture.NEAREST_SAMPLINGMODE, scene.getEngine(), false, defines, BABYLON.Engine.TEXTURETYPE_FLOAT);
+                this._downSamplePostProcesses[i].onApply = downSampleCallback(i);
+                if (i === 0) {
+                    this._downSamplePostProcesses[i].onAfterRender = downSampleAfterRenderCallback;
+                }
+            }
+        };
+        /**
+        * Gaussian blur post-processes. Horizontal and Vertical
+        */
+        HDRRenderingPipeline.prototype._createGaussianBlurPostProcess = function (scene, ratio) {
+            var _this = this;
+            var blurOffsetsW = new Array(9);
+            var blurOffsetsH = new Array(9);
+            var blurWeights = new Array(9);
+            var uniforms = ["blurOffsets", "blurWeights"];
+            // Utils for gaussian blur
+            var calculateBlurOffsets = function (height) {
+                var lastOutputDimensions = {
+                    width: scene.getEngine().getRenderWidth() * (ratio / 4),
+                    height: scene.getEngine().getRenderHeight() * (ratio / 4)
+                };
+                for (var i = 0; i < 9; i++) {
+                    var value = (i - 4.0) * (1.0 / (height === true ? lastOutputDimensions.height : lastOutputDimensions.width));
+                    if (height) {
+                        blurOffsetsH[i] = value;
+                    }
+                    else {
+                        blurOffsetsW[i] = value;
+                    }
+                }
+            };
+            var calculateWeights = function () {
+                var x = 0.0;
+                for (var i = 0; i < 9; i++) {
+                    x = (i - 4.0) / 4.0;
+                    blurWeights[i] = _this.gaussCoeff * (1.0 / Math.sqrt(2.0 * Math.PI * _this.gaussStandDev * _this.gaussStandDev)) * Math.exp((-((x - _this.gaussMean) * (x - _this.gaussMean))) / (2.0 * _this.gaussStandDev * _this.gaussStandDev));
+                }
+            };
+            // Callback
+            var gaussianBlurCallback = function (height) {
+                return function (effect) {
+                    if (_this._needUpdate) {
+                        calculateWeights();
+                        calculateBlurOffsets(height);
+                    }
+                    effect.setArray("blurOffsets", height ? blurOffsetsH : blurOffsetsW);
+                    effect.setArray("blurWeights", blurWeights);
+                };
+            };
+            // Create horizontal gaussian blur post-processes
+            this._guassianBlurHPostProcess = new BABYLON.PostProcess("hdr", "hdr", uniforms, [], ratio / 4, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_H");
+            this._guassianBlurHPostProcess.onApply = gaussianBlurCallback(false);
+            // Create vertical gaussian blur post-process
+            this._guassianBlurVPostProcess = new BABYLON.PostProcess("hdr", "hdr", uniforms, [], ratio / 4, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_V");
+            this._guassianBlurVPostProcess.onApply = gaussianBlurCallback(true);
+        };
+        // Luminance generator
+        HDRRenderingPipeline.LUM_STEPS = 6;
+        return HDRRenderingPipeline;
+    })(BABYLON.PostProcessRenderPipeline);
+    BABYLON.HDRRenderingPipeline = HDRRenderingPipeline;
+})(BABYLON || (BABYLON = {}));
+//# sourceMappingURL=babylon.hdrRenderingPipeline.js.map

+ 419 - 0
Babylon/PostProcess/babylon.hdrRenderingPipeline.ts

@@ -0,0 +1,419 @@
+module BABYLON {
+    export class HDRRenderingPipeline extends PostProcessRenderPipeline {
+
+        /**
+        * Public members
+        */
+        
+        // Gaussian Blur
+        /**
+        * Gaussian blur coefficient
+        * @type {number}
+        */
+        public gaussCoeff: number = 0.3;
+        /**
+        * Gaussian blur mean
+        * @type {number}
+        */
+        public gaussMean: number = 1.0;
+        /**
+        * Gaussian blur standard derivation
+        * @type {number}
+        */
+        public gaussStandDev: number = 0.8;
+
+        // HDR
+        /**
+        * Exposure, controls the overall intensity of the pipeline
+        * @type {number}
+        */
+        public exposure: number = 1.0;
+        /**
+        * Minimum luminance that the post-process can output. Luminance is >= 0
+        * @type {number}
+        */
+        public minimumLuminance: number = 1.0;
+        /**
+        * Maximum luminance that the post-process can output. Must be suprerior to minimumLuminance
+        * @type {number}
+        */
+        public maximumLuminance: number = 1e20;
+        /**
+        * Increase rate for luminance: eye adaptation speed to bright
+        * @type {number}
+        */
+        public luminanceIncreaserate: number = 0.5;
+        /**
+        * Decrease rate for luminance: eye adaptation speed to dark
+        * @type {number}
+        */
+        public luminanceDecreaseRate: number = 0.5;
+
+        // Bright pass
+        /**
+        * Minimum luminance needed to compute HDR
+        * @type {number}
+        */
+        public brightThreshold: number = 0.8;
+
+        /**
+        * Private members
+        */
+        // Gaussian blur
+        private _guassianBlurHPostProcess: PostProcess;
+        private _guassianBlurVPostProcess: PostProcess;
+
+        // Bright pass
+        private _brightPassPostProcess: PostProcess;
+
+        // Texture adder
+        private _textureAdderPostProcess: PostProcess;
+
+        // Down Sampling
+        private _downSampleX4PostProcess: PostProcess;
+
+        // Original Post-process
+        private _originalPostProcess: PostProcess;
+
+        // HDR
+        private _hdrPostProcess: PostProcess;
+        private _hdrCurrentLuminance: number;
+        private _hdrOutputLuminance: number;
+
+        // Luminance generator
+        public static LUM_STEPS: number = 6;
+        private _downSamplePostProcesses: Array<PostProcess>;
+
+        // Global
+        private _needUpdate: boolean = true;
+
+        /**
+         * @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 (0.5 means that your postprocess will have a width = canvas.width 0.5 and a height = canvas.height 0.5)
+         * @param {BABYLON.PostProcess} originalPostProcess - the custom original color post-process. Must be "reusable". Can be null.
+         * @param {BABYLON.Camera[]} cameras - The array of cameras that the rendering pipeline will be attached to
+         */
+        constructor(name: string, scene: Scene, ratio: number, originalPostProcess: PostProcess = null, cameras?: Camera[]) {
+            super(scene.getEngine(), name);
+
+            // Bright pass
+            this._createBrightPassPostProcess(scene, ratio);
+
+            // Down sample X4
+            this._createDownSampleX4PostProcess(scene, ratio);
+
+            // Create gaussian blur post-processes
+            this._createGaussianBlurPostProcess(scene, ratio);
+
+            // Texture adder
+            this._createTextureAdderPostProcess(scene, ratio);
+
+            // Luminance generator
+            this._createLuminanceGeneratorPostProcess(scene);
+
+            // HDR
+            this._createHDRPostProcess(scene, ratio);
+
+            // Pass postprocess
+            if (originalPostProcess === null) {
+                this._originalPostProcess = new PassPostProcess("hdr", ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            } else {
+                this._originalPostProcess = originalPostProcess;
+            }
+
+            // Configure pipeline
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRPassPostProcess", () => { return this._originalPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBrightPass", () => { return this._brightPassPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRDownSampleX4", () => { return this._downSampleX4PostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRGaussianBlurH", () => { return this._guassianBlurHPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRGaussianBlurV", () => { return this._guassianBlurVPostProcess; }, true));
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRTextureAdder", () => { return this._textureAdderPostProcess; }, true));
+
+            var addDownSamplerPostProcess = (id: number) => {
+                this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRDownSampler" + id, () => { return this._downSamplePostProcesses[id]; }, true));
+            };
+            for (var i = HDRRenderingPipeline.LUM_STEPS - 1; i >= 0; i--) {
+                addDownSamplerPostProcess(i);
+            }
+
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDR", () => { return this._hdrPostProcess; }, true));
+
+            // Finish
+            scene.postProcessRenderPipelineManager.addPipeline(this);
+            this.update();
+        }
+
+        /**
+        * Tells the pipeline to update its post-processes
+        */
+        public update(): void {
+            this._needUpdate = true;
+        }
+
+        /**
+        * Returns the current calculated luminance
+        */
+        public getCurrentLuminance(): number {
+            return this._hdrCurrentLuminance;
+        }
+
+        /**
+        * Returns the currently drawn luminance
+        */
+        public getOutputLuminance(): number {
+            return this._hdrOutputLuminance;
+        }
+
+        /**
+        * Creates the HDR post-process and computes the luminance adaptation
+        */
+        private _createHDRPostProcess(scene: Scene, ratio: number): void {
+            var hdrLastLuminance = 0.0;
+            this._hdrOutputLuminance = -1.0;
+            this._hdrCurrentLuminance = 1.0;
+            this._hdrPostProcess = new PostProcess("hdr", "hdr", ["exposure", "avgLuminance"], ["otherSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define HDR");
+
+            this._hdrPostProcess.onApply = (effect: Effect) => {
+                if (this._hdrOutputLuminance < 0.0) {
+                    this._hdrOutputLuminance = this._hdrCurrentLuminance;
+                }
+                else {
+                    var dt = (hdrLastLuminance - (hdrLastLuminance + scene.getEngine().getDeltaTime())) / 1000.0;
+
+                    if (this._hdrCurrentLuminance < this._hdrOutputLuminance + this.luminanceDecreaseRate * dt) {
+                        this._hdrOutputLuminance += this.luminanceDecreaseRate * dt;
+                    }
+                    else if (this._hdrCurrentLuminance > this._hdrOutputLuminance - this.luminanceIncreaserate * dt) {
+                        this._hdrOutputLuminance -= this.luminanceIncreaserate * dt;
+                    }
+                    else {
+                        this._hdrOutputLuminance = this._hdrCurrentLuminance;
+                    }
+                }
+
+                this._hdrOutputLuminance = Tools.Clamp(this._hdrOutputLuminance, this.minimumLuminance, this.maximumLuminance);
+                hdrLastLuminance += scene.getEngine().getDeltaTime();
+
+                effect.setTextureFromPostProcess("textureSampler", this._originalPostProcess);
+                effect.setTextureFromPostProcess("otherSampler", this._textureAdderPostProcess);
+                effect.setFloat("exposure", this.exposure);
+                effect.setFloat("avgLuminance", this._hdrOutputLuminance);
+
+                this._needUpdate = false;
+            };
+
+        }
+
+        /**
+        * Texture Adder post-process
+        */
+        private _createTextureAdderPostProcess(scene: Scene, ratio: number): void {
+            this._textureAdderPostProcess = new PostProcess("hdr", "hdr", [], ["otherSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define TEXTURE_ADDER");
+
+            this._textureAdderPostProcess.onApply = (effect: Effect) => {
+                effect.setTextureFromPostProcess("otherSampler", this._originalPostProcess);
+            };
+        }
+
+        /**
+        * Down sample X4 post-process
+        */
+        private _createDownSampleX4PostProcess(scene: Scene, ratio: number): void {
+            var downSampleX4Offsets = new Array<number>(32);
+            this._downSampleX4PostProcess = new PostProcess("hdr", "hdr", ["dsOffsets"], [], ratio / 4, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define DOWN_SAMPLE_X4");
+
+            this._downSampleX4PostProcess.onApply = (effect: Effect) => {
+                if (this._needUpdate) {
+                    var id = 0;
+                    for (var i = -2; i < 2; i++) {
+                        for (var j = -2; j < 2; j++) {
+                            downSampleX4Offsets[id] = (i + 0.5) * (1.0 / this._downSampleX4PostProcess.width);
+                            downSampleX4Offsets[id + 1] = (j + 0.5) * (1.0 / this._downSampleX4PostProcess.height);
+                            id += 2;
+                        }
+                    }
+                }
+
+                effect.setArray2("dsOffsets", downSampleX4Offsets);
+            };
+        }
+
+        /**
+        * Bright pass post-process
+        */
+        private _createBrightPassPostProcess(scene: Scene, ratio: number): void {
+            var brightOffsets = new Array<number>(8);
+
+            var brightPassCallback = (effect: Effect) => {
+                if (this._needUpdate) {
+                    var sU = (1.0 / this._brightPassPostProcess.width);
+                    var sV = (1.0 / this._brightPassPostProcess.height);
+
+                    brightOffsets[0] = -0.5 * sU;
+                    brightOffsets[1] = 0.5 * sV;
+                    brightOffsets[2] = 0.5 * sU;
+                    brightOffsets[3] = 0.5 * sV;
+                    brightOffsets[4] = -0.5 * sU;
+                    brightOffsets[5] = -0.5 * sV;
+                    brightOffsets[6] = 0.5 * sU;
+                    brightOffsets[7] = -0.5 * sV;
+                }
+
+                effect.setArray2("dsOffsets", brightOffsets);
+                effect.setFloat("brightThreshold", this.brightThreshold);
+            };
+
+            this._brightPassPostProcess = new PostProcess("hdr", "hdr", ["dsOffsets", "brightThreshold"], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define BRIGHT_PASS");
+            this._brightPassPostProcess.onApply = brightPassCallback;
+        }
+
+        /**
+        * Luminance generator. Creates the luminance post-process and down sample post-processes
+        */
+        private _createLuminanceGeneratorPostProcess(scene: Scene): void {
+            var lumSteps: number = HDRRenderingPipeline.LUM_STEPS;
+            var luminanceOffsets = new Array<number>(8);
+            var downSampleOffsets = new Array<number>(18);
+            var halfDestPixelSize: number;
+            this._downSamplePostProcesses = new Array<PostProcess>(lumSteps);
+
+            // Utils for luminance
+            var luminanceUpdateSourceOffsets = (width: number, height: number) => {
+                var sU = (1.0 / width);
+                var sV = (1.0 / height);
+
+                luminanceOffsets[0] = -0.5 * sU;
+                luminanceOffsets[1] = 0.5 * sV;
+                luminanceOffsets[2] = 0.5 * sU;
+                luminanceOffsets[3] = 0.5 * sV;
+                luminanceOffsets[4] = -0.5 * sU;
+                luminanceOffsets[5] = -0.5 * sV;
+                luminanceOffsets[6] = 0.5 * sU;
+                luminanceOffsets[7] = -0.5 * sV;
+            };
+
+            var luminanceUpdateDestOffsets = (width: number, height: number) => {
+                var id = 0;
+                for (var x = -1; x < 2; x++) {
+                    for (var y = -1; y < 2; y++) {
+                        downSampleOffsets[id] = (x) / width;
+                        downSampleOffsets[id + 1] = (y) / height;
+                        id += 2;
+                    }
+                }
+            };
+
+            // Luminance callback
+            var luminanceCallback = (effect: Effect) => {
+                if (this._needUpdate) {
+                    luminanceUpdateSourceOffsets(this._textureAdderPostProcess.width, this._textureAdderPostProcess.height);
+                }
+
+                effect.setTextureFromPostProcess("textureSampler", this._textureAdderPostProcess);
+                effect.setArray2("lumOffsets", luminanceOffsets);
+            }
+
+            // Down sample callbacks
+            var downSampleCallback = (indice: number) => {
+                var i = indice;
+                return (effect: Effect) => {
+                    luminanceUpdateSourceOffsets(this._downSamplePostProcesses[i].width, this._downSamplePostProcesses[i].height);
+                    luminanceUpdateDestOffsets(this._downSamplePostProcesses[i].width, this._downSamplePostProcesses[i].height);
+                    halfDestPixelSize = 0.5 / this._downSamplePostProcesses[i].width;
+
+                    effect.setTextureFromPostProcess("textureSampler", this._downSamplePostProcesses[i + 1]);
+                    effect.setFloat("halfDestPixelSize", halfDestPixelSize);
+                    effect.setArray2("dsOffsets", downSampleOffsets);
+                }
+            };
+
+            var downSampleAfterRenderCallback = (effect: Effect) => {
+                // Unpack result
+                var pixel = scene.getEngine().readPixels(0, 0, 1, 1);
+                var bit_shift = new Vector4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
+                this._hdrCurrentLuminance = (pixel[0] * bit_shift.x + pixel[1] * bit_shift.y + pixel[2] * bit_shift.z + pixel[3] * bit_shift.w) / 100.0;
+            };
+
+            // Create luminance post-process
+            var ratio = { width: Math.pow(3, lumSteps - 1), height: Math.pow(3, lumSteps - 1) };
+            this._downSamplePostProcesses[lumSteps - 1] = new PostProcess("hdr", "hdr", ["lumOffsets"], [], ratio, null, Texture.NEAREST_SAMPLINGMODE, scene.getEngine(), false, "#define LUMINANCE_GENERATOR", Engine.TEXTURETYPE_FLOAT);
+            this._downSamplePostProcesses[lumSteps - 1].onApply = luminanceCallback;
+
+            // Create down sample post-processes
+            for (var i = lumSteps - 2; i >= 0; i--) {
+                var length = Math.pow(3, i);
+                ratio = { width: length, height: length };
+
+                var defines = "#define DOWN_SAMPLE\n";
+                if (i === 0) {
+                    defines += "#define FINAL_DOWN_SAMPLE\n"; // To pack the result
+                }
+
+                this._downSamplePostProcesses[i] = new PostProcess("hdr", "hdr", ["dsOffsets", "halfDestPixelSize"], [], ratio, null, Texture.NEAREST_SAMPLINGMODE, scene.getEngine(), false, defines, Engine.TEXTURETYPE_FLOAT);
+                this._downSamplePostProcesses[i].onApply = downSampleCallback(i);
+
+                if (i === 0) {
+                    this._downSamplePostProcesses[i].onAfterRender = downSampleAfterRenderCallback;
+                }
+            }
+        }
+
+        /**
+        * Gaussian blur post-processes. Horizontal and Vertical
+        */
+        private _createGaussianBlurPostProcess(scene: Scene, ratio: number): void {
+            var blurOffsetsW = new Array<number>(9);
+            var blurOffsetsH = new Array<number>(9);
+            var blurWeights = new Array<number>(9);
+            var uniforms: string[] = ["blurOffsets", "blurWeights"];
+
+            // Utils for gaussian blur
+            var calculateBlurOffsets = (height: boolean) => {
+                var lastOutputDimensions: any = {
+                    width: scene.getEngine().getRenderWidth() * (ratio / 4),
+                    height: scene.getEngine().getRenderHeight() * (ratio / 4)
+                };
+
+                for (var i = 0; i < 9; i++) {
+                    var value = (i - 4.0) * (1.0 / (height === true ? lastOutputDimensions.height : lastOutputDimensions.width));
+                    if (height) {
+                        blurOffsetsH[i] = value;
+                    } else {
+                        blurOffsetsW[i] = value;
+                    }
+                }
+            };
+
+            var calculateWeights = () => {
+                var x: number = 0.0;
+
+                for (var i = 0; i < 9; i++) {
+                    x = (i - 4.0) / 4.0;
+                    blurWeights[i] = this.gaussCoeff * (1.0 / Math.sqrt(2.0 * Math.PI * this.gaussStandDev * this.gaussStandDev)) * Math.exp((-((x - this.gaussMean) * (x - this.gaussMean))) / (2.0 * this.gaussStandDev * this.gaussStandDev));
+                }
+            }
+
+            // Callback
+            var gaussianBlurCallback = (height: boolean) => {
+                return (effect: Effect) => {
+                    if (this._needUpdate) {
+                        calculateWeights();
+                        calculateBlurOffsets(height);
+                    }
+                    effect.setArray("blurOffsets", height ? blurOffsetsH : blurOffsetsW);
+                    effect.setArray("blurWeights", blurWeights);
+                };
+            };
+
+            // Create horizontal gaussian blur post-processes
+            this._guassianBlurHPostProcess = new PostProcess("hdr", "hdr", uniforms, [], ratio / 4, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_H");
+            this._guassianBlurHPostProcess.onApply = gaussianBlurCallback(false);
+
+            // Create vertical gaussian blur post-process
+            this._guassianBlurVPostProcess = new PostProcess("hdr", "hdr", uniforms, [], ratio / 4, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define GAUSSIAN_BLUR_V");
+            this._guassianBlurVPostProcess.onApply = gaussianBlurCallback(true);
+        }
+    }
+}

+ 3 - 2
Babylon/PostProcess/babylon.volumetricLightScatteringPostProcess.js

@@ -19,8 +19,9 @@ var BABYLON;
          * @param {number} samplingMode - The post-process filtering mode
          * @param {BABYLON.Engine} engine - The babylon engine
          * @param {boolean} reusable - If the post-process is reusable
+         * @param {BABYLON.Scene} scene - The constructor needs a scene reference to initialize internal components. If "camera" is null (RenderPipelineà, "scene" must be provided
          */
-        function VolumetricLightScatteringPostProcess(name, ratio, camera, mesh, samples, samplingMode, engine, reusable) {
+        function VolumetricLightScatteringPostProcess(name, ratio, camera, mesh, samples, samplingMode, engine, reusable, scene) {
             var _this = this;
             if (samples === void 0) { samples = 100; }
             if (samplingMode === void 0) { samplingMode = BABYLON.Texture.BILINEAR_SAMPLINGMODE; }
@@ -65,7 +66,7 @@ var BABYLON;
             * @type {number}
             */
             this.density = 0.926;
-            var scene = camera.getScene();
+            scene = (camera === null) ? scene : camera.getScene(); // parameter "scene" can be null.
             this._viewPort = new BABYLON.Viewport(0, 0, 1, 1).toGlobal(scene.getEngine());
             // Configure mesh
             this.mesh = (mesh !== null) ? mesh : VolumetricLightScatteringPostProcess.CreateDefaultMesh("VolumetricLightScatteringMesh", scene);

+ 3 - 2
Babylon/PostProcess/babylon.volumetricLightScatteringPostProcess.ts

@@ -66,10 +66,11 @@
          * @param {number} samplingMode - The post-process filtering mode
          * @param {BABYLON.Engine} engine - The babylon engine
          * @param {boolean} reusable - If the post-process is reusable
+         * @param {BABYLON.Scene} scene - The constructor needs a scene reference to initialize internal components. If "camera" is null (RenderPipelineà, "scene" must be provided
          */
-        constructor(name: string, ratio: any, camera: Camera, mesh?: Mesh, samples: number = 100, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
+        constructor(name: string, ratio: any, camera: Camera, mesh?: Mesh, samples: number = 100, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, scene?: Scene) {
             super(name, "volumetricLightScattering", ["decay", "exposure", "weight", "meshPositionOnScreen", "density"], ["lightScatteringSampler"], ratio.postProcessRatio || ratio, camera, samplingMode, engine, reusable, "#define NUM_SAMPLES " + samples);
-            var scene = camera.getScene();
+            scene = (camera === null) ? scene : camera.getScene(); // parameter "scene" can be null.
 
             this._viewPort = new Viewport(0, 0, 1, 1).toGlobal(scene.getEngine());
 

+ 153 - 0
Babylon/Shaders/hdr.fragment.fx

@@ -0,0 +1,153 @@
+#ifdef GL_ES
+precision highp float;
+#endif
+
+uniform sampler2D textureSampler;
+varying vec2 vUV;
+
+#if defined(GAUSSIAN_BLUR_H) || defined(GAUSSIAN_BLUR_V)
+uniform float blurOffsets[9];
+uniform float blurWeights[9];
+
+void main(void) {
+	vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
+
+	for (int i = 0; i < 9; i++) {
+		#ifdef GAUSSIAN_BLUR_H
+		color += (texture2D(textureSampler, vUV + vec2(blurOffsets[i], 0.0)) * blurWeights[i]);
+		#else
+		color += (texture2D(textureSampler, vUV + vec2(0.0, blurOffsets[i])) * blurWeights[i]);
+		#endif
+	}
+
+	color.a = 1.0;
+	gl_FragColor = color;
+}
+#endif
+
+#if defined(TEXTURE_ADDER)
+uniform sampler2D otherSampler;
+
+void main() {
+	vec4 sum = texture2D(textureSampler, vUV) + texture2D(otherSampler, vUV);
+	sum.a = clamp(sum.a, 0.0, 1.0);
+
+	gl_FragColor = sum;
+}
+#endif
+
+#if defined(LUMINANCE_GENERATOR)
+uniform vec2 lumOffsets[4];
+
+void main() {
+	float average = 0.0;
+	vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
+	float maximum = -1e20;
+
+	for (int i = 0; i < 4; i++) {
+		color = texture2D(textureSampler, vUV + lumOffsets[i]);
+
+		float GreyValue = length(color.rgb);
+
+		maximum = max(maximum, GreyValue);
+		average += (0.25 * log(1e-5 + GreyValue));
+	}
+
+	average = exp(average);
+
+	gl_FragColor = vec4(average, maximum, 0.0, 1.0);
+
+}
+#endif
+
+#if defined(DOWN_SAMPLE)
+uniform vec2 dsOffsets[9];
+uniform float halfDestPixelSize;
+
+#ifdef FINAL_DOWN_SAMPLE
+vec4 pack(float value) {
+	const vec4 bit_shift = vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0);
+	const vec4 bit_mask = vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
+
+	vec4 res = fract(value * bit_shift);
+	res -= res.xxyz * bit_mask;
+
+	return res;
+}
+#endif
+
+void main() {
+	vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
+	float average = 0.0;
+
+	for (int i = 0; i < 9; i++) {
+		color = texture2D(textureSampler, vUV + vec2(halfDestPixelSize, halfDestPixelSize) + dsOffsets[i]);
+		average += color.r;
+	}
+
+	average /= 9.0;
+
+	#ifndef FINAL_DOWN_SAMPLE
+	gl_FragColor = vec4(average, average, 0.0, 1.0);
+	#else
+	gl_FragColor = pack(average);
+	#endif
+}
+#endif
+
+#if defined(BRIGHT_PASS)
+uniform vec2 dsOffsets[4];
+uniform float brightThreshold;
+
+void main() {
+	
+	vec4 average = vec4(0.0, 0.0, 0.0, 0.0);
+
+	for (int i = 0; i < 4; i++) {
+		average += texture2D(textureSampler, vUV + vec2(dsOffsets[i].x, dsOffsets[i].y));
+	}
+
+	average *= 0.25;
+
+	float luminance = length(average.rgb);
+
+	if (luminance < brightThreshold) {
+		average = vec4(0.0, 0.0, 0.0, 1.0);
+	}
+
+	gl_FragColor = average;
+}
+#endif
+
+#if defined(DOWN_SAMPLE_X4)
+uniform vec2 dsOffsets[16];
+
+void main() {
+	vec4 average = vec4(0.0, 0.0, 0.0, 0.0);
+
+	for (int i = 0; i < 16; i++) {
+		average += texture2D(textureSampler, vUV + dsOffsets[i]);
+	}
+
+	average /= 16.0;
+
+	gl_FragColor = average;
+}
+#endif
+
+#if defined(HDR)
+uniform sampler2D otherSampler;
+
+uniform float exposure;
+uniform float avgLuminance;
+
+void main() {
+	vec4 color = texture2D(textureSampler, vUV) + texture2D(otherSampler, vUV);
+	vec4 adjustedColor = color / avgLuminance * exposure;
+
+	color = adjustedColor;
+	color.a = 1.0;
+
+	gl_FragColor = color;
+}
+#endif

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 12 - 12
Preview release - Alpha/babylon.2.2.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 356 - 5
Preview release - Alpha/babylon.2.2.max.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 24 - 24
Preview release - Alpha/babylon.2.2.noworker.js


+ 2 - 1
Preview release - Alpha/what's new.md

@@ -1,12 +1,13 @@
 - 2.2.0:
   - **Major updates**
-    
+    - HDR Rendering pipeline. See [demo here]() [julien-moreau](https://github.com/julien-moreau)
   - **Updates**
     - Depth-of-field improvements [PR](https://github.com/BabylonJS/Babylon.js/pull/567) [jahow](https://github.com/jahow)
     - Engine now initialize WebGL with preserveDrawingBuffer = false by default [deltakosh](https://github.com/deltakosh)
     - withEpsilon with a user defined epsilon [PR](https://github.com/BabylonJS/Babylon.js/pull/573) [RaananW](https://github.com/RaananW)
     - Adding onAfterRender function in BABYLON.PostProcess [PR](https://github.com/BabylonJS/Babylon.js/pull/572) [julien-moreau](https://github.com/julien-moreau)
     - Improved shaders optimizer to remove specular code when not needed [deltakosh](https://github.com/deltakosh)    
+    - Added some utility functions to Vector2/3/4 [PR](https://github.com/BabylonJS/Babylon.js/pull/578) [jahow](https://github.com/jahow)
   - **Bug fixes**
  
   - **Breaking changes**

+ 2 - 1
Tools/Gulp/config.json

@@ -140,7 +140,8 @@
       "../../Babylon/PostProcess/babylon.volumetricLightScatteringPostProcess.js",
       "../../Babylon/PostProcess/babylon.lensRenderingPipeline.js",
       "../../Babylon/PostProcess/babylon.colorCorrectionPostProcess.js",
-      "../../Babylon/Cameras/babylon.stereoscopicCameras.js"
+      "../../Babylon/Cameras/babylon.stereoscopicCameras.js",
+      "../../Babylon/PostProcess/babylon.hdrRenderingPipeline.js"
     ]
   },
   "shadersDirectories": [