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:
@@ -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 */
|
||||
|
@@ -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,
|
||||
|
@@ -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()
|
||||
|
@@ -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()) {
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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 &&
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user