Blender  V3.3
COM_SunBeamsOperation.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2014 Blender Foundation. */
3 
4 #include "MEM_guardedalloc.h"
5 
7 
8 namespace blender::compositor {
9 
11 {
14  this->set_canvas_input_index(0);
15 
16  flags_.complex = true;
17 }
18 
19 void SunBeamsOperation::calc_rays_common_data()
20 {
21  /* convert to pixels */
22  source_px_[0] = data_.source[0] * this->get_width();
23  source_px_[1] = data_.source[1] * this->get_height();
24  ray_length_px_ = data_.ray_length * MAX2(this->get_width(), this->get_height());
25 }
26 
28 {
29  calc_rays_common_data();
30 }
31 
49 template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
50 
51  /* utility functions implementing the matrix transform to/from sector space */
52 
53  static inline void buffer_to_sector(const float source[2], float x, float y, float &u, float &v)
54  {
55  int x0 = (int)source[0];
56  int y0 = (int)source[1];
57  x -= (float)x0;
58  y -= (float)y0;
59  u = x * fxu + y * fyu;
60  v = x * fxv + y * fyv;
61  }
62 
63  static inline void sector_to_buffer(const float source[2], int u, int v, int &x, int &y)
64  {
65  int x0 = (int)source[0];
66  int y0 = (int)source[1];
67  x = x0 + u * fxu + v * fxv;
68  y = y0 + u * fyu + v * fyv;
69  }
70 
83  const float source[2],
84  const float co[2],
85  float dist_min,
86  float dist_max,
87  int &x,
88  int &y,
89  int &num,
90  float &v,
91  float &dv,
92  float &falloff_factor)
93  {
94  float pu, pv;
95  buffer_to_sector(source, co[0], co[1], pu, pv);
96 
97  /* line angle */
98  double tan_phi = pv / (double)pu;
99  double dr = sqrt(tan_phi * tan_phi + 1.0);
100  double cos_phi = 1.0 / dr;
101 
102  /* clamp u range to avoid influence of pixels "behind" the source */
103  float umin = max_ff(pu - cos_phi * dist_min, 0.0f);
104  float umax = max_ff(pu - cos_phi * dist_max, 0.0f);
105  v = umin * tan_phi;
106  dv = tan_phi;
107 
108  int start = (int)floorf(umax);
109  int end = (int)ceilf(umin);
110  num = end - start;
111 
112  sector_to_buffer(source, end, (int)ceilf(v), x, y);
113 
114  falloff_factor = dist_max > dist_min ? dr / (double)(dist_max - dist_min) : 0.0f;
115 
116  float *iter = input->get_buffer() + input->get_coords_offset(x, y);
117  return iter;
118  }
119 
127  static void eval(MemoryBuffer *input,
128  float output[4],
129  const float co[2],
130  const float source[2],
131  float dist_min,
132  float dist_max)
133  {
134  const rcti &rect = input->get_rect();
135  int x, y, num;
136  float v, dv;
137  float falloff_factor;
138  float border[4];
139 
140  zero_v4(output);
141 
142  if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) {
143  copy_v4_v4(output, input->get_elem(source[0], source[1]));
144  return;
145  }
146 
147  /* Initialize the iteration variables. */
148  float *buffer = init_buffer_iterator(
149  input, source, co, dist_min, dist_max, x, y, num, v, dv, falloff_factor);
150  zero_v3(border);
151  border[3] = 1.0f;
152 
153  /* v_local keeps track of when to decrement v (see below) */
154  float v_local = v - floorf(v);
155 
156  for (int i = 0; i < num; i++) {
157  float weight = 1.0f - (float)i * falloff_factor;
158  weight *= weight;
159 
160  /* range check, use last valid color when running beyond the image border */
161  if (x >= rect.xmin && x < rect.xmax && y >= rect.ymin && y < rect.ymax) {
162  madd_v4_v4fl(output, buffer, buffer[3] * weight);
163  /* use as border color in case subsequent pixels are out of bounds */
165  }
166  else {
167  madd_v4_v4fl(output, border, border[3] * weight);
168  }
169 
170  /* TODO: implement proper filtering here, see
171  * https://en.wikipedia.org/wiki/Lanczos_resampling
172  * https://en.wikipedia.org/wiki/Sinc_function
173  *
174  * using lanczos with x = distance from the line segment,
175  * normalized to a == 0.5f, could give a good result
176  *
177  * for now just divide equally at the end ...
178  */
179 
180  /* decrement u */
181  x -= fxu;
182  y -= fyu;
183  buffer -= fxu * input->elem_stride + fyu * input->row_stride;
184 
185  /* decrement v (in steps of dv < 1) */
186  v_local -= dv;
187  if (v_local < 0.0f) {
188  v_local += 1.0f;
189 
190  x -= fxv;
191  y -= fyv;
192  buffer -= fxv * input->elem_stride + fyv * input->row_stride;
193  }
194  }
195 
196  /* normalize */
197  if (num > 0) {
198  mul_v4_fl(output, 1.0f / (float)num);
199  }
200  }
201 };
202 
212  float output[4],
213  const float co[2],
214  const float source[2],
215  float dist_min,
216  float dist_max)
217 {
218  /* coordinates relative to source */
219  float pt_ofs[2] = {co[0] - source[0], co[1] - source[1]};
220 
221  /* The source sectors are defined like so:
222  *
223  * \ 3 | 2 /
224  * \ | /
225  * 4 \ | / 1
226  * \|/
227  * -----------
228  * /|\
229  * 5 / | \ 8
230  * / | \
231  * / 6 | 7 \
232  *
233  * The template arguments encode the transformation into "sector space",
234  * by means of rotation/mirroring matrix elements.
235  */
236 
237  if (fabsf(pt_ofs[1]) > fabsf(pt_ofs[0])) {
238  if (pt_ofs[0] > 0.0f) {
239  if (pt_ofs[1] > 0.0f) {
240  /* 2 */
241  BufferLineAccumulator<0, 1, 1, 0>::eval(input, output, co, source, dist_min, dist_max);
242  }
243  else {
244  /* 7 */
245  BufferLineAccumulator<0, 1, -1, 0>::eval(input, output, co, source, dist_min, dist_max);
246  }
247  }
248  else {
249  if (pt_ofs[1] > 0.0f) {
250  /* 3 */
251  BufferLineAccumulator<0, -1, 1, 0>::eval(input, output, co, source, dist_min, dist_max);
252  }
253  else {
254  /* 6 */
255  BufferLineAccumulator<0, -1, -1, 0>::eval(input, output, co, source, dist_min, dist_max);
256  }
257  }
258  }
259  else {
260  if (pt_ofs[0] > 0.0f) {
261  if (pt_ofs[1] > 0.0f) {
262  /* 1 */
263  BufferLineAccumulator<1, 0, 0, 1>::eval(input, output, co, source, dist_min, dist_max);
264  }
265  else {
266  /* 8 */
267  BufferLineAccumulator<1, 0, 0, -1>::eval(input, output, co, source, dist_min, dist_max);
268  }
269  }
270  else {
271  if (pt_ofs[1] > 0.0f) {
272  /* 4 */
273  BufferLineAccumulator<-1, 0, 0, 1>::eval(input, output, co, source, dist_min, dist_max);
274  }
275  else {
276  /* 5 */
277  BufferLineAccumulator<-1, 0, 0, -1>::eval(input, output, co, source, dist_min, dist_max);
278  }
279  }
280  }
281 }
282 
284 {
285  void *buffer = get_input_operation(0)->initialize_tile_data(nullptr);
286  return buffer;
287 }
288 
289 void SunBeamsOperation::execute_pixel(float output[4], int x, int y, void *data)
290 {
291  const float co[2] = {(float)x, (float)y};
292 
293  accumulate_line((MemoryBuffer *)data, output, co, source_px_, 0.0f, ray_length_px_);
294 }
295 
296 static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length)
297 {
298  float co[2] = {(float)x, (float)y};
299  float dir[2], dist;
300 
301  /* move (x,y) vector toward the source by ray_length distance */
302  sub_v2_v2v2(dir, co, source);
303  dist = normalize_v2(dir);
304  mul_v2_fl(dir, min_ff(dist, ray_length));
305  sub_v2_v2(co, dir);
306 
307  int ico[2] = {(int)co[0], (int)co[1]};
308  BLI_rcti_do_minmax_v(rect, ico);
309 }
310 
312  ReadBufferOperation *read_operation,
313  rcti *output)
314 {
315  /* Enlarges the rect by moving each corner toward the source.
316  * This is the maximum distance that pixels can influence each other
317  * and gives a rect that contains all possible accumulated pixels.
318  */
319  rcti rect = *input;
320  calc_ray_shift(&rect, input->xmin, input->ymin, source_px_, ray_length_px_);
321  calc_ray_shift(&rect, input->xmin, input->ymax, source_px_, ray_length_px_);
322  calc_ray_shift(&rect, input->xmax, input->ymin, source_px_, ray_length_px_);
323  calc_ray_shift(&rect, input->xmax, input->ymax, source_px_, ray_length_px_);
324 
325  return NodeOperation::determine_depending_area_of_interest(&rect, read_operation, output);
326 }
327 
329  const rcti &output_area,
330  rcti &r_input_area)
331 {
332  BLI_assert(input_idx == 0);
333  UNUSED_VARS(input_idx);
334  calc_rays_common_data();
335 
336  r_input_area = output_area;
337  /* Enlarges the rect by moving each corner toward the source.
338  * This is the maximum distance that pixels can influence each other
339  * and gives a rect that contains all possible accumulated pixels. */
340  calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, source_px_, ray_length_px_);
341  calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, source_px_, ray_length_px_);
342  calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, source_px_, ray_length_px_);
343  calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, source_px_, ray_length_px_);
344 }
345 
347  const rcti &area,
349 {
350  MemoryBuffer *input = inputs[0];
351  float coords[2];
352  for (int y = area.ymin; y < area.ymax; y++) {
353  coords[1] = y;
354  float *out_elem = output->get_elem(area.xmin, y);
355  for (int x = area.xmin; x < area.xmax; x++) {
356  coords[0] = x;
357  accumulate_line(input, out_elem, coords, source_px_, 0.0f, ray_length_px_);
358  out_elem += output->elem_stride;
359  }
360  }
361 }
362 
363 } // namespace blender::compositor
typedef float(TangentPoint)[2]
#define BLI_assert(a)
Definition: BLI_assert.h:46
sqrt(x)+1/max(0
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE void mul_v4_fl(float r[4], float f)
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void zero_v4(float r[4])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void madd_v4_v4fl(float r[4], const float a[4], float f)
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v2(float r[2])
void BLI_rcti_do_minmax_v(struct rcti *rect, const int xy[2])
Definition: rct.c:489
#define UNUSED_VARS(...)
#define MAX2(a, b)
typedef double(DMatrix)[4][4]
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v
a MemoryBuffer contains access to the data of a chunk
void add_output_socket(DataType datatype)
NodeOperation * get_input_operation(int index)
virtual bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
void set_canvas_input_index(unsigned int index)
set the index of the input socket that will determine the canvas of this operation
virtual void * initialize_tile_data(rcti *)
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override
Get input operation area being read by this operation on rendering given output area.
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void execute_pixel(float output[4], int x, int y, void *data) override
calculate a single pixel
void * initialize_tile_data(rcti *rect) override
bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output) override
IconTextureDrawCall border
ccl_global float * buffer
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_global KernelShaderEvalInput * input
#define ceilf(x)
Definition: metal/compat.h:225
#define floorf(x)
Definition: metal/compat.h:224
#define fabsf(x)
Definition: metal/compat.h:219
static void area(int d1, int d2, int e1, int e2, float weights[2])
static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length)
static void accumulate_line(MemoryBuffer *input, float output[4], const float co[2], const float source[2], float dist_min, float dist_max)
static bNodeSocketTemplate inputs[]
static void sector_to_buffer(const float source[2], int u, int v, int &x, int &y)
static void buffer_to_sector(const float source[2], float x, float y, float &u, float &v)
static void eval(MemoryBuffer *input, float output[4], const float co[2], const float source[2], float dist_min, float dist_max)
static float * init_buffer_iterator(MemoryBuffer *input, const float source[2], const float co[2], float dist_min, float dist_max, int &x, int &y, int &num, float &v, float &dv, float &falloff_factor)
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63