Browse Source

Merge pull request #51281 from BastiaanOlij/revert_50723

Revert "Combined the DOF far and DOF near passes"
Rémi Verschelde 4 years ago
parent
commit
d835b46e09

+ 2 - 2
doc/classes/Environment.xml

@@ -121,7 +121,7 @@
 			The length of the transition between the no-blur area and far blur.
 		</member>
 		<member name="dof_blur_near_amount" type="float" setter="set_dof_blur_near_amount" getter="get_dof_blur_near_amount" default="0.1">
-			The amount of near blur for the depth-of-field effect. [member dof_blur_far_amount] is used if both far and near DOF are enabled.
+			The amount of near blur for the depth-of-field effect.
 		</member>
 		<member name="dof_blur_near_distance" type="float" setter="set_dof_blur_near_distance" getter="get_dof_blur_near_distance" default="2.0">
 			Distance from the camera where the near blur effect affects the rendering.
@@ -130,7 +130,7 @@
 			If [code]true[/code], enables the depth-of-field near blur effect.
 		</member>
 		<member name="dof_blur_near_quality" type="int" setter="set_dof_blur_near_quality" getter="get_dof_blur_near_quality" enum="Environment.DOFBlurQuality" default="1">
-			The depth-of-field near blur's quality. Higher values can mitigate the visible banding effect seen at higher strengths, but are much slower. [member dof_blur_far_quality] is used if both far and near DOF are enabled.
+			The depth-of-field near blur's quality. Higher values can mitigate the visible banding effect seen at higher strengths, but are much slower.
 		</member>
 		<member name="dof_blur_near_transition" type="float" setter="set_dof_blur_near_transition" getter="get_dof_blur_near_transition" default="1.0">
 			The length of the transition between the near blur and no-blur area.

+ 93 - 21
drivers/gles2/rasterizer_scene_gles2.cpp

@@ -2999,36 +2999,23 @@ void RasterizerSceneGLES2::_post_process(Environment *env, const CameraMatrix &p
 
 	// DOF Blur
 
-	if (env && (env->dof_blur_near_enabled || env->dof_blur_far_enabled)) {
+	if (env && env->dof_blur_far_enabled) {
 		int vp_h = storage->frame.current_rt->height;
 		int vp_w = storage->frame.current_rt->width;
 
-		// If both near and far are used, we use the far quality and amount settings.
-		// We should just have one setting like in Godot 4 but that would be a serious breaking change.
-		// This is defendable.
-		float dof_blur_amount = env->dof_blur_far_enabled ? env->dof_blur_far_amount : env->dof_blur_near_amount;
-		VS::EnvironmentDOFBlurQuality quality = env->dof_blur_far_enabled ? env->dof_blur_far_quality : env->dof_blur_near_quality;
-
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal());
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_FAR_BLUR, env->dof_blur_far_enabled);
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_NEAR_BLUR, env->dof_blur_near_enabled);
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_LOW, quality == VS::ENV_DOF_BLUR_QUALITY_LOW);
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_MEDIUM, quality == VS::ENV_DOF_BLUR_QUALITY_MEDIUM);
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_HIGH, quality == VS::ENV_DOF_BLUR_QUALITY_HIGH);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_FAR_BLUR, true);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_LOW, env->dof_blur_far_quality == VS::ENV_DOF_BLUR_QUALITY_LOW);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_MEDIUM, env->dof_blur_far_quality == VS::ENV_DOF_BLUR_QUALITY_MEDIUM);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_HIGH, env->dof_blur_far_quality == VS::ENV_DOF_BLUR_QUALITY_HIGH);
 
 		state.effect_blur_shader.bind();
 		int qsteps[3] = { 4, 10, 20 };
 
-		float radius = (dof_blur_amount * dof_blur_amount) / qsteps[quality];
+		float radius = (env->dof_blur_far_amount * env->dof_blur_far_amount) / qsteps[env->dof_blur_far_quality];
 
-		if (env->dof_blur_far_enabled) {
-			state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_FAR_BEGIN, env->dof_blur_far_distance);
-			state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_FAR_END, env->dof_blur_far_distance + env->dof_blur_far_transition);
-		}
-		if (env->dof_blur_near_enabled) {
-			state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_NEAR_BEGIN, env->dof_blur_near_distance);
-			state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_NEAR_END, env->dof_blur_near_distance - env->dof_blur_near_transition);
-		}
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_BEGIN, env->dof_blur_far_distance);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_END, env->dof_blur_far_distance + env->dof_blur_far_transition);
 		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_DIR, Vector2(1, 0));
 		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_RADIUS, radius);
 		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::PIXEL_SIZE, Vector2(1.0 / vp_w, 1.0 / vp_h));
@@ -3062,12 +3049,97 @@ void RasterizerSceneGLES2::_post_process(Environment *env, const CameraMatrix &p
 		storage->_copy_screen();
 
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_FAR_BLUR, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_LOW, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_MEDIUM, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_HIGH, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::USE_ORTHOGONAL_PROJECTION, false);
+	}
+
+	if (env && env->dof_blur_near_enabled) {
+		//convert texture to RGBA format if not already
+		if (!storage->frame.current_rt->used_dof_blur_near) {
+			glActiveTexture(GL_TEXTURE0);
+			glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->color);
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, storage->frame.current_rt->width, storage->frame.current_rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+		}
+
+		int vp_h = storage->frame.current_rt->height;
+		int vp_w = storage->frame.current_rt->width;
+
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal());
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_NEAR_BLUR, true);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_NEAR_FIRST_TAP, true);
+
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_LOW, env->dof_blur_near_quality == VS::ENV_DOF_BLUR_QUALITY_LOW);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_MEDIUM, env->dof_blur_near_quality == VS::ENV_DOF_BLUR_QUALITY_MEDIUM);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_HIGH, env->dof_blur_near_quality == VS::ENV_DOF_BLUR_QUALITY_HIGH);
+
+		state.effect_blur_shader.bind();
+		int qsteps[3] = { 4, 10, 20 };
+
+		float radius = (env->dof_blur_near_amount * env->dof_blur_near_amount) / qsteps[env->dof_blur_near_quality];
+
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_BEGIN, env->dof_blur_near_distance);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_END, env->dof_blur_near_distance - env->dof_blur_near_transition);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_DIR, Vector2(1, 0));
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_RADIUS, radius);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::PIXEL_SIZE, Vector2(1.0 / vp_w, 1.0 / vp_h));
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::CAMERA_Z_NEAR, p_cam_projection.get_z_near());
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::CAMERA_Z_FAR, p_cam_projection.get_z_far());
+
+		glActiveTexture(GL_TEXTURE1);
+		glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->depth);
+
+		glActiveTexture(GL_TEXTURE0);
+		if (storage->frame.current_rt->mip_maps[0].color) {
+			glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->mip_maps[0].color);
+		} else {
+			glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->mip_maps[0].sizes[0].color);
+		}
+
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+		glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); //copy to front first
+
+		storage->_copy_screen();
+
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_NEAR_FIRST_TAP, false);
+		state.effect_blur_shader.bind();
+
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_BEGIN, env->dof_blur_near_distance);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_END, env->dof_blur_near_distance - env->dof_blur_near_transition);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_DIR, Vector2(0, 1));
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::DOF_RADIUS, radius);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::PIXEL_SIZE, Vector2(1.0 / vp_w, 1.0 / vp_h));
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::CAMERA_Z_NEAR, p_cam_projection.get_z_near());
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES2::CAMERA_Z_FAR, p_cam_projection.get_z_far());
+
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->color);
+
+		glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->mip_maps[0].sizes[0].fbo); // copy to base level
+
+		glEnable(GL_BLEND);
+		glBlendEquation(GL_FUNC_ADD);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+		storage->_copy_screen();
+
+		glDisable(GL_BLEND);
+
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_NEAR_BLUR, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_NEAR_FIRST_TAP, false);
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_LOW, false);
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_MEDIUM, false);
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::DOF_QUALITY_HIGH, false);
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES2::USE_ORTHOGONAL_PROJECTION, false);
+		storage->frame.current_rt->used_dof_blur_near = true;
+	}
 
+	if (env && (env->dof_blur_near_enabled || env->dof_blur_far_enabled)) {
 		//these needed to disable filtering, reenamble
 		glActiveTexture(GL_TEXTURE0);
 		if (storage->frame.current_rt->mip_maps[0].color) {

+ 60 - 42
drivers/gles2/shaders/effect_blur.glsl

@@ -105,10 +105,8 @@ const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.0
 #endif
 
 uniform sampler2D dof_source_depth; //texunit:1
-uniform float dof_far_begin;
-uniform float dof_far_end;
-uniform float dof_near_begin;
-uniform float dof_near_end;
+uniform float dof_begin;
+uniform float dof_end;
 uniform vec2 dof_dir;
 uniform float dof_radius;
 
@@ -212,7 +210,7 @@ void main() {
 #endif
 #endif //!USE_GLES_OVER_GL
 
-#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR)
+#ifdef DOF_FAR_BLUR
 
 	vec4 color_accum = vec4(0.0);
 
@@ -224,59 +222,79 @@ void main() {
 	depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near));
 #endif
 
-	// Combine near and far, our depth is unlikely to be in both ranges
-	float amount = 1.0;
-#ifdef DOF_FAR_BLUR
-	amount *= 1.0 - smoothstep(dof_far_begin, dof_far_end, depth);
-#endif
-#ifdef DOF_NEAR_BLUR
-	amount *= smoothstep(dof_near_end, dof_near_begin, depth);
-#endif
-	amount = 1.0 - amount;
-
-	if (amount > 0.0) {
-		float k_accum = 0.0;
+	float amount = smoothstep(dof_begin, dof_end, depth);
+	float k_accum = 0.0;
 
-		for (int i = 0; i < dof_kernel_size; i++) {
-			int int_ofs = i - dof_kernel_from;
-			vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * amount * dof_radius;
+	for (int i = 0; i < dof_kernel_size; i++) {
+		int int_ofs = i - dof_kernel_from;
+		vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * amount * dof_radius;
 
-			float tap_k = dof_kernel[i];
+		float tap_k = dof_kernel[i];
 
-			float tap_depth = texture2D(dof_source_depth, tap_uv, 0.0).r;
-			tap_depth = tap_depth * 2.0 - 1.0;
+		float tap_depth = texture2D(dof_source_depth, tap_uv, 0.0).r;
+		tap_depth = tap_depth * 2.0 - 1.0;
 #ifdef USE_ORTHOGONAL_PROJECTION
-			tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
+		tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
 #else
-			tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
+		tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
 #endif
-			float tap_amount = 1.0;
-#ifdef DOF_FAR_BLUR
-			tap_amount *= int_ofs == 0 ? 0.0 : (1.0 - smoothstep(dof_far_begin, dof_far_end, tap_depth));
+		float tap_amount = int_ofs == 0 ? 1.0 : smoothstep(dof_begin, dof_end, tap_depth);
+		tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
+
+		vec4 tap_color = texture2DLod(source_color, tap_uv, 0.0) * tap_k;
+
+		k_accum += tap_k * tap_amount;
+		color_accum += tap_color * tap_amount;
+	}
+
+	if (k_accum > 0.0) {
+		color_accum /= k_accum;
+	}
+
+	gl_FragColor = color_accum; ///k_accum;
+
 #endif
+
 #ifdef DOF_NEAR_BLUR
-			tap_amount *= int_ofs == 0 ? 0.0 : (smoothstep(dof_near_end, dof_near_begin, tap_depth));
+
+	vec4 color_accum = vec4(0.0);
+
+	float max_accum = 0.0;
+
+	for (int i = 0; i < dof_kernel_size; i++) {
+		int int_ofs = i - dof_kernel_from;
+		vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * dof_radius;
+		float ofs_influence = max(0.0, 1.0 - abs(float(int_ofs)) / float(dof_kernel_from));
+
+		float tap_k = dof_kernel[i];
+
+		vec4 tap_color = texture2DLod(source_color, tap_uv, 0.0);
+
+		float tap_depth = texture2D(dof_source_depth, tap_uv, 0.0).r;
+		tap_depth = tap_depth * 2.0 - 1.0;
+#ifdef USE_ORTHOGONAL_PROJECTION
+		tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
+#else
+		tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
 #endif
-			tap_amount = 1.0 - tap_amount;
+		float tap_amount = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
+		tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
 
-			tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
+#ifdef DOF_NEAR_FIRST_TAP
 
-			vec4 tap_color = texture2DLod(source_color, tap_uv, 0.0) * tap_k;
+		tap_color.a = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
 
-			k_accum += tap_k * tap_amount;
-			color_accum += tap_color * tap_amount;
-		}
+#endif
 
-		if (k_accum > 0.0) {
-			color_accum /= k_accum;
-		}
+		max_accum = max(max_accum, tap_amount * ofs_influence);
 
-		gl_FragColor = color_accum; ///k_accum;
-	} else {
-		// We're in focus, no need to waste time sampling the same UV a bunch of times...
-		gl_FragColor = texture2DLod(source_color, uv_interp, 0.0);
+		color_accum += tap_color * tap_k;
 	}
 
+	color_accum.a = max(color_accum.a, sqrt(max_accum));
+
+	gl_FragColor = color_accum;
+
 #endif
 
 #ifdef GLOW_FIRST_PASS

+ 95 - 21
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -3544,39 +3544,26 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p
 
 	GLuint composite_from = storage->frame.current_rt->effects.mip_maps[0].color;
 
-	if (env && (env->dof_blur_near_enabled || env->dof_blur_far_enabled)) {
+	if (env && env->dof_blur_far_enabled) {
 		//blur diffuse into effect mipmaps using separatable convolution
 		//storage->shaders.copy.set_conditional(CopyShaderGLES3::GAUSSIAN_HORIZONTAL,true);
 
 		int vp_h = storage->frame.current_rt->height;
 		int vp_w = storage->frame.current_rt->width;
 
-		// If both near and far are used, we use the far quality and amount settings.
-		// We should just have one setting like in Godot 4 but that would be a serious breaking change.
-		// This is defendable.
-		float dof_blur_amount = env->dof_blur_far_enabled ? env->dof_blur_far_amount : env->dof_blur_near_amount;
-		VS::EnvironmentDOFBlurQuality quality = env->dof_blur_far_enabled ? env->dof_blur_far_quality : env->dof_blur_near_quality;
-
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal());
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_FAR_BLUR, env->dof_blur_far_enabled);
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_BLUR, env->dof_blur_near_enabled);
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_LOW, quality == VS::ENV_DOF_BLUR_QUALITY_LOW);
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_MEDIUM, quality == VS::ENV_DOF_BLUR_QUALITY_MEDIUM);
-		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_HIGH, quality == VS::ENV_DOF_BLUR_QUALITY_HIGH);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_FAR_BLUR, true);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_LOW, env->dof_blur_far_quality == VS::ENV_DOF_BLUR_QUALITY_LOW);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_MEDIUM, env->dof_blur_far_quality == VS::ENV_DOF_BLUR_QUALITY_MEDIUM);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_HIGH, env->dof_blur_far_quality == VS::ENV_DOF_BLUR_QUALITY_HIGH);
 
 		state.effect_blur_shader.bind();
 		int qsteps[3] = { 4, 10, 20 };
 
-		float radius = (dof_blur_amount * dof_blur_amount) / qsteps[quality];
+		float radius = (env->dof_blur_far_amount * env->dof_blur_far_amount) / qsteps[env->dof_blur_far_quality];
 
-		if (env->dof_blur_far_enabled) {
-			state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_FAR_BEGIN, env->dof_blur_far_distance);
-			state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_FAR_END, env->dof_blur_far_distance + env->dof_blur_far_transition);
-		}
-		if (env->dof_blur_near_enabled) {
-			state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_NEAR_BEGIN, env->dof_blur_near_distance);
-			state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_NEAR_END, env->dof_blur_near_distance - env->dof_blur_near_transition);
-		}
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_BEGIN, env->dof_blur_far_distance);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_END, env->dof_blur_far_distance + env->dof_blur_far_transition);
 		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_DIR, Vector2(1, 0));
 		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_RADIUS, radius);
 		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::PIXEL_SIZE, Vector2(1.0 / vp_w, 1.0 / vp_h));
@@ -3604,14 +3591,101 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p
 		_copy_screen();
 
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_FAR_BLUR, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_LOW, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_MEDIUM, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_HIGH, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::USE_ORTHOGONAL_PROJECTION, false);
+
+		composite_from = storage->frame.current_rt->effects.mip_maps[0].color;
+	}
+
+	if (env && env->dof_blur_near_enabled) {
+		//blur diffuse into effect mipmaps using separatable convolution
+		//storage->shaders.copy.set_conditional(CopyShaderGLES3::GAUSSIAN_HORIZONTAL,true);
+
+		int vp_h = storage->frame.current_rt->height;
+		int vp_w = storage->frame.current_rt->width;
+
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal());
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_BLUR, true);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_FIRST_TAP, true);
+
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_LOW, env->dof_blur_near_quality == VS::ENV_DOF_BLUR_QUALITY_LOW);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_MEDIUM, env->dof_blur_near_quality == VS::ENV_DOF_BLUR_QUALITY_MEDIUM);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_HIGH, env->dof_blur_near_quality == VS::ENV_DOF_BLUR_QUALITY_HIGH);
+
+		state.effect_blur_shader.bind();
+		int qsteps[3] = { 4, 10, 20 };
+
+		float radius = (env->dof_blur_near_amount * env->dof_blur_near_amount) / qsteps[env->dof_blur_near_quality];
+
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_BEGIN, env->dof_blur_near_distance);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_END, env->dof_blur_near_distance - env->dof_blur_near_transition);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_DIR, Vector2(1, 0));
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_RADIUS, radius);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::PIXEL_SIZE, Vector2(1.0 / vp_w, 1.0 / vp_h));
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::CAMERA_Z_NEAR, p_cam_projection.get_z_near());
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::CAMERA_Z_FAR, p_cam_projection.get_z_far());
+
+		glActiveTexture(GL_TEXTURE1);
+		glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->depth);
+
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, composite_from);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+		glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo); //copy to front first
+
+		_copy_screen();
+		//manually do the blend if this is the first operation resolving from the diffuse buffer
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_BLUR_MERGE, composite_from == storage->frame.current_rt->buffers.diffuse);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_FIRST_TAP, false);
+		state.effect_blur_shader.bind();
+
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_BEGIN, env->dof_blur_near_distance);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_END, env->dof_blur_near_distance - env->dof_blur_near_transition);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_DIR, Vector2(0, 1));
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::DOF_RADIUS, radius);
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::PIXEL_SIZE, Vector2(1.0 / vp_w, 1.0 / vp_h));
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::CAMERA_Z_NEAR, p_cam_projection.get_z_near());
+		state.effect_blur_shader.set_uniform(EffectBlurShaderGLES3::CAMERA_Z_FAR, p_cam_projection.get_z_far());
+
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->color);
+
+		glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->effects.mip_maps[0].sizes[0].fbo); // copy to base level
+
+		if (composite_from != storage->frame.current_rt->buffers.diffuse) {
+			glEnable(GL_BLEND);
+			glBlendEquation(GL_FUNC_ADD);
+			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+		} else {
+			glActiveTexture(GL_TEXTURE2);
+			glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->buffers.diffuse);
+		}
+
+		_copy_screen(true);
+
+		if (composite_from != storage->frame.current_rt->buffers.diffuse) {
+			glDisable(GL_BLEND);
+		}
+
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_BLUR, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_FIRST_TAP, false);
+		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_BLUR_MERGE, false);
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_LOW, false);
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_MEDIUM, false);
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_HIGH, false);
 		state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::USE_ORTHOGONAL_PROJECTION, false);
 
 		composite_from = storage->frame.current_rt->effects.mip_maps[0].color;
+	}
 
+	if (env && (env->dof_blur_near_enabled || env->dof_blur_far_enabled)) {
 		//these needed to disable filtering, reenamble
 		glActiveTexture(GL_TEXTURE0);
 		glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->effects.mip_maps[0].color);

+ 77 - 42
drivers/gles3/shaders/effect_blur.glsl

@@ -77,13 +77,16 @@ const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.0
 #endif
 
 uniform sampler2D dof_source_depth; //texunit:1
-uniform float dof_far_begin;
-uniform float dof_far_end;
-uniform float dof_near_begin;
-uniform float dof_near_end;
+uniform float dof_begin;
+uniform float dof_end;
 uniform vec2 dof_dir;
 uniform float dof_radius;
 
+#ifdef DOF_NEAR_BLUR_MERGE
+
+uniform sampler2D source_dof_original; //texunit:2
+#endif
+
 #endif
 
 #ifdef GLOW_FIRST_PASS
@@ -158,7 +161,7 @@ void main() {
 	frag_color = color;
 #endif
 
-#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR)
+#ifdef DOF_FAR_BLUR
 
 	vec4 color_accum = vec4(0.0);
 
@@ -170,57 +173,89 @@ void main() {
 	depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near));
 #endif
 
-	// Combine near and far, our depth is unlikely to be in both ranges
-	float amount = 1.0;
-#ifdef DOF_FAR_BLUR
-	amount *= 1.0 - smoothstep(dof_far_begin, dof_far_end, depth);
-#endif
-#ifdef DOF_NEAR_BLUR
-	amount *= smoothstep(dof_near_end, dof_near_begin, depth);
-#endif
-	amount = 1.0 - amount;
-
-	if (amount > 0.0) {
-		float k_accum = 0.0;
+	float amount = smoothstep(dof_begin, dof_end, depth);
+	float k_accum = 0.0;
 
-		for (int i = 0; i < dof_kernel_size; i++) {
-			int int_ofs = i - dof_kernel_from;
-			vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * amount * dof_radius;
+	for (int i = 0; i < dof_kernel_size; i++) {
+		int int_ofs = i - dof_kernel_from;
+		vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * amount * dof_radius;
 
-			float tap_k = dof_kernel[i];
+		float tap_k = dof_kernel[i];
 
-			float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
-			tap_depth = tap_depth * 2.0 - 1.0;
+		float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
+		tap_depth = tap_depth * 2.0 - 1.0;
 #ifdef USE_ORTHOGONAL_PROJECTION
-			tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
+		tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
 #else
-			tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
+		tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
 #endif
-			float tap_amount = 1.0;
-#ifdef DOF_FAR_BLUR
-			tap_amount *= mix(1.0 - smoothstep(dof_far_begin, dof_far_end, tap_depth), 0.0, int_ofs == 0);
+		float tap_amount = mix(smoothstep(dof_begin, dof_end, tap_depth), 1.0, int_ofs == 0);
+		tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
+
+		vec4 tap_color = textureLod(source_color, tap_uv, 0.0) * tap_k;
+
+		k_accum += tap_k * tap_amount;
+		color_accum += tap_color * tap_amount;
+	}
+
+	if (k_accum > 0.0) {
+		color_accum /= k_accum;
+	}
+
+	frag_color = color_accum; ///k_accum;
+
 #endif
+
 #ifdef DOF_NEAR_BLUR
-			tap_amount *= mix(smoothstep(dof_near_end, dof_near_begin, tap_depth), 0.0, int_ofs == 0);
+
+	vec4 color_accum = vec4(0.0);
+
+	float max_accum = 0.0;
+
+	for (int i = 0; i < dof_kernel_size; i++) {
+		int int_ofs = i - dof_kernel_from;
+		vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * dof_radius;
+		float ofs_influence = max(0.0, 1.0 - float(abs(int_ofs)) / float(dof_kernel_from));
+
+		float tap_k = dof_kernel[i];
+
+		vec4 tap_color = textureLod(source_color, tap_uv, 0.0);
+
+		float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
+		tap_depth = tap_depth * 2.0 - 1.0;
+#ifdef USE_ORTHOGONAL_PROJECTION
+		tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
+#else
+		tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
 #endif
-			tap_amount = 1.0 - tap_amount;
-			tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
+		float tap_amount = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
+		tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
 
-			vec4 tap_color = textureLod(source_color, tap_uv, 0.0) * tap_k;
+#ifdef DOF_NEAR_FIRST_TAP
 
-			k_accum += tap_k * tap_amount;
-			color_accum += tap_color * tap_amount;
-		}
+		tap_color.a = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
 
-		if (k_accum > 0.0) {
-			color_accum /= k_accum;
-		}
+#endif
 
-		frag_color = color_accum; ///k_accum;
-	} else {
-		// We're in focus, no need to waste time sampling the same UV a bunch of times...
-		frag_color = textureLod(source_color, uv_interp, 0.0);
+		max_accum = max(max_accum, tap_amount * ofs_influence);
+
+		color_accum += tap_color * tap_k;
 	}
+
+	color_accum.a = max(color_accum.a, sqrt(max_accum));
+
+#ifdef DOF_NEAR_BLUR_MERGE
+
+	vec4 original = textureLod(source_dof_original, uv_interp, 0.0);
+	color_accum = mix(original, color_accum, color_accum.a);
+
+#endif
+
+#ifndef DOF_NEAR_FIRST_TAP
+	//color_accum=vec4(vec3(color_accum.a),1.0);
+#endif
+	frag_color = color_accum;
+
 #endif
 
 #ifdef GLOW_FIRST_PASS