Selaa lähdekoodia

Merge pull request #567 from jahow/lens_vfx_patch

Depth-of-field improvements
David Catuhe 10 vuotta sitten
vanhempi
commit
345a9fa3f8

+ 25 - 16
Babylon/PostProcess/babylon.lensRenderingPipeline.ts

@@ -43,8 +43,9 @@ module BABYLON {
         private _distortion: number;
         private _highlightsGain: number;
         private _highlightsThreshold: number;
-        private _dofDepth: number;
+        private _dofDistance: number;
         private _dofAperture: number;
+        private _dofDarken: number;
         private _dofPentagon: boolean;
         private _blurNoise: boolean;
 
@@ -59,11 +60,12 @@ module BABYLON {
          *      distortion: number;                 // from 0 to x (1 for realism)
          *      grain_amount: number;               // from 0 to 1
          *      grain_texture: BABYLON.Texture;     // texture to use for grain effect; if unset, use random B&W noise
-         *      dof_focus_depth: number;            // depth-of-field: focus depth; unset to disable (disabled by default)
+         *      dof_focus_distance: number;         // depth-of-field: focus distance; unset to disable (disabled by default)
          *      dof_aperture: number;               // depth-of-field: focus blur bias (default: 1)
+         *      dof_darken: number;                 // depth-of-field: darken that which is out of focus (from 0 to 1, disabled by default)
          *      dof_pentagon: boolean;              // depth-of-field: makes a pentagon-like "bokeh" effect
-         *      dof_gain: number;                   // depth-of-field: depthOfField gain; unset to disable (disabled by default)
-         *      dof_threshold: number;              // depth-of-field: depthOfField threshold (default: 1)
+         *      dof_gain: number;                   // depth-of-field: highlights gain; unset to disable (disabled by default)
+         *      dof_threshold: number;              // depth-of-field: highlights threshold (default: 1)
          *      blur_noise: boolean;                // add a little bit of noise to the blur (default: true)
          * }
          * Note: if an effect parameter is unset, effect is disabled
@@ -91,15 +93,16 @@ module BABYLON {
             this._distortion = parameters.distortion ? parameters.distortion : 0;
             this._highlightsGain = parameters.dof_gain !== undefined ? parameters.dof_gain : -1;
             this._highlightsThreshold = parameters.dof_threshold ? parameters.dof_threshold : 1;
-            this._dofDepth = parameters.dof_focus_depth !== undefined ? parameters.dof_focus_depth : -1;
+            this._dofDistance = parameters.dof_focus_distance !== undefined ? parameters.dof_focus_distance : -1;
             this._dofAperture = parameters.dof_aperture ? parameters.dof_aperture : 1;
+            this._dofDarken = parameters.dof_darken ? parameters.dof_darken : 0;
             this._dofPentagon = parameters.dof_pentagon !== undefined ? parameters.dof_pentagon : true;
             this._blurNoise = parameters.blur_noise !== undefined ? parameters.blur_noise : true;
 
             // Create effects
             this._createChromaticAberrationPostProcess(ratio);
             this._createHighlightsPostProcess(ratio);
-            this._createDepthOfFieldPostProcess(ratio);
+            this._createDepthOfFieldPostProcess(ratio/4);
 
             // Set up pipeline
             this.addEffect(new PostProcessRenderEffect(scene.getEngine(), this.LensChromaticAberrationEffect, () => { return this._chromaticAberrationPostProcess; }, true));
@@ -127,9 +130,10 @@ module BABYLON {
         public disableChromaticAberration() { this._chromaticAberration = 0; }
         public setEdgeDistortion(amount: number) { this._distortion = amount; }
         public disableEdgeDistortion() { this._distortion = 0; }
-        public setFocusDepth(amount: number) { this._dofDepth = amount; }
-        public disableDepthOfField() { this._dofDepth = -1; }
+        public setFocusDistance(amount: number) { this._dofDistance = amount; }
+        public disableDepthOfField() { this._dofDistance = -1; }
         public setAperture(amount: number) { this._dofAperture = amount; }
+        public setDarkenOutOfFocus(amount: number) { this._dofDarken = amount; }
         public enablePentagonBokeh() { this._dofPentagon = true; }
         public disablePentagonBokeh() { this._dofPentagon = false; }
         public enableNoiseBlur() { this._blurNoise = true; }
@@ -166,8 +170,8 @@ module BABYLON {
         // colors shifting and distortion
         private _createChromaticAberrationPostProcess(ratio: number): void {
             this._chromaticAberrationPostProcess = new PostProcess("LensChromaticAberration", "chromaticAberration",
-                ["chromatic_aberration", "screen_width", "screen_height"],		// uniforms
-                [],											// samplers
+                ["chromatic_aberration", "screen_width", "screen_height"],      // uniforms
+                [],                                         // samplers
                 ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
                 this._scene.getEngine(), false);
 
@@ -200,34 +204,39 @@ module BABYLON {
         private _createDepthOfFieldPostProcess(ratio: number): void {
             this._depthOfFieldPostProcess = new PostProcess("LensDepthOfField", "depthOfField",
                 [
-                    "focus_depth", "aperture", "pentagon", "maxZ", "edge_blur", "chromatic_aberration",
-                    "distortion", "blur_noise", "grain_amount", "screen_width", "screen_height", "highlights"
+                    "grain_amount", "blur_noise", "screen_width", "screen_height", "distortion", "dof_enabled",
+                    "screen_distance", "aperture", "darken", "edge_blur", "highlights", "near", "far"
                 ],
                 ["depthSampler", "grainSampler", "highlightsSampler"],
                 ratio, null, Texture.TRILINEAR_SAMPLINGMODE,
                 this._scene.getEngine(), false);
 
             this._depthOfFieldPostProcess.onApply = (effect: Effect) => {
-                effect.setBool('blur_noise', this._blurNoise);
-                effect.setFloat('maxZ', this._scene.activeCamera.maxZ);
-                effect.setFloat('grain_amount', this._grainAmount);
 
                 effect.setTexture("depthSampler", this._depthTexture);
                 effect.setTexture("grainSampler", this._grainTexture);
                 effect.setTextureFromPostProcess("textureSampler", this._highlightsPostProcess);
                 effect.setTextureFromPostProcess("highlightsSampler", this._depthOfFieldPostProcess);
 
+                effect.setFloat('grain_amount', this._grainAmount);
+                effect.setBool('blur_noise', this._blurNoise);
+
                 effect.setFloat('screen_width', this._scene.getEngine().getRenderingCanvas().width);
                 effect.setFloat('screen_height', this._scene.getEngine().getRenderingCanvas().height);
 
                 effect.setFloat('distortion', this._distortion);
 
-                effect.setFloat('focus_depth', this._dofDepth);
+                effect.setBool('dof_enabled', (this._dofDistance != -1));
+                effect.setFloat('screen_distance',  1.0 / ( 0.1 - 1.0 / this._dofDistance ));
                 effect.setFloat('aperture', this._dofAperture);
+                effect.setFloat('darken', this._dofDarken);
 
                 effect.setFloat('edge_blur', this._edgeBlur);
 
                 effect.setBool('highlights', (this._highlightsGain != -1));
+
+                effect.setFloat('near', this._scene.activeCamera.minZ);
+                effect.setFloat('far', this._scene.activeCamera.maxZ);
             };
         }
 

+ 99 - 73
Babylon/Shaders/depthOfField.fragment.fx

@@ -16,28 +16,45 @@ uniform sampler2D grainSampler;
 
 // uniforms
 uniform float grain_amount;
-uniform float maxZ;
 uniform bool blur_noise;
 uniform float screen_width;
 uniform float screen_height;
 uniform float distortion;
-uniform float focus_depth;
+uniform bool dof_enabled;
+//uniform float focus_distance;		// not needed; already used to compute screen distance
+uniform float screen_distance;		// precomputed screen distance from lens center; based on focal length & desired focus distance
 uniform float aperture;
+uniform float darken;
 uniform float edge_blur;
 uniform bool highlights;
 
+// preconputed uniforms (not effect parameters)
+uniform float near;
+uniform float far;
+
 // varyings
 varying vec2 vUV;
 
 // constants
-#define PI 3.14159265
+#define PI 		3.14159265
+#define TWOPI 	6.28318530
+#define inverse_focal_length 0.1	// a property of the lens used
 
 // common calculations
 vec2 centered_screen_pos;
+vec2 distorted_coords;
 float radius2;
 float radius;
 
 
+// on-the-fly constant noise
+vec2 rand(vec2 co)
+{
+	float noise1 = (fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453));
+	float noise2 = (fract(sin(dot(co, vec2(12.9898, 78.233)*2.0)) * 43758.5453));
+	return clamp(vec2(noise1, noise2), 0.0, 1.0);
+}
+
 // applies edge distortion on texture coords
 vec2 getDistortedCoords(vec2 coords) {
 
@@ -54,104 +71,111 @@ vec2 getDistortedCoords(vec2 coords) {
 	return dist_coords;
 }
 
+// sample screen with an offset (randomize offset angle for better smothness), returns partial sample weight
+float sampleScreen(inout vec4 color, const in vec2 offset, const in float weight) {
+
+	// compute coords with offset (a random angle is added)
+	vec2 coords = distorted_coords;
+	float angle = rand(coords * 100.0).x * TWOPI;
+	coords += vec2(offset.x * cos(angle) - offset.y * sin(angle), offset.x * sin(angle) + offset.y * cos(angle));
+
+	color += texture2D(textureSampler, coords)*weight;
+
+	return weight;
+}
+
+// returns blur level according to blur size required
+float getBlurLevel(float size) {
+	return min(3.0, ceil(size / 1.0));
+}
+
 // returns original screen color after blur
-vec4 getBlurColor(vec2 coords, float size) {
+vec4 getBlurColor(float size) {
 
-	vec4 col = texture2D(textureSampler, coords);
+	vec4 col = texture2D(textureSampler, distorted_coords);
 	if (size == 0.0) { return col; }
 
 	// there are max. 30 samples; the number of samples chosen is dependant on the blur size
 	// there can be 10, 20 or 30 samples chosen; levels of blur are then 1, 2 or 3
-	float blur_level = min(3.0, ceil(size / 1.0));
+	float blur_level = getBlurLevel(size);
 
 	float w = (size / screen_width);
 	float h = (size / screen_height);
 	float total_weight = 1.0;
-
-	col += texture2D(textureSampler, coords + vec2(-0.53*w, 0.15*h))*0.93;
-	col += texture2D(textureSampler, coords + vec2(0.42*w, -0.69*h))*0.90;
-	col += texture2D(textureSampler, coords + vec2(0.20*w, 1.00*h))*0.87;
-	col += texture2D(textureSampler, coords + vec2(-0.97*w, -0.72*h))*0.85;
-	col += texture2D(textureSampler, coords + vec2(1.37*w, -0.14*h))*0.83;
-	col += texture2D(textureSampler, coords + vec2(-1.02*w, 1.16*h))*0.80;
-	col += texture2D(textureSampler, coords + vec2(-0.03*w, -1.69*h))*0.78;
-	col += texture2D(textureSampler, coords + vec2(1.27*w, 1.34*h))*0.76;
-	col += texture2D(textureSampler, coords + vec2(-1.98*w, -0.14*h))*0.74;
-	col += texture2D(textureSampler, coords + vec2(1.66*w, -1.32*h))*0.72;
-	total_weight += 8.18;
+	vec2 sample_coords;
+
+	total_weight += sampleScreen(col, vec2(-0.50*w, 0.24*h), 0.93);
+	total_weight += sampleScreen(col, vec2(0.30*w, -0.75*h), 0.90);
+	total_weight += sampleScreen(col, vec2(0.36*w, 0.96*h), 0.87);
+	total_weight += sampleScreen(col, vec2(-1.08*w, -0.55*h), 0.85);
+	total_weight += sampleScreen(col, vec2(1.33*w, -0.37*h), 0.83);
+	total_weight += sampleScreen(col, vec2(-0.82*w, 1.31*h), 0.80);
+	total_weight += sampleScreen(col, vec2(-0.31*w, -1.67*h), 0.78);
+	total_weight += sampleScreen(col, vec2(1.47*w, 1.11*h), 0.76);
+	total_weight += sampleScreen(col, vec2(-1.97*w, 0.19*h), 0.74);
+	total_weight += sampleScreen(col, vec2(1.42*w, -1.57*h), 0.72);
 
 	if (blur_level > 1.0) {
-		col += texture2D(textureSampler, coords + vec2(-0.35*w, 2.22*h))*0.70;
-		col += texture2D(textureSampler, coords + vec2(-1.31*w, -1.98*h))*0.67;
-		col += texture2D(textureSampler, coords + vec2(2.42*w, 0.61*h))*0.65;
-		col += texture2D(textureSampler, coords + vec2(-2.31*w, 1.25*h))*0.63;
-		col += texture2D(textureSampler, coords + vec2(0.90*w, -2.59*h))*0.61;
-		col += texture2D(textureSampler, coords + vec2(1.14*w, 2.62*h))*0.59;
-		col += texture2D(textureSampler, coords + vec2(-2.72*w, -1.21*h))*0.56;
-		col += texture2D(textureSampler, coords + vec2(2.93*w, -0.98*h))*0.54;
-		col += texture2D(textureSampler, coords + vec2(-1.56*w, 2.80*h))*0.52;
-		col += texture2D(textureSampler, coords + vec2(-0.77*w, -3.22*h))*0.49;
-		total_weight += 5.96;
+		total_weight += sampleScreen(col, vec2(0.01*w, 2.25*h), 0.70);
+		total_weight += sampleScreen(col, vec2(-1.62*w, -1.74*h), 0.67);
+		total_weight += sampleScreen(col, vec2(2.49*w, 0.20*h), 0.65);
+		total_weight += sampleScreen(col, vec2(-2.07*w, 1.61*h), 0.63);
+		total_weight += sampleScreen(col, vec2(0.46*w, -2.70*h), 0.61);
+		total_weight += sampleScreen(col, vec2(1.55*w, 2.40*h), 0.59);
+		total_weight += sampleScreen(col, vec2(-2.88*w, -0.75*h), 0.56);
+		total_weight += sampleScreen(col, vec2(2.73*w, -1.44*h), 0.54);
+		total_weight += sampleScreen(col, vec2(-1.08*w, 3.02*h), 0.52);
+		total_weight += sampleScreen(col, vec2(-1.28*w, -3.05*h), 0.49);
 	}
 
 	if (blur_level > 2.0) {
-		col += texture2D(textureSampler, coords + vec2(2.83*w, 1.92*h))*0.46;
-		col += texture2D(textureSampler, coords + vec2(-3.49*w, 0.51*h))*0.44;
-		col += texture2D(textureSampler, coords + vec2(2.30*w, -2.82*h))*0.41;
-		col += texture2D(textureSampler, coords + vec2(0.22*w, 3.74*h))*0.38;
-		col += texture2D(textureSampler, coords + vec2(-2.76*w, -2.68*h))*0.34;
-		col += texture2D(textureSampler, coords + vec2(3.95*w, 0.11*h))*0.31;
-		col += texture2D(textureSampler, coords + vec2(-3.07*w, 2.65*h))*0.26;
-		col += texture2D(textureSampler, coords + vec2(0.48*w, -4.13*h))*0.22;
-		col += texture2D(textureSampler, coords + vec2(2.49*w, 3.46*h))*0.15;
-		total_weight += 2.97;
+		total_weight += sampleScreen(col, vec2(3.11*w, 1.43*h), 0.46);
+		total_weight += sampleScreen(col, vec2(-3.36*w, 1.08*h), 0.44);
+		total_weight += sampleScreen(col, vec2(1.80*w, -3.16*h), 0.41);
+		total_weight += sampleScreen(col, vec2(0.83*w, 3.65*h), 0.38);
+		total_weight += sampleScreen(col, vec2(-3.16*w, -2.19*h), 0.34);
+		total_weight += sampleScreen(col, vec2(3.92*w, -0.53*h), 0.31);
+		total_weight += sampleScreen(col, vec2(-2.59*w, 3.12*h), 0.26);
+		total_weight += sampleScreen(col, vec2(-0.20*w, -4.15*h), 0.22);
+		total_weight += sampleScreen(col, vec2(3.02*w, 3.00*h), 0.15);
 	}
 
 	col /= total_weight;		// scales color according to weights
-	col.a = 1.0;
+
+	// darken if out of focus
+	if(darken > 0.0) {
+		col.rgb *= clamp(0.3, 1.0, 1.05-size*0.5*darken);
+	}
 
 	// blur levels debug
-	// if(blur_level == 1.0) { col.b = 0.0; }
-	// if(blur_level == 2.0) { col.r = 0.0; }
-	// if(blur_level == 3.0) { col.g = 0.0; }
+	// if(blur_level == 1.0) { col.b *= 0.5; }
+	// if(blur_level == 2.0) { col.r *= 0.5; }
+	// if(blur_level == 3.0) { col.g *= 0.5; }
 
 	return col;
 }
 
-// on-the-fly constant noise
-vec2 rand(vec2 co)
-{
-	float noise1 = (fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453));
-	float noise2 = (fract(sin(dot(co, vec2(12.9898, 78.233)*2.0)) * 43758.5453));
-	return clamp(vec2(noise1, noise2), 0.0, 1.0);
-}
-
 void main(void)
 {
 
-	// Common calc
+	// Common calc: position relative to screen center, screen radius, distorted coords, position in texel space
 	centered_screen_pos = vec2(vUV.x - 0.5, vUV.y - 0.5);
 	radius2 = centered_screen_pos.x*centered_screen_pos.x + centered_screen_pos.y*centered_screen_pos.y;
 	radius = sqrt(radius2);
-
-	vec4 final_color;
-	vec2 distorted_coords = getDistortedCoords(vUV);
+	distorted_coords = getDistortedCoords(vUV);		// we distort the screen coordinates (lens "magnifying" effect)
 	vec2 texels_coords = vec2(vUV.x * screen_width, vUV.y * screen_height);	// varies from 0 to SCREEN_WIDTH or _HEIGHT
 
-	// blur from depth of field effect
-	float dof_blur_amount = 0.0;
-	float depth_bias = 0.0;		// positive if the pixel is further than focus depth; negative if closer
-	if (focus_depth != -1.0) {
-		vec4 depth_sample = texture2D(depthSampler, distorted_coords);
-		float depth = depth_sample.r;
-		depth_bias = depth - focus_depth;
+	float depth = texture2D(depthSampler, distorted_coords).r;	// depth value from DepthRenderer: 0 to 1
+	float distance = near + (far-near)*depth;		// actual distance from the lens
+	vec4 color = texture2D(textureSampler, vUV);	// original raster
 
-		// compute blur amount with distance
-		if (depth_bias > 0.0) { dof_blur_amount = depth_bias * aperture * 2.2; }
-		else { dof_blur_amount = depth_bias * depth_bias * aperture * 30.0; }
 
-		if (dof_blur_amount < 0.05) { dof_blur_amount = 0.0; }	// no blur at all
-	}
+	// compute the circle of confusion size (CoC), i.e. blur radius depending on depth
+	// screen_distance is precomputed in code
+	float coc = abs( aperture * ( screen_distance * ( inverse_focal_length - 1.0 / distance ) - 1.0 ) );
+
+	// disable blur
+	if (dof_enabled == false || coc < 0.07) { coc = 0.0; }
 
 	// blur from edge blur effect
 	float edge_blur_amount = 0.0;
@@ -160,7 +184,7 @@ void main(void)
 	}
 
 	// total blur amount
-	float blur_amount = max(edge_blur_amount, dof_blur_amount);
+	float blur_amount = max(edge_blur_amount, coc);
 
 	// apply blur if necessary
 	if (blur_amount == 0.0) {
@@ -169,11 +193,11 @@ void main(void)
 	else {
 
 		// add blurred color
-		gl_FragColor = getBlurColor(distorted_coords, blur_amount * 1.7);
+		gl_FragColor = getBlurColor(blur_amount * 1.7);
 
-		// if further than focus depth & we have computed highlights: enhance highlights
-		if (depth_bias > 0.0 && highlights) {
-			gl_FragColor += clamp(dof_blur_amount, 0.0, 1.0)*texture2D(highlightsSampler, distorted_coords);
+		// if we have computed highlights: enhance highlights
+		if (highlights) {
+			gl_FragColor.rgb += clamp(coc, 0.0, 1.0)*texture2D(highlightsSampler, distorted_coords).rgb;
 		}
 
 		if (blur_noise) {
@@ -184,9 +208,11 @@ void main(void)
 		}
 	}
 
+
 	// apply grain
 	if (grain_amount > 0.0) {
 		vec4 grain_color = texture2D(grainSampler, texels_coords*0.003);
-		gl_FragColor.rgb += (-0.5 + grain_color.rgb) * 0.20;
+		gl_FragColor.rgb += (-0.5 + grain_color.rgb) * 0.30 * grain_amount;
 	}
+
 }