Blender  V2.93
COM_SunBeamsOperation.cc
Go to the documentation of this file.
1 /* This program is free software; you can redistribute it and/or
2  * modify it under the terms of the GNU General Public License
3  * as published by the Free Software Foundation; either version 2
4  * of the License, or (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program; if not, write to the Free Software Foundation,
13  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
14  *
15  * Copyright 2014, Blender Foundation.
16  */
17 
18 #include "MEM_guardedalloc.h"
19 
20 #include "COM_SunBeamsOperation.h"
21 
22 namespace blender::compositor {
23 
25 {
29 
30  this->flags.complex = true;
31 }
32 
34 {
35  /* convert to pixels */
36  this->m_source_px[0] = this->m_data.source[0] * this->getWidth();
37  this->m_source_px[1] = this->m_data.source[1] * this->getHeight();
38  this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight());
39 }
40 
58 template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
59 
60  /* utility functions implementing the matrix transform to/from sector space */
61 
62  static inline void buffer_to_sector(const float source[2], int x, int y, int &u, int &v)
63  {
64  int x0 = (int)source[0];
65  int y0 = (int)source[1];
66  x -= x0;
67  y -= y0;
68  u = x * fxu + y * fyu;
69  v = x * fxv + y * fyv;
70  }
71 
72  static inline void buffer_to_sector(const float source[2], float x, float y, float &u, float &v)
73  {
74  int x0 = (int)source[0];
75  int y0 = (int)source[1];
76  x -= (float)x0;
77  y -= (float)y0;
78  u = x * fxu + y * fyu;
79  v = x * fxv + y * fyv;
80  }
81 
82  static inline void sector_to_buffer(const float source[2], int u, int v, int &x, int &y)
83  {
84  int x0 = (int)source[0];
85  int y0 = (int)source[1];
86  x = x0 + u * fxu + v * fxv;
87  y = y0 + u * fyu + v * fyv;
88  }
89 
90  static inline void sector_to_buffer(const float source[2], float u, float v, float &x, float &y)
91  {
92  int x0 = (int)source[0];
93  int y0 = (int)source[1];
94  x = (float)x0 + u * fxu + v * fxv;
95  y = (float)y0 + u * fyu + v * fyv;
96  }
97 
109  static float *init_buffer_iterator(MemoryBuffer *input,
110  const float source[2],
111  const float co[2],
112  float dist_min,
113  float dist_max,
114  int &x,
115  int &y,
116  int &num,
117  float &v,
118  float &dv,
119  float &falloff_factor)
120  {
121  float pu, pv;
122  buffer_to_sector(source, co[0], co[1], pu, pv);
123 
124  /* line angle */
125  float tan_phi = pv / pu;
126  float dr = sqrtf(tan_phi * tan_phi + 1.0f);
127  float cos_phi = 1.0f / dr;
128 
129  /* clamp u range to avoid influence of pixels "behind" the source */
130  float umin = max_ff(pu - cos_phi * dist_min, 0.0f);
131  float umax = max_ff(pu - cos_phi * dist_max, 0.0f);
132  v = umin * tan_phi;
133  dv = tan_phi;
134 
135  int start = (int)floorf(umax);
136  int end = (int)ceilf(umin);
137  num = end - start;
138 
139  sector_to_buffer(source, end, (int)ceilf(v), x, y);
140 
141  falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f;
142 
143  float *iter = input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * (x + input->getWidth() * y);
144  return iter;
145  }
146 
154  static void eval(MemoryBuffer *input,
155  float output[4],
156  const float co[2],
157  const float source[2],
158  float dist_min,
159  float dist_max)
160  {
161  const rcti &rect = input->get_rect();
162  int buffer_width = input->getWidth();
163  int x, y, num;
164  float v, dv;
165  float falloff_factor;
166  float border[4];
167 
168  zero_v4(output);
169 
170  if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) {
173  ((int)source[0] + input->getWidth() * (int)source[1]));
174  return;
175  }
176 
177  /* Initialize the iteration variables. */
178  float *buffer = init_buffer_iterator(
179  input, source, co, dist_min, dist_max, x, y, num, v, dv, falloff_factor);
180  zero_v3(border);
181  border[3] = 1.0f;
182 
183  /* v_local keeps track of when to decrement v (see below) */
184  float v_local = v - floorf(v);
185 
186  for (int i = 0; i < num; i++) {
187  float weight = 1.0f - (float)i * falloff_factor;
188  weight *= weight;
189 
190  /* range check, use last valid color when running beyond the image border */
191  if (x >= rect.xmin && x < rect.xmax && y >= rect.ymin && y < rect.ymax) {
192  madd_v4_v4fl(output, buffer, buffer[3] * weight);
193  /* use as border color in case subsequent pixels are out of bounds */
195  }
196  else {
197  madd_v4_v4fl(output, border, border[3] * weight);
198  }
199 
200  /* TODO implement proper filtering here, see
201  * https://en.wikipedia.org/wiki/Lanczos_resampling
202  * https://en.wikipedia.org/wiki/Sinc_function
203  *
204  * using lanczos with x = distance from the line segment,
205  * normalized to a == 0.5f, could give a good result
206  *
207  * for now just divide equally at the end ...
208  */
209 
210  /* decrement u */
211  x -= fxu;
212  y -= fyu;
213  buffer -= (fxu + fyu * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS;
214 
215  /* decrement v (in steps of dv < 1) */
216  v_local -= dv;
217  if (v_local < 0.0f) {
218  v_local += 1.0f;
219 
220  x -= fxv;
221  y -= fyv;
222  buffer -= (fxv + fyv * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS;
223  }
224  }
225 
226  /* normalize */
227  if (num > 0) {
228  mul_v4_fl(output, 1.0f / (float)num);
229  }
230  }
231 };
232 
241 static void accumulate_line(MemoryBuffer *input,
242  float output[4],
243  const float co[2],
244  const float source[2],
245  float dist_min,
246  float dist_max)
247 {
248  /* coordinates relative to source */
249  float pt_ofs[2] = {co[0] - source[0], co[1] - source[1]};
250 
251  /* The source sectors are defined like so:
252  *
253  * \ 3 | 2 /
254  * \ | /
255  * 4 \ | / 1
256  * \|/
257  * -----------
258  * /|\
259  * 5 / | \ 8
260  * / | \
261  * / 6 | 7 \
262  *
263  * The template arguments encode the transformation into "sector space",
264  * by means of rotation/mirroring matrix elements.
265  */
266 
267  if (fabsf(pt_ofs[1]) > fabsf(pt_ofs[0])) {
268  if (pt_ofs[0] > 0.0f) {
269  if (pt_ofs[1] > 0.0f) {
270  /* 2 */
271  BufferLineAccumulator<0, 1, 1, 0>::eval(input, output, co, source, dist_min, dist_max);
272  }
273  else {
274  /* 7 */
275  BufferLineAccumulator<0, 1, -1, 0>::eval(input, output, co, source, dist_min, dist_max);
276  }
277  }
278  else {
279  if (pt_ofs[1] > 0.0f) {
280  /* 3 */
281  BufferLineAccumulator<0, -1, 1, 0>::eval(input, output, co, source, dist_min, dist_max);
282  }
283  else {
284  /* 6 */
285  BufferLineAccumulator<0, -1, -1, 0>::eval(input, output, co, source, dist_min, dist_max);
286  }
287  }
288  }
289  else {
290  if (pt_ofs[0] > 0.0f) {
291  if (pt_ofs[1] > 0.0f) {
292  /* 1 */
293  BufferLineAccumulator<1, 0, 0, 1>::eval(input, output, co, source, dist_min, dist_max);
294  }
295  else {
296  /* 8 */
297  BufferLineAccumulator<1, 0, 0, -1>::eval(input, output, co, source, dist_min, dist_max);
298  }
299  }
300  else {
301  if (pt_ofs[1] > 0.0f) {
302  /* 4 */
303  BufferLineAccumulator<-1, 0, 0, 1>::eval(input, output, co, source, dist_min, dist_max);
304  }
305  else {
306  /* 5 */
307  BufferLineAccumulator<-1, 0, 0, -1>::eval(input, output, co, source, dist_min, dist_max);
308  }
309  }
310  }
311 }
312 
314 {
315  void *buffer = getInputOperation(0)->initializeTileData(nullptr);
316  return buffer;
317 }
318 
319 void SunBeamsOperation::executePixel(float output[4], int x, int y, void *data)
320 {
321  const float co[2] = {(float)x, (float)y};
322 
324  (MemoryBuffer *)data, output, co, this->m_source_px, 0.0f, this->m_ray_length_px);
325 }
326 
327 static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length)
328 {
329  float co[2] = {(float)x, (float)y};
330  float dir[2], dist;
331 
332  /* move (x,y) vector toward the source by ray_length distance */
333  sub_v2_v2v2(dir, co, source);
334  dist = normalize_v2(dir);
335  mul_v2_fl(dir, min_ff(dist, ray_length));
336  sub_v2_v2(co, dir);
337 
338  int ico[2] = {(int)co[0], (int)co[1]};
339  BLI_rcti_do_minmax_v(rect, ico);
340 }
341 
343  ReadBufferOperation *readOperation,
344  rcti *output)
345 {
346  /* Enlarges the rect by moving each corner toward the source.
347  * This is the maximum distance that pixels can influence each other
348  * and gives a rect that contains all possible accumulated pixels.
349  */
350  rcti rect = *input;
351  calc_ray_shift(&rect, input->xmin, input->ymin, this->m_source_px, this->m_ray_length_px);
352  calc_ray_shift(&rect, input->xmin, input->ymax, this->m_source_px, this->m_ray_length_px);
353  calc_ray_shift(&rect, input->xmax, input->ymin, this->m_source_px, this->m_ray_length_px);
354  calc_ray_shift(&rect, input->xmax, input->ymax, this->m_source_px, this->m_ray_length_px);
355 
356  return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output);
357 }
358 
359 } // namespace blender::compositor
typedef float(TangentPoint)[2]
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:528
#define MAX2(a, b)
_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
#define output
a MemoryBuffer contains access to the data of a chunk
const rcti & get_rect() const
get the rect of this MemoryBuffer
const int getWidth() const
get the width of this MemoryBuffer
float * getBuffer()
get the data of this MemoryBuffer
virtual void * initializeTileData(rcti *)
void addInputSocket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
NodeOperation * getInputOperation(unsigned int inputSocketindex)
void addOutputSocket(DataType datatype)
void setResolutionInputSocketIndex(unsigned int index)
set the index of the input socket that will determine the resolution of this operation
virtual bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override
void * initializeTileData(rcti *rect) override
void executePixel(float output[4], int x, int y, void *data) override
calculate a single pixel
IconTextureDrawCall border
#define ceilf(x)
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
__kernel void ccl_constant KernelData ccl_global void ccl_global char ccl_global int ccl_global char ccl_global unsigned int ccl_global float * buffer
constexpr int COM_DATA_TYPE_COLOR_CHANNELS
Definition: COM_defines.h:53
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 void sector_to_buffer(const float source[2], int u, int v, int &x, int &y)
static void sector_to_buffer(const float source[2], float u, float v, float &x, float &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)
static void buffer_to_sector(const float source[2], int x, int y, int &u, int &v)
int ymin
Definition: DNA_vec_types.h:80
int ymax
Definition: DNA_vec_types.h:80
int xmin
Definition: DNA_vec_types.h:79
int xmax
Definition: DNA_vec_types.h:79