Cycles: first step for implementation of non-progressive sampler that handles

direct and indirect lighting differently. Rather than picking one light for each
point on the path, it now loops over all lights for direct lighting. For indirect
lighting it still picks a random light each time.

It gives control over the number of AA samples, and the number of Diffuse, Glossy,
Transmission, AO, Mesh Light, Background and Lamp samples for each AA sample.

This helps tuning render performance/noise and tends to give less noise for renders
dominated by direct lighting.

This sampling mode only works on the CPU, and still needs proper tile rendering
to show progress (will follow tommorrow or so), because each AA sample can be quite
slow now and so the delay between each update wil be too long.
This commit is contained in:
Brecht Van Lommel 2012-06-13 11:44:48 +00:00
parent dcda234a3d
commit 4ba456d175
14 changed files with 745 additions and 90 deletions

@ -57,6 +57,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default='GPU_COMPATIBLE',
)
cls.progressive = BoolProperty(
name="Progressive",
description="Use progressive sampling of lighting",
default=True,
)
cls.samples = IntProperty(
name="Samples",
description="Number of samples to render for each pixel",
@ -80,6 +86,49 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=False,
)
cls.aa_samples = IntProperty(
name="AA Samples",
description="Number of antialiasing samples to render for each pixel",
min=1, max=10000,
default=4,
)
cls.preview_aa_samples = IntProperty(
name="AA Samples",
description="Number of antialiasing samples to in viewport, unlimited if 0",
min=1, max=10000,
default=4,
)
cls.diffuse_samples = IntProperty(
name="Diffuse Samples",
description="Number of diffuse bounce samples to render for each AA sample",
min=1, max=10000,
default=1,
)
cls.glossy_samples = IntProperty(
name="Glossy Samples",
description="Number of glossy bounce samples to render for each AA sample",
min=1, max=10000,
default=1,
)
cls.transmission_samples = IntProperty(
name="Transmission Samples",
description="Number of transmission bounce samples to render for each AA sample",
min=1, max=10000,
default=1,
)
cls.ao_samples = IntProperty(
name="Ambient Occlusion Samples",
description="Number of ambient occlusion samples to render for each AA sample",
min=1, max=10000,
default=1,
)
cls.mesh_light_samples = IntProperty(
name="Mesh Light Samples",
description="Number of mesh emission light samples to render for each AA sample",
min=1, max=10000,
default=1,
)
cls.no_caustics = BoolProperty(
name="No Caustics",
description="Leave out caustics, resulting in a darker image with less noise",
@ -340,6 +389,12 @@ class CyclesLampSettings(bpy.types.PropertyGroup):
description="Lamp casts shadows",
default=True,
)
cls.samples = IntProperty(
name="Samples",
description="Number of light samples to render for each AA sample",
min=1, max=10000,
default=1,
)
@classmethod
def unregister(cls):
@ -365,6 +420,12 @@ class CyclesWorldSettings(bpy.types.PropertyGroup):
min=4, max=8096,
default=256,
)
cls.samples = IntProperty(
name="Samples",
description="Number of light samples to render for each AA sample",
min=1, max=10000,
default=4,
)
@classmethod
def unregister(cls):

@ -44,8 +44,48 @@ class CyclesButtonsPanel():
return rd.engine == 'CYCLES'
class CyclesRender_PT_integrator(CyclesButtonsPanel, Panel):
bl_label = "Integrator"
class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
bl_label = "Sampling"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
scene = context.scene
cscene = scene.cycles
split = layout.split()
col = split.column()
sub = col.column(align=True)
sub.active = cscene.device == 'CPU'
sub.prop(cscene, "progressive")
sub = col.column(align=True)
sub.prop(cscene, "seed")
sub.prop(cscene, "sample_clamp")
if cscene.progressive or cscene.device != 'CPU':
col = split.column(align=True)
col.label(text="Samples:")
col.prop(cscene, "samples", text="Render")
col.prop(cscene, "preview_samples", text="Preview")
else:
sub = col.column(align=True)
sub.label(text="AA Samples:")
sub.prop(cscene, "aa_samples", text="Render")
sub.prop(cscene, "preview_aa_samples", text="Preview")
col = split.column(align=True)
col.label(text="Samples:")
col.prop(cscene, "diffuse_samples", text="Diffuse")
col.prop(cscene, "glossy_samples", text="Glossy")
col.prop(cscene, "transmission_samples", text="Transmission")
col.prop(cscene, "ao_samples", text="AO")
col.prop(cscene, "mesh_light_samples", text="Mesh Light")
class CyclesRender_PT_light_paths(CyclesButtonsPanel, Panel):
bl_label = "Light Paths"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
@ -62,12 +102,6 @@ class CyclesRender_PT_integrator(CyclesButtonsPanel, Panel):
split = layout.split()
col = split.column()
sub = col.column(align=True)
sub.label(text="Samples:")
sub.prop(cscene, "samples", text="Render")
sub.prop(cscene, "preview_samples", text="Preview")
sub.prop(cscene, "seed")
sub.prop(cscene, "sample_clamp")
sub = col.column(align=True)
sub.label("Transparency:")
@ -75,6 +109,11 @@ class CyclesRender_PT_integrator(CyclesButtonsPanel, Panel):
sub.prop(cscene, "transparent_min_bounces", text="Min")
sub.prop(cscene, "use_transparent_shadows", text="Shadows")
col.separator()
col.prop(cscene, "no_caustics")
col.prop(cscene, "blur_glossy")
col = split.column()
sub = col.column(align=True)
@ -83,16 +122,10 @@ class CyclesRender_PT_integrator(CyclesButtonsPanel, Panel):
sub.prop(cscene, "min_bounces", text="Min")
sub = col.column(align=True)
sub.label(text="Light Paths:")
sub.prop(cscene, "diffuse_bounces", text="Diffuse")
sub.prop(cscene, "glossy_bounces", text="Glossy")
sub.prop(cscene, "transmission_bounces", text="Transmission")
col.separator()
col.prop(cscene, "no_caustics")
col.prop(cscene, "blur_glossy")
class CyclesRender_PT_motion_blur(CyclesButtonsPanel, Panel):
bl_label = "Motion Blur"
@ -467,6 +500,7 @@ class CyclesLamp_PT_lamp(CyclesButtonsPanel, Panel):
lamp = context.lamp
clamp = lamp.cycles
cscene = context.scene.cycles
layout.prop(lamp, "type", expand=True)
@ -485,6 +519,9 @@ class CyclesLamp_PT_lamp(CyclesButtonsPanel, Panel):
sub.prop(lamp, "size", text="Size X")
sub.prop(lamp, "size_y", text="Size Y")
if not cscene.progressive and cscene.device == 'CPU':
col.prop(clamp, "samples")
col = split.column()
col.prop(clamp, "cast_shadow")
@ -604,13 +641,16 @@ class CyclesWorld_PT_settings(CyclesButtonsPanel, Panel):
world = context.world
cworld = world.cycles
cscene = context.scene.cycles
col = layout.column()
col.prop(cworld, "sample_as_light")
row = col.row()
row.active = cworld.sample_as_light
row.prop(cworld, "sample_map_resolution")
sub = col.row(align=True)
sub.active = cworld.sample_as_light
sub.prop(cworld, "sample_map_resolution")
if not cscene.progressive and cscene.device == 'CPU':
sub.prop(cworld, "samples")
class CyclesMaterial_PT_surface(CyclesButtonsPanel, Panel):

@ -154,6 +154,7 @@ void BlenderSync::sync_light(BL::Object b_parent, int b_index, BL::Object b_ob,
/* shadow */
PointerRNA clamp = RNA_pointer_get(&b_lamp.ptr, "cycles");
light->cast_shadow = get_boolean(clamp, "cast_shadow");
light->samples = get_int(clamp, "samples");
/* tag */
light->tag_update(scene);
@ -178,6 +179,7 @@ void BlenderSync::sync_background_light()
{
light->type = LIGHT_BACKGROUND;
light->map_resolution = get_int(cworld, "sample_map_resolution");
light->samples = get_int(cworld, "samples");
light->shader = scene->default_background;
light->tag_update(scene);

@ -168,6 +168,13 @@ void BlenderSync::sync_integrator()
integrator->motion_blur = (!preview && r.use_motion_blur());
#endif
integrator->diffuse_samples = get_int(cscene, "diffuse_samples");
integrator->glossy_samples = get_int(cscene, "glossy_samples");
integrator->transmission_samples = get_int(cscene, "transmission_samples");
integrator->ao_samples = get_int(cscene, "ao_samples");
integrator->mesh_light_samples = get_int(cscene, "mesh_light_samples");
integrator->progressive = get_boolean(cscene, "progressive");
if(integrator->modified(previntegrator))
integrator->tag_update(scene);
}
@ -308,15 +315,27 @@ SessionParams BlenderSync::get_session_params(BL::UserPreferences b_userpref, BL
/* Background */
params.background = background;
/* samples */
if(background) {
params.samples = get_int(cscene, "samples");
if(get_boolean(cscene, "progressive")) {
if(background) {
params.samples = get_int(cscene, "samples");
}
else {
params.samples = get_int(cscene, "preview_samples");
if(params.samples == 0)
params.samples = INT_MAX;
}
}
else {
params.samples = get_int(cscene, "preview_samples");
if(params.samples == 0)
params.samples = INT_MAX;
if(background) {
params.samples = get_int(cscene, "aa_samples");
}
else {
params.samples = get_int(cscene, "preview_aa_samples");
if(params.samples == 0)
params.samples = INT_MAX;
}
}
/* other parameters */

@ -67,7 +67,7 @@ __device bool direct_emission(KernelGlobals *kg, ShaderData *sd, int lindex,
float pdf = -1.0f;
#ifdef __MULTI_LIGHT__
#ifdef __NON_PROGRESSIVE__
if(lindex != -1) {
/* sample position on a specified light */
light_select(kg, lindex, randu, randv, sd->P, &ls, &pdf);

@ -388,6 +388,12 @@ __device float light_sample_pdf(KernelGlobals *kg, LightSample *ls, float3 I, fl
return pdf;
}
__device int light_select_num_samples(KernelGlobals *kg, int index)
{
float4 data3 = kernel_tex_fetch(__light_data, index*LIGHT_SIZE + 3);
return __float_as_int(data3.x);
}
__device void light_select(KernelGlobals *kg, int index, float randu, float randv, float3 P, LightSample *ls, float *pdf)
{
regular_light_sample(kg, index, randu, randv, P, ls, pdf);

@ -218,7 +218,7 @@ __device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *ra
return result;
}
__device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample, Ray ray, __global float *buffer)
__device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample, Ray ray, __global float *buffer)
{
/* initialize */
PathRadiance L;
@ -366,26 +366,15 @@ __device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample, R
light_ray.time = sd.time;
#endif
#ifdef __MULTI_LIGHT__
/* index -1 means randomly sample from distribution */
int i = (kernel_data.integrator.num_all_lights)? 0: -1;
if(direct_emission(kg, &sd, -1, light_t, light_o, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
for(; i < kernel_data.integrator.num_all_lights; i++) {
#else
const int i = -1;
#endif
if(direct_emission(kg, &sd, i, light_t, light_o, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(&L, throughput, &L_light, shadow, state.bounce, is_lamp);
}
if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(&L, throughput, &L_light, shadow, state.bounce, is_lamp);
}
#ifdef __MULTI_LIGHT__
}
#endif
}
}
#endif
@ -444,6 +433,451 @@ __device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample, R
return make_float4(L_sum.x, L_sum.y, L_sum.z, 1.0f - L_transparent);
}
#ifdef __NON_PROGRESSIVE__
__device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray ray, __global float *buffer,
float3 throughput, float min_ray_pdf, float ray_pdf, PathState state, int rng_offset, PathRadiance *L)
{
/* path iteration */
for(;; rng_offset += PRNG_BOUNCE_NUM) {
/* intersect scene */
Intersection isect;
uint visibility = path_state_ray_visibility(kg, &state);
if(!scene_intersect(kg, &ray, visibility, &isect)) {
#ifdef __BACKGROUND__
/* sample background shader */
float3 L_background = indirect_background(kg, &ray, state.flag, ray_pdf);
path_radiance_accum_background(L, throughput, L_background, state.bounce);
#endif
break;
}
/* setup shading */
ShaderData sd;
shader_setup_from_ray(kg, &sd, &isect, &ray);
float rbsdf = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF);
shader_eval_surface(kg, &sd, rbsdf, state.flag);
shader_merge_closures(kg, &sd);
/* blurring of bsdf after bounces, for rays that have a small likelihood
* of following this particular path (diffuse, rough glossy) */
if(kernel_data.integrator.filter_glossy != FLT_MAX) {
float blur_pdf = kernel_data.integrator.filter_glossy*min_ray_pdf;
if(blur_pdf < 1.0f) {
float blur_roughness = sqrtf(1.0f - blur_pdf)*0.5f;
shader_bsdf_blur(kg, &sd, blur_roughness);
}
}
#ifdef __EMISSION__
/* emission */
if(sd.flag & SD_EMISSION) {
float3 emission = indirect_emission(kg, &sd, isect.t, state.flag, ray_pdf);
path_radiance_accum_emission(L, throughput, emission, state.bounce);
}
#endif
/* path termination. this is a strange place to put the termination, it's
* mainly due to the mixed in MIS that we use. gives too many unneeded
* shader evaluations, only need emission if we are going to terminate */
float probability = path_state_terminate_probability(kg, &state, throughput);
float terminate = path_rng(kg, rng, sample, rng_offset + PRNG_TERMINATE);
if(terminate >= probability)
break;
throughput /= probability;
#ifdef __AO__
/* ambient occlusion */
if(kernel_data.integrator.use_ambient_occlusion) {
/* todo: solve correlation */
float bsdf_u = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF_U);
float bsdf_v = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF_V);
float3 ao_D;
float ao_pdf;
sample_cos_hemisphere(sd.N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);
if(dot(sd.Ng, ao_D) > 0.0f && ao_pdf != 0.0f) {
Ray light_ray;
float3 ao_shadow;
light_ray.P = ray_offset(sd.P, sd.Ng);
light_ray.D = ao_D;
light_ray.t = kernel_data.background.ao_distance;
#ifdef __MOTION__
light_ray.time = sd.time;
#endif
if(!shadow_blocked(kg, &state, &light_ray, &ao_shadow)) {
float3 ao_bsdf = shader_bsdf_diffuse(kg, &sd)*kernel_data.background.ao_factor;
path_radiance_accum_ao(L, throughput, ao_bsdf, ao_shadow, state.bounce);
}
}
}
#endif
#ifdef __EMISSION__
if(kernel_data.integrator.use_direct_light) {
/* sample illumination from lights to find path contribution */
if(sd.flag & SD_BSDF_HAS_EVAL) {
float light_t = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT);
float light_o = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_F);
float light_u = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_U);
float light_v = path_rng(kg, rng, sample, rng_offset + PRNG_LIGHT_V);
Ray light_ray;
BsdfEval L_light;
bool is_lamp;
#ifdef __MOTION__
light_ray.time = sd.time;
#endif
/* sample random light */
if(direct_emission(kg, &sd, -1, light_t, light_o, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(L, throughput, &L_light, shadow, state.bounce, is_lamp);
}
}
}
}
#endif
/* no BSDF? we can stop here */
if(!(sd.flag & SD_BSDF))
break;
/* sample BSDF */
float bsdf_pdf;
BsdfEval bsdf_eval;
float3 bsdf_omega_in;
differential3 bsdf_domega_in;
float bsdf_u = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF_U);
float bsdf_v = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF_V);
int label;
label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
&bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
shader_release(kg, &sd);
if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
break;
/* modify throughput */
path_radiance_bsdf_bounce(L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
/* set labels */
if(!(label & LABEL_TRANSPARENT)) {
ray_pdf = bsdf_pdf;
min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
}
/* update path state */
path_state_next(kg, &state, label);
/* setup ray */
ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
ray.D = bsdf_omega_in;
ray.t = FLT_MAX;
#ifdef __RAY_DIFFERENTIALS__
ray.dP = sd.dP;
ray.dD = bsdf_domega_in;
#endif
}
}
__device float4 kernel_path_non_progressive(KernelGlobals *kg, RNG *rng, int sample, Ray ray, __global float *buffer)
{
/* initialize */
PathRadiance L;
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
float L_transparent = 0.0f;
path_radiance_init(&L, kernel_data.film.use_light_pass);
float ray_pdf = 0.0f;
PathState state;
int rng_offset = PRNG_BASE_NUM;
path_state_init(&state);
for(;; rng_offset += PRNG_BOUNCE_NUM) {
/* intersect scene */
Intersection isect;
uint visibility = path_state_ray_visibility(kg, &state);
if(!scene_intersect(kg, &ray, visibility, &isect)) {
/* eval background shader if nothing hit */
if(kernel_data.background.transparent) {
L_transparent += average(throughput);
#ifdef __PASSES__
if(!(kernel_data.film.pass_flag & PASS_BACKGROUND))
#endif
break;
}
#ifdef __BACKGROUND__
/* sample background shader */
float3 L_background = indirect_background(kg, &ray, state.flag, ray_pdf);
path_radiance_accum_background(&L, throughput, L_background, state.bounce);
#endif
break;
}
/* setup shading */
ShaderData sd;
shader_setup_from_ray(kg, &sd, &isect, &ray);
float rbsdf = path_rng(kg, rng, sample, rng_offset + PRNG_BSDF);
shader_eval_surface(kg, &sd, rbsdf, state.flag);
shader_merge_closures(kg, &sd);
kernel_write_data_passes(kg, buffer, &L, &sd, sample, state.flag, throughput);
/* holdout */
#ifdef __HOLDOUT__
if((sd.flag & (SD_HOLDOUT|SD_HOLDOUT_MASK))) {
if(kernel_data.background.transparent) {
float3 holdout_weight;
if(sd.flag & SD_HOLDOUT_MASK)
holdout_weight = make_float3(1.0f, 1.0f, 1.0f);
else
shader_holdout_eval(kg, &sd);
/* any throughput is ok, should all be identical here */
L_transparent += average(holdout_weight*throughput);
}
if(sd.flag & SD_HOLDOUT_MASK)
break;
}
#endif
#ifdef __EMISSION__
/* emission */
if(sd.flag & SD_EMISSION) {
float3 emission = indirect_emission(kg, &sd, isect.t, state.flag, ray_pdf);
path_radiance_accum_emission(&L, throughput, emission, state.bounce);
}
#endif
/* transparency termination */
if(state.flag & PATH_RAY_TRANSPARENT) {
/* path termination. this is a strange place to put the termination, it's
* mainly due to the mixed in MIS that we use. gives too many unneeded
* shader evaluations, only need emission if we are going to terminate */
float probability = path_state_terminate_probability(kg, &state, throughput);
float terminate = path_rng(kg, rng, sample, rng_offset + PRNG_TERMINATE);
if(terminate >= probability)
break;
throughput /= probability;
}
#ifdef __AO__
/* ambient occlusion */
if(kernel_data.integrator.use_ambient_occlusion) {
int num_samples = kernel_data.integrator.ao_samples;
float ao_factor = kernel_data.background.ao_factor/num_samples;
for(int j = 0; j < num_samples; j++) {
/* todo: solve correlation */
float bsdf_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_U);
float bsdf_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_V);
float3 ao_D;
float ao_pdf;
sample_cos_hemisphere(sd.N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);
if(dot(sd.Ng, ao_D) > 0.0f && ao_pdf != 0.0f) {
Ray light_ray;
float3 ao_shadow;
light_ray.P = ray_offset(sd.P, sd.Ng);
light_ray.D = ao_D;
light_ray.t = kernel_data.background.ao_distance;
#ifdef __MOTION__
light_ray.time = sd.time;
#endif
if(!shadow_blocked(kg, &state, &light_ray, &ao_shadow)) {
float3 ao_bsdf = shader_bsdf_diffuse(kg, &sd)*ao_factor;
path_radiance_accum_ao(&L, throughput, ao_bsdf, ao_shadow, state.bounce);
}
}
}
}
#endif
#ifdef __EMISSION__
/* sample illumination from lights to find path contribution */
if(sd.flag & SD_BSDF_HAS_EVAL) {
Ray light_ray;
BsdfEval L_light;
bool is_lamp;
#ifdef __MOTION__
light_ray.time = sd.time;
#endif
/* lamp sampling */
for(int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
int num_samples = light_select_num_samples(kg, i);
float num_samples_inv = 1.0f/(num_samples*kernel_data.integrator.num_all_lights);
if(kernel_data.integrator.pdf_triangles != 0.0f)
num_samples_inv *= 0.5f;
for(int j = 0; j < num_samples; j++) {
float light_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_U);
float light_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_V);
if(direct_emission(kg, &sd, i, 0.0f, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(&L, throughput*num_samples_inv, &L_light, shadow, state.bounce, is_lamp);
}
}
}
}
/* mesh light sampling */
if(kernel_data.integrator.pdf_triangles != 0.0f) {
int num_samples = kernel_data.integrator.mesh_light_samples;
float num_samples_inv = 1.0f/num_samples;
if(kernel_data.integrator.num_all_lights)
num_samples_inv *= 0.5f;
for(int j = 0; j < num_samples; j++) {
float light_t = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT);
float light_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_U);
float light_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_V);
/* only sample triangle lights */
if(kernel_data.integrator.num_all_lights)
light_t = 0.5f*light_t;
if(direct_emission(kg, &sd, -1, light_t, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
/* trace shadow ray */
float3 shadow;
if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
/* accumulate */
path_radiance_accum_light(&L, throughput*num_samples_inv, &L_light, shadow, state.bounce, is_lamp);
}
}
}
}
}
#endif
for(int i = 0; i< sd.num_closure; i++) {
const ShaderClosure *sc = &sd.closure[i];
if(!CLOSURE_IS_BSDF(sc->type))
continue;
/* transparency is not handled here, but in outer loop */
if(sc->type == CLOSURE_BSDF_TRANSPARENT_ID)
continue;
int num_samples;
if(CLOSURE_IS_BSDF_DIFFUSE(sc->type))
num_samples = kernel_data.integrator.diffuse_samples;
else if(CLOSURE_IS_BSDF_GLOSSY(sc->type))
num_samples = kernel_data.integrator.glossy_samples;
else
num_samples = kernel_data.integrator.transmission_samples;
float num_samples_inv = 1.0f/num_samples;
for(int j = 0; j < num_samples; j++) {
/* sample BSDF */
float bsdf_pdf;
BsdfEval bsdf_eval;
float3 bsdf_omega_in;
differential3 bsdf_domega_in;
float bsdf_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_U);
float bsdf_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_V);
int label;
label = shader_bsdf_sample_closure(kg, &sd, sc, bsdf_u, bsdf_v, &bsdf_eval,
&bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
continue;
/* modify throughput */
float3 tp = throughput;
path_radiance_bsdf_bounce(&L, &tp, &bsdf_eval, bsdf_pdf, state.bounce, label);
/* set labels */
float min_ray_pdf = FLT_MAX;
if(!(label & LABEL_TRANSPARENT))
min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
/* modify path state */
PathState ps = state;
path_state_next(kg, &ps, label);
/* setup ray */
ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
ray.D = bsdf_omega_in;
ray.t = FLT_MAX;
#ifdef __RAY_DIFFERENTIALS__
ray.dP = sd.dP;
ray.dD = bsdf_domega_in;
#endif
kernel_path_indirect(kg, rng, sample*num_samples, ray, buffer,
tp*num_samples_inv, min_ray_pdf, ray_pdf, ps, rng_offset+PRNG_BOUNCE_NUM, &L);
}
}
/* continue in case of transparency */
throughput *= shader_bsdf_transparency(kg, &sd);
shader_release(kg, &sd);
if(is_zero(throughput))
break;
path_state_next(kg, &state, LABEL_TRANSPARENT);
ray.P = ray_offset(sd.P, -sd.Ng);
}
float3 L_sum = path_radiance_sum(kg, &L);
#ifdef __CLAMP_SAMPLE__
path_radiance_clamp(&L, &L_sum, kernel_data.integrator.sample_clamp);
#endif
kernel_write_light_passes(kg, buffer, &L, sample);
return make_float4(L_sum.x, L_sum.y, L_sum.z, 1.0f - L_transparent);
}
#endif
__device void kernel_path_trace(KernelGlobals *kg,
__global float *buffer, __global uint *rng_state,
int sample, int x, int y, int offset, int stride)
@ -480,8 +914,16 @@ __device void kernel_path_trace(KernelGlobals *kg,
/* integrate */
float4 L;
if (ray.t != 0.f)
L = kernel_path_integrate(kg, &rng, sample, ray, buffer);
if (ray.t != 0.0f) {
#ifdef __NON_PROGRESSIVE__
if(kernel_data.integrator.progressive)
#endif
L = kernel_path_progressive(kg, &rng, sample, ray, buffer);
#ifdef __NON_PROGRESSIVE__
else
L = kernel_path_non_progressive(kg, &rng, sample, ray, buffer);
#endif
}
else
L = make_float4(0.f, 0.f, 0.f, 0.f);

@ -407,6 +407,25 @@ __device int shader_bsdf_sample(KernelGlobals *kg, const ShaderData *sd,
#endif
}
__device int shader_bsdf_sample_closure(KernelGlobals *kg, const ShaderData *sd,
const ShaderClosure *sc, float randu, float randv, BsdfEval *bsdf_eval,
float3 *omega_in, differential3 *domega_in, float *pdf)
{
int label;
float3 eval;
*pdf = 0.0f;
#ifdef __OSL__
label = OSLShader::bsdf_sample(sd, sc, randu, randv, eval, *omega_in, *domega_in, *pdf);
#else
label = svm_bsdf_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf);
#endif
if(*pdf != 0.0f)
bsdf_eval_init(bsdf_eval, sc->type, eval*sc->weight, kernel_data.film.use_light_pass);
return label;
}
__device void shader_bsdf_blur(KernelGlobals *kg, ShaderData *sd, float roughness)
{
#ifndef __OSL__
@ -679,6 +698,35 @@ __device bool shader_transparent_shadow(KernelGlobals *kg, Intersection *isect)
}
#endif
/* Merging */
#ifdef __NON_PROGRESSIVE__
__device void shader_merge_closures(KernelGlobals *kg, ShaderData *sd)
{
#ifndef __OSL__
/* merge identical closures, better when we sample a single closure at a time */
for(int i = 0; i < sd->num_closure; i++) {
ShaderClosure *sci = &sd->closure[i];
for(int j = i + 1; j < sd->num_closure; j++) {
ShaderClosure *scj = &sd->closure[j];
if(sci->type == scj->type && sci->data0 == scj->data0 && sci->data1 == scj->data1) {
sci->weight += scj->weight;
sci->sample_weight += scj->sample_weight;
int size = sd->num_closure - (j+1);
if(size > 0)
memmove(scj, scj+1, size*sizeof(ShaderClosure));
sd->num_closure--;
}
}
}
#endif
}
#endif
/* Free ShaderData */
__device void shader_release(KernelGlobals *kg, ShaderData *sd)

@ -43,6 +43,7 @@ CCL_NAMESPACE_BEGIN
#ifdef WITH_OSL
#define __OSL__
#endif
#define __NON_PROGRESSIVE__
#endif
#ifdef __KERNEL_CUDA__
@ -110,7 +111,6 @@ CCL_NAMESPACE_BEGIN
//#define __MOTION__
#endif
//#define __MULTI_LIGHT__
//#define __SOBOL_FULL_SCREEN__
//#define __QBVH__
@ -627,6 +627,15 @@ typedef struct KernelIntegrator {
/* clamp */
float sample_clamp;
/* non-progressive */
int progressive;
int diffuse_samples;
int glossy_samples;
int transmission_samples;
int ao_samples;
int mesh_light_samples;
int pad1, pad2;
} KernelIntegrator;
typedef struct KernelBVH {

@ -18,9 +18,11 @@
#include "device.h"
#include "integrator.h"
#include "light.h"
#include "scene.h"
#include "sobol.h"
#include "util_foreach.h"
#include "util_hash.h"
CCL_NAMESPACE_BEGIN
@ -47,6 +49,13 @@ Integrator::Integrator()
sample_clamp = 0.0f;
motion_blur = false;
diffuse_samples = 1;
glossy_samples = 1;
transmission_samples = 1;
ao_samples = 1;
mesh_light_samples = 1;
progressive = true;
need_update = true;
}
@ -54,7 +63,7 @@ Integrator::~Integrator()
{
}
void Integrator::device_update(Device *device, DeviceScene *dscene)
void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene)
{
if(!need_update)
return;
@ -93,8 +102,27 @@ void Integrator::device_update(Device *device, DeviceScene *dscene)
kintegrator->sample_clamp = (sample_clamp == 0.0f)? FLT_MAX: sample_clamp*3.0f;
kintegrator->progressive = progressive;
kintegrator->diffuse_samples = diffuse_samples;
kintegrator->glossy_samples = glossy_samples;
kintegrator->transmission_samples = transmission_samples;
kintegrator->ao_samples = ao_samples;
kintegrator->mesh_light_samples = mesh_light_samples;
/* sobol directions table */
int dimensions = PRNG_BASE_NUM + (max_bounce + transparent_max_bounce + 2)*PRNG_BOUNCE_NUM;
int max_samples = 1;
if(!progressive) {
foreach(Light *light, scene->lights)
max_samples = max(max_samples, light->samples);
max_samples = max(max_samples, max(diffuse_samples, max(glossy_samples, transmission_samples)));
max_samples = max(max_samples, max(ao_samples, mesh_light_samples));
}
max_samples *= (max_bounce + transparent_max_bounce + 2);
int dimensions = PRNG_BASE_NUM + max_samples*PRNG_BOUNCE_NUM;
uint *directions = dscene->sobol_directions.resize(SOBOL_BITS*dimensions);
sobol_generate_direction_vectors((uint(*)[SOBOL_BITS])directions, dimensions);
@ -127,6 +155,12 @@ bool Integrator::modified(const Integrator& integrator)
layer_flag == integrator.layer_flag &&
seed == integrator.seed &&
sample_clamp == integrator.sample_clamp &&
progressive == integrator.progressive &&
diffuse_samples == integrator.diffuse_samples &&
glossy_samples == integrator.glossy_samples &&
transmission_samples == integrator.transmission_samples &&
ao_samples == integrator.ao_samples &&
mesh_light_samples == integrator.mesh_light_samples &&
motion_blur == integrator.motion_blur);
}

@ -49,12 +49,20 @@ public:
float sample_clamp;
bool motion_blur;
int diffuse_samples;
int glossy_samples;
int transmission_samples;
int ao_samples;
int mesh_light_samples;
bool progressive;
bool need_update;
Integrator();
~Integrator();
void device_update(Device *device, DeviceScene *dscene);
void device_update(Device *device, DeviceScene *dscene, Scene *scene);
void device_free(Device *device, DeviceScene *dscene);
bool modified(const Integrator& integrator);

@ -17,6 +17,7 @@
*/
#include "device.h"
#include "integrator.h"
#include "light.h"
#include "mesh.h"
#include "object.h"
@ -114,6 +115,7 @@ Light::Light()
cast_shadow = true;
shader = 0;
samples = 1;
}
void Light::tag_update(Scene *scene)
@ -136,9 +138,6 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
{
progress.set_status("Updating Lights", "Computing distribution");
/* option to always sample all point lights */
bool multi_light = false;
/* count */
size_t num_lights = scene->lights.size();
size_t num_triangles = 0;
@ -169,9 +168,7 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
}
size_t num_distribution = num_triangles;
if(!multi_light)
num_distribution += num_lights;
num_distribution += num_lights;
/* emission area */
float4 *distribution = dscene->light_distribution.resize(num_distribution + 1);
@ -231,16 +228,14 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
float trianglearea = totarea;
/* point lights */
if(!multi_light) {
float lightarea = (totarea > 0.0f)? totarea/scene->lights.size(): 1.0f;
float lightarea = (totarea > 0.0f)? totarea/scene->lights.size(): 1.0f;
for(int i = 0; i < scene->lights.size(); i++, offset++) {
distribution[offset].x = totarea;
distribution[offset].y = __int_as_float(~(int)i);
distribution[offset].z = 1.0f;
distribution[offset].w = scene->lights[i]->size;
totarea += lightarea;
}
for(int i = 0; i < scene->lights.size(); i++, offset++) {
distribution[offset].x = totarea;
distribution[offset].y = __int_as_float(~(int)i);
distribution[offset].z = 1.0f;
distribution[offset].w = scene->lights[i]->size;
totarea += lightarea;
}
/* normalize cumulative distribution functions */
@ -259,7 +254,7 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
/* update device */
KernelIntegrator *kintegrator = &dscene->data.integrator;
kintegrator->use_direct_light = (totarea > 0.0f) || (multi_light && num_lights);
kintegrator->use_direct_light = (totarea > 0.0f);
if(kintegrator->use_direct_light) {
/* number of emissives */
@ -269,30 +264,19 @@ void LightManager::device_update_distribution(Device *device, DeviceScene *dscen
kintegrator->pdf_triangles = 0.0f;
kintegrator->pdf_lights = 0.0f;
if(multi_light) {
/* sample one of all triangles and all lights */
kintegrator->num_all_lights = num_lights;
/* sample one, with 0.5 probability of light or triangle */
kintegrator->num_all_lights = num_lights;
if(trianglearea > 0.0f)
kintegrator->pdf_triangles = 1.0f/trianglearea;
if(trianglearea > 0.0f) {
kintegrator->pdf_triangles = 1.0f/trianglearea;
if(num_lights)
kintegrator->pdf_lights = 1.0f;
kintegrator->pdf_triangles *= 0.5f;
}
else {
/* sample one, with 0.5 probability of light or triangle */
kintegrator->num_all_lights = 0;
if(trianglearea > 0.0f) {
kintegrator->pdf_triangles = 1.0f/trianglearea;
if(num_lights)
kintegrator->pdf_triangles *= 0.5f;
}
if(num_lights) {
kintegrator->pdf_lights = 1.0f/num_lights;
if(trianglearea > 0.0f)
kintegrator->pdf_lights *= 0.5f;
}
if(num_lights) {
kintegrator->pdf_lights = 1.0f/num_lights;
if(trianglearea > 0.0f)
kintegrator->pdf_lights *= 0.5f;
}
/* CDF */
@ -417,6 +401,7 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
float3 co = light->co;
float3 dir = normalize(light->dir);
int shader_id = scene->shader_manager->get_shader_id(scene->lights[i]->shader);
float samples = __int_as_float(light->samples);
if(!light->cast_shadow)
shader_id &= ~SHADER_CAST_SHADOW;
@ -427,7 +412,7 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), light->size, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 3] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
}
else if(light->type == LIGHT_DISTANT) {
shader_id &= ~SHADER_AREA_LIGHT;
@ -435,7 +420,7 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), dir.x, dir.y, dir.z);
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), light->size, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 3] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
}
else if(light->type == LIGHT_BACKGROUND) {
shader_id &= ~SHADER_AREA_LIGHT;
@ -443,7 +428,7 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 3] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
}
else if(light->type == LIGHT_AREA) {
float3 axisu = light->axisu*(light->sizeu*light->size);
@ -452,7 +437,7 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), axisu.x, axisu.y, axisu.z);
light_data[i*LIGHT_SIZE + 2] = make_float4(0.0f, axisv.x, axisv.y, axisv.z);
light_data[i*LIGHT_SIZE + 3] = make_float4(0.0f, dir.x, dir.y, dir.z);
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, dir.x, dir.y, dir.z);
}
else if(light->type == LIGHT_SPOT) {
shader_id &= ~SHADER_AREA_LIGHT;
@ -463,7 +448,7 @@ void LightManager::device_update_points(Device *device, DeviceScene *dscene, Sce
light_data[i*LIGHT_SIZE + 0] = make_float4(__int_as_float(light->type), co.x, co.y, co.z);
light_data[i*LIGHT_SIZE + 1] = make_float4(__int_as_float(shader_id), light->size, dir.x, dir.y);
light_data[i*LIGHT_SIZE + 2] = make_float4(dir.z, spot_angle, spot_smooth, 0.0f);
light_data[i*LIGHT_SIZE + 3] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
light_data[i*LIGHT_SIZE + 3] = make_float4(samples, 0.0f, 0.0f, 0.0f);
}
}

@ -54,6 +54,7 @@ public:
bool cast_shadow;
int shader;
int samples;
void tag_update(Scene *scene);
};

@ -160,7 +160,7 @@ void Scene::device_update(Device *device_, Progress& progress)
if(progress.get_cancel()) return;
progress.set_status("Updating Integrator");
integrator->device_update(device, &dscene);
integrator->device_update(device, &dscene, this);
if(progress.get_cancel()) return;