瀏覽代碼

Merge pull request #3682 from TrevorDev/dofImprovements

Dof improvements
David Catuhe 7 年之前
父節點
當前提交
fa32b1e8e4

+ 2 - 2
dist/preview release/typedocValidationBaseline.json

@@ -1,7 +1,7 @@
 {
-  "errors": 7349,
+  "errors": 7933,
   "babylon.typedoc.json": {
-    "errors": 7349,
+    "errors": 7933,
     "AnimationKeyInterpolation": {
       "Enumeration": {
         "Comments": {

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

@@ -38,7 +38,7 @@
    ([carloslanderas](https://github.com/carloslanderas))
 - (Viewer) initScene and initEngine can now be extended. onProgress during model loading is implemented as observable. ([RaananW](https://github.com/RaananW))
 - glTF loader now supports the KHR_lights extension ([MiiBond](https://github.com/MiiBond))
-- Added depth of field effect to default pipeline ([trevordev](https://github.com/trevordev))
+- Added depth of field effect to the default pipeline ([trevordev](https://github.com/trevordev))
 - The observable can now notify observers using promise-based callback chain. ([RaananW](https://github.com/RaananW))
 - Added base64 helper functions to `Tools` ([bghgary](https://github.com/bghgary))
 - Added `createDefaultCamera` and `createDefaultLight` functions to `Scene` ([bghgary](https://github.com/bghgary))

+ 1 - 1
src/Engine/babylon.engine.ts

@@ -5008,7 +5008,7 @@
 
                 if (internalTexture && internalTexture._cachedWrapR !== texture.wrapR) {
                     internalTexture._cachedWrapR = texture.wrapR;
-                    switch (texture.wrapV) {
+                    switch (texture.wrapR) {
                         case Texture.WRAP_ADDRESSMODE:
                             this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_WRAP_R, this._gl.REPEAT);
                             break;

+ 22 - 5
src/PostProcess/RenderPipeline/Pipelines/babylon.defaultRenderingPipeline.ts

@@ -85,6 +85,7 @@
         // Values       
         private _bloomEnabled: boolean = false;
         private _depthOfFieldEnabled: boolean = false;
+        private _depthOfFieldBlurLevel = DepthOfFieldEffectBlurLevel.Low;
         private _fxaaEnabled: boolean = false;
         private _imageProcessingEnabled: boolean = true;
         private _defaultPipelineTextureType: number;
@@ -178,6 +179,22 @@
         }
 
         /**
+         * Blur level of the depth of field effect. (Higher blur will effect performance)
+         */
+        @serialize()
+        public get depthOfFieldBlurLevel(): DepthOfFieldEffectBlurLevel {
+            return this._depthOfFieldBlurLevel;
+        }   
+        
+        public set depthOfFieldBlurLevel(value: DepthOfFieldEffectBlurLevel) {
+            if (this._depthOfFieldBlurLevel === value) {
+                return;
+            }
+            this._depthOfFieldBlurLevel = value;
+            this._buildPipeline();
+        }
+
+        /**
          * If the anti aliasing is enabled.
          */
         public set fxaaEnabled(enabled: boolean) {
@@ -268,6 +285,11 @@
             this._disposePostProcesses();
             this._reset();
 
+            if(this.depthOfFieldEnabled){
+                this.depthOfField = new DepthOfFieldEffect(this._scene, this._depthOfFieldBlurLevel, this._defaultPipelineTextureType);
+                this.addEffect(this.depthOfField);
+            }
+
             if (this.bloomEnabled) {
                 this.pass = new PassPostProcess("sceneRenderTarget", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
                 this.addEffect(new PostProcessRenderEffect(engine, this.PassPostProcessId, () => { return this.pass; }, true));
@@ -310,11 +332,6 @@
                 this.copyBack.autoClear = false;
             }
 
-            if(this.depthOfFieldEnabled){
-                this.depthOfField = new DepthOfFieldEffect(this._scene, this._defaultPipelineTextureType);
-                this.addEffect(this.depthOfField);
-            }
-
             if (this._imageProcessingEnabled) {
                 this.imageProcessing = new ImageProcessingPostProcess("imageProcessing", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
                 if (this._hdr) {

+ 4 - 4
src/PostProcess/babylon.blurPostProcess.ts

@@ -7,7 +7,7 @@
 		protected _kernel: number;
 		protected _idealKernel: number;
 		protected _packedFloat: boolean	= false;
-		protected _staticDefines:string = ""
+		private _staticDefines:string = ""
 		/**
 		 * Sets the length in pixels of the blur sample region
 		 */
@@ -59,9 +59,9 @@
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          */
-        constructor(name: string, /** The direction in which to blur the image. */ public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
-			super(name, "kernelBlur", ["delta", "direction", "cameraMinMaxZ"], ["depthSampler"], options, camera, samplingMode, engine, reusable, null, textureType, "kernelBlur", {varyingCount: 0, depCount: 0}, true);
-			
+        constructor(name: string, /** The direction in which to blur the image. */ public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, defines = "") {
+			super(name, "kernelBlur", ["delta", "direction", "cameraMinMaxZ"], ["circleOfConfusionSampler"], options, camera, samplingMode, engine, reusable, null, textureType, "kernelBlur", {varyingCount: 0, depCount: 0}, true);
+			this._staticDefines = defines;
 			this.onApplyObservable.add((effect: Effect) => {
 				effect.setFloat2('delta', (1 / this.width) * this.direction.x, (1 / this.height) * this.direction.y);
 			});

+ 4 - 6
src/PostProcess/babylon.depthOfFieldBlurPostProcess.ts

@@ -14,23 +14,21 @@ module BABYLON {
          * @param kernel The size of the kernel used to blur.
          * @param options The required width/height ratio to downsize to before computing the render pass.
          * @param camera The camera to apply the render pass to.
-         * @param depthMap The depth map to be used to avoid blurring accross edges
+         * @param circleOfConfusion The circle of confusion + depth map to be used to avoid blurring accross edges
          * @param imageToBlur The image to apply the blur to (default: Current rendered frame)
          * @param samplingMode The sampling mode to be used when computing the pass. (default: 0)
          * @param engine The engine which the post process will be applied. (default: current engine)
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          */
-        constructor(name: string, scene: Scene, public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, depthMap:RenderTargetTexture, imageToBlur:Nullable<PostProcess> = null, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
-            super(name, direction, kernel, options, camera, samplingMode = Texture.BILINEAR_SAMPLINGMODE, engine, reusable, textureType = Engine.TEXTURETYPE_UNSIGNED_INT);
-            this._staticDefines += `#define DOF 1\r\n`;
+        constructor(name: string, scene: Scene, public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Nullable<Camera>, circleOfConfusion:PostProcess, imageToBlur:Nullable<PostProcess> = null, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
+            super(name, direction, kernel, options, camera, samplingMode = Texture.BILINEAR_SAMPLINGMODE, engine, reusable, textureType = Engine.TEXTURETYPE_UNSIGNED_INT, `#define DOF 1\r\n`);
 			
 			this.onApplyObservable.add((effect: Effect) => {
-                // TODO: setTextureFromPostProcess seems to be setting the input texture instead of output of the post process passed in 
                 if(imageToBlur != null){
                     effect.setTextureFromPostProcess("textureSampler", imageToBlur);
                 }
-                effect.setTexture("depthSampler", depthMap);
+                effect.setTextureFromPostProcess("circleOfConfusionSampler", circleOfConfusion);
                 if(scene.activeCamera){
                     effect.setFloat2('cameraMinMaxZ', scene.activeCamera.minZ, scene.activeCamera.maxZ);
                 }

+ 82 - 29
src/PostProcess/babylon.depthOfFieldEffect.ts

@@ -1,26 +1,32 @@
 module BABYLON {
-    
+    /**
+     * Specifies the level of max blur that should be applied when using the depth of field effect
+     */
+    export enum DepthOfFieldEffectBlurLevel {
+        /**
+         * Subtle blur
+         */
+        Low,
+        /**
+         * Medium blur
+         */
+        Medium,
+        /**
+         * Large blur
+         */
+        High
+    };
     /**
      * The depth of field effect applies a blur to objects that are closer or further from where the camera is focusing.
      */
     export class DepthOfFieldEffect extends PostProcessRenderEffect{
         private _depthOfFieldPass: PassPostProcess;
         private _circleOfConfusion: CircleOfConfusionPostProcess;
-        private _depthOfFieldBlurX: DepthOfFieldBlurPostProcess;
-        private _depthOfFieldBlurY: DepthOfFieldBlurPostProcess;
+        private _depthOfFieldBlurX: Array<DepthOfFieldBlurPostProcess>;
+        private _depthOfFieldBlurY: Array<DepthOfFieldBlurPostProcess>;
         private _depthOfFieldMerge: DepthOfFieldMergePostProcess;
 
         /**
-         * The size of the kernel to be used for the blur
-         */
-        public set kernelSize(value: number){
-            this._depthOfFieldBlurX.kernel = value;
-            this._depthOfFieldBlurY.kernel = value;
-        }
-        public get kernelSize(){
-            return this._depthOfFieldBlurX.kernel;
-        }
-        /**
          * The focal the length of the camera used in the effect
          */
         public set focalLength(value: number){
@@ -62,20 +68,63 @@ module BABYLON {
          * @param scene The scene the effect belongs to.
          * @param pipelineTextureType The type of texture to be used when performing the post processing.
          */
-        constructor(scene: Scene, pipelineTextureType = 0) {
-            super(scene.getEngine(), "depth of field", ()=>{return [this._circleOfConfusion, this._depthOfFieldPass, this._depthOfFieldBlurY, this._depthOfFieldBlurX, this._depthOfFieldMerge]}, true);
-            // Enable and get current depth map
-            var depthMap = scene.enableDepthRenderer().getDepthMap();
-            // Circle of confusion value for each pixel is used to determine how much to blur that pixel
-            this._circleOfConfusion = new BABYLON.CircleOfConfusionPostProcess("circleOfConfusion", scene, depthMap, 1, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-            // Capture circle of confusion texture
-            this._depthOfFieldPass = new PassPostProcess("depthOfFieldPass", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-            // Blur the image but do not blur on sharp far to near distance changes to avoid bleeding artifacts 
-            // See section 2.6.2 http://fileadmin.cs.lth.se/cs/education/edan35/lectures/12dof.pdf
-            this._depthOfFieldBlurY = new DepthOfFieldBlurPostProcess("verticle blur", scene, new Vector2(0, 1.0), 15, 1.0, null, depthMap, this._circleOfConfusion, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-            this._depthOfFieldBlurX = new DepthOfFieldBlurPostProcess("horizontal blur", scene, new Vector2(1.0, 0), 15, 1.0, null,  depthMap, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-            // Merge blurred images with original image based on circleOfConfusion
-            this._depthOfFieldMerge = new DepthOfFieldMergePostProcess("depthOfFieldMerge", this._circleOfConfusion, this._depthOfFieldPass, 1, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+        constructor(scene: Scene, blurLevel: DepthOfFieldEffectBlurLevel = DepthOfFieldEffectBlurLevel.Low, pipelineTextureType = 0) {
+            super(scene.getEngine(), "depth of field", ()=>{
+                // Enable and get current depth map
+                var depthMap = scene.enableDepthRenderer().getDepthMap();
+                // Circle of confusion value for each pixel is used to determine how much to blur that pixel
+                this._circleOfConfusion = new BABYLON.CircleOfConfusionPostProcess("circleOfConfusion", scene, depthMap, 1, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+                // Capture circle of confusion texture
+                this._depthOfFieldPass = new PassPostProcess("depthOfFieldPass", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+                this._depthOfFieldPass.autoClear = false;
+
+                // Create a pyramid of blurred images (eg. fullSize 1/4 blur, half size 1/2 blur, quarter size 3/4 blur, eith size 4/4 blur)
+                // Blur the image but do not blur on sharp far to near distance changes to avoid bleeding artifacts 
+                // See section 2.6.2 http://fileadmin.cs.lth.se/cs/education/edan35/lectures/12dof.pdf
+                this._depthOfFieldBlurY = []
+                this._depthOfFieldBlurX = []
+                var blurCount = 1;
+                var kernelSize = 15;
+                switch(blurLevel){
+                    case DepthOfFieldEffectBlurLevel.High: {
+                        blurCount = 3;
+                        kernelSize = 51;
+                        break;
+                    }
+                    case DepthOfFieldEffectBlurLevel.Medium: {
+                        blurCount = 2;
+                        kernelSize = 31;
+                        break;
+                    }
+                    default: {
+                        kernelSize = 15;
+                        blurCount = 1;
+                        break;
+                    }
+                }
+                var adjustedKernelSize = kernelSize/Math.pow(2, blurCount-1);
+                for(var i = 0;i<blurCount;i++){
+                    var blurY = new DepthOfFieldBlurPostProcess("verticle blur", scene, new Vector2(0, 1.0), adjustedKernelSize, 1.0/Math.pow(2, i), null, this._depthOfFieldPass, i == 0 ? this._circleOfConfusion : null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+                    blurY.autoClear = false;
+                    var blurX = new DepthOfFieldBlurPostProcess("horizontal blur", scene, new Vector2(1.0, 0), adjustedKernelSize, 1.0/Math.pow(2, i), null,  this._depthOfFieldPass, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+                    blurX.autoClear = false;
+                    this._depthOfFieldBlurY.push(blurY);
+                    this._depthOfFieldBlurX.push(blurX);
+                }
+
+                // Merge blurred images with original image based on circleOfConfusion
+                this._depthOfFieldMerge = new DepthOfFieldMergePostProcess("depthOfFieldMerge", this._circleOfConfusion, this._depthOfFieldPass, this._depthOfFieldBlurY.slice(1), 1, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+                this._depthOfFieldMerge.autoClear = false;
+                
+                // Set all post processes on the effect.
+                var effects= [this._circleOfConfusion, this._depthOfFieldPass];
+                for(var i=0;i<this._depthOfFieldBlurX.length;i++){
+                    effects.push(this._depthOfFieldBlurY[i]);
+                    effects.push(this._depthOfFieldBlurX[i]);
+                }
+                effects.push(this._depthOfFieldMerge);
+                return effects;
+            }, true);
         }
 
         /**
@@ -85,8 +134,12 @@ module BABYLON {
         public disposeEffects(camera:Camera){
             this._depthOfFieldPass.dispose(camera);
             this._circleOfConfusion.dispose(camera);
-            this._depthOfFieldBlurX.dispose(camera);
-            this._depthOfFieldBlurY.dispose(camera);
+            this._depthOfFieldBlurX.forEach(element => {
+                element.dispose(camera);
+            });
+            this._depthOfFieldBlurY.forEach(element => {
+                element.dispose(camera);
+            });
             this._depthOfFieldMerge.dispose(camera);
         }
     }

+ 7 - 3
src/PostProcess/babylon.depthOfFieldMergePostProcess.ts

@@ -8,6 +8,7 @@ module BABYLON {
          * @param name The name of the effect.
          * @param original The non-blurred image to be modified
          * @param circleOfConfusion The circle of confusion post process that will determine how blurred each pixel should become.
+         * @param blurSteps Incrimental bluring post processes.
          * @param options The required width/height ratio to downsize to before computing the render pass.
          * @param camera The camera to apply the render pass to.
          * @param samplingMode The sampling mode to be used when computing the pass. (default: 0)
@@ -15,12 +16,15 @@ module BABYLON {
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          */
-        constructor(name: string, original: PostProcess, circleOfConfusion: PostProcess, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
-            super(name, "depthOfFieldMerge", [], ["circleOfConfusionSampler", "originalSampler"], options, camera, samplingMode, engine, reusable, null, textureType);
+        constructor(name: string, original: PostProcess, circleOfConfusion: PostProcess, blurSteps: Array<PostProcess>, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
+            super(name, "depthOfFieldMerge", [], ["circleOfConfusionSampler", "originalSampler", "blurStep1", "blurStep2"], options, camera, samplingMode, engine, reusable, "#define BLUR_LEVEL "+blurSteps.length+"\n", textureType);
             this.onApplyObservable.add((effect: Effect) => {
                 effect.setTextureFromPostProcess("circleOfConfusionSampler", circleOfConfusion);
                 effect.setTextureFromPostProcess("originalSampler", original);
-            })
+                blurSteps.forEach((step,index)=>{
+                    effect.setTextureFromPostProcess("blurStep"+(index+1), step);
+                });
+            });
         }
     }
 }

+ 1 - 1
src/Shaders/circleOfConfusion.fragment.fx

@@ -21,5 +21,5 @@ void main(void)
     float pixelDistance = sampleDistance(vUV);
     float coc = abs(cocPrecalculation* ((focusDistance - pixelDistance)/pixelDistance));
     coc = clamp(coc, 0.0, 1.0);
-    gl_FragColor = vec4(coc, coc, coc, 1.0);
+    gl_FragColor = vec4(coc, texture2D(depthSampler, vUV).r, coc, 1.0);
 }

+ 32 - 3
src/Shaders/depthOfFieldMerge.fragment.fx

@@ -3,13 +3,42 @@ uniform sampler2D textureSampler;
 uniform sampler2D originalSampler;
 uniform sampler2D circleOfConfusionSampler;
 
+#if BLUR_LEVEL > 0
+uniform sampler2D blurStep1;
+#endif
+#if BLUR_LEVEL > 1
+uniform sampler2D blurStep2;
+#endif
 // varyings
 varying vec2 vUV;
 
 void main(void)
 {
-    vec4 blurred = texture2D(textureSampler, vUV);
-    vec4 original = texture2D(originalSampler, vUV);
     float coc = texture2D(circleOfConfusionSampler, vUV).r;
-    gl_FragColor = mix(original, blurred, coc);
+    vec4 original = texture2D(originalSampler, vUV);
+#if BLUR_LEVEL == 0
+    vec4 blurred1 = texture2D(textureSampler, vUV);
+    gl_FragColor = mix(original, blurred1, coc);
+#endif
+#if BLUR_LEVEL == 1
+    vec4 blurred1 = texture2D(blurStep1, vUV);
+    vec4 blurred2 = texture2D(textureSampler, vUV);    
+    if(coc < 0.5){
+        gl_FragColor = mix(original, blurred1, coc/0.5);
+    }else{
+        gl_FragColor = mix(blurred1, blurred2, (coc-0.5)/0.5);
+    }
+#endif
+#if BLUR_LEVEL == 2
+    vec4 blurred1 = texture2D(blurStep1, vUV);
+    vec4 blurred2 = texture2D(blurStep2, vUV);
+    vec4 blurred3 = texture2D(textureSampler, vUV);
+    if(coc < 0.33){
+        gl_FragColor = mix(original, blurred1, coc/0.33);
+    }else if(coc < 0.66){
+        gl_FragColor = mix(blurred1, blurred2, (coc-0.33)/0.33);
+    }else{
+        gl_FragColor = mix(blurred2, blurred3, (coc-0.66)/0.34);
+    }
+#endif
 }

+ 2 - 2
src/Shaders/kernelBlur.fragment.fx

@@ -6,12 +6,12 @@ uniform vec2 delta;
 varying vec2 sampleCenter;
 
 #ifdef DOF
-	uniform sampler2D depthSampler;
+	uniform sampler2D circleOfConfusionSampler;
 
 	uniform vec2 cameraMinMaxZ;
 
 	float sampleDistance(const in vec2 offset) {
-		float depth = texture2D(depthSampler, offset).r; // depth value from DepthRenderer: 0 to 1 
+		float depth = texture2D(circleOfConfusionSampler, offset).g; // depth value from DepthRenderer: 0 to 1 
 		return cameraMinMaxZ.x + (cameraMinMaxZ.y - cameraMinMaxZ.x)*depth; // actual distance from the lens 
 	}
 #endif