Blender V4.3
node_composite_defocus.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <climits>
10
11#include "DNA_camera_types.h"
12#include "DNA_object_types.h"
13#include "DNA_scene_types.h"
14
15#include "BKE_camera.h"
16
17#include "RNA_access.hh"
18
19#include "UI_interface.hh"
20#include "UI_resources.hh"
21
23#include "COM_bokeh_kernel.hh"
24#include "COM_node_operation.hh"
25#include "COM_utilities.hh"
26
28
29/* ************ Defocus Node ****************** */
30
32
34
36{
37 b.add_input<decl::Color>("Image")
38 .default_value({1.0f, 1.0f, 1.0f, 1.0f})
39 .compositor_domain_priority(0);
40 b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f).compositor_domain_priority(
41 1);
42 b.add_output<decl::Color>("Image");
43}
44
45static void node_composit_init_defocus(bNodeTree * /*ntree*/, bNode *node)
46{
47 /* defocus node */
48 NodeDefocus *nbd = MEM_cnew<NodeDefocus>(__func__);
49 nbd->bktype = 0;
50 nbd->rotation = 0.0f;
51 nbd->preview = 1;
52 nbd->gamco = 0;
53 nbd->samples = 16;
54 nbd->fstop = 128.0f;
55 nbd->maxblur = 16;
56 nbd->bthresh = 1.0f;
57 nbd->scale = 1.0f;
58 nbd->no_zbuf = 1;
59 node->storage = nbd;
60}
61
63{
64 uiLayout *sub, *col;
65
66 col = uiLayoutColumn(layout, false);
67 uiItemL(col, IFACE_("Bokeh Type:"), ICON_NONE);
68 uiItemR(col, ptr, "bokeh", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
69 uiItemR(col, ptr, "angle", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
70
71 uiItemR(layout, ptr, "use_gamma_correction", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
72
73 col = uiLayoutColumn(layout, false);
74 uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_zbuffer") == true);
75 uiItemR(col, ptr, "f_stop", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
76
77 uiItemR(layout, ptr, "blur_max", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
78 uiItemR(layout, ptr, "threshold", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
79
80 col = uiLayoutColumn(layout, false);
81 uiItemR(col, ptr, "use_preview", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
82
83 uiTemplateID(layout, C, ptr, "scene", nullptr, nullptr, nullptr);
84
85 col = uiLayoutColumn(layout, false);
86 uiItemR(col, ptr, "use_zbuffer", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
87 sub = uiLayoutColumn(col, false);
88 uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_zbuffer") == false);
89 uiItemR(sub, ptr, "z_scale", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
90}
91
92using namespace blender::realtime_compositor;
93
95 public:
97
98 void execute() override
99 {
100 Result &input = get_input("Image");
101 Result &output = get_result("Image");
102 if (input.is_single_value() || node_storage(bnode()).maxblur < 1.0f) {
103 input.pass_through(output);
104 return;
105 }
106
108
109 const int maximum_defocus_radius = math::ceil(compute_maximum_defocus_radius());
110
111 /* The special zero value indicate a circle, in which case, the roundness should be set to
112 * 1, and the number of sides can be anything and is arbitrarily set to 3. */
113 const bool is_circle = node_storage(bnode()).bktype == 0;
114 const int2 kernel_size = int2(maximum_defocus_radius * 2 + 1);
115 const int sides = is_circle ? 3 : node_storage(bnode()).bktype;
116 const float rotation = node_storage(bnode()).rotation;
117 const float roundness = is_circle ? 1.0f : 0.0f;
118 const Result &bokeh_kernel = context().cache_manager().bokeh_kernels.get(
119 context(), kernel_size, sides, rotation, roundness, 0.0f, 0.0f);
120
121 GPUShader *shader = context().get_shader("compositor_defocus_blur");
122 GPU_shader_bind(shader);
123
124 GPU_shader_uniform_1b(shader, "gamma_correct", node_storage(bnode()).gamco);
125 GPU_shader_uniform_1i(shader, "search_radius", maximum_defocus_radius);
126
127 input.bind_as_texture(shader, "input_tx");
128
129 radius.bind_as_texture(shader, "radius_tx");
130
131 GPU_texture_filter_mode(bokeh_kernel, true);
132 bokeh_kernel.bind_as_texture(shader, "weights_tx");
133
134 const Domain domain = compute_domain();
135 output.allocate_texture(domain);
136 output.bind_as_image(shader, "output_img");
137
138 compute_dispatch_threads_at_least(shader, domain.size);
139
141 input.unbind_as_texture();
142 radius.unbind_as_texture();
143 bokeh_kernel.unbind_as_texture();
144 output.unbind_as_image();
145
146 radius.release();
147 }
148
150 {
151 if (node_storage(bnode()).no_zbuf) {
153 }
154 else {
156 }
157 }
158
160 {
161 GPUShader *shader = context().get_shader("compositor_defocus_radius_from_scale");
162 GPU_shader_bind(shader);
163
164 GPU_shader_uniform_1f(shader, "scale", node_storage(bnode()).scale);
165 GPU_shader_uniform_1f(shader, "max_radius", node_storage(bnode()).maxblur);
166
167 Result &input_radius = get_input("Z");
168 input_radius.bind_as_texture(shader, "radius_tx");
169
170 Result output_radius = context().create_result(ResultType::Float);
171 const Domain domain = input_radius.domain();
172 output_radius.allocate_texture(domain);
173 output_radius.bind_as_image(shader, "radius_img");
174
175 compute_dispatch_threads_at_least(shader, domain.size);
176
178 input_radius.unbind_as_texture();
179 output_radius.unbind_as_image();
180
181 return output_radius;
182 }
183
185 {
186 GPUShader *shader = context().get_shader("compositor_defocus_radius_from_depth");
187 GPU_shader_bind(shader);
188
189 const float distance_to_image_of_focus = compute_distance_to_image_of_focus();
190 GPU_shader_uniform_1f(shader, "f_stop", get_f_stop());
191 GPU_shader_uniform_1f(shader, "focal_length", get_focal_length());
192 GPU_shader_uniform_1f(shader, "max_radius", node_storage(bnode()).maxblur);
193 GPU_shader_uniform_1f(shader, "pixels_per_meter", compute_pixels_per_meter());
194 GPU_shader_uniform_1f(shader, "distance_to_image_of_focus", distance_to_image_of_focus);
195
196 Result &input_depth = get_input("Z");
197 input_depth.bind_as_texture(shader, "depth_tx");
198
199 Result output_radius = context().create_result(ResultType::Float);
200 const Domain domain = input_depth.domain();
201 output_radius.allocate_texture(domain);
202 output_radius.bind_as_image(shader, "radius_img");
203
204 compute_dispatch_threads_at_least(shader, domain.size);
205
207 input_depth.unbind_as_texture();
208 output_radius.unbind_as_image();
209
210 /* We apply a dilate morphological operator on the radius computed from depth, the operator
211 * radius is the maximum possible defocus radius. This is done such that objects in
212 * focus---that is, objects whose defocus radius is small---are not affected by nearby out of
213 * focus objects, hence the use of dilation. */
214 const float morphological_radius = compute_maximum_defocus_radius();
215 Result eroded_radius = context().create_result(ResultType::Float);
216 morphological_blur(context(), output_radius, eroded_radius, float2(morphological_radius));
217 output_radius.release();
218
219 return eroded_radius;
220 }
221
222 /* Computes the maximum possible defocus radius in pixels. */
224 {
225 if (node_storage(bnode()).no_zbuf) {
226 return node_storage(bnode()).maxblur;
227 }
228
229 const float maximum_diameter = compute_maximum_diameter_of_circle_of_confusion();
230 const float pixels_per_meter = compute_pixels_per_meter();
231 const float radius = (maximum_diameter / 2.0f) * pixels_per_meter;
232 return math::min(radius, node_storage(bnode()).maxblur);
233 }
234
235 /* Computes the diameter of the circle of confusion at infinity. This computes the limit in
236 * figure (5) of the paper:
237 *
238 * Potmesil, Michael, and Indranil Chakravarty. "A lens and aperture camera model for synthetic
239 * image generation." ACM SIGGRAPH Computer Graphics 15.3 (1981): 297-305.
240 *
241 * Notice that the diameter is asymmetric around the focus point, and we are computing the
242 * limiting diameter at infinity, while another limiting diameter exist at zero distance from the
243 * lens. This is a limitation of the implementation, as it assumes far defocusing only. */
245 {
246 const float f_stop = get_f_stop();
247 const float focal_length = get_focal_length();
248 const float distance_to_image_of_focus = compute_distance_to_image_of_focus();
249 return math::abs((distance_to_image_of_focus / (f_stop * focal_length)) -
250 (focal_length / f_stop));
251 }
252
253 /* Computes the distance in meters to the image of the focus point across a lens of the specified
254 * focal length. This computes `Vp` in equation (7) of the paper:
255 *
256 * Potmesil, Michael, and Indranil Chakravarty. "A lens and aperture camera model for synthetic
257 * image generation." ACM SIGGRAPH Computer Graphics 15.3 (1981): 297-305. */
259 {
260 const float focal_length = get_focal_length();
261 const float focus_distance = compute_focus_distance();
262 return (focal_length * focus_distance) / (focus_distance - focal_length);
263 }
264
265 /* Returns the focal length in meters. Fallback to 50 mm in case of an invalid camera. Ensure a
266 * minimum of 1e-6. */
268 {
269 const Camera *camera = get_camera();
270 return camera ? math::max(1e-6f, camera->lens / 1000.0f) : 50.0f / 1000.0f;
271 }
272
273 /* Computes the distance to the point that is completely in focus. Default to 10 meters for null
274 * camera. */
276 {
277 const Object *camera_object = get_camera_object();
278 if (!camera_object) {
279 return 10.0f;
280 }
281 return BKE_camera_object_dof_distance(camera_object);
282 }
283
284 /* Computes the number of pixels per meter of the sensor size. This is essentially the resolution
285 * over the sensor size, using the sensor fit axis. Fallback to DEFAULT_SENSOR_WIDTH in case of
286 * an invalid camera. Note that the stored sensor size is in millimeter, so convert to meters. */
288 {
289 const int2 size = compute_domain().size;
290 const Camera *camera = get_camera();
291 const float default_value = size.x / (DEFAULT_SENSOR_WIDTH / 1000.0f);
292 if (!camera) {
293 return default_value;
294 }
295
296 switch (camera->sensor_fit) {
298 return size.x / (camera->sensor_x / 1000.0f);
300 return size.y / (camera->sensor_y / 1000.0f);
302 return size.x > size.y ? size.x / (camera->sensor_x / 1000.0f) :
303 size.y / (camera->sensor_y / 1000.0f);
304 }
305 default:
306 break;
307 }
308
309 return default_value;
310 }
311
312 /* Returns the f-stop number. Fallback to 1e-3 for zero f-stop. */
313 const float get_f_stop()
314 {
315 return math::max(1e-3f, node_storage(bnode()).fstop);
316 }
317
319 {
320 const Object *camera_object = get_camera_object();
321 if (!camera_object || camera_object->type != OB_CAMERA) {
322 return nullptr;
323 }
324
325 return reinterpret_cast<Camera *>(camera_object->data);
326 }
327
329 {
330 return get_scene()->camera;
331 }
332
334 {
335 return bnode().id ? reinterpret_cast<Scene *>(bnode().id) : &context().get_scene();
336 }
337};
338
340{
341 return new DefocusOperation(context, node);
342}
343
344} // namespace blender::nodes::node_composite_defocus_cc
345
347{
349
350 static blender::bke::bNodeType ntype;
351
352 cmp_node_type_base(&ntype, CMP_NODE_DEFOCUS, "Defocus", NODE_CLASS_OP_FILTER);
353 ntype.declare = file_ns::cmp_node_defocus_declare;
354 ntype.draw_buttons = file_ns::node_composit_buts_defocus;
355 ntype.initfunc = file_ns::node_composit_init_defocus;
358 ntype.get_compositor_operation = file_ns::get_compositor_operation;
359
361}
Camera data-block and utility functions.
float BKE_camera_object_dof_distance(const struct Object *ob)
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_OP_FILTER
Definition BKE_node.hh:408
#define IFACE_(msgid)
@ CAMERA_SENSOR_FIT_HOR
@ CAMERA_SENSOR_FIT_AUTO
@ CAMERA_SENSOR_FIT_VERT
#define DEFAULT_SENSOR_WIDTH
Object is a sort of wrapper for general info.
@ OB_CAMERA
void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value)
void GPU_shader_bind(GPUShader *shader)
void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value)
void GPU_shader_unbind()
void GPU_texture_filter_mode(GPUTexture *texture, bool use_filter)
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiItemL(uiLayout *layout, const char *name, int icon)
void uiTemplateID(uiLayout *layout, const bContext *C, PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, int filter=UI_TEMPLATE_ID_FILTER_ALL, bool live_icon=false, const char *text=nullptr)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
struct GPUShader GPUShader
Result & get(Context &context, int2 size, int sides, float rotation, float roundness, float catadioptric, float lens_shift)
virtual const Scene & get_scene() const =0
GPUShader * get_shader(const char *info_name, ResultPrecision precision)
Result create_result(ResultType type, ResultPrecision precision)
NodeOperation(Context &context, DNode node)
Result & get_input(StringRef identifier) const
Definition operation.cc:144
Result & get_result(StringRef identifier)
Definition operation.cc:46
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
Definition result.cc:264
const Domain & domain() const
Definition result.cc:712
void allocate_texture(Domain domain, bool from_pool=true)
Definition result.cc:204
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:253
local_group_size(16, 16) .push_constant(Type b
uint col
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
T min(const T &a, const T &b)
T ceil(const T &a)
T max(const T &a, const T &b)
T abs(const T &a)
static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void cmp_node_defocus_declare(NodeDeclarationBuilder &b)
static void node_composit_init_defocus(bNodeTree *, bNode *node)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
void morphological_blur(Context &context, Result &input, Result &output, float2 radius, MorphologicalBlurOperation operation=MorphologicalBlurOperation::Erode, int filter_type=R_FILTER_GAUSS)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:131
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
void register_node_type_cmp_defocus()
void cmp_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
struct Object * camera
struct ID * id
Defines a node type.
Definition BKE_node.hh:218
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:324
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126