Dynamic Depth of Field

A dynamic depth of field shader that changes focus based on the distance to the camera using raycasting.

This shader uses the (brilliant) box blur shader written by NightEyes: https://godotshaders.com/shader/configurable-box-blur/

3D ONLY

INSTRUCTIONS:

1. Add a MeshInstance3D to the Camera in your scene and set the mesh to a Quad.

2. Set the Quad size to 2×2.

3. Enable “Flip Faces” under the Geometry section.

4. Set Position to (0,0,-1) under the Transform section

5. Add a new script to the Quad and copy/paste  ## GD SCRIPT ## section

6. Add a new shader to the Surface Material Override section and copy/paste ## SHADER ## section

 

NOTES

– Godot 4.1

– If objects in your scene are rendered invisible by the Quad, under the Surface Material Override section lower the Render Priorty until the objects are visible. In my project the Quad has a Render Priority of -1.

– DOF may not be applied to objects that use shader materials due to the esoteric ways in which Godot renders objects. I do plan on addressing these issues in the future.

– Interpolation between near and far objects may produce graphical artifacts; I plan on adding a max filter  to the code in order to smooth object edges (please be patient).

– Make sure the COLLISION_MASK var in the GD SCRIPT code section includes the objects you wish the DOF to be applied to.

Shader code
## GD SCRIPT ##
extends MeshInstance3D

var DOF_LENGTH = 250;
var COLLISION_MASK = 1;

func _physics_process(_delta):
	var end = global_position - global_transform.basis.z * DOF_LENGTH

	var rayParams = PhysicsRayQueryParameters3D.create(global_position, end, COLLISION_MASK)
	var ray = get_world_3d().direct_space_state.intersect_ray(rayParams)
	if !ray.is_empty():
		end = ray["position"]
	
	var sm = get_surface_override_material(0) as ShaderMaterial
	sm.set_shader_parameter("ray_position", end)

## SHADER ##
shader_type spatial;
render_mode unshaded, cull_back;

uniform vec3 ray_position;
uniform int blur_strength: hint_range(1,8) = 3;
uniform float blur_falloff: hint_range(1.0, 20.0) = 7.0;
uniform float blur_near: hint_range(0.0, 100.0) = 20.0;
uniform float blur_far: hint_range(0.0, 250.0) = 200.0;
uniform sampler2D depth_texture : source_color, hint_depth_texture, filter_linear;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_linear;

void vertex() {
	POSITION = vec4(VERTEX, 1.0);
}

// Configurable Box Blur by Nighteyes -> https://godotshaders.com/shader/configurable-box-blur/
vec3 blur_size(sampler2D tex,vec2 uv, vec2 pixelSize) {
	vec2 pixel = uv / pixelSize;
	int x_min = max(int(pixel.x) - blur_strength, 0);
	int y_min = max(int(pixel.y) - blur_strength, 0);
	int x_max = min(int(pixel.x) + blur_strength, int(1.0 / pixelSize.x));
	int y_max = min(int(pixel.y) + blur_strength, int(1.0 / pixelSize.y));
	
	int count = 0;
	vec3 color = vec3(0.0);
	for(int x = x_min; x <= x_max; x++) {
		for(int y = y_min; y <= y_max; y++) {           
			color += texture(tex, vec2(float(x), float(y)) * pixelSize).rgb;
			count++;
		}
	}
	return color / float(count);
}

void fragment() {
	float dist = distance(CAMERA_POSITION_WORLD, ray_position);
	float depth = texture(depth_texture, SCREEN_UV).r;
	depth = depth * 2.0 - 1.0;
	float z = -PROJECTION_MATRIX[3][2] / (depth + PROJECTION_MATRIX[2][2]);
	z = z + dist/2.0;
	
	// Calculate clear/blur threshold
	float w = dist/blur_falloff;
	float dz = dist >= blur_far ? 1.0 : (1.0 - smoothstep(0.001, w, -(z + w)));
	dz *= dist <= blur_near ? 1.0 : smoothstep(0.001, w, -(z - w));
	
	vec3 screen = texture(screen_texture, SCREEN_UV).rgb;
	vec3 blur = blur_size(screen_texture, SCREEN_UV, 1.0/VIEWPORT_SIZE);
	
	ALBEDO = mix(blur, screen, dz);
}
Tags
blur, camera, depth, Depth buffer, depth of field, dof, focus, Post processing
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Qtan1

Camera Distance UV Scaling

Related shaders

Linear Depth/Depth Fog

Simple dynamic holographic card effect ( foil )

Depth-based Edge Detection with Sobel Operator – Screenspace

Subscribe
Notify of
guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
F1xed
F1xed
10 months ago

Somehow its not working for me( Anyway thx for your work!

alefranart
alefranart
8 months ago

Thank You very much! Excellent….I love it!