From a7aee250b82161592bb673adf59508e759a867f4 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 26 Mar 2018 12:13:36 +0200 Subject: Fix T54420: too much volume render noise with multiple volume objects. Random numbers for step offset were correlated, now use stratified samples which reduces noise as well for some types of volumes, mainly procedural ones where the step size is bigger than the volume features. --- intern/cycles/kernel/kernel_path_state.h | 2 - intern/cycles/kernel/kernel_random.h | 13 +++++ intern/cycles/kernel/kernel_types.h | 1 - intern/cycles/kernel/kernel_volume.h | 81 +++++++++++++++++++------------- 4 files changed, 61 insertions(+), 36 deletions(-) diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h index 90937dbf86e..479bb22d780 100644 --- a/intern/cycles/kernel/kernel_path_state.h +++ b/intern/cycles/kernel/kernel_path_state.h @@ -60,8 +60,6 @@ ccl_device_inline void path_state_init(KernelGlobals *kg, if(kernel_data.integrator.use_volumes) { /* Initialize volume stack with volume we are inside of. */ kernel_volume_stack_init(kg, stack_sd, state, ray, state->volume_stack); - /* Seed RNG for cases where we can't use stratified samples .*/ - state->rng_congruential = lcg_init(rng_hash + sample*0x51633e2d); } else { state->volume_stack[0].shader = SHADER_NONE; diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h index efb9048beb8..93152e9ff1c 100644 --- a/intern/cycles/kernel/kernel_random.h +++ b/intern/cycles/kernel/kernel_random.h @@ -199,6 +199,19 @@ ccl_device_inline void path_state_rng_2D(KernelGlobals *kg, fx, fy); } +ccl_device_inline float path_state_rng_1D_hash(KernelGlobals *kg, + const ccl_addr_space PathState *state, + uint hash) +{ + /* Use a hash instead of dimension, this is not great but avoids adding + * more dimensions to each bounce which reduces quality of dimensions we + * are already using. */ + return path_rng_1D(kg, + cmj_hash_simple(state->rng_hash, hash), + state->sample, state->num_samples, + state->rng_offset); +} + ccl_device_inline float path_branched_rng_1D( KernelGlobals *kg, uint rng_hash, diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 2945cdb4593..af1ecb05788 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1107,7 +1107,6 @@ typedef struct PathState { #ifdef __VOLUME__ int volume_bounce; int volume_bounds_bounce; - uint rng_congruential; VolumeStack volume_stack[VOLUME_STACK_SIZE]; #endif } PathState; diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h index 88360e5f1ae..86378289b02 100644 --- a/intern/cycles/kernel/kernel_volume.h +++ b/intern/cycles/kernel/kernel_volume.h @@ -156,6 +156,24 @@ ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stac return method; } +ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg, + ccl_addr_space PathState *state, + float t, + float *step_size, + float *step_offset) +{ + const int max_steps = kernel_data.integrator.volume_max_steps; + float step = min(kernel_data.integrator.volume_step_size, t); + + /* compute exact steps in advance for malloc */ + if(t > max_steps * step) { + step = t / (float)max_steps; + } + + *step_size = step; + *step_offset = path_state_rng_1D_hash(kg, state, 0x1e31d8a4) * step; +} + /* Volume Shadows * * These functions are used to attenuate shadow rays to lights. Both absorption @@ -188,8 +206,8 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, /* prepare for stepping */ int max_steps = kernel_data.integrator.volume_max_steps; - float step = kernel_data.integrator.volume_step_size; - float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step; + float step_offset, step_size; + kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset); /* compute extinction at the start */ float t = 0.0f; @@ -198,14 +216,15 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, for(int i = 0; i < max_steps; i++) { /* advance to new position */ - float new_t = min(ray->t, (i+1) * step); - float dt = new_t - t; + float new_t = min(ray->t, (i+1) * step_size); - /* use random position inside this segment to sample shader */ - if(new_t == ray->t) - random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt; + /* use random position inside this segment to sample shader, adjust + * for last step that is shorter than other steps. */ + if(new_t == ray->t) { + step_offset *= (new_t - t) / step_size; + } - float3 new_P = ray->P + ray->D * (t + random_jitter_offset); + float3 new_P = ray->P + ray->D * (t + step_offset); float3 sigma_t; /* compute attenuation over segment */ @@ -504,8 +523,8 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance( /* prepare for stepping */ int max_steps = kernel_data.integrator.volume_max_steps; - float step_size = kernel_data.integrator.volume_step_size; - float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step_size; + float step_offset, step_size; + kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset); /* compute coefficients at the start */ float t = 0.0f; @@ -522,11 +541,13 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance( float new_t = min(ray->t, (i+1) * step_size); float dt = new_t - t; - /* use random position inside this segment to sample shader */ - if(new_t == ray->t) - random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt; + /* use random position inside this segment to sample shader, + * for last shorter step we remap it to fit within the segment. */ + if(new_t == ray->t) { + step_offset *= (new_t - t) / step_size; + } - float3 new_P = ray->P + ray->D * (t + random_jitter_offset); + float3 new_P = ray->P + ray->D * (t + step_offset); VolumeShaderCoefficients coeff; /* compute segment */ @@ -694,19 +715,12 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta /* prepare for volume stepping */ int max_steps; - float step_size, random_jitter_offset; + float step_size, step_offset; if(heterogeneous) { - const int global_max_steps = kernel_data.integrator.volume_max_steps; - step_size = kernel_data.integrator.volume_step_size; - /* compute exact steps in advance for malloc */ - if(ray->t > global_max_steps*step_size) { - max_steps = global_max_steps; - step_size = ray->t / (float)max_steps; - } - else { - max_steps = max((int)ceilf(ray->t/step_size), 1); - } + max_steps = kernel_data.integrator.volume_max_steps; + kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset); + #ifdef __KERNEL_CPU__ /* NOTE: For the branched path tracing it's possible to have direct * and indirect light integration both having volume segments allocated. @@ -723,19 +737,18 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta sizeof(*kg->decoupled_volume_steps)); if(kg->decoupled_volume_steps[index] == NULL) { kg->decoupled_volume_steps[index] = - (VolumeStep*)malloc(sizeof(VolumeStep)*global_max_steps); + (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps); } segment->steps = kg->decoupled_volume_steps[index]; ++kg->decoupled_volume_steps_index; #else segment->steps = (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps); #endif - random_jitter_offset = lcg_step_float(&state->rng_congruential) * step_size; } else { max_steps = 1; step_size = ray->t; - random_jitter_offset = 0.0f; + step_offset = 0.0f; segment->steps = &segment->stack_step; } @@ -757,11 +770,13 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta float new_t = min(ray->t, (i+1) * step_size); float dt = new_t - t; - /* use random position inside this segment to sample shader */ - if(heterogeneous && new_t == ray->t) - random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt; + /* use random position inside this segment to sample shader, + * for last shorter step we remap it to fit within the segment. */ + if(new_t == ray->t) { + step_offset *= (new_t - t) / step_size; + } - float3 new_P = ray->P + ray->D * (t + random_jitter_offset); + float3 new_P = ray->P + ray->D * (t + step_offset); VolumeShaderCoefficients coeff; /* compute segment */ @@ -818,7 +833,7 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta step->accum_transmittance = accum_transmittance; step->cdf_distance = cdf_distance; step->t = new_t; - step->shade_t = t + random_jitter_offset; + step->shade_t = t + step_offset; /* stop if at the end of the volume */ t = new_t; -- cgit v1.2.3 From 3bca1ef26bcbd65f1a9063f2d073c92f83c79b70 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 27 Mar 2018 07:05:07 +0200 Subject: Cleanup: move paint curve undo into own file --- source/blender/editors/sculpt_paint/CMakeLists.txt | 1 + source/blender/editors/sculpt_paint/paint_curve.c | 96 +-------------- .../editors/sculpt_paint/paint_curve_undo.c | 129 +++++++++++++++++++++ source/blender/editors/sculpt_paint/paint_intern.h | 4 + 4 files changed, 138 insertions(+), 92 deletions(-) create mode 100644 source/blender/editors/sculpt_paint/paint_curve_undo.c diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 2b09e7aa20a..adead9a8b9e 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -42,6 +42,7 @@ set(INC_SYS set(SRC paint_cursor.c paint_curve.c + paint_curve_undo.c paint_hide.c paint_image.c paint_image_2d.c diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index 337f7a1ef2b..8d9812f41d9 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -34,13 +34,11 @@ #include "DNA_view3d_types.h" #include "BLI_math_vector.h" -#include "BLI_string.h" #include "BKE_context.h" #include "BKE_main.h" #include "BKE_paint.h" -#include "ED_paint.h" #include "ED_view3d.h" #include "WM_api.h" @@ -56,7 +54,6 @@ #define PAINT_CURVE_SELECT_THRESHOLD 40.0f #define PAINT_CURVE_POINT_SELECT(pcp, i) (*(&pcp->bez.f1 + i) = SELECT) - int paint_curve_poll(bContext *C) { Object *ob = CTX_data_active_object(C); @@ -81,91 +78,6 @@ int paint_curve_poll(bContext *C) return false; } -/* Paint Curve Undo*/ - -typedef struct UndoCurve { - struct UndoImageTile *next, *prev; - - PaintCurvePoint *points; /* points of curve */ - int tot_points; - int active_point; - - char idname[MAX_ID_NAME]; /* name instead of pointer*/ -} UndoCurve; - -static void paintcurve_undo_restore(bContext *C, ListBase *lb) -{ - Paint *p = BKE_paint_get_active_from_context(C); - UndoCurve *uc; - PaintCurve *pc = NULL; - - if (p->brush) { - pc = p->brush->paint_curve; - } - - if (!pc) - return; - - uc = (UndoCurve *)lb->first; - - if (STREQLEN(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname)))) { - SWAP(PaintCurvePoint *, pc->points, uc->points); - SWAP(int, pc->tot_points, uc->tot_points); - SWAP(int, pc->add_index, uc->active_point); - } -} - -static void paintcurve_undo_delete(ListBase *lb) -{ - UndoCurve *uc; - uc = (UndoCurve *)lb->first; - - if (uc->points) - MEM_freeN(uc->points); - uc->points = NULL; -} - - -static void paintcurve_undo_begin(bContext *C, wmOperator *op, PaintCurve *pc) -{ - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - ListBase *lb = NULL; - int undo_stack_id; - UndoCurve *uc; - - switch (mode) { - case ePaintTexture2D: - case ePaintTextureProjective: - undo_stack_id = UNDO_PAINT_IMAGE; - break; - - case ePaintSculpt: - undo_stack_id = UNDO_PAINT_MESH; - break; - - default: - /* do nothing, undo is handled by global */ - return; - } - - - ED_undo_paint_push_begin(undo_stack_id, op->type->name, - paintcurve_undo_restore, paintcurve_undo_delete, NULL); - lb = undo_paint_push_get_list(undo_stack_id); - - uc = MEM_callocN(sizeof(*uc), "Undo_curve"); - - lb->first = uc; - - BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname)); - uc->tot_points = pc->tot_points; - uc->active_point = pc->add_index; - uc->points = MEM_dupallocN(pc->points); - - undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points); - - ED_undo_paint_push_end(undo_stack_id); -} #define SEL_F1 (1 << 0) #define SEL_F2 (1 << 1) #define SEL_F3 (1 << 2) @@ -291,7 +203,7 @@ static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve"); } - paintcurve_undo_begin(C, op, pc); + ED_paintcurve_undo_push(C, op, pc); pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint"); add_index = pc->add_index; @@ -390,7 +302,7 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - paintcurve_undo_begin(C, op, pc); + ED_paintcurve_undo_push(C, op, pc); #define DELETE_TAG 2 @@ -467,7 +379,7 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2 if (!pc) return false; - paintcurve_undo_begin(C, op, pc); + ED_paintcurve_undo_push(C, op, pc); if (toggle) { PaintCurvePoint *pcp; @@ -651,7 +563,7 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e op->customdata = psd; if (do_select) - paintcurve_undo_begin(C, op, pc); + ED_paintcurve_undo_push(C, op, pc); /* first, clear all selection from points */ for (i = 0; i < pc->tot_points; i++) diff --git a/source/blender/editors/sculpt_paint/paint_curve_undo.c b/source/blender/editors/sculpt_paint/paint_curve_undo.c new file mode 100644 index 00000000000..70f92999864 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_curve_undo.c @@ -0,0 +1,129 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/sculpt_paint/paint_curve_undo.c + * \ingroup edsculpt + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_brush_types.h" +#include "DNA_space_types.h" + +#include "BLI_string.h" + +#include "BKE_context.h" +#include "BKE_paint.h" + +#include "ED_paint.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "paint_intern.h" + +typedef struct UndoCurve { + struct UndoImageTile *next, *prev; + + PaintCurvePoint *points; /* points of curve */ + int tot_points; + int active_point; + + char idname[MAX_ID_NAME]; /* name instead of pointer*/ +} UndoCurve; + +static void paintcurve_undo_restore(bContext *C, ListBase *lb) +{ + Paint *p = BKE_paint_get_active_from_context(C); + UndoCurve *uc; + PaintCurve *pc = NULL; + + if (p->brush) { + pc = p->brush->paint_curve; + } + + if (!pc) { + return; + } + + uc = (UndoCurve *)lb->first; + + if (STREQLEN(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname)))) { + SWAP(PaintCurvePoint *, pc->points, uc->points); + SWAP(int, pc->tot_points, uc->tot_points); + SWAP(int, pc->add_index, uc->active_point); + } +} + +static void paintcurve_undo_delete(ListBase *lb) +{ + UndoCurve *uc; + uc = (UndoCurve *)lb->first; + + if (uc->points) + MEM_freeN(uc->points); + uc->points = NULL; +} + +/** + * \note This is called before executing steps (not after). + */ +void ED_paintcurve_undo_push(bContext *C, wmOperator *op, PaintCurve *pc) +{ + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + ListBase *lb = NULL; + int undo_stack_id; + UndoCurve *uc; + + switch (mode) { + case ePaintTexture2D: + case ePaintTextureProjective: + undo_stack_id = UNDO_PAINT_IMAGE; + break; + + case ePaintSculpt: + undo_stack_id = UNDO_PAINT_MESH; + break; + + default: + /* do nothing, undo is handled by global */ + return; + } + + + ED_undo_paint_push_begin(undo_stack_id, op->type->name, + paintcurve_undo_restore, paintcurve_undo_delete, NULL); + lb = undo_paint_push_get_list(undo_stack_id); + + uc = MEM_callocN(sizeof(*uc), "Undo_curve"); + + lb->first = uc; + + BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname)); + uc->tot_points = pc->tot_points; + uc->active_point = pc->add_index; + uc->points = MEM_dupallocN(pc->points); + + undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points); + + ED_undo_paint_push_end(undo_stack_id); +} diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 8a0d1013499..52be4be4c0d 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -43,6 +43,7 @@ struct MTex; struct Object; struct PaintStroke; struct Paint; +struct PaintCurve; struct PointerRNA; struct rcti; struct Scene; @@ -343,6 +344,9 @@ void PAINTCURVE_OT_slide(struct wmOperatorType *ot); void PAINTCURVE_OT_draw(struct wmOperatorType *ot); void PAINTCURVE_OT_cursor(struct wmOperatorType *ot); +/* paint_curve_undo.c */ +void ED_paintcurve_undo_push(struct bContext *C, struct wmOperator *op, struct PaintCurve *pc); + /* image painting blur kernel */ typedef struct { float *wdata; /* actual kernel */ -- cgit v1.2.3