diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 3ade04c4658..7ce3b949bb1 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -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): diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 3b906bb4bdf..6f2b33b2815 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -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): diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 9c98cacc9b8..d5b884cfccd 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -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); diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 13040e551bd..5640a411fd7 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -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 */ diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index f582ace69f0..53d53b4bedd 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -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); diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index edc302cd6e3..1084415d0cf 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -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); diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 8dbf66c108c..80d66532506 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -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); diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 53a41d58e20..bc873f4e112 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -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) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 77a800b0e67..d204b114b8e 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -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 { diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index b26ebfd91e1..da563c9c4ec 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -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); } diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index afda41a857d..8fb341182b7 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -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); diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index e918de990c2..6c03d0859a7 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -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); } } diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index fb8684fa59b..3cedde2596e 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -54,6 +54,7 @@ public: bool cast_shadow; int shader; + int samples; void tag_update(Scene *scene); }; diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index a5f90bfe34b..45c8a05c27d 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -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;