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
8
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
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
171 const Domain domain = input_radius.domain();
172 output_radius.allocate_texture(domain);
173 output_radius.bind_as_image(shader, "radius_img");
174
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
200 const Domain domain = input_depth.domain();
201 output_radius.allocate_texture(domain);
202 output_radius.bind_as_image(shader, "radius_img");
203
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();
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
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 CMP_NODE_DEFOCUS
Definition BKE_node.hh:1056
#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)
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
#define C
Definition RandGen.cpp:29
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
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define output
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
void pass_through(Result &target)
Definition result.cc:289
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)
#define min(a, b)
Definition sort.c:32
struct Object * camera
struct ID * id
void * storage
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
float max
PointerRNA * ptr
Definition wm_files.cc:4126