Cycles: Separation of Indirect and Direct clamping.

Indirect and Direct samples can now be clamped individually. This way we can clamp the indirect samples (fireflies), while keeping the direct highlights.
Example render: http://www.pasteall.org/pic/show.php?id=66586

WARNING: This breaks backwards compatibility. If you had Clamping enabled in an old file, you must re-enable either Direct/Indirect clamping or both again.

Reviewed by: brecht
Differential Revision: https://developer.blender.org/D303
This commit is contained in:
Thomas Dinges
2014-02-10 21:44:49 +01:00
parent e920206a90
commit e29a45b396
10 changed files with 77 additions and 43 deletions

View File

@@ -276,7 +276,8 @@ static void xml_read_integrator(const XMLReadState& state, pugi::xml_node node)
xml_read_float(&integrator->filter_glossy, node, "filter_glossy");
xml_read_int(&integrator->seed, node, "seed");
xml_read_float(&integrator->sample_clamp, node, "sample_clamp");
xml_read_float(&integrator->sample_clamp_direct, node, "sample_clamp_direct");
xml_read_float(&integrator->sample_clamp_indirect, node, "sample_clamp_indirect");
}
/* Camera */

View File

@@ -363,9 +363,18 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=0,
)
cls.sample_clamp = FloatProperty(
name="Clamp",
description="If non-zero, the maximum value for a sample, "
cls.sample_clamp_direct = FloatProperty(
name="Clamp Direct",
description="If non-zero, the maximum value for a direct sample, "
"higher values will be scaled down to avoid too "
"much noise and slow convergence at the cost of accuracy",
min=0.0, max=1e8,
default=0.0,
)
cls.sample_clamp_indirect = FloatProperty(
name="Clamp Indirect",
description="If non-zero, the maximum value for an indirect sample, "
"higher values will be scaled down to avoid too "
"much noise and slow convergence at the cost of accuracy",
min=0.0, max=1e8,

View File

@@ -120,7 +120,8 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
sub = col.column(align=True)
sub.label("Settings:")
sub.prop(cscene, "seed")
sub.prop(cscene, "sample_clamp")
sub.prop(cscene, "sample_clamp_direct")
sub.prop(cscene, "sample_clamp_indirect")
if cscene.progressive == 'PATH':
col = split.column()

View File

@@ -182,7 +182,8 @@ void BlenderSync::sync_integrator()
integrator->layer_flag = render_layer.layer;
integrator->sample_clamp = get_float(cscene, "sample_clamp");
integrator->sample_clamp_direct = get_float(cscene, "sample_clamp_direct");
integrator->sample_clamp_indirect = get_float(cscene, "sample_clamp_indirect");
#ifdef __CAMERA_MOTION__
if(!preview) {
if(integrator->motion_blur != r.use_motion_blur()) {

View File

@@ -316,15 +316,50 @@ ccl_device_inline void path_radiance_reset_indirect(PathRadiance *L)
#endif
}
ccl_device_inline float3 path_radiance_sum(KernelGlobals *kg, PathRadiance *L)
ccl_device_inline float3 path_radiance_clamp_and_sum(KernelGlobals *kg, PathRadiance *L)
{
#ifdef __PASSES__
if(L->use_light_pass) {
path_radiance_sum_indirect(L);
float3 L_sum = L->emission
+ L->direct_diffuse + L->direct_glossy + L->direct_transmission + L->direct_subsurface
+ L->indirect_diffuse + L->indirect_glossy + L->indirect_transmission + L->indirect_subsurface;
float3 L_direct = L->direct_diffuse + L->direct_glossy + L->direct_transmission + L->direct_subsurface + L->emission;
float3 L_indirect = L->indirect_diffuse + L->indirect_glossy + L->indirect_transmission + L->indirect_subsurface;
#ifdef __CLAMP_SAMPLE__
float clamp_direct = kernel_data.integrator.sample_clamp_direct;
float clamp_indirect = kernel_data.integrator.sample_clamp_indirect;
if(clamp_direct != FLT_MAX || clamp_indirect != FLT_MAX) {
float scale;
/* Direct */
float sum_direct = fabsf(L_direct.x) + fabsf(L_direct.y) + fabsf(L_direct.z);
if(sum_direct > clamp_direct) {
scale = clamp_direct/sum_direct;
L_direct *= scale;
L->direct_diffuse *= scale;
L->direct_glossy *= scale;
L->direct_transmission *= scale;
L->direct_subsurface *= scale;
L->emission *= scale;
}
/* Indirect */
float sum_indirect = fabsf(L_indirect.x) + fabsf(L_indirect.y) + fabsf(L_indirect.z);
if(sum_indirect > clamp_indirect) {
scale = clamp_indirect/sum_indirect;
L_indirect *= scale;
L->indirect_diffuse *= scale;
L->indirect_glossy *= scale;
L->indirect_transmission *= scale;
L->indirect_subsurface *= scale;
}
}
#endif
/* Combine */
float3 L_sum = L_direct + L_indirect;
if(!kernel_data.background.transparent)
L_sum += L->background;
@@ -338,7 +373,7 @@ ccl_device_inline float3 path_radiance_sum(KernelGlobals *kg, PathRadiance *L)
#endif
}
ccl_device_inline void path_radiance_clamp(PathRadiance *L, float3 *L_sum, float clamp)
ccl_device_inline void path_radiance_reject(PathRadiance *L, float3 *L_sum)
{
float sum = fabsf((*L_sum).x) + fabsf((*L_sum).y) + fabsf((*L_sum).z);
@@ -360,28 +395,6 @@ ccl_device_inline void path_radiance_clamp(PathRadiance *L, float3 *L_sum, float
L->emission = make_float3(0.0f, 0.0f, 0.0f);
}
#endif
}
else if(sum > clamp) {
/* value to high, scale down */
float scale = clamp/sum;
*L_sum *= scale;
#ifdef __PASSES__
if(L->use_light_pass) {
L->direct_diffuse *= scale;
L->direct_glossy *= scale;
L->direct_transmission *= scale;
L->direct_subsurface *= scale;
L->indirect_diffuse *= scale;
L->indirect_glossy *= scale;
L->indirect_transmission *= scale;
L->indirect_subsurface *= scale;
L->emission *= scale;
}
#endif
}
}

View File

@@ -884,10 +884,10 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
ray.t = FLT_MAX;
}
float3 L_sum = path_radiance_sum(kg, &L);
float3 L_sum = path_radiance_clamp_and_sum(kg, &L);
#ifdef __CLAMP_SAMPLE__
path_radiance_clamp(&L, &L_sum, kernel_data.integrator.sample_clamp);
path_radiance_reject(&L, &L_sum);
#endif
kernel_write_light_passes(kg, buffer, &L, sample);
@@ -1320,10 +1320,10 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
#endif
}
float3 L_sum = path_radiance_sum(kg, &L);
float3 L_sum = path_radiance_clamp_and_sum(kg, &L);
#ifdef __CLAMP_SAMPLE__
path_radiance_clamp(&L, &L_sum, kernel_data.integrator.sample_clamp);
path_radiance_reject(&L, &L_sum);
#endif
kernel_write_light_passes(kg, buffer, &L, sample);

View File

@@ -822,7 +822,9 @@ typedef struct KernelIntegrator {
int layer_flag;
/* clamp */
float sample_clamp;
float sample_clamp_direct;
float sample_clamp_indirect;
float pad1, pad2, pad3;
/* branched path */
int branched;

View File

@@ -288,12 +288,15 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
device_free(device, dscene, scene);
KernelFilm *kfilm = &dscene->data.film;
KernelIntegrator *kintegrator = &dscene->data.integrator;
bool use_clamping = (kintegrator->sample_clamp_direct != FLT_MAX) || (kintegrator->sample_clamp_indirect != FLT_MAX);
/* update __data */
kfilm->exposure = exposure;
kfilm->pass_flag = 0;
kfilm->pass_stride = 0;
kfilm->use_light_pass = use_light_visibility;
kfilm->use_light_pass = use_light_visibility || use_clamping;
foreach(Pass& pass, passes) {
kfilm->pass_flag |= pass.type;

View File

@@ -48,7 +48,8 @@ Integrator::Integrator()
filter_glossy = 0.0f;
seed = 0;
layer_flag = ~0;
sample_clamp = 0.0f;
sample_clamp_direct = 0.0f;
sample_clamp_indirect = 0.0f;
motion_blur = false;
aa_samples = 0;
@@ -115,7 +116,8 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
kintegrator->use_ambient_occlusion =
((dscene->data.film.pass_flag & PASS_AO) || dscene->data.background.ao_factor != 0.0f);
kintegrator->sample_clamp = (sample_clamp == 0.0f)? FLT_MAX: sample_clamp*3.0f;
kintegrator->sample_clamp_direct = (sample_clamp_direct == 0.0f)? FLT_MAX: sample_clamp_direct*3.0f;
kintegrator->sample_clamp_indirect = (sample_clamp_indirect == 0.0f)? FLT_MAX: sample_clamp_indirect*3.0f;
kintegrator->branched = (method == BRANCHED_PATH);
kintegrator->aa_samples = aa_samples;
@@ -180,7 +182,8 @@ bool Integrator::modified(const Integrator& integrator)
filter_glossy == integrator.filter_glossy &&
layer_flag == integrator.layer_flag &&
seed == integrator.seed &&
sample_clamp == integrator.sample_clamp &&
sample_clamp_direct == integrator.sample_clamp_direct &&
sample_clamp_indirect == integrator.sample_clamp_indirect &&
method == integrator.method &&
aa_samples == integrator.aa_samples &&
diffuse_samples == integrator.diffuse_samples &&

View File

@@ -50,7 +50,8 @@ public:
int seed;
int layer_flag;
float sample_clamp;
float sample_clamp_direct;
float sample_clamp_indirect;
bool motion_blur;
int aa_samples;