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
19
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/* -------------------------------------------------------------------- */
42
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;
60 data_.scatter_color_threshold = sce_eevee.bokeh_threshold;
61 data_.scatter_neighbor_max_color = sce_eevee.bokeh_neighbor_max;
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);
95 data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale;
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));
207 sample *= data_.bokeh_anisotropic_scale;
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
225
226/* -------------------------------------------------------------------- */
229
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();
271 stabilize_ps_.shader_set(inst_.shaders.static_shader_get(DOF_STABILIZE));
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();
304 reduce_ps_.shader_set(inst_.shaders.static_shader_get(DOF_REDUCE));
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);
437 hole_fill_ps_.shader_set(inst_.shaders.static_shader_get(DOF_GATHER_HOLE_FILL));
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{
452 GPUSamplerState with_filter = {GPU_SAMPLER_FILTERING_LINEAR};
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);
474 resolve_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
475 resolve_ps_.dispatch(&dispatch_resolve_size_);
476 resolve_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
477}
478
480
481/* -------------------------------------------------------------------- */
484
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
769
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between camera
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 clear(float4 values)
bool ensure_2d(eGPUTextureFormat format, int2 extent, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL, const float *data=nullptr, int mip_len=1)
void bind_texture(const char *name, GPUTexture *texture, GPUSamplerState state=sampler_auto)
void bind_image(const char *name, GPUTexture *image)
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 shader_set(GPUShader *shader)
Definition draw_pass.hh:971
void render(View &view, GPUTexture **input_tx, GPUTexture **output_tx, DepthOfFieldBuffer &dof_buffer)
void jitter_apply(float4x4 &winmat, float4x4 &viewmat)
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)
MatBase< float, 4, 4 > float4x4
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
const c_style_mat & ptr() const