Changeset View
Changeset View
Standalone View
Standalone View
source/blender/compositor/operations/COM_SunBeamsOperation.cc
| Show All 15 Lines | |||||
| */ | */ | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "COM_SunBeamsOperation.h" | #include "COM_SunBeamsOperation.h" | ||||
| namespace blender::compositor { | namespace blender::compositor { | ||||
| SunBeamsOperation::SunBeamsOperation() | SunBeamsOperation::SunBeamsOperation() : FullFrameBufferedOperation(DataType::Color) | ||||
| { | { | ||||
| this->addInputSocket(DataType::Color); | this->addInputSocket(DataType::Color); | ||||
| this->addOutputSocket(DataType::Color); | this->addOutputSocket(DataType::Color); | ||||
| this->setResolutionInputSocketIndex(0); | this->setResolutionInputSocketIndex(0); | ||||
| this->flags.complex = true; | this->flags.complex = true; | ||||
| } | } | ||||
| void SunBeamsOperation::initExecution() | void SunBeamsOperation::initExecution() | ||||
| { | { | ||||
| WriteBufferOperation::initExecution(); | |||||
| /* convert to pixels */ | /* convert to pixels */ | ||||
| this->m_source_px[0] = this->m_data.source[0] * this->getWidth(); | this->m_source_px[0] = this->m_data.source[0] * this->getWidth(); | ||||
| this->m_source_px[1] = this->m_data.source[1] * this->getHeight(); | this->m_source_px[1] = this->m_data.source[1] * this->getHeight(); | ||||
| this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); | this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); | ||||
| } | } | ||||
| /** | /** | ||||
| * Defines a line accumulator for a specific sector, | * Defines a line accumulator for a specific sector, | ||||
| ▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | static float *init_buffer_iterator(MemoryBuffer *input, | ||||
| int start = (int)floorf(umax); | int start = (int)floorf(umax); | ||||
| int end = (int)ceilf(umin); | int end = (int)ceilf(umin); | ||||
| num = end - start; | num = end - start; | ||||
| sector_to_buffer(source, end, (int)ceilf(v), x, y); | sector_to_buffer(source, end, (int)ceilf(v), x, y); | ||||
| falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; | falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; | ||||
| float *iter = input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * (x + input->getWidth() * y); | float *iter = input->get_elem(x, y); | ||||
| return iter; | return iter; | ||||
| } | } | ||||
| /** | /** | ||||
| * Perform the actual accumulation along a ray segment from source to pt. | * Perform the actual accumulation along a ray segment from source to pt. | ||||
| * Only pixels within dist_min..dist_max contribute. | * Only pixels within dist_min..dist_max contribute. | ||||
| * | * | ||||
| * The loop runs backwards(!) over the primary sector space axis u, i.e. increasing distance to | * The loop runs backwards(!) over the primary sector space axis u, i.e. increasing distance to | ||||
| * pt. After each step it decrements v by dv < 1, adding a buffer shift when necessary. | * pt. After each step it decrements v by dv < 1, adding a buffer shift when necessary. | ||||
| */ | */ | ||||
| static void eval(MemoryBuffer *input, | static void eval(MemoryBuffer *input, | ||||
| float output[4], | float output[4], | ||||
| const float co[2], | const float co[2], | ||||
| const float source[2], | const float source[2], | ||||
| float dist_min, | float dist_min, | ||||
| float dist_max) | float dist_max) | ||||
| { | { | ||||
| const rcti &rect = input->get_rect(); | const rcti &rect = input->get_rect(); | ||||
| int buffer_width = input->getWidth(); | |||||
| int x, y, num; | int x, y, num; | ||||
| float v, dv; | float v, dv; | ||||
| float falloff_factor; | float falloff_factor; | ||||
| float border[4]; | float border[4]; | ||||
| zero_v4(output); | zero_v4(output); | ||||
| if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) { | if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) { | ||||
| copy_v4_v4(output, | copy_v4_v4(output, input->get_elem((int)source[0], (int)source[1])); | ||||
| input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * | |||||
| ((int)source[0] + input->getWidth() * (int)source[1])); | |||||
| return; | return; | ||||
| } | } | ||||
| /* Initialize the iteration variables. */ | /* Initialize the iteration variables. */ | ||||
| float *buffer = init_buffer_iterator( | float *input_elem = init_buffer_iterator( | ||||
| input, source, co, dist_min, dist_max, x, y, num, v, dv, falloff_factor); | input, source, co, dist_min, dist_max, x, y, num, v, dv, falloff_factor); | ||||
| zero_v3(border); | zero_v3(border); | ||||
| border[3] = 1.0f; | border[3] = 1.0f; | ||||
| /* v_local keeps track of when to decrement v (see below) */ | /* v_local keeps track of when to decrement v (see below) */ | ||||
| float v_local = v - floorf(v); | float v_local = v - floorf(v); | ||||
| for (int i = 0; i < num; i++) { | for (int i = 0; i < num; i++) { | ||||
| float weight = 1.0f - (float)i * falloff_factor; | float weight = 1.0f - (float)i * falloff_factor; | ||||
| weight *= weight; | weight *= weight; | ||||
| /* range check, use last valid color when running beyond the image border */ | /* range check, use last valid color when running beyond the image border */ | ||||
| if (x >= rect.xmin && x < rect.xmax && y >= rect.ymin && y < rect.ymax) { | if (x >= rect.xmin && x < rect.xmax && y >= rect.ymin && y < rect.ymax) { | ||||
| madd_v4_v4fl(output, buffer, buffer[3] * weight); | madd_v4_v4fl(output, input_elem, input_elem[3] * weight); | ||||
| /* use as border color in case subsequent pixels are out of bounds */ | /* use as border color in case subsequent pixels are out of bounds */ | ||||
| copy_v4_v4(border, buffer); | copy_v4_v4(border, input_elem); | ||||
| } | } | ||||
| else { | else { | ||||
| madd_v4_v4fl(output, border, border[3] * weight); | madd_v4_v4fl(output, border, border[3] * weight); | ||||
| } | } | ||||
| /* TODO implement proper filtering here, see | /* TODO implement proper filtering here, see | ||||
| * https://en.wikipedia.org/wiki/Lanczos_resampling | * https://en.wikipedia.org/wiki/Lanczos_resampling | ||||
| * https://en.wikipedia.org/wiki/Sinc_function | * https://en.wikipedia.org/wiki/Sinc_function | ||||
| * | * | ||||
| * using lanczos with x = distance from the line segment, | * using lanczos with x = distance from the line segment, | ||||
| * normalized to a == 0.5f, could give a good result | * normalized to a == 0.5f, could give a good result | ||||
| * | * | ||||
| * for now just divide equally at the end ... | * for now just divide equally at the end ... | ||||
| */ | */ | ||||
| /* decrement u */ | /* decrement u */ | ||||
| x -= fxu; | x -= fxu; | ||||
| y -= fyu; | y -= fyu; | ||||
| buffer -= (fxu + fyu * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS; | |||||
| /* decrement v (in steps of dv < 1) */ | /* decrement v (in steps of dv < 1) */ | ||||
| v_local -= dv; | v_local -= dv; | ||||
| if (v_local < 0.0f) { | if (v_local < 0.0f) { | ||||
| v_local += 1.0f; | v_local += 1.0f; | ||||
| x -= fxv; | x -= fxv; | ||||
| y -= fyv; | y -= fyv; | ||||
| buffer -= (fxv + fyv * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS; | |||||
| } | } | ||||
| input_elem = input->get_elem(x, y); | |||||
| } | } | ||||
| /* normalize */ | /* normalize */ | ||||
| if (num > 0) { | if (num > 0) { | ||||
| mul_v4_fl(output, 1.0f / (float)num); | mul_v4_fl(output, 1.0f / (float)num); | ||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| ▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | else { | ||||
| else { | else { | ||||
| /* 5 */ | /* 5 */ | ||||
| BufferLineAccumulator<-1, 0, 0, -1>::eval(input, output, co, source, dist_min, dist_max); | BufferLineAccumulator<-1, 0, 0, -1>::eval(input, output, co, source, dist_min, dist_max); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void *SunBeamsOperation::initializeTileData(rcti * /*rect*/) | |||||
| { | |||||
| void *buffer = getInputOperation(0)->initializeTileData(nullptr); | |||||
| return buffer; | |||||
| } | |||||
| void SunBeamsOperation::executePixel(float output[4], int x, int y, void *data) | |||||
| { | |||||
| const float co[2] = {(float)x, (float)y}; | |||||
| accumulate_line( | |||||
| (MemoryBuffer *)data, output, co, this->m_source_px, 0.0f, this->m_ray_length_px); | |||||
| } | |||||
| static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length) | static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length) | ||||
| { | { | ||||
| float co[2] = {(float)x, (float)y}; | float co[2] = {(float)x, (float)y}; | ||||
| float dir[2], dist; | float dir[2], dist; | ||||
| /* move (x,y) vector toward the source by ray_length distance */ | /* move (x,y) vector toward the source by ray_length distance */ | ||||
| sub_v2_v2v2(dir, co, source); | sub_v2_v2v2(dir, co, source); | ||||
| dist = normalize_v2(dir); | dist = normalize_v2(dir); | ||||
| mul_v2_fl(dir, min_ff(dist, ray_length)); | mul_v2_fl(dir, min_ff(dist, ray_length)); | ||||
| sub_v2_v2(co, dir); | sub_v2_v2(co, dir); | ||||
| int ico[2] = {(int)co[0], (int)co[1]}; | int ico[2] = {(int)co[0], (int)co[1]}; | ||||
| BLI_rcti_do_minmax_v(rect, ico); | BLI_rcti_do_minmax_v(rect, ico); | ||||
| } | } | ||||
| // | |||||
| // void SunBeamsOperation::executePixel(float output[4], int x, int y, void *data) | |||||
| //{ | |||||
| // const float co[2] = {(float)x, (float)y}; | |||||
| // | |||||
| // accumulate_line( | |||||
| // (MemoryBuffer *)data, output, co, this->m_source_px, 0.0f, this->m_ray_length_px); | |||||
| //} | |||||
| void SunBeamsOperation::update_memory_buffer(MemoryBuffer *output_buffer, | |||||
| rcti *output_rect, | |||||
| blender::Span<MemoryBuffer *> inputs) | |||||
| { | |||||
| MemoryBuffer &output = *output_buffer; | |||||
| MemoryBuffer *input = inputs[0]; | |||||
| for (int y = output_rect->ymin; y < output_rect->ymax; y++) { | |||||
| int x = output_rect->xmin; | |||||
| float *output_elem = output.get_elem(x, y); | |||||
| while (x < output_rect->xmax) { | |||||
| const float co[2] = {(float)x, (float)y}; | |||||
| accumulate_line(input, output_elem, co, this->m_source_px, 0.0f, this->m_ray_length_px); | |||||
| bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, | output_elem += output.elem_jump; | ||||
| ReadBufferOperation *readOperation, | x++; | ||||
| rcti *output) | } | ||||
| } | |||||
| } | |||||
| void SunBeamsOperation::get_input_area_of_interest(int input_index, | |||||
| const rcti *rect, | |||||
| rcti *r_input_area) | |||||
| { | { | ||||
| /* Enlarges the rect by moving each corner toward the source. | /* Enlarges the rect by moving each corner toward the source. | ||||
| * This is the maximum distance that pixels can influence each other | * This is the maximum distance that pixels can influence each other | ||||
| * and gives a rect that contains all possible accumulated pixels. | * and gives a rect that contains all possible accumulated pixels. | ||||
| */ | */ | ||||
| rcti rect = *input; | calc_ray_shift(r_input_area, rect->xmin, rect->ymin, this->m_source_px, this->m_ray_length_px); | ||||
| calc_ray_shift(&rect, input->xmin, input->ymin, this->m_source_px, this->m_ray_length_px); | calc_ray_shift(r_input_area, rect->xmin, rect->ymax, this->m_source_px, this->m_ray_length_px); | ||||
| calc_ray_shift(&rect, input->xmin, input->ymax, this->m_source_px, this->m_ray_length_px); | calc_ray_shift(r_input_area, rect->xmax, rect->ymin, this->m_source_px, this->m_ray_length_px); | ||||
| calc_ray_shift(&rect, input->xmax, input->ymin, this->m_source_px, this->m_ray_length_px); | calc_ray_shift(r_input_area, rect->xmax, rect->ymax, this->m_source_px, this->m_ray_length_px); | ||||
| calc_ray_shift(&rect, input->xmax, input->ymax, this->m_source_px, this->m_ray_length_px); | |||||
| return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output); | |||||
| } | } | ||||
| } // namespace blender::compositor | } // namespace blender::compositor | ||||