Browse Source

Adding Volumetric Lights post-process into Standard Rendering Pipeline
Adding "Position Buffer" to geometry renderer as optional
Fixed BloomEnable in Standard pipeline

Julien Moreau-Mathis 8 years ago
parent
commit
21c2ace406

+ 132 - 8
src/PostProcess/RenderPipeline/Pipelines/babylon.standardRenderingPipeline.ts

@@ -11,6 +11,12 @@
         public gaussianBlurVPostProcesses: PostProcess[] = [];
         public gaussianBlurVPostProcesses: PostProcess[] = [];
         public textureAdderPostProcess: PostProcess = null;
         public textureAdderPostProcess: PostProcess = null;
 
 
+        public vlsPostProcess: PostProcess = null;
+        public vlsSmoothXPostProcess: BlurPostProcess = null;
+        public vlsSmoothYPostProcess: BlurPostProcess = null;
+        public vlsMergePostProces: PostProcess = null;
+        public vlsFinalPostProcess: PostProcess = null;
+
         public luminancePostProcess: PostProcess = null;
         public luminancePostProcess: PostProcess = null;
         public luminanceDownSamplePostProcesses: PostProcess[] = [];
         public luminanceDownSamplePostProcesses: PostProcess[] = [];
         public hdrPostProcess: PostProcess = null;
         public hdrPostProcess: PostProcess = null;
@@ -47,6 +53,15 @@
         public lensTexture: Texture = null;
         public lensTexture: Texture = null;
 
 
         @serialize()
         @serialize()
+        public volumetricLightCoefficient: number = 0.2;
+        @serialize()
+        public volumetricLightPower: number = 4.0;
+        @serialize()
+        public volumetricLightBlurScale: number = 64.0;
+
+        public sourceLight: SpotLight | DirectionalLight = null;
+
+        @serialize()
         public hdrMinimumLuminance: number = 1.0;
         public hdrMinimumLuminance: number = 1.0;
         @serialize()
         @serialize()
         public hdrDecreaseRate: number = 0.5;
         public hdrDecreaseRate: number = 0.5;
@@ -84,7 +99,6 @@
         * Private members
         * Private members
         */
         */
         private _scene: Scene;
         private _scene: Scene;
-        private _depthRenderer: DepthRenderer = null;
         private _currentDepthOfFieldSource: PostProcess = null;
         private _currentDepthOfFieldSource: PostProcess = null;
 
 
         private _currentHDRSource: PostProcess = null;
         private _currentHDRSource: PostProcess = null;
@@ -95,6 +109,7 @@
         // Getters and setters
         // Getters and setters
         private _bloomEnabled: boolean = true;
         private _bloomEnabled: boolean = true;
         private _depthOfFieldEnabled: boolean = true;
         private _depthOfFieldEnabled: boolean = true;
+        private _vlsEnabled: boolean = true;
         private _lensFlareEnabled: boolean = true;
         private _lensFlareEnabled: boolean = true;
         private _hdrEnabled: boolean = true;
         private _hdrEnabled: boolean = true;
         private _motionBlurEnabled: boolean = true;
         private _motionBlurEnabled: boolean = true;
@@ -104,23 +119,25 @@
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRDownSampleX4", this._cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRDownSampleX4", this._cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRBrightPass", this._cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRBrightPass", this._cameras);
 
 
-                for (var i = 0; i < this.gaussianBlurHPostProcesses.length - 1; i++) {
+                for (var i = 0; i < 4; i++) {
                     this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + i, this._cameras);
                     this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + i, this._cameras);
                     this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + i, this._cameras);
                     this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + i, this._cameras);
                 }
                 }
 
 
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRTextureAdder", this._cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRTextureAdder", this._cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRBaseDepthOfFieldSource", this._cameras);
             }
             }
             else if (!enabled && this._bloomEnabled) {
             else if (!enabled && this._bloomEnabled) {
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRDownSampleX4", this._cameras);
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRDownSampleX4", this._cameras);
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRBrightPass", this._cameras);
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRBrightPass", this._cameras);
 
 
-                for (var i = 0; i < this.gaussianBlurHPostProcesses.length - 1; i++) {
+                for (var i = 0; i < 4; i++) {
                     this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurH" + i, this._cameras);
                     this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurH" + i, this._cameras);
                     this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurV" + i, this._cameras);
                     this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurV" + i, this._cameras);
                 }
                 }
 
 
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRTextureAdder", this._cameras);
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRTextureAdder", this._cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRBaseDepthOfFieldSource", this._cameras);
             }
             }
 
 
             this._bloomEnabled = enabled;
             this._bloomEnabled = enabled;
@@ -138,7 +155,6 @@
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRGaussianBlurV" + blurIndex, this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRDepthOfField", this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRDepthOfField", this._scene.cameras);
-                this._depthRenderer = this._scene.enableDepthRenderer();
             }
             }
             else if (!enabled && this._depthOfFieldEnabled) {
             else if (!enabled && this._depthOfFieldEnabled) {
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._cameras);
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRGaussianBlurH" + blurIndex, this._cameras);
@@ -208,10 +224,39 @@
             return this._hdrEnabled;
             return this._hdrEnabled;
         }
         }
 
 
+        @serialize()
+        public get VLSEnabled(): boolean {
+            return this._vlsEnabled;
+        }
+
+        public set VLSEnabled(enabled) {
+            var geometry = this._scene.enableGeometryBufferRenderer();
+            if (!geometry.isSupported) {
+                Tools.Warn("Geometry renderer is not supported, cannot create volumetric lights in Standard Rendering Pipeline");
+                return;
+            }
+
+            if (enabled && !this._vlsEnabled) {
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRVLS", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRVLSSmoothX", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRVLSSmoothY", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRVLSMerge", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRVLSFinal", this._scene.cameras);
+            }
+            else if (!enabled && this._vlsEnabled) {
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRVLS", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRVLSSmoothX", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRVLSSmoothY", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRVLSMerge", this._scene.cameras);
+                this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRVLSFinal", this._scene.cameras);
+            }
+
+            this._vlsEnabled = enabled;
+        }
+
         public set MotionBlurEnabled(enabled: boolean) {
         public set MotionBlurEnabled(enabled: boolean) {
             if (enabled && !this._motionBlurEnabled) {
             if (enabled && !this._motionBlurEnabled) {
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRMotionBlur", this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.enableEffectInPipeline(this._name, "HDRMotionBlur", this._scene.cameras);
-                this._depthRenderer = this._scene.enableDepthRenderer();
             }
             }
             else if (!enabled && this._motionBlurEnabled) {
             else if (!enabled && this._motionBlurEnabled) {
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRMotionBlur", this._scene.cameras);
                 this._scene.postProcessRenderPipelineManager.disableEffectInPipeline(this._name, "HDRMotionBlur", this._scene.cameras);
@@ -282,6 +327,13 @@
             this.textureAdderFinalPostProcess = new PostProcess("HDRDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_UNSIGNED_INT);
             this.textureAdderFinalPostProcess = new PostProcess("HDRDepthOfFieldSource", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_UNSIGNED_INT);
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBaseDepthOfFieldSource", () => { return this.textureAdderFinalPostProcess; }, true));
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRBaseDepthOfFieldSource", () => { return this.textureAdderFinalPostProcess; }, true));
 
 
+            // Create volumetric light
+            this._createVolumetricLightPostProcess(scene, ratio);
+
+            // Create volumetric light final post-process
+            this.vlsFinalPostProcess = new PostProcess("HDRVLSFinal", "standard", [], [], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define PASS_POST_PROCESS", Engine.TEXTURETYPE_UNSIGNED_INT);
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRVLSFinal", () => { return this.vlsFinalPostProcess; }, true));
+
             // Create lens flare post-process
             // Create lens flare post-process
             this._createLensFlarePostProcess(scene, ratio);
             this._createLensFlarePostProcess(scene, ratio);
 
 
@@ -319,6 +371,7 @@
             this.LensFlareEnabled = false;
             this.LensFlareEnabled = false;
             this.DepthOfFieldEnabled = false;
             this.DepthOfFieldEnabled = false;
             this.HDREnabled = false;
             this.HDREnabled = false;
+            this.VLSEnabled = false;
             this.MotionBlurEnabled = false;
             this.MotionBlurEnabled = false;
         }
         }
 
 
@@ -445,6 +498,68 @@
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRTextureAdder", () => { return this.textureAdderPostProcess; }, true));
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRTextureAdder", () => { return this.textureAdderPostProcess; }, true));
         }
         }
 
 
+        private _createVolumetricLightPostProcess(scene: Scene, ratio: number): void {
+            var geometryRenderer = scene.enableGeometryBufferRenderer();
+            geometryRenderer.enablePosition = true;
+
+            var geometry = geometryRenderer.getGBuffer();
+
+            // Base post-process
+            this.vlsPostProcess = new PostProcess("HDRVLS", "standard", ["shadowViewProjection", "cameraPosition", "sunDirection", "sunColor", "scatteringCoefficient", "scatteringPower"], ["shadowMapSampler", "positionSampler" ], ratio / 8, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define VLS");
+
+            this.vlsPostProcess.onApply = (effect: Effect) => {
+                if (this.sourceLight && this.sourceLight.getShadowGenerator()) {
+                    var generator = <ShadowGenerator>this.sourceLight.getShadowGenerator();
+
+                    effect.setTexture("shadowMapSampler", generator.getShadowMap());
+                    effect.setTexture("positionSampler", geometry.textures[2]);
+
+                    effect.setColor3("sunColor", this.sourceLight.diffuse);
+                    effect.setVector3("sunDirection", this.sourceLight.getShadowDirection());
+                    
+                    effect.setVector3("cameraPosition", scene.activeCamera.globalPosition);
+                    effect.setMatrix("shadowViewProjection", generator.getTransformMatrix());
+
+                    effect.setFloat("scatteringCoefficient", this.volumetricLightCoefficient);
+                    effect.setFloat("scatteringPower", this.volumetricLightPower);
+                }
+            };
+
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRVLS", () => { return this.vlsPostProcess; }, true));
+
+            // Smooth
+            this.vlsSmoothXPostProcess = new BABYLON.BlurPostProcess("HDRVLSSmoothX", new BABYLON.Vector2(1.0, 0), 10.0, ratio / 4, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this.vlsSmoothXPostProcess.alwaysForcePOT = true;
+            this.vlsSmoothXPostProcess.autoClear = false;
+            this.vlsSmoothXPostProcess.onActivateObservable.add(() => {
+                let dw = this.vlsSmoothXPostProcess.width / scene.getEngine().getRenderingCanvas().width;
+                this.vlsSmoothXPostProcess.kernel = this.volumetricLightBlurScale * dw;
+            });
+
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRVLSSmoothX", () => { return this.vlsSmoothXPostProcess; }, true));
+
+            this.vlsSmoothYPostProcess = new BABYLON.BlurPostProcess("HDRVLSSmoothY", new BABYLON.Vector2(0, 1.0), 10.0, ratio / 4, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false);
+            this.vlsSmoothYPostProcess.alwaysForcePOT = true;
+            this.vlsSmoothYPostProcess.autoClear = false;
+            this.vlsSmoothYPostProcess.onActivateObservable.add(() => {
+                let dh = this.vlsSmoothYPostProcess.height / scene.getEngine().getRenderingCanvas().height;
+                this.vlsSmoothYPostProcess.kernel = this.volumetricLightBlurScale * dh;
+            });
+
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRVLSSmoothY", () => { return this.vlsSmoothYPostProcess; }, true));
+
+            // Merge
+            this.vlsMergePostProces = new PostProcess("HDRVLSMerge", "standard", [], ["originalSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define VLSMERGE");
+
+            this.vlsMergePostProces.onApply = (effect: Effect) => {
+                effect.setTextureFromPostProcess("originalSampler", this._bloomEnabled ? this.textureAdderFinalPostProcess : this.originalPostProcess);
+
+                this._currentDepthOfFieldSource = this.vlsFinalPostProcess;
+            };
+
+            this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRVLSMerge", () => { return this.vlsMergePostProces; }, true));
+        }
+
         // Create luminance
         // Create luminance
         private _createLuminancePostProcesses(scene: Scene, textureType: number): void {
         private _createLuminancePostProcesses(scene: Scene, textureType: number): void {
             // Create luminance
             // Create luminance
@@ -607,7 +722,7 @@
             );
             );
 
 
             this.lensFlareComposePostProcess.onApply = (effect: Effect) => {
             this.lensFlareComposePostProcess.onApply = (effect: Effect) => {
-                effect.setTextureFromPostProcess("otherSampler", this.textureAdderFinalPostProcess);
+                effect.setTextureFromPostProcess("otherSampler", this._currentDepthOfFieldSource);
                 effect.setTexture("lensDirtSampler", this.lensFlareDirtTexture);
                 effect.setTexture("lensDirtSampler", this.lensFlareDirtTexture);
                 effect.setTexture("lensStarSampler", this.lensStarTexture);
                 effect.setTexture("lensStarSampler", this.lensStarTexture);
 
 
@@ -638,7 +753,7 @@
             this.depthOfFieldPostProcess = new PostProcess("HDRDepthOfField", "standard", ["distance"], ["otherSampler", "depthSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define DEPTH_OF_FIELD", Engine.TEXTURETYPE_UNSIGNED_INT);
             this.depthOfFieldPostProcess = new PostProcess("HDRDepthOfField", "standard", ["distance"], ["otherSampler", "depthSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define DEPTH_OF_FIELD", Engine.TEXTURETYPE_UNSIGNED_INT);
             this.depthOfFieldPostProcess.onApply = (effect: Effect) => {
             this.depthOfFieldPostProcess.onApply = (effect: Effect) => {
                 effect.setTextureFromPostProcess("otherSampler", this._currentDepthOfFieldSource);
                 effect.setTextureFromPostProcess("otherSampler", this._currentDepthOfFieldSource);
-                effect.setTexture("depthSampler", this._depthRenderer.getDepthMap());
+                effect.setTexture("depthSampler", this._getDepthTexture());
 
 
                 effect.setFloat("distance", this.depthOfFieldDistance);
                 effect.setFloat("distance", this.depthOfFieldDistance);
             };
             };
@@ -677,12 +792,21 @@
                 effect.setFloat("motionScale", motionScale);
                 effect.setFloat("motionScale", motionScale);
                 effect.setFloat("motionStrength", this.motionStrength);
                 effect.setFloat("motionStrength", this.motionStrength);
 
 
-                effect.setTexture("depthSampler", this._depthRenderer.getDepthMap());
+                effect.setTexture("depthSampler", this._getDepthTexture());
             };
             };
 
 
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRMotionBlur", () => { return this.motionBlurPostProcess; }, true));
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), "HDRMotionBlur", () => { return this.motionBlurPostProcess; }, true));
         }
         }
 
 
+        private _getDepthTexture(): Texture {
+            var geometry = this._scene.enableGeometryBufferRenderer();
+            if (geometry.isSupported) {
+                return geometry.getGBuffer().textures[0];
+            }
+
+            return this._scene.enableDepthRenderer().getDepthMap();
+        }
+
         // Dispose
         // Dispose
         public dispose(): void {
         public dispose(): void {
             for (var i = 0; i < this._cameras.length; i++) {
             for (var i = 0; i < this._cameras.length; i++) {

+ 97 - 73
src/Rendering/babylon.geometryBufferRenderer.ts

@@ -3,6 +3,7 @@ module BABYLON {
         private _scene: Scene;
         private _scene: Scene;
         private _multiRenderTarget: MultiRenderTarget;
         private _multiRenderTarget: MultiRenderTarget;
         private _effect: Effect;
         private _effect: Effect;
+        private _ratio: number;
 
 
         private _viewMatrix = Matrix.Zero();
         private _viewMatrix = Matrix.Zero();
         private _projectionMatrix = Matrix.Zero();
         private _projectionMatrix = Matrix.Zero();
@@ -11,6 +12,8 @@ module BABYLON {
 
 
         private _cachedDefines: string;
         private _cachedDefines: string;
 
 
+        private _enablePosition: boolean = false;
+
         public set renderList(meshes: Mesh[]) {
         public set renderList(meshes: Mesh[]) {
             this._multiRenderTarget.renderList = meshes;
             this._multiRenderTarget.renderList = meshes;
         }
         }
@@ -19,83 +22,22 @@ module BABYLON {
             return this._multiRenderTarget.isSupported;
             return this._multiRenderTarget.isSupported;
         }
         }
 
 
+        public get enablePosition(): boolean {
+            return this._enablePosition;
+        }
+
+        public set enablePosition(enable: boolean) {
+            this._enablePosition = enable;
+            this.dispose();
+            this._createRenderTargets();
+        }
+
         constructor(scene: Scene, ratio: number = 1) {
         constructor(scene: Scene, ratio: number = 1) {
             this._scene = scene;
             this._scene = scene;
-            var engine = scene.getEngine();
+            this._ratio = ratio;
 
 
             // Render target
             // 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]);
-                }
-            };
-
+            this._createRenderTargets();
         }
         }
 
 
         public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
         public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
@@ -125,6 +67,11 @@ module BABYLON {
                 }
                 }
             }
             }
 
 
+            // Buffers
+            if (this._enablePosition) {
+                defines.push("#define POSITION");
+            }
+
             // Bones
             // Bones
             if (mesh.useBones && mesh.computeBonesUsingShaders) {
             if (mesh.useBones && mesh.computeBonesUsingShaders) {
                 attribs.push(VertexBuffer.MatricesIndicesKind);
                 attribs.push(VertexBuffer.MatricesIndicesKind);
@@ -169,5 +116,82 @@ module BABYLON {
         public dispose(): void {
         public dispose(): void {
             this.getGBuffer().dispose();
             this.getGBuffer().dispose();
         }
         }
+
+        private _createRenderTargets(): void {
+            var engine = this._scene.getEngine();
+            var count = this._enablePosition ? 3 : 2;
+
+            this._multiRenderTarget = new MultiRenderTarget("gBuffer", { width: engine.getRenderWidth() * this._ratio, height: engine.getRenderHeight() * this._ratio }, count, 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]);
+                }
+            };
+        }
     }
     }
 } 
 } 

+ 13 - 1
src/Shaders/geometry.fragment.fx

@@ -6,6 +6,10 @@ precision highp int;
 in vec3 vNormalV;
 in vec3 vNormalV;
 in vec4 vViewPos;
 in vec4 vViewPos;
 
 
+#ifdef POSITION
+in vec3 vPosition;
+#endif
+
 #ifdef ALPHATEST
 #ifdef ALPHATEST
 in vec2 vUV;
 in vec2 vUV;
 uniform sampler2D diffuseSampler;
 uniform sampler2D diffuseSampler;
@@ -14,13 +18,21 @@ uniform sampler2D diffuseSampler;
 layout(location = 0) out vec4 color0;
 layout(location = 0) out vec4 color0;
 layout(location = 1) out vec4 color1;
 layout(location = 1) out vec4 color1;
 
 
+#ifdef POSITION
+layout(location = 2) out vec4 color2;
+#endif
+
 void main() {
 void main() {
 #ifdef ALPHATEST
 #ifdef ALPHATEST
 	if (texture(diffuseSampler, vUV).a < 0.4)
 	if (texture(diffuseSampler, vUV).a < 0.4)
 		discard;
 		discard;
 #endif
 #endif
 
 
-    color0 = vec4(vViewPos.z / vViewPos.w, 0.0, 0.0, 0.0);
+    color0 = vec4(vViewPos.z / vViewPos.w, 0.0, 0.0, 1.0);
     color1 = vec4(normalize(vNormalV), 1.0);
     color1 = vec4(normalize(vNormalV), 1.0);
     //color2 = vec4(vPositionV, 1.0);
     //color2 = vec4(vPositionV, 1.0);
+
+    #ifdef POSITION
+    color2 = vec4(vPosition, 1.0);
+    #endif
 }
 }

+ 11 - 1
src/Shaders/geometry.vertex.fx

@@ -27,14 +27,24 @@ uniform mat4 view;
 out vec3 vNormalV;
 out vec3 vNormalV;
 out vec4 vViewPos;
 out vec4 vViewPos;
 
 
+#ifdef POSITION
+out vec3 vPosition;
+#endif
+
 void main(void)
 void main(void)
 {
 {
 #include<instancesVertex>
 #include<instancesVertex>
 
 
 #include<bonesVertex>
 #include<bonesVertex>
+	vec4 pos = vec4(finalWorld * vec4(position, 1.0));
 
 
 	vNormalV = normalize(vec3((view * finalWorld) * vec4(normal, 0.0)));
 	vNormalV = normalize(vec3((view * finalWorld) * vec4(normal, 0.0)));
-	vViewPos = view * finalWorld * vec4(position, 1.0);
+	vViewPos = view * pos;
+
+	#ifdef POSITION
+	vPosition = pos.xyz / pos.w;
+	#endif
+
 	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
 	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
 
 
 #if defined(ALPHATEST) || defined(BASIC_RENDER)
 #if defined(ALPHATEST) || defined(BASIC_RENDER)

+ 70 - 0
src/Shaders/standard.fragment.fx

@@ -112,6 +112,76 @@ void main(void)
 }
 }
 #endif
 #endif
 
 
+#if defined(VLS)
+#define NB_STEPS 50.0
+#define PI 3.1415926535897932384626433832795
+
+uniform mat4 shadowViewProjection;
+
+uniform vec3 cameraPosition;
+uniform vec3 sunDirection;
+uniform vec3 sunColor;
+
+uniform float scatteringCoefficient;
+uniform float scatteringPower;
+
+uniform sampler2D shadowMapSampler;
+uniform sampler2D positionSampler;
+
+float computeScattering(float lightDotView)
+{
+	float result = 1.0 - scatteringCoefficient * scatteringCoefficient;
+	result /= (4.0 * PI * pow(1.0 + scatteringCoefficient * scatteringCoefficient - (2.0 * scatteringCoefficient) * lightDotView, 1.5));
+	return result;
+}
+
+void main(void)
+{
+	// Compute
+	vec3 worldPos = texture2D(positionSampler, vUV).rgb;
+	vec3 startPosition = cameraPosition;
+
+	vec3 rayVector = worldPos - startPosition;
+
+	float rayLength = length(rayVector);
+	vec3 rayDirection = rayVector / rayLength;
+
+	float stepLength = rayLength / NB_STEPS;
+	vec3 stepL = rayDirection * stepLength;
+	vec3 currentPosition = startPosition;
+	vec3 accumFog = vec3(0.0);
+
+	for (int i = 0; i < int(NB_STEPS); i++)
+	{
+		vec4 worldInShadowCameraSpace = shadowViewProjection * vec4(currentPosition, 1.0);
+		worldInShadowCameraSpace.xyz /= worldInShadowCameraSpace.w;
+		worldInShadowCameraSpace.xyz = 0.5 * worldInShadowCameraSpace.xyz + vec3(0.5);
+
+		float shadowMapValue = texture2D(shadowMapSampler, worldInShadowCameraSpace.xy).r;
+		
+		if (shadowMapValue > worldInShadowCameraSpace.z)
+			accumFog += sunColor * computeScattering(dot(rayDirection, sunDirection));
+		
+		currentPosition += stepL;
+	}
+
+	accumFog /= NB_STEPS;
+
+	vec3 color = accumFog * scatteringPower;
+	gl_FragColor = vec4(color * exp(color) , 1.0);
+}
+
+#endif
+
+#if defined(VLSMERGE)
+uniform sampler2D originalSampler;
+
+void main(void)
+{
+	gl_FragColor = texture2D(originalSampler, vUV) + texture2D(textureSampler, vUV);
+}
+#endif
+
 #if defined(LUMINANCE)
 #if defined(LUMINANCE)
 uniform vec2 lumOffsets[4];
 uniform vec2 lumOffsets[4];