Blender V4.3
eevee_depth_of_field.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
20#include "DRW_render.hh"
21
22#include "BKE_camera.h"
23#include "DNA_camera_types.h"
24
25#include "GPU_platform.hh"
26#include "GPU_texture.hh"
27#include "GPU_uniform_buffer.hh"
28
29#include "eevee_camera.hh"
30#include "eevee_instance.hh"
31#include "eevee_sampling.hh"
32#include "eevee_shader.hh"
34
36
37namespace blender::eevee {
38
39/* -------------------------------------------------------------------- */
44{
45 const SceneEEVEE &sce_eevee = inst_.scene->eevee;
46 const Object *camera_object_eval = inst_.camera_eval_object;
47 const ::Camera *camera = (camera_object_eval && camera_object_eval->type == OB_CAMERA) ?
48 reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
49 nullptr;
50 if (camera == nullptr) {
51 /* Set to invalid value for update detection */
52 data_.scatter_color_threshold = -1.0f;
53 return;
54 }
55 /* Reminder: These are parameters not interpolated by motion blur. */
56 int sce_flag = sce_eevee.flag;
57 do_jitter_ = (sce_flag & SCE_EEVEE_DOF_JITTER) != 0;
58 user_overblur_ = sce_eevee.bokeh_overblur / 100.0f;
59 fx_max_coc_ = sce_eevee.bokeh_max_size;
62 data_.bokeh_blades = float(camera->dof.aperture_blades);
63}
64
66{
67 const Camera &camera = inst_.camera;
68 const Object *camera_object_eval = inst_.camera_eval_object;
69 const ::Camera *camera_data = (camera_object_eval && camera_object_eval->type == OB_CAMERA) ?
70 reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
71 nullptr;
72
73 if (inst_.debug_mode == DEBUG_DOF_PLANES) {
74 /* Set debug message even if DOF is not enabled. */
75 inst_.info_append(
76 "Debug Mode: Depth Of Field Buffers\n"
77 " - Purple: Gap Fill\n"
78 " - Blue: Background\n"
79 " - Red: Slight Out Of Focus\n"
80 " - Yellow: In Focus\n"
81 " - Green: Foreground\n");
82 }
83
84 if (camera_data == nullptr || (camera_data->dof.flag & CAM_DOF_ENABLED) == 0) {
85 jitter_radius_ = 0.0f;
86 fx_radius_ = 0.0f;
87 return;
88 }
89
90 float2 anisotropic_scale = {clamp_f(1.0f / camera_data->dof.aperture_ratio, 1e-5f, 1.0f),
91 clamp_f(camera_data->dof.aperture_ratio, 1e-5f, 1.0f)};
92 data_.bokeh_anisotropic_scale = anisotropic_scale;
93 data_.bokeh_rotation = camera_data->dof.aperture_rotation;
94 focus_distance_ = BKE_camera_object_dof_distance(camera_object_eval);
96
97 float fstop = max_ff(camera_data->dof.aperture_fstop, 1e-5f);
98
99 float aperture = 1.0f / (2.0f * fstop);
100 if (camera.is_perspective()) {
101 aperture *= camera_data->lens * 1e-3f;
102 }
103
104 if (camera.is_orthographic()) {
105 /* FIXME: Why is this needed? Some kind of implicit unit conversion? */
106 aperture *= 0.04f;
107 }
108
109 if (camera.is_panoramic()) {
110 /* FIXME: Eyeballed. */
111 aperture *= 0.185f;
112 }
113
114 if (camera_data->dof.aperture_ratio < 1.0) {
115 /* If ratio is scaling the bokeh outwards, we scale the aperture so that
116 * the gather kernel size will encompass the maximum axis. */
117 aperture /= max_ff(camera_data->dof.aperture_ratio, 1e-5f);
118 }
119
120 float jitter_radius, fx_radius;
121
122 /* Balance blur radius between fx dof and jitter dof. */
123 if (do_jitter_ && (inst_.sampling.dof_ring_count_get() > 0) && !camera.is_panoramic() &&
124 !inst_.is_viewport())
125 {
126 /* Compute a minimal overblur radius to fill the gaps between the samples.
127 * This is just the simplified form of dividing the area of the bokeh by
128 * the number of samples. */
129 float minimal_overblur = 1.0f / sqrtf(inst_.sampling.dof_sample_count_get());
130
131 fx_radius = (minimal_overblur + user_overblur_) * aperture;
132 /* Avoid dilating the shape. Over-blur only soften. */
133 jitter_radius = max_ff(0.0f, aperture - fx_radius);
134 }
135 else {
136 jitter_radius = 0.0f;
137 fx_radius = aperture;
138 }
139
140 /* Disable post fx if result wouldn't be noticeable. */
141 if (fx_max_coc_ <= 0.5f) {
142 fx_radius = 0.0f;
143 }
144
145 jitter_radius_ = jitter_radius;
146 fx_radius_ = fx_radius;
147
148 if (fx_radius_ == 0.0f) {
149 return;
150 }
151
152 /* TODO(fclem): Once we render into multiple view, we will need to use the maximum resolution. */
153 int2 max_render_res = inst_.film.render_extent_get();
154 int2 half_res = math::divide_ceil(max_render_res, int2(2));
155 int2 reduce_size = math::ceil_to_multiple(half_res, int2(DOF_REDUCE_GROUP_SIZE));
156
157 data_.gather_uv_fac = 1.0f / float2(reduce_size);
158
159 /* Now that we know the maximum render resolution of every view, using depth of field, allocate
160 * the reduced buffers. Color needs to be signed format here. See note in shader for
161 * explanation. Do not use texture pool because of needs mipmaps. */
164 reduced_color_tx_.ensure_2d(GPU_RGBA16F, reduce_size, usage, nullptr, DOF_MIP_COUNT);
165 reduced_coc_tx_.ensure_2d(GPU_R16F, reduce_size, usage, nullptr, DOF_MIP_COUNT);
166 reduced_color_tx_.ensure_mip_views();
167 reduced_coc_tx_.ensure_mip_views();
168
169 /* Resize the scatter list to contain enough entry to cover half the screen with sprites (which
170 * is unlikely due to local contrast test). */
171 data_.scatter_max_rect = (reduced_color_tx_.pixel_count() / 4) / 2;
172 scatter_fg_list_buf_.resize(data_.scatter_max_rect);
173 scatter_bg_list_buf_.resize(data_.scatter_max_rect);
174
175 bokeh_lut_pass_sync();
176 setup_pass_sync();
177 stabilize_pass_sync();
178 downsample_pass_sync();
179 reduce_pass_sync();
180 tiles_flatten_pass_sync();
181 tiles_dilate_pass_sync();
182 gather_pass_sync();
183 filter_pass_sync();
184 scatter_pass_sync();
185 hole_fill_pass_sync();
186 resolve_pass_sync();
187}
188
190{
191 if (jitter_radius_ == 0.0f) {
192 return;
193 }
194
195 float radius, theta;
196 inst_.sampling.dof_disk_sample_get(&radius, &theta);
197
198 if (data_.bokeh_blades >= 3.0f) {
199 theta = circle_to_polygon_angle(data_.bokeh_blades, theta);
200 radius *= circle_to_polygon_radius(data_.bokeh_blades, theta);
201 }
202 radius *= jitter_radius_;
203 theta += data_.bokeh_rotation;
204
205 /* Sample in View Space. */
206 float2 sample = float2(radius * cosf(theta), radius * sinf(theta));
208 /* Convert to NDC Space. */
209 float3 jitter = float3(UNPACK2(sample), -focus_distance_);
210 float3 center = float3(0.0f, 0.0f, -focus_distance_);
211 mul_project_m4_v3(winmat.ptr(), jitter);
212 mul_project_m4_v3(winmat.ptr(), center);
213
214 const bool is_ortho = (winmat[2][3] != -1.0f);
215 if (is_ortho) {
216 sample *= focus_distance_;
217 }
218 /* Translate origin. */
219 sub_v2_v2(viewmat[3], sample);
220 /* Skew winmat Z axis. */
221 add_v2_v2(winmat[2], center - jitter);
222}
223
226/* -------------------------------------------------------------------- */
230void DepthOfField::bokeh_lut_pass_sync()
231{
232 const bool has_anisotropy = data_.bokeh_anisotropic_scale != float2(1.0f);
233 if (!has_anisotropy && (data_.bokeh_blades == 0.0)) {
234 /* No need for LUTs in these cases. */
235 use_bokeh_lut_ = false;
236 return;
237 }
238 use_bokeh_lut_ = true;
239
240 /* Precompute bokeh texture. */
241 bokeh_lut_ps_.init();
243 bokeh_lut_ps_.bind_ubo("dof_buf", data_);
244 bokeh_lut_ps_.bind_image("out_gather_lut_img", &bokeh_gather_lut_tx_);
245 bokeh_lut_ps_.bind_image("out_scatter_lut_img", &bokeh_scatter_lut_tx_);
246 bokeh_lut_ps_.bind_image("out_resolve_lut_img", &bokeh_resolve_lut_tx_);
247 bokeh_lut_ps_.dispatch(int3(1, 1, 1));
248}
249
250void DepthOfField::setup_pass_sync()
251{
252 RenderBuffers &render_buffers = inst_.render_buffers;
253
254 setup_ps_.init();
256 setup_ps_.bind_texture("color_tx", &input_color_tx_, no_filter);
257 setup_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
258 setup_ps_.bind_ubo("dof_buf", data_);
259 setup_ps_.bind_image("out_color_img", &setup_color_tx_);
260 setup_ps_.bind_image("out_coc_img", &setup_coc_tx_);
261 setup_ps_.dispatch(&dispatch_setup_size_);
263}
264
265void DepthOfField::stabilize_pass_sync()
266{
267 RenderBuffers &render_buffers = inst_.render_buffers;
268 VelocityModule &velocity = inst_.velocity;
269
270 stabilize_ps_.init();
272 stabilize_ps_.bind_ubo("camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS]));
273 stabilize_ps_.bind_ubo("camera_curr", &(*velocity.camera_steps[STEP_CURRENT]));
274 /* This is only for temporal stability. The next step is not needed. */
275 stabilize_ps_.bind_ubo("camera_next", &(*velocity.camera_steps[STEP_PREVIOUS]));
276 stabilize_ps_.bind_texture("coc_tx", &setup_coc_tx_, no_filter);
277 stabilize_ps_.bind_texture("color_tx", &setup_color_tx_, no_filter);
278 stabilize_ps_.bind_texture("velocity_tx", &render_buffers.vector_tx, no_filter);
279 stabilize_ps_.bind_texture("in_history_tx", &stabilize_input_, with_filter);
280 stabilize_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
281 stabilize_ps_.bind_ubo("dof_buf", data_);
282 stabilize_ps_.push_constant("u_use_history", &stabilize_valid_history_, 1);
283 stabilize_ps_.bind_image("out_coc_img", reduced_coc_tx_.mip_view(0));
284 stabilize_ps_.bind_image("out_color_img", reduced_color_tx_.mip_view(0));
285 stabilize_ps_.bind_image("out_history_img", &stabilize_output_tx_);
286 stabilize_ps_.dispatch(&dispatch_stabilize_size_);
288}
289
290void DepthOfField::downsample_pass_sync()
291{
292 downsample_ps_.init();
293 downsample_ps_.shader_set(inst_.shaders.static_shader_get(DOF_DOWNSAMPLE));
294 downsample_ps_.bind_texture("color_tx", reduced_color_tx_.mip_view(0), no_filter);
295 downsample_ps_.bind_texture("coc_tx", reduced_coc_tx_.mip_view(0), no_filter);
296 downsample_ps_.bind_image("out_color_img", &downsample_tx_);
297 downsample_ps_.dispatch(&dispatch_downsample_size_);
298 downsample_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
299}
300
301void DepthOfField::reduce_pass_sync()
302{
303 reduce_ps_.init();
305 reduce_ps_.bind_ubo("dof_buf", data_);
306 reduce_ps_.bind_texture("downsample_tx", &downsample_tx_, no_filter);
307 reduce_ps_.bind_ssbo("scatter_fg_list_buf", scatter_fg_list_buf_);
308 reduce_ps_.bind_ssbo("scatter_bg_list_buf", scatter_bg_list_buf_);
309 reduce_ps_.bind_ssbo("scatter_fg_indirect_buf", scatter_fg_indirect_buf_);
310 reduce_ps_.bind_ssbo("scatter_bg_indirect_buf", scatter_bg_indirect_buf_);
311 reduce_ps_.bind_image("inout_color_lod0_img", reduced_color_tx_.mip_view(0));
312 reduce_ps_.bind_image("out_color_lod1_img", reduced_color_tx_.mip_view(1));
313 reduce_ps_.bind_image("out_color_lod2_img", reduced_color_tx_.mip_view(2));
314 reduce_ps_.bind_image("out_color_lod3_img", reduced_color_tx_.mip_view(3));
315 reduce_ps_.bind_image("in_coc_lod0_img", reduced_coc_tx_.mip_view(0));
316 reduce_ps_.bind_image("out_coc_lod1_img", reduced_coc_tx_.mip_view(1));
317 reduce_ps_.bind_image("out_coc_lod2_img", reduced_coc_tx_.mip_view(2));
318 reduce_ps_.bind_image("out_coc_lod3_img", reduced_coc_tx_.mip_view(3));
319 reduce_ps_.dispatch(&dispatch_reduce_size_);
320 /* NOTE: Command buffer barrier is done automatically by the GPU backend. */
322}
323
324void DepthOfField::tiles_flatten_pass_sync()
325{
326 tiles_flatten_ps_.init();
327 tiles_flatten_ps_.shader_set(inst_.shaders.static_shader_get(DOF_TILES_FLATTEN));
328 /* NOTE(fclem): We should use the reduced_coc_tx_ as it is stable, but we need the slight focus
329 * flag from the setup pass. A better way would be to do the brute-force in focus gather without
330 * this. */
331 tiles_flatten_ps_.bind_texture("coc_tx", &setup_coc_tx_, no_filter);
332 tiles_flatten_ps_.bind_image("out_tiles_fg_img", &tiles_fg_tx_.current());
333 tiles_flatten_ps_.bind_image("out_tiles_bg_img", &tiles_bg_tx_.current());
334 tiles_flatten_ps_.dispatch(&dispatch_tiles_flatten_size_);
335 tiles_flatten_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
336}
337
338void DepthOfField::tiles_dilate_pass_sync()
339{
340 for (int pass = 0; pass < 2; pass++) {
341 PassSimple &drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_;
343 drw_pass.init();
344 drw_pass.shader_set(inst_.shaders.static_shader_get(sh_type));
345 drw_pass.bind_image("in_tiles_fg_img", &tiles_fg_tx_.previous());
346 drw_pass.bind_image("in_tiles_bg_img", &tiles_bg_tx_.previous());
347 drw_pass.bind_image("out_tiles_fg_img", &tiles_fg_tx_.current());
348 drw_pass.bind_image("out_tiles_bg_img", &tiles_bg_tx_.current());
349 drw_pass.push_constant("ring_count", &tiles_dilate_ring_count_, 1);
350 drw_pass.push_constant("ring_width_multiplier", &tiles_dilate_ring_width_mul_, 1);
351 drw_pass.dispatch(&dispatch_tiles_dilate_size_);
352 drw_pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
353 }
354}
355
356void DepthOfField::gather_pass_sync()
357{
358 const GPUSamplerState gather_bilinear = {GPU_SAMPLER_FILTERING_MIPMAP |
360 const GPUSamplerState gather_nearest = {GPU_SAMPLER_FILTERING_MIPMAP};
361
362 for (int pass = 0; pass < 2; pass++) {
363 PassSimple &drw_pass = (pass == 0) ? gather_fg_ps_ : gather_bg_ps_;
364 SwapChain<TextureFromPool, 2> &color_chain = (pass == 0) ? color_fg_tx_ : color_bg_tx_;
365 SwapChain<TextureFromPool, 2> &weight_chain = (pass == 0) ? weight_fg_tx_ : weight_bg_tx_;
366 eShaderType sh_type = (pass == 0) ?
367 (use_bokeh_lut_ ? DOF_GATHER_FOREGROUND_LUT :
370 drw_pass.init();
371 drw_pass.bind_resources(inst_.sampling);
372 drw_pass.shader_set(inst_.shaders.static_shader_get(sh_type));
373 drw_pass.bind_ubo("dof_buf", data_);
374 drw_pass.bind_texture("color_bilinear_tx", reduced_color_tx_, gather_bilinear);
375 drw_pass.bind_texture("color_tx", reduced_color_tx_, gather_nearest);
376 drw_pass.bind_texture("coc_tx", reduced_coc_tx_, gather_nearest);
377 drw_pass.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current());
378 drw_pass.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current());
379 drw_pass.bind_image("out_color_img", &color_chain.current());
380 drw_pass.bind_image("out_weight_img", &weight_chain.current());
381 drw_pass.bind_image("out_occlusion_img", &occlusion_tx_);
382 drw_pass.bind_texture("bokeh_lut_tx", &bokeh_gather_lut_tx_);
383 drw_pass.dispatch(&dispatch_gather_size_);
384 drw_pass.barrier(GPU_BARRIER_TEXTURE_FETCH);
385 }
386}
387
388void DepthOfField::filter_pass_sync()
389{
390 for (int pass = 0; pass < 2; pass++) {
391 PassSimple &drw_pass = (pass == 0) ? filter_fg_ps_ : filter_bg_ps_;
392 SwapChain<TextureFromPool, 2> &color_chain = (pass == 0) ? color_fg_tx_ : color_bg_tx_;
393 SwapChain<TextureFromPool, 2> &weight_chain = (pass == 0) ? weight_fg_tx_ : weight_bg_tx_;
394 drw_pass.init();
395 drw_pass.shader_set(inst_.shaders.static_shader_get(DOF_FILTER));
396 drw_pass.bind_texture("color_tx", &color_chain.previous());
397 drw_pass.bind_texture("weight_tx", &weight_chain.previous());
398 drw_pass.bind_image("out_color_img", &color_chain.current());
399 drw_pass.bind_image("out_weight_img", &weight_chain.current());
400 drw_pass.dispatch(&dispatch_filter_size_);
401 drw_pass.barrier(GPU_BARRIER_TEXTURE_FETCH);
402 }
403}
404
405void DepthOfField::scatter_pass_sync()
406{
407 for (int pass = 0; pass < 2; pass++) {
408 PassSimple &drw_pass = (pass == 0) ? scatter_fg_ps_ : scatter_bg_ps_;
409 drw_pass.init();
411 drw_pass.shader_set(inst_.shaders.static_shader_get(DOF_SCATTER));
412 drw_pass.bind_ubo("dof_buf", data_);
413 drw_pass.push_constant("use_bokeh_lut", use_bokeh_lut_);
414 drw_pass.bind_texture("bokeh_lut_tx", &bokeh_scatter_lut_tx_);
415 drw_pass.bind_texture("occlusion_tx", &occlusion_tx_);
416 if (pass == 0) {
417 drw_pass.bind_ssbo("scatter_list_buf", scatter_fg_list_buf_);
418 drw_pass.draw_procedural_indirect(GPU_PRIM_TRI_STRIP, scatter_fg_indirect_buf_);
419 /* Avoid background gather pass writing to the occlusion_tx mid pass. */
420 drw_pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
421 }
422 else {
423 drw_pass.bind_ssbo("scatter_list_buf", scatter_bg_list_buf_);
424 drw_pass.draw_procedural_indirect(GPU_PRIM_TRI_STRIP, scatter_bg_indirect_buf_);
425 }
426 }
427}
428
429void DepthOfField::hole_fill_pass_sync()
430{
431 const GPUSamplerState gather_bilinear = {GPU_SAMPLER_FILTERING_MIPMAP |
433 const GPUSamplerState gather_nearest = {GPU_SAMPLER_FILTERING_MIPMAP};
434
435 hole_fill_ps_.init();
436 hole_fill_ps_.bind_resources(inst_.sampling);
438 hole_fill_ps_.bind_ubo("dof_buf", data_);
439 hole_fill_ps_.bind_texture("color_bilinear_tx", reduced_color_tx_, gather_bilinear);
440 hole_fill_ps_.bind_texture("color_tx", reduced_color_tx_, gather_nearest);
441 hole_fill_ps_.bind_texture("coc_tx", reduced_coc_tx_, gather_nearest);
442 hole_fill_ps_.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current());
443 hole_fill_ps_.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current());
444 hole_fill_ps_.bind_image("out_color_img", &hole_fill_color_tx_);
445 hole_fill_ps_.bind_image("out_weight_img", &hole_fill_weight_tx_);
446 hole_fill_ps_.dispatch(&dispatch_gather_size_);
447 hole_fill_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
448}
449
450void DepthOfField::resolve_pass_sync()
451{
453 RenderBuffers &render_buffers = inst_.render_buffers;
454 GPUShader *sh = inst_.shaders.static_shader_get(use_bokeh_lut_ ? DOF_RESOLVE_LUT : DOF_RESOLVE);
455
456 resolve_ps_.init();
457 resolve_ps_.specialize_constant(sh, "do_debug_color", inst_.debug_mode == DEBUG_DOF_PLANES);
458 resolve_ps_.shader_set(sh);
459 resolve_ps_.bind_ubo("dof_buf", data_);
460 resolve_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
461 resolve_ps_.bind_texture("color_tx", &input_color_tx_, no_filter);
462 resolve_ps_.bind_texture("stable_color_tx", &resolve_stable_color_tx_, no_filter);
463 resolve_ps_.bind_texture("color_bg_tx", &color_bg_tx_.current(), with_filter);
464 resolve_ps_.bind_texture("color_fg_tx", &color_fg_tx_.current(), with_filter);
465 resolve_ps_.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current());
466 resolve_ps_.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current());
467 resolve_ps_.bind_texture("weight_bg_tx", &weight_bg_tx_.current());
468 resolve_ps_.bind_texture("weight_fg_tx", &weight_fg_tx_.current());
469 resolve_ps_.bind_texture("color_hole_fill_tx", &hole_fill_color_tx_);
470 resolve_ps_.bind_texture("weight_hole_fill_tx", &hole_fill_weight_tx_);
471 resolve_ps_.bind_texture("bokeh_lut_tx", &bokeh_resolve_lut_tx_);
472 resolve_ps_.bind_image("out_color_img", &output_color_tx_);
473 resolve_ps_.bind_resources(inst_.sampling);
475 resolve_ps_.dispatch(&dispatch_resolve_size_);
477}
478
481/* -------------------------------------------------------------------- */
485/* Similar to Film::update_sample_table() but with constant filter radius and constant sample
486 * count. */
487void DepthOfField::update_sample_table()
488{
489 float2 subpixel_offset = inst_.film.pixel_jitter_get();
490 /* Since the film jitter is in full-screen res, divide by 2 to get the jitter in half res. */
491 subpixel_offset *= 0.5;
492
493 /* Same offsets as in dof_spatial_filtering(). */
494 const std::array<int2, 4> plus_offsets = {int2(-1, 0), int2(0, -1), int2(1, 0), int2(0, 1)};
495
496 const float radius = 1.5f;
497 int i = 0;
498 for (int2 offset : plus_offsets) {
499 float2 pixel_ofs = float2(offset) - subpixel_offset;
500 data_.filter_samples_weight[i++] = film_filter_weight(radius, math::length_squared(pixel_ofs));
501 }
502 data_.filter_center_weight = film_filter_weight(radius, math::length_squared(subpixel_offset));
503}
504
506 GPUTexture **input_tx,
507 GPUTexture **output_tx,
508 DepthOfFieldBuffer &dof_buffer)
509{
510 if (fx_radius_ == 0.0f) {
511 return;
512 }
513
514 input_color_tx_ = *input_tx;
515 output_color_tx_ = *output_tx;
516 extent_ = {GPU_texture_width(input_color_tx_), GPU_texture_height(input_color_tx_)};
517
518 {
519 const CameraData &cam_data = inst_.camera.data_get();
520 data_.camera_type = cam_data.type;
521 /* OPTI(fclem) Could be optimized. */
522 float3 jitter = float3(fx_radius_, 0.0f, -focus_distance_);
523 float3 center = float3(0.0f, 0.0f, -focus_distance_);
524 mul_project_m4_v3(cam_data.winmat.ptr(), jitter);
525 mul_project_m4_v3(cam_data.winmat.ptr(), center);
526 /* Simplify CoC calculation to a simple MADD. */
527 if (inst_.camera.is_orthographic()) {
528 data_.coc_mul = (center[0] - jitter[0]) * 0.5f * extent_[0];
529 data_.coc_bias = focus_distance_ * data_.coc_mul;
530 }
531 else {
532 data_.coc_bias = -(center[0] - jitter[0]) * 0.5f * extent_[0];
533 data_.coc_mul = focus_distance_ * data_.coc_bias;
534 }
535
536 float min_fg_coc = coc_radius_from_camera_depth(data_, -cam_data.clip_near);
537 float max_bg_coc = coc_radius_from_camera_depth(data_, -cam_data.clip_far);
538 if (data_.camera_type != CAMERA_ORTHO) {
539 /* Background is at infinity so maximum CoC is the limit of coc_radius_from_camera_depth
540 * at -inf. We only do this for perspective camera since orthographic coc limit is inf. */
541 max_bg_coc = data_.coc_bias;
542 }
543 /* Clamp with user defined max. */
544 data_.coc_abs_max = min_ff(max_ff(fabsf(min_fg_coc), fabsf(max_bg_coc)), fx_max_coc_);
545 /* TODO(fclem): Make this dependent of the quality of the gather pass. */
546 data_.scatter_coc_threshold = 4.0f;
547
548 update_sample_table();
549
550 data_.push_update();
551 }
552
553 int2 half_res = math::divide_ceil(extent_, int2(2));
554 int2 quarter_res = math::divide_ceil(extent_, int2(4));
555 int2 tile_res = math::divide_ceil(half_res, int2(DOF_TILES_SIZE));
556
557 dispatch_setup_size_ = int3(math::divide_ceil(half_res, int2(DOF_DEFAULT_GROUP_SIZE)), 1);
558 dispatch_stabilize_size_ = int3(math::divide_ceil(half_res, int2(DOF_STABILIZE_GROUP_SIZE)), 1);
559 dispatch_downsample_size_ = int3(math::divide_ceil(quarter_res, int2(DOF_DEFAULT_GROUP_SIZE)),
560 1);
561 dispatch_reduce_size_ = int3(math::divide_ceil(half_res, int2(DOF_REDUCE_GROUP_SIZE)), 1);
562 dispatch_tiles_flatten_size_ = int3(math::divide_ceil(half_res, int2(DOF_TILES_SIZE)), 1);
563 dispatch_tiles_dilate_size_ = int3(
565 dispatch_gather_size_ = int3(math::divide_ceil(half_res, int2(DOF_GATHER_GROUP_SIZE)), 1);
566 dispatch_filter_size_ = int3(math::divide_ceil(half_res, int2(DOF_FILTER_GROUP_SIZE)), 1);
567 dispatch_resolve_size_ = int3(math::divide_ceil(extent_, int2(DOF_RESOLVE_GROUP_SIZE)), 1);
568
570 /* On Mesa, there is a sync bug which can make a portion of the main pass (usually one shader)
571 * leave blocks of un-initialized memory. Doing a flush seems to alleviate the issue. */
572 GPU_flush();
573 }
574
575 DRW_stats_group_start("Depth of Field");
576
577 Manager &drw = *inst_.manager;
578
579 constexpr eGPUTextureUsage usage_readwrite = GPU_TEXTURE_USAGE_SHADER_READ |
581 constexpr eGPUTextureUsage usage_readwrite_attach = usage_readwrite |
583 {
584 DRW_stats_group_start("Setup");
585 {
586 bokeh_gather_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_RG16F);
587 bokeh_scatter_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_R16F);
588 bokeh_resolve_lut_tx_.acquire(int2(DOF_MAX_SLIGHT_FOCUS_RADIUS * 2 + 1), GPU_R16F);
589
590 if (use_bokeh_lut_) {
591 drw.submit(bokeh_lut_ps_, view);
592 }
593 }
594 {
595 setup_color_tx_.acquire(half_res, GPU_RGBA16F, usage_readwrite);
596 setup_coc_tx_.acquire(half_res, GPU_R16F);
597
598 drw.submit(setup_ps_, view);
599 }
600 {
601 stabilize_output_tx_.acquire(half_res, GPU_RGBA16F);
602 stabilize_valid_history_ = !dof_buffer.stabilize_history_tx_.ensure_2d(GPU_RGBA16F,
603 half_res);
604
605 if (stabilize_valid_history_ == false) {
606 /* Avoid uninitialized memory that can contain NaNs. */
607 dof_buffer.stabilize_history_tx_.clear(float4(0.0f));
608 }
609
610 stabilize_input_ = dof_buffer.stabilize_history_tx_;
611 /* Outputs to reduced_*_tx_ mip 0. */
612 drw.submit(stabilize_ps_, view);
613
614 /* WATCH(fclem): Swap Texture an TextureFromPool internal GPUTexture in order to reuse
615 * the one that we just consumed. */
616 TextureFromPool::swap(stabilize_output_tx_, dof_buffer.stabilize_history_tx_);
617
618 /* Used by stabilize pass. */
619 stabilize_output_tx_.release();
620 setup_color_tx_.release();
621 }
622 {
623 DRW_stats_group_start("Tile Prepare");
624
625 /* WARNING: If format changes, make sure dof_tile_* GLSL constants are properly encoded. */
626 tiles_fg_tx_.previous().acquire(tile_res, GPU_R11F_G11F_B10F, usage_readwrite);
627 tiles_bg_tx_.previous().acquire(tile_res, GPU_R11F_G11F_B10F, usage_readwrite);
628 tiles_fg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F, usage_readwrite);
629 tiles_bg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F, usage_readwrite);
630
631 drw.submit(tiles_flatten_ps_, view);
632
633 /* Used by tile_flatten and stabilize_ps pass. */
634 setup_coc_tx_.release();
635
636 /* Error introduced by gather center jittering. */
637 const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f);
638 int dilation_end_radius = ceilf((fx_max_coc_ * error_multiplier) / (DOF_TILES_SIZE * 2));
639
640 /* Run dilation twice. One for minmax and one for minabs. */
641 for (int pass = 0; pass < 2; pass++) {
642 /* This algorithm produce the exact dilation radius by dividing it in multiple passes. */
643 int dilation_radius = 0;
644 while (dilation_radius < dilation_end_radius) {
645 int remainder = dilation_end_radius - dilation_radius;
646 /* Do not step over any unvisited tile. */
647 int max_multiplier = dilation_radius + 1;
648
649 int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / float(max_multiplier)));
650 int multiplier = min_ii(max_multiplier, floorf(remainder / float(ring_count)));
651
652 dilation_radius += ring_count * multiplier;
653
654 tiles_dilate_ring_count_ = ring_count;
655 tiles_dilate_ring_width_mul_ = multiplier;
656
657 tiles_fg_tx_.swap();
658 tiles_bg_tx_.swap();
659
660 drw.submit((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_, view);
661 }
662 }
663
664 tiles_fg_tx_.previous().release();
665 tiles_bg_tx_.previous().release();
666
668 }
669
670 downsample_tx_.acquire(quarter_res, GPU_RGBA16F, usage_readwrite);
671
672 drw.submit(downsample_ps_, view);
673
674 scatter_fg_indirect_buf_.clear_to_zero();
675 scatter_bg_indirect_buf_.clear_to_zero();
676
677 drw.submit(reduce_ps_, view);
678
679 /* Used by reduce pass. */
680 downsample_tx_.release();
681
683 }
684
685 for (int is_background = 0; is_background < 2; is_background++) {
686 DRW_stats_group_start(is_background ? "Background Convolution" : "Foreground Convolution");
687
688 SwapChain<TextureFromPool, 2> &color_tx = is_background ? color_bg_tx_ : color_fg_tx_;
689 SwapChain<TextureFromPool, 2> &weight_tx = is_background ? weight_bg_tx_ : weight_fg_tx_;
690 Framebuffer &scatter_fb = is_background ? scatter_bg_fb_ : scatter_fg_fb_;
691 PassSimple &gather_ps = is_background ? gather_bg_ps_ : gather_fg_ps_;
692 PassSimple &filter_ps = is_background ? filter_bg_ps_ : filter_fg_ps_;
693 PassSimple &scatter_ps = is_background ? scatter_bg_ps_ : scatter_fg_ps_;
694
695 color_tx.current().acquire(half_res, GPU_RGBA16F, usage_readwrite_attach);
696 weight_tx.current().acquire(half_res, GPU_R16F, usage_readwrite);
697 occlusion_tx_.acquire(half_res, GPU_RG16F);
698
699 drw.submit(gather_ps, view);
700
701 {
702 /* Filtering pass. */
703 color_tx.swap();
704 weight_tx.swap();
705
706 color_tx.current().acquire(half_res, GPU_RGBA16F, usage_readwrite_attach);
707 weight_tx.current().acquire(half_res, GPU_R16F, usage_readwrite);
708
709 drw.submit(filter_ps, view);
710
711 color_tx.previous().release();
712 weight_tx.previous().release();
713 }
714
716
718
719 GPU_framebuffer_bind(scatter_fb);
720 drw.submit(scatter_ps, view);
721
722 /* Used by scatter pass. */
723 occlusion_tx_.release();
724
726 }
727 {
728 DRW_stats_group_start("Hole Fill");
729
730 bokeh_gather_lut_tx_.release();
731 bokeh_scatter_lut_tx_.release();
732
733 hole_fill_color_tx_.acquire(half_res, GPU_RGBA16F, usage_readwrite);
734 hole_fill_weight_tx_.acquire(half_res, GPU_R16F, usage_readwrite);
735
736 drw.submit(hole_fill_ps_, view);
737
738 /* NOTE: We do not filter the hole-fill pass as effect is likely to not be noticeable. */
739
741 }
742 {
743 DRW_stats_group_start("Resolve");
744
745 resolve_stable_color_tx_ = dof_buffer.stabilize_history_tx_;
746
747 drw.submit(resolve_ps_, view);
748
749 color_bg_tx_.current().release();
750 color_fg_tx_.current().release();
751 weight_bg_tx_.current().release();
752 weight_fg_tx_.current().release();
753 tiles_fg_tx_.current().release();
754 tiles_bg_tx_.current().release();
755 hole_fill_color_tx_.release();
756 hole_fill_weight_tx_.release();
757 bokeh_resolve_lut_tx_.release();
758
760 }
761
763
764 /* Swap buffers so that next effect has the right input. */
765 std::swap(*input_tx, *output_tx);
766}
767
770} // namespace blender::eevee
Camera data-block and utility functions.
float BKE_camera_object_dof_distance(const struct Object *ob)
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
void mul_project_m4_v3(const float mat[4][4], float vec[3])
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
#define UNPACK2(a)
@ CAM_DOF_ENABLED
@ OB_CAMERA
@ SCE_EEVEE_DOF_JITTER
static AppView * view
#define GPU_ATTACHMENT_TEXTURE(_texture)
#define GPU_ATTACHMENT_NONE
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
@ GPU_DRIVER_ANY
bool GPU_type_matches_ex(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver, eGPUBackendType backend)
@ GPU_OS_UNIX
@ GPU_DEVICE_ATI
@ GPU_PRIM_TRI_STRIP
void GPU_memory_barrier(eGPUBarrier barrier)
Definition gpu_state.cc:374
void GPU_flush()
Definition gpu_state.cc:294
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
@ GPU_BARRIER_TEXTURE_FETCH
Definition GPU_state.hh:37
@ GPU_BARRIER_SHADER_IMAGE_ACCESS
Definition GPU_state.hh:35
@ GPU_BARRIER_FRAMEBUFFER
Definition GPU_state.hh:33
int GPU_texture_height(const GPUTexture *texture)
int GPU_texture_width(const GPUTexture *texture)
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_SHADER_WRITE
@ GPU_TEXTURE_USAGE_ATTACHMENT
@ GPU_SAMPLER_FILTERING_MIPMAP
@ GPU_SAMPLER_FILTERING_LINEAR
struct GPUShader GPUShader
void ensure(GPUAttachment depth=GPU_ATTACHMENT_NONE, GPUAttachment color1=GPU_ATTACHMENT_NONE, GPUAttachment color2=GPU_ATTACHMENT_NONE, GPUAttachment color3=GPU_ATTACHMENT_NONE, GPUAttachment color4=GPU_ATTACHMENT_NONE, GPUAttachment color5=GPU_ATTACHMENT_NONE, GPUAttachment color6=GPU_ATTACHMENT_NONE, GPUAttachment color7=GPU_ATTACHMENT_NONE, GPUAttachment color8=GPU_ATTACHMENT_NONE)
void submit(PassSimple &pass, View &view)
static void swap(TextureFromPool &a, Texture &b)
void acquire(int2 extent, eGPUTextureFormat format, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL)
bool ensure_mip_views(bool cube_as_array=false)
void clear(float4 values)
bool ensure_2d(eGPUTextureFormat format, int2 extent, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL, const float *data=nullptr, int mip_len=1)
GPUTexture * mip_view(int miplvl)
void bind_texture(const char *name, GPUTexture *texture, GPUSamplerState state=sampler_auto)
void bind_resources(U &resources)
Definition draw_pass.hh:426
void bind_image(const char *name, GPUTexture *image)
void specialize_constant(GPUShader *shader, const char *name, const float &data)
void dispatch(int group_len)
Definition draw_pass.hh:874
void barrier(eGPUBarrier type)
Definition draw_pass.hh:943
void bind_ubo(const char *name, GPUUniformBuf *buffer)
void push_constant(const char *name, const float &data)
void bind_ssbo(const char *name, GPUStorageBuf *buffer)
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
bool is_orthographic() const
const CameraData & data_get() const
void render(View &view, GPUTexture **input_tx, GPUTexture **output_tx, DepthOfFieldBuffer &dof_buffer)
void jitter_apply(float4x4 &winmat, float4x4 &viewmat)
float2 pixel_jitter_get() const
int2 render_extent_get() const
void info_append(const char *msg, Args &&...args)
uint64_t dof_ring_count_get() const
void dof_disk_sample_get(float *r_radius, float *r_theta) const
uint64_t dof_sample_count_get() const
GPUShader * static_shader_get(eShaderType shader_type)
#define sinf(x)
#define cosf(x)
#define ceilf(x)
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
void DRW_stats_group_start(const char *name)
void DRW_stats_group_end()
@ DRW_STATE_BLEND_ADD_FULL
Definition draw_state.hh:53
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
#define DOF_DEFAULT_GROUP_SIZE
#define DOF_REDUCE_GROUP_SIZE
#define DOF_TILES_DILATE_GROUP_SIZE
#define DOF_MIP_COUNT
#define DOF_FILTER_GROUP_SIZE
#define DOF_STABILIZE_GROUP_SIZE
#define DOF_BOKEH_LUT_SIZE
#define DOF_GATHER_GROUP_SIZE
#define DOF_TILES_SIZE
#define DOF_RESOLVE_GROUP_SIZE
#define DOF_MAX_SLIGHT_FOCUS_RADIUS
DOF_TILES_FLATTEN_GROUP_SIZE coc_tx GPU_R11F_G11F_B10F
draw_view in_light_buf[] float
#define DOF_DILATE_RING_COUNT
#define DOF_GATHER_RING_COUNT
detail::Pass< command::DrawCommandBuf > PassSimple
constexpr GPUSamplerState no_filter
static float film_filter_weight(float filter_radius, float sample_distance_sqr)
constexpr GPUSamplerState with_filter
static float circle_to_polygon_angle(float sides_count, float theta)
static float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth)
static float circle_to_polygon_radius(float sides_count, float theta)
T length_squared(const VecBase< T, Size > &a)
VecBase< T, Size > divide_ceil(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< T, Size > ceil_to_multiple(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
float bokeh_neighbor_max
struct SceneEEVEE eevee
const c_style_mat & ptr() const