Explorar el Código

Added kernel blur

David Catuhe hace 8 años
padre
commit
ecf42d1521

+ 8 - 1
Tools/Gulp/config.json

@@ -581,7 +581,8 @@
                 "postProcesses"
             ], 
             "shaders" : [
-                "blur.fragment",
+                "kernelBlur.vertex",
+                "kernelBlur.fragment",
                 "refraction.fragment",
                 "blackAndWhite.fragment",
                 "convolution.fragment",
@@ -592,6 +593,12 @@
                 "colorCorrection.fragment",
                 "tonemap.fragment",
                 "displayPass.fragment"
+            ],
+            "shadersIncludeFiles": [
+                "kernelBlurFragment",
+                "kernelBlurFragment2",
+                "kernelBlurVaryingDeclaration",
+                "kernelBlurVertex"
             ]
         },                         
         "renderingPipeline" : 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 825 - 786
dist/preview release/babylon.d.ts


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 23 - 23
dist/preview release/babylon.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 158 - 16
dist/preview release/babylon.max.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 825 - 786
dist/preview release/babylon.module.d.ts


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 17 - 17
dist/preview release/babylon.worker.js


+ 2 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -406,7 +406,9 @@ declare module BABYLON.GUI {
         pointerOutAnimation: () => void;
         pointerDownAnimation: () => void;
         pointerUpAnimation: () => void;
+        private _buttonIsDown;
         constructor(name?: string);
+        private _ensureButtonUp();
         _processPicking(x: number, y: number, type: number): boolean;
         protected _onPointerEnter(): void;
         protected _onPointerOut(): void;

+ 13 - 3
dist/preview release/gui/babylon.gui.js

@@ -2307,6 +2307,7 @@ var BABYLON;
             function Button(name) {
                 var _this = _super.call(this, name) || this;
                 _this.name = name;
+                _this._buttonIsDown = false;
                 _this.thickness = 1;
                 _this.isPointerBlocker = true;
                 _this.pointerEnterAnimation = function () {
@@ -2325,6 +2326,14 @@ var BABYLON;
                 };
                 return _this;
             }
+            Button.prototype._ensureButtonUp = function () {
+                if (this._buttonIsDown === true) {
+                    if (this.pointerUpAnimation) {
+                        this.pointerUpAnimation();
+                    }
+                    this._buttonIsDown = false;
+                }
+            };
             // While being a container, the button behaves like a control.
             Button.prototype._processPicking = function (x, y, type) {
                 if (!this.contains(x, y)) {
@@ -2343,18 +2352,19 @@ var BABYLON;
                 if (this.pointerOutAnimation) {
                     this.pointerOutAnimation();
                 }
+                // in case you move the pointer out of view while pointer is "down".
+                this._ensureButtonUp();
                 _super.prototype._onPointerOut.call(this);
             };
             Button.prototype._onPointerDown = function () {
                 if (this.pointerDownAnimation) {
                     this.pointerDownAnimation();
                 }
+                this._buttonIsDown = true;
                 _super.prototype._onPointerDown.call(this);
             };
             Button.prototype._onPointerUp = function () {
-                if (this.pointerUpAnimation) {
-                    this.pointerUpAnimation();
-                }
+                this._ensureButtonUp();
                 _super.prototype._onPointerUp.call(this);
             };
             // Statics

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


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

@@ -14,13 +14,14 @@
  - Support for multisample render targets. [Demo](http://www.babylonjs-playground.com/#12MKMN) ([deltakosh](https://github.com/deltakosh))
  - New Unity 5 Editor Toolkit. Complete pipeline integration [Doc](TODO) - ([MackeyK24](https://github.com/MackeyK24))
  - New DebugLayer. [Doc](TODO) - ([temechon](https://github.com/temechon))
- - New `VideoTexture.CreateFromWebCam` to generate video texture using WebRTC. [Demo](https://www.babylonjs-playground.com#1R77YT#2) - (Sebastien Vandenberghe)(https://github.com/sebavanmicrosoft) / ([deltakosh](https://github.com/deltakosh))
+ - New `VideoTexture.CreateFromWebCam` to generate video texture using WebRTC. [Demo](https://www.babylonjs-playground.com#1R77YT#2) (Sebastien Vandenberghe)(https://github.com/sebavanmicrosoft) / ([deltakosh](https://github.com/deltakosh))
  - New Facet Data feature ([jerome](https://github.com/jbousquie))
  - babylon.fontTexture.ts was moved from babylon.js to canvas2D ([nockawa](https://github.com/nockawa))
  - Multi-platform Compressed Textures for Desktops & Mobile Devices with fall back.  Batch (dos) scripts to convert entire directories of .jpg's & .png's [Doc](http://doc.babylonjs.com/tutorials/multi-platform_compressed_textures) ([jcpalmer](https://github.com/Palmer-JC))
  - All deprecated functions and properties were removed ([deltakosh](https://github.com/deltakosh))
  - New build system based on workloads. [More info here](http://doc.babylonjs.com/generals/how_to_start#custom-builds) ([deltakosh](https://github.com/deltakosh))
- - New `Cell Shading` material added into `Materials Library` [Demo](http://www.babylonjs.com/Demos/CellShading/) - ([Julien Moreau-Mathis](https://github.com/julien-moreau))
+ - New `Cell Shading` material added into `Materials Library` [Demo](http://www.babylonjs.com/Demos/CellShading/) ([Julien Moreau-Mathis](https://github.com/julien-moreau))
+ - New kernel based blur. [Demo]( ([deltakosh](https://github.com/deltakosh))
  
 ### Updates
 - Added `ArcRotateCamera.panningInertia` to decouple inertia from panning inertia ([deltakosh](https://github.com/deltakosh))
@@ -66,7 +67,7 @@
 - Fixed SPS particle access start index when used with `setParticles(start, end)` ([jerome](https://github.com/jbousquie))  
 
 ### API Documentation
-`- File `abstractMesh.ts` documented  ([jerome](https://github.com/jbousquie))  
+`- File `abstractMesh.ts` documented ([jerome](https://github.com/jbousquie))  
 - File `mesh.ts` documented ([jerome](https://github.com/jbousquie))  
 - File `groundMesh.ts` documented ([jerome](https://github.com/jbousquie))  
 - File `instancedMesh.ts` documented ([jerome](https://github.com/jbousquie))  

+ 161 - 6
src/PostProcess/babylon.blurPostProcess.ts

@@ -1,13 +1,168 @@
 module BABYLON {
     export class BlurPostProcess extends PostProcess {
-        constructor(name: string, public direction: Vector2, public blurWidth: number, options: number | PostProcessOptions, camera: Camera, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
-            super(name, "blur", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
+		protected _kernel: number;
+		protected _idealKernel: number;
+
+		/**
+		 * Sets the length in pixels of the blur sample region
+		 */
+		public set kernel(v: number) {
+			v = Math.max(v, 1);
+			this._idealKernel = v;
+			this._kernel = this._nearestBestKernel(v);
+            this._updateParameters();
+		}
+
+		/**
+		 * Gets the length in pixels of the blur sample region
+		 */
+		public get kernel(): number {
+			return this._idealKernel;
+		}
+
+        constructor(name: string, public direction: Vector2, kernel: number, options: number | PostProcessOptions, camera: Camera, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
+            super(name, "kernelBlur", ["delta", "direction"], null, options, camera, samplingMode, engine, reusable, null, Engine.TEXTURETYPE_UNSIGNED_INT, "kernelBlur", {varyingCount: 0, depCount: 0}, true);
             this.onApplyObservable.add((effect: Effect) => {
-                effect.setFloat2("screenSize", this.width, this.height);
-                effect.setVector2("direction", this.direction);
-                effect.setFloat("blurWidth", this.blurWidth);
+                effect.setFloat2('delta', (1 / this.width) * this.direction.x, (1 / this.height) * this.direction.y);
             });
+
+            this.kernel = kernel;
         }
-    
+
+        protected _updateParameters(): void {
+            // Generate sampling offsets and weights
+			let N = this._kernel;
+			let centerIndex = (N - 1) / 2;
+
+			// Generate Gaussian sampling weights over kernel
+			let offsets = [];
+			let weights = [];
+			let totalWeight = 0;
+			for (let i = 0; i < N; i++) {
+				let u = i / (N - 1);
+				let w = this._gaussianWeight(u * 2.0 - 1);
+				offsets[i] = (i - centerIndex);
+				weights[i] = w;
+				totalWeight += w;
+			}
+
+			// Normalize weights
+			for (let i = 0; i < weights.length; i++) {
+				weights[i] /= totalWeight;
+			}
+
+			// Optimize: combine samples to take advantage of hardware linear sampling
+			// Walk from left to center, combining pairs (symmetrically)
+			let linearSamplingWeights = [];
+			let linearSamplingOffsets = [];
+
+			let linearSamplingMap = [];
+
+			for (let i = 0; i <= centerIndex; i += 2) {
+				let j = Math.min(i + 1, Math.floor(centerIndex));
+
+				let singleCenterSample = i === j;
+
+				if (singleCenterSample) {
+					linearSamplingMap.push({ o: offsets[i], w: weights[i] });
+				} else {
+					let sharedCell = j === centerIndex;
+
+					let weightLinear = (weights[i] + weights[j] * (sharedCell ? .5 : 1.));
+					let offsetLinear = offsets[i] + 1 / (1 + weights[i] / weights[j]);
+
+					if (offsetLinear === 0) {
+						linearSamplingMap.push({ o: offsets[i], w: weights[i] });
+						linearSamplingMap.push({ o: offsets[i + 1], w: weights[i + 1] });
+					} else {
+						linearSamplingMap.push({ o: offsetLinear, w: weightLinear });
+						linearSamplingMap.push({ o: -offsetLinear, w: weightLinear });
+					}
+
+				}
+			}
+
+			for (let i = 0; i < linearSamplingMap.length; i++) {
+				linearSamplingOffsets[i] = linearSamplingMap[i].o;
+				linearSamplingWeights[i] = linearSamplingMap[i].w;
+			}
+
+			// Replace with optimized
+			offsets = linearSamplingOffsets;
+			weights = linearSamplingWeights;
+
+			// Generate shaders
+			let maxVaryingRows = this.getEngine().getCaps().maxVaryingVectors;
+			let freeVaryingVec2 = Math.max(maxVaryingRows, 0.) - 1; // Because of sampleCenter
+
+            let varyingCount = Math.min(offsets.length, freeVaryingVec2);
+        
+            let defines = "";
+            for (let i = 0; i < varyingCount; i++) {
+                defines += `#define KERNEL_OFFSET${i} ${this._glslFloat(offsets[i])}\r\n`;
+                defines += `#define KERNEL_WEIGHT${i} ${this._glslFloat(weights[i])}\r\n`;
+			}
+
+            let depCount = 0;
+            for (let i = freeVaryingVec2; i < offsets.length; i++) {
+                defines += `#define KERNEL_DEP_OFFSET${depCount} ${this._glslFloat(offsets[i])}\r\n`;
+                defines += `#define KERNEL_DEP_WEIGHT${depCount} ${this._glslFloat(weights[i])}\r\n`;
+                depCount++;
+			}
+
+            this._indexParameters.varyingCount = varyingCount;
+            this._indexParameters.depCount = depCount;
+
+            this.updateEffect(defines);
+        }
+
+        /**
+		 * Best kernels are odd numbers that when divided by 2, their integer part is even, so 5, 9 or 13.
+		 * Other odd kernels optimize correctly but require proportionally more samples, even kernels are
+		 * possible but will produce minor visual artifacts. Since each new kernel requires a new shader we
+		 * want to minimize kernel changes, having gaps between physical kernels is helpful in that regard.
+		 * The gaps between physical kernels are compensated for in the weighting of the samples
+		 * @param idealKernel Ideal blur kernel.
+		 * @return Nearest best kernel.
+		 */
+		protected _nearestBestKernel(idealKernel: number): number {
+			let v = Math.round(idealKernel);
+			for (let k of [v, v - 1, v + 1, v - 2, v + 2]) {
+				if (((k % 2) !== 0) && ((Math.floor(k / 2) % 2) === 0) && k > 0) {
+					return Math.max(k, 3);
+				}
+			}
+			return Math.max(v, 3);
+		}
+
+		/**
+		 * Calculates the value of a Gaussian distribution with sigma 3 at a given point.
+		 * @param x The point on the Gaussian distribution to sample.
+		 * @return the value of the Gaussian function at x.
+		 */
+		protected _gaussianWeight(x: number): number {
+			//reference: Engine/ImageProcessingBlur.cpp #dcc760
+			// We are evaluating the Gaussian (normal) distribution over a kernel parameter space of [-1,1],
+			// so we truncate at three standard deviations by setting stddev (sigma) to 1/3.
+			// The choice of 3-sigma truncation is common but arbitrary, and means that the signal is
+			// truncated at around 1.3% of peak strength.
+
+			//the distribution is scaled to account for the difference between the actual kernel size and the requested kernel size
+			let sigma = (1 / 3);
+			let denominator = Math.sqrt(2.0 * Math.PI) * sigma;
+			let exponent = -((x * x) / (2.0 * sigma * sigma));
+			let weight = (1.0 / denominator) * Math.exp(exponent);
+			return weight;
+		}      
+
+       /**
+		 * Generates a string that can be used as a floating point number in GLSL.
+		 * @param x Value to print.
+		 * @param decimalFigures Number of decimal places to print the number to (excluding trailing 0s).
+		 * @return GLSL float string.
+		 */
+		protected _glslFloat(x: number, decimalFigures = 8) {
+			return x.toFixed(decimalFigures).replace(/0+$/, '');
+		}      
     }
 } 

+ 20 - 5
src/PostProcess/babylon.postProcess.ts

@@ -30,6 +30,7 @@
         private _vertexUrl: string;
         private _parameters: string[];
         private _scaleRatio = new Vector2(1, 1);
+        protected _indexParameters: any;
 
         // Events
 
@@ -103,7 +104,7 @@
             this._onAfterRenderObserver = this.onAfterRenderObservable.add(callback);
         }
 
-        constructor(public name: string, fragmentUrl: string, parameters: string[], samplers: string[], options: number | PostProcessOptions, camera: Camera, samplingMode: number = Texture.NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean, defines?: string, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, vertexUrl: string = 'postprocess') {
+        constructor(public name: string, fragmentUrl: string, parameters: string[], samplers: string[], options: number | PostProcessOptions, camera: Camera, samplingMode: number = Texture.NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean, defines?: string, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, vertexUrl: string = "postprocess", indexParameters?: any, blockCompilation = false) {
             if (camera != null) {
                 this._camera = camera;
                 this._scene = camera.getScene();
@@ -128,14 +129,28 @@
 
             this._parameters.push("scale");
 
-            this.updateEffect(defines);
+            this._indexParameters = indexParameters;
+
+            if (!blockCompilation) {
+                this.updateEffect(defines);
+            }
         }
+
+        public getEngine(): Engine {
+            return this._engine;
+        }        
         
-        public updateEffect(defines?: string, uniforms?: string[], samplers?: string[]) {
+        public updateEffect(defines?: string, uniforms?: string[], samplers?: string[], indexParameters?: any) {
             this._effect = this._engine.createEffect({ vertex: this._vertexUrl, fragment: this._fragmentUrl },
                 ["position"],
                 uniforms || this._parameters,
-                samplers || this._samplers, defines !== undefined ? defines : "");
+                samplers || this._samplers, 
+                defines !== undefined ? defines : "",
+                null,
+                null,
+                null,
+                indexParameters || this._indexParameters
+                );
         }
 
         public isReusable(): boolean {
@@ -232,7 +247,7 @@
 
         public apply(): Effect {
             // Check
-            if (!this._effect.isReady())
+            if (!this._effect || !this._effect.isReady())
                 return null;
 
             // States

+ 1 - 0
src/Shaders/ShadersInclude/kernelBlurFragment.fx

@@ -0,0 +1 @@
+blend += texture2D(textureSampler, sampleCoord{X}) * KERNEL_WEIGHT{X};

+ 1 - 0
src/Shaders/ShadersInclude/kernelBlurFragment2.fx

@@ -0,0 +1 @@
+blend += texture2D(textureSampler, sampleCenter + delta * KERNEL_DEP_OFFSET{X}) * KERNEL_DEP_WEIGHT{X};

+ 1 - 0
src/Shaders/ShadersInclude/kernelBlurVaryingDeclaration.fx

@@ -0,0 +1 @@
+varying vec2 sampleCoord{X};

+ 1 - 0
src/Shaders/ShadersInclude/kernelBlurVertex.fx

@@ -0,0 +1 @@
+sampleCoord{X} = sampleCenter + delta * KERNEL_OFFSET{X};

+ 15 - 0
src/Shaders/kernelBlur.fragment.fx

@@ -0,0 +1,15 @@
+// Parameters
+uniform sampler2D textureSampler;
+uniform vec2 delta;
+
+// Varying
+varying vec2 sampleCenter;
+#include<kernelBlurVaryingDeclaration>[0..varyingCount]
+
+void main(void)
+{
+	vec4 blend = vec4(0.);
+	#include<kernelBlurFragment>[0..varyingCount]
+	#include<kernelBlurFragment2>[0..depCount]
+	gl_FragColor = blend;
+}

+ 20 - 0
src/Shaders/kernelBlur.vertex.fx

@@ -0,0 +1,20 @@
+// Attributes
+attribute vec2 position;
+
+// Uniform
+uniform vec2 delta;
+
+// Output
+varying vec2 sampleCenter;
+#include<kernelBlurVaryingDeclaration>[0..varyingCount]
+
+const vec2 madd = vec2(0.5, 0.5);
+
+void main(void) {	
+
+	sampleCenter = (position * madd + madd);
+
+	#include<kernelBlurVertex>[0..varyingCount]
+
+	gl_Position = vec4(position, 0.0, 1.0);
+}