Blender V4.3
COM_BokehImageOperation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BLI_math_base.h"
8#include "BLI_math_base.hh"
9#include "BLI_math_numbers.hh"
10#include "BLI_math_vector.hh"
11
12namespace blender::compositor {
13
19
20/* The exterior angle is the angle between each two consecutive vertices of the regular polygon
21 * from its center. */
22static float compute_exterior_angle(int sides)
23{
24 return (math::numbers::pi * 2.0f) / sides;
25}
26
27static float compute_rotation(float angle, int sides)
28{
29 /* Offset the rotation such that the second vertex of the regular polygon lies on the positive
30 * y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming
31 * the first vertex lies on the positive x axis. */
32 const float offset = math::numbers::pi / 2.0f - compute_exterior_angle(sides);
33 return angle - offset;
34}
35
37{
38 exterior_angle_ = compute_exterior_angle(data_->flaps);
39 rotation_ = compute_rotation(data_->angle, data_->flaps);
40 roundness_ = data_->rounding;
41 catadioptric_ = data_->catadioptric;
42 lens_shift_ = data_->lensshift;
43}
44
45float2 BokehImageOperation::get_regular_polygon_vertex_position(int vertex_index)
46{
47 float angle = exterior_angle_ * vertex_index - rotation_;
48 return float2(math::cos(angle), math::sin(angle));
49}
50
51float2 BokehImageOperation::closest_point_on_line(float2 point, float2 line_start, float2 line_end)
52{
53 float2 line_vector = line_end - line_start;
54 float2 point_vector = point - line_start;
55 float line_length_squared = math::dot(line_vector, line_vector);
56 float parameter = math::dot(point_vector, line_vector) / line_length_squared;
57 return line_start + line_vector * parameter;
58}
59
60float BokehImageOperation::bokeh(float2 point, float circumradius)
61{
62 /* Get the index of the vertex of the regular polygon whose polar angle is maximum but less than
63 * the polar angle of the given point, taking rotation into account. This essentially finds the
64 * vertex closest to the given point in the clock-wise direction. */
65 float angle = floored_fmod(math::atan2(point.y, point.x) + rotation_,
67 int vertex_index = int(angle / exterior_angle_);
68
69 /* Compute the shortest distance between the origin and the polygon edge composed from the
70 * previously selected vertex and the one following it. */
71 float2 first_vertex = this->get_regular_polygon_vertex_position(vertex_index) * circumradius;
72 float2 second_vertex = this->get_regular_polygon_vertex_position(vertex_index + 1) *
73 circumradius;
74 float2 closest_point = this->closest_point_on_line(point, first_vertex, second_vertex);
75 float distance_to_edge = math::length(closest_point);
76
77 /* Mix the distance to the edge with the circumradius, making it tend to the distance to a
78 * circle when roundness tends to 1. */
79 float distance_to_edge_round = math::interpolate(distance_to_edge, circumradius, roundness_);
80
81 /* The point is outside of the bokeh, so we return 0. */
82 float distance = math::length(point);
83 if (distance > distance_to_edge_round) {
84 return 0.0f;
85 }
86
87 /* The point is inside the catadioptric hole and is not part of the bokeh, so we return 0. */
88 float catadioptric_distance = distance_to_edge_round * catadioptric_;
89 if (distance < catadioptric_distance) {
90 return 0.0f;
91 }
92
93 /* The point is very close to the edge of the bokeh, so we return the difference between the
94 * distance to the edge and the distance as a form of anti-aliasing. */
95 if (distance_to_edge_round - distance < 1.0f) {
96 return distance_to_edge_round - distance;
97 }
98
99 /* The point is very close to the edge of the catadioptric hole, so we return the difference
100 * between the distance to the hole and the distance as a form of anti-aliasing. */
101 if (catadioptric_ != 0.0f && distance - catadioptric_distance < 1.0f) {
102 return distance - catadioptric_distance;
103 }
104
105 /* Otherwise, the point is part of the bokeh and we return 1. */
106 return 1.0f;
107}
108
110 const rcti &area,
111 Span<MemoryBuffer *> /*inputs*/)
112{
113 /* Since we need the regular polygon to occupy the entirety of the output image, the circumradius
114 * of the regular polygon is half the width of the output image. */
115 float circumradius = float(resolution_) / 2.0f;
116
117 for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
118 int2 texel = int2(it.x, it.y);
119
120 /* Move the texel coordinates such that the regular polygon is centered. */
121 float2 point = float2(texel) + float2(0.5f) - circumradius;
122
123 /* Each of the color channels of the output image contains a bokeh with a different
124 * circumradius. The largest one occupies the whole image as stated above, while the other two
125 * have circumradii that are shifted by an amount that is proportional to the "lens_shift"
126 * value. The alpha channel of the output is the average of all three values. */
127 float min_shift = math::abs(lens_shift_ * circumradius);
128 float min = min_shift == circumradius ? 0.0f : this->bokeh(point, circumradius - min_shift);
129
130 float median_shift = min_shift / 2.0f;
131 float median = this->bokeh(point, circumradius - median_shift);
132
133 float max = this->bokeh(point, circumradius);
134 float4 bokeh = float4(min, median, max, (max + median + min) / 3.0f);
135
136 /* If the lens shift is negative, swap the min and max bokeh values, which are stored in the
137 * red and blue channels respectively. Note that we take the absolute value of the lens shift
138 * above, so the sign of the lens shift only controls this swap. */
139 if (lens_shift_ < 0.0f) {
140 std::swap(bokeh.x, bokeh.z);
141 }
142
143 copy_v4_v4(it.out, bokeh);
144 }
145}
146
148{
149 if (delete_data_) {
150 if (data_) {
151 delete data_;
152 data_ = nullptr;
153 }
154 }
155}
156
157void BokehImageOperation::determine_canvas(const rcti & /*preferred_area*/, rcti &r_area)
158{
159 BLI_rcti_init(&r_area, 0, resolution_, 0, resolution_);
160}
161
162} // namespace blender::compositor
MINLINE float floored_fmod(float f, float n)
MINLINE void copy_v4_v4(float r[4], const float a[4])
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.c:418
void determine_canvas(const rcti &preferred_area, rcti &r_area) override
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
a MemoryBuffer contains access to the data
void add_output_socket(DataType datatype)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float compute_exterior_angle(int sides)
static float compute_rotation(float angle, int sides)
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
T cos(const AngleRadianBase< T > &a)
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T atan2(const T &y, const T &x)
T sin(const AngleRadianBase< T > &a)
T abs(const T &a)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
float distance(float a, float b)
#define min(a, b)
Definition sort.c:32