diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 8dbd80f3747..4484dcfbfd7 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -128,6 +128,12 @@ enum_volume_interpolation = ( ('CUBIC', "Cubic", "Smoothed high quality interpolation, but slower") ) +enum_world_mis = ( + ('NONE', "None", "Don't sample the background, faster but might cause noise for non-solid backgrounds"), + ('AUTOMATIC', "Auto", "Automatically try to determine the best setting"), + ('MANUAL', "Manual", "Manually set the resolution of the sampling map, higher values are slower and require more memory but reduce noise") + ) + enum_device_type = ( ('CPU', "CPU", "CPU", 0), ('CUDA', "CUDA", "CUDA", 1), @@ -938,15 +944,15 @@ class CyclesWorldSettings(bpy.types.PropertyGroup): description="Cycles world settings", type=cls, ) - cls.sample_as_light = BoolProperty( - name="Multiple Importance Sample", - description="Use multiple importance sampling for the environment, " - "enabling for non-solid colors is recommended", - default=True, + cls.sampling_method = EnumProperty( + name="Sampling method", + description="How to sample the background light", + items=enum_world_mis, + default='AUTOMATIC', ) cls.sample_map_resolution = IntProperty( name="Map Resolution", - description="Importance map size is resolution x resolution; " + description="Importance map size is resolution x resolution/2; " "higher values potentially produce less noise, at the cost of memory and speed", min=4, max=8192, default=1024, diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 707f8756f6f..2b11a2eefb0 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -1214,11 +1214,13 @@ class CYCLES_WORLD_PT_settings(CyclesButtonsPanel, Panel): col = split.column() col.label(text="Surface:") - col.prop(cworld, "sample_as_light", text="Multiple Importance") + col.prop(cworld, "sampling_method", text="Sampling") sub = col.column(align=True) - sub.active = cworld.sample_as_light - sub.prop(cworld, "sample_map_resolution") + sub.active = cworld.sampling_method != 'NONE' + subsub = sub.row(align=True) + subsub.active = cworld.sampling_method == 'MANUAL' + subsub.prop(cworld, "sample_map_resolution") if use_branched_path(context): subsub = sub.row(align=True) subsub.active = use_sample_all_lights(context) diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index 292f0a1fa90..dc28bc647b5 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -377,10 +377,6 @@ def do_versions(self): for world in bpy.data.worlds: cworld = world.cycles - # World MIS - if not cworld.is_property_set("sample_as_light"): - cworld.sample_as_light = False - # World MIS Samples if not cworld.is_property_set("samples"): cworld.samples = 4 @@ -431,3 +427,12 @@ def do_versions(self): if bpy.data.version <= (2, 79, 3): # Switch to squared roughness convention square_roughness_nodes_insert() + + for world in bpy.data.worlds: + cworld = world.cycles + # World MIS + if not cworld.is_property_set("sampling_method"): + if cworld.get("sample_as_light", False): + cworld.sampling_method = 'MANUAL' + else: + cworld.sampling_method = 'NONE' diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 4919bc4325f..86b04f5030c 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -227,7 +227,15 @@ void BlenderSync::sync_background_light(bool use_portal) if(b_world) { PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles"); - bool sample_as_light = get_boolean(cworld, "sample_as_light"); + + enum SamplingMethod { + SAMPLING_NONE = 0, + SAMPLING_AUTOMATIC, + SAMPLING_MANUAL, + SAMPLING_NUM + }; + int sampling_method = get_enum(cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC); + bool sample_as_light = (sampling_method != SAMPLING_NONE); if(sample_as_light || use_portal) { /* test if we need to sync */ @@ -239,7 +247,12 @@ void BlenderSync::sync_background_light(bool use_portal) b_world.ptr.data != world_map) { light->type = LIGHT_BACKGROUND; - light->map_resolution = get_int(cworld, "sample_map_resolution"); + if(sampling_method == SAMPLING_MANUAL) { + light->map_resolution = get_int(cworld, "sample_map_resolution"); + } + else { + light->map_resolution = 0; + } light->shader = scene->default_background; light->use_mis = sample_as_light; light->max_bounces = get_int(cworld, "max_bounces"); diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index a5556c3be8f..524e2467ebc 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -319,9 +319,9 @@ ccl_device_noinline float3 indirect_background(KernelGlobals *kg, #ifdef __BACKGROUND_MIS__ /* check if background light exists or if we should skip pdf */ - int res = kernel_data.integrator.pdf_background_res; + int res_x = kernel_data.integrator.pdf_background_res_x; - if(!(state->flag & PATH_RAY_MIS_SKIP) && res) { + if(!(state->flag & PATH_RAY_MIS_SKIP) && res_x) { /* multiple importance sampling, get background light pdf for ray * direction, and compute weight with respect to BSDF pdf */ float pdf = background_light_pdf(kg, ray->P, ray->D); diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index ec7203d36eb..bb182ef1f25 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -143,12 +143,13 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float /* for the following, the CDF values are actually a pair of floats, with the * function value as X and the actual CDF as Y. The last entry's function * value is the CDF total. */ - int res = kernel_data.integrator.pdf_background_res; - int cdf_count = res + 1; + int res_x = kernel_data.integrator.pdf_background_res_x; + int res_y = kernel_data.integrator.pdf_background_res_y; + int cdf_width = res_x + 1; /* this is basically std::lower_bound as used by pbrt */ int first = 0; - int count = res; + int count = res_y; while(count > 0) { int step = count >> 1; @@ -163,24 +164,24 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float } int index_v = max(0, first - 1); - kernel_assert(index_v >= 0 && index_v < res); + kernel_assert(index_v >= 0 && index_v < res_y); float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v); float2 cdf_next_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v + 1); - float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res); + float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y); /* importance-sampled V direction */ float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, randv); - float v = (index_v + dv) / res; + float v = (index_v + dv) / res_y; /* this is basically std::lower_bound as used by pbrt */ first = 0; - count = res; + count = res_x; while(count > 0) { int step = count >> 1; int middle = first + step; - if(kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + middle).y < randu) { + if(kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + middle).y < randu) { first = middle + 1; count -= step + 1; } @@ -189,15 +190,15 @@ float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float } int index_u = max(0, first - 1); - kernel_assert(index_u >= 0 && index_u < res); + kernel_assert(index_u >= 0 && index_u < res_x); - float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + index_u); - float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + index_u + 1); - float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_count + res); + float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + index_u); + float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + index_u + 1); + float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + res_x); /* importance-sampled U direction */ float du = inverse_lerp(cdf_u.y, cdf_next_u.y, randu); - float u = (index_u + du) / res; + float u = (index_u + du) / res_x; /* compute pdf */ float denom = cdf_last_u.x * cdf_last_v.x; @@ -223,19 +224,21 @@ ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction) { float2 uv = direction_to_equirectangular(direction); - int res = kernel_data.integrator.pdf_background_res; + int res_x = kernel_data.integrator.pdf_background_res_x; + int res_y = kernel_data.integrator.pdf_background_res_y; + int cdf_width = res_x + 1; float sin_theta = sinf(uv.y * M_PI_F); if(sin_theta == 0.0f) return 0.0f; - int index_u = clamp(float_to_int(uv.x * res), 0, res - 1); - int index_v = clamp(float_to_int(uv.y * res), 0, res - 1); + int index_u = clamp(float_to_int(uv.x * res_x), 0, res_x - 1); + int index_v = clamp(float_to_int(uv.y * res_y), 0, res_y - 1); /* pdfs in V direction */ - float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * (res + 1) + res); - float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res); + float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + res_x); + float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y); float denom = cdf_last_u.x * cdf_last_v.x; @@ -243,7 +246,7 @@ float background_map_pdf(KernelGlobals *kg, float3 direction) return 0.0f; /* pdfs in U direction */ - float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * (res + 1) + index_u); + float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + index_u); float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v); return (cdf_u.x * cdf_v.x)/(M_2PI_F * M_PI_F * sin_theta * denom); diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 72fbf7be557..5382213e6f7 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1306,7 +1306,8 @@ typedef struct KernelIntegrator { int num_all_lights; float pdf_triangles; float pdf_lights; - int pdf_background_res; + int pdf_background_res_x; + int pdf_background_res_y; float light_inv_rr_threshold; /* light portals */ @@ -1368,6 +1369,8 @@ typedef struct KernelIntegrator { int start_sample; int max_closures; + + int pad1, pad2, pad3; } KernelIntegrator; static_assert_align(KernelIntegrator, 16); diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 9c5e32e8219..9c6536edc4f 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -94,6 +94,25 @@ device_memory *ImageManager::image_memory(int flat_slot) return img->mem; } +bool ImageManager::get_image_metadata(int flat_slot, + ImageMetaData& metadata) +{ + if(flat_slot == -1) { + return false; + } + + ImageDataType type; + int slot = flattened_slot_to_type_index(flat_slot, &type); + + Image *img = images[type][slot]; + if(img) { + metadata = img->metadata; + return true; + } + + return false; +} + bool ImageManager::get_image_metadata(const string& filename, void *builtin_data, ImageMetaData& metadata) @@ -329,7 +348,7 @@ int ImageManager::add_image(const string& filename, img = new Image(); img->filename = filename; img->builtin_data = builtin_data; - img->builtin_free_cache = metadata.builtin_free_cache; + img->metadata = metadata; img->need_load = true; img->animated = animated; img->frame = frame; @@ -417,11 +436,7 @@ void ImageManager::tag_reload_image(const string& filename, } bool ImageManager::file_load_image_generic(Image *img, - ImageInput **in, - int &width, - int &height, - int &depth, - int &components) + ImageInput **in) { if(img->filename == "") return false; @@ -449,28 +464,15 @@ bool ImageManager::file_load_image_generic(Image *img, *in = NULL; return false; } - - width = spec.width; - height = spec.height; - depth = spec.depth; - components = spec.nchannels; } else { /* load image using builtin images callbacks */ if(!builtin_image_info_cb || !builtin_image_pixels_cb) return false; - - ImageMetaData metadata; - builtin_image_info_cb(img->filename, img->builtin_data, metadata); - - width = metadata.width; - height = metadata.height; - depth = metadata.depth; - components = metadata.channels; } /* we only handle certain number of components */ - if(!(components >= 1 && components <= 4)) { + if(!(img->metadata.channels >= 1 && img->metadata.channels <= 4)) { if(*in) { (*in)->close(); delete *in; @@ -493,10 +495,16 @@ bool ImageManager::file_load_image(Image *img, { const StorageType alpha_one = (FileFormat == TypeDesc::UINT8)? 255 : 1; ImageInput *in = NULL; - int width, height, depth, components; - if(!file_load_image_generic(img, &in, width, height, depth, components)) { + if(!file_load_image_generic(img, &in)) { return false; } + + /* Get metadata. */ + int width = img->metadata.width; + int height = img->metadata.height; + int depth = img->metadata.depth; + int components = img->metadata.channels; + /* Read RGBA pixels. */ vector pixels_storage; StorageType *pixels; @@ -557,14 +565,14 @@ bool ImageManager::file_load_image(Image *img, img->builtin_data, (float*)&pixels[0], num_pixels * components, - img->builtin_free_cache); + img->metadata.builtin_free_cache); } else if(FileFormat == TypeDesc::UINT8) { builtin_image_pixels_cb(img->filename, img->builtin_data, (uchar*)&pixels[0], num_pixels * components, - img->builtin_free_cache); + img->metadata.builtin_free_cache); } else { /* TODO(dingto): Support half for ImBuf. */ diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 5391490d993..4fd09adaa64 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -71,6 +71,8 @@ public: bool get_image_metadata(const string& filename, void *builtin_data, ImageMetaData& metadata); + bool get_image_metadata(int flat_slot, + ImageMetaData& metadata); void device_update(Device *device, Scene *scene, @@ -110,7 +112,7 @@ public: struct Image { string filename; void *builtin_data; - bool builtin_free_cache; + ImageMetaData metadata; bool use_alpha; bool need_load; @@ -137,11 +139,7 @@ private: void *osl_texture_system; bool file_load_image_generic(Image *img, - ImageInput **in, - int &width, - int &height, - int &depth, - int &components); + ImageInput **in); template& pixels, Progress& progress) +static void shade_background_pixels(Device *device, DeviceScene *dscene, int width, int height, vector& pixels, Progress& progress) { /* create input */ - int width = res; - int height = res; - device_vector d_input(device, "background_input", MEM_READ_ONLY); device_vector d_output(device, "background_output", MEM_READ_WRITE); @@ -120,7 +119,7 @@ NODE_DEFINE(Light) SOCKET_VECTOR(axisv, "Axis V", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_FLOAT(sizev, "Size V", 1.0f); - SOCKET_INT(map_resolution, "Map Resolution", 512); + SOCKET_INT(map_resolution, "Map Resolution", 0); SOCKET_FLOAT(spot_angle, "Spot Angle", M_PI_4_F); SOCKET_FLOAT(spot_smooth, "Spot Smooth", 0.0f); @@ -481,40 +480,41 @@ void LightManager::device_update_distribution(Device *, DeviceScene *dscene, Sce static void background_cdf(int start, int end, - int res, - int cdf_count, + int res_x, + int res_y, const vector *pixels, float2 *cond_cdf) { + int cdf_width = res_x+1; /* Conditional CDFs (rows, U direction). */ for(int i = start; i < end; i++) { - float sin_theta = sinf(M_PI_F * (i + 0.5f) / res); - float3 env_color = (*pixels)[i * res]; + float sin_theta = sinf(M_PI_F * (i + 0.5f) / res_y); + float3 env_color = (*pixels)[i * res_x]; float ave_luminance = average(env_color); - cond_cdf[i * cdf_count].x = ave_luminance * sin_theta; - cond_cdf[i * cdf_count].y = 0.0f; + cond_cdf[i * cdf_width].x = ave_luminance * sin_theta; + cond_cdf[i * cdf_width].y = 0.0f; - for(int j = 1; j < res; j++) { - env_color = (*pixels)[i * res + j]; + for(int j = 1; j < res_x; j++) { + env_color = (*pixels)[i * res_x + j]; ave_luminance = average(env_color); - cond_cdf[i * cdf_count + j].x = ave_luminance * sin_theta; - cond_cdf[i * cdf_count + j].y = cond_cdf[i * cdf_count + j - 1].y + cond_cdf[i * cdf_count + j - 1].x / res; + cond_cdf[i * cdf_width + j].x = ave_luminance * sin_theta; + cond_cdf[i * cdf_width + j].y = cond_cdf[i * cdf_width + j - 1].y + cond_cdf[i * cdf_width + j - 1].x / res_x; } - float cdf_total = cond_cdf[i * cdf_count + res - 1].y + cond_cdf[i * cdf_count + res - 1].x / res; + float cdf_total = cond_cdf[i * cdf_width + res_x - 1].y + cond_cdf[i * cdf_width + res_x - 1].x / res_x; float cdf_total_inv = 1.0f / cdf_total; /* stuff the total into the brightness value for the last entry, because * we are going to normalize the CDFs to 0.0 to 1.0 afterwards */ - cond_cdf[i * cdf_count + res].x = cdf_total; + cond_cdf[i * cdf_width + res_x].x = cdf_total; if(cdf_total > 0.0f) - for(int j = 1; j < res; j++) - cond_cdf[i * cdf_count + j].y *= cdf_total_inv; + for(int j = 1; j < res_x; j++) + cond_cdf[i * cdf_width + j].y *= cdf_total_inv; - cond_cdf[i * cdf_count + res].y = 1.0f; + cond_cdf[i * cdf_width + res_x].y = 1.0f; } } @@ -536,7 +536,8 @@ void LightManager::device_update_background(Device *device, /* no background light found, signal renderer to skip sampling */ if(!background_light || !background_light->is_enabled) { - kintegrator->pdf_background_res = 0; + kintegrator->pdf_background_res_x = 0; + kintegrator->pdf_background_res_y = 0; return; } @@ -545,41 +546,62 @@ void LightManager::device_update_background(Device *device, assert(kintegrator->use_direct_light); /* get the resolution from the light's size (we stuff it in there) */ - int res = background_light->map_resolution; - kintegrator->pdf_background_res = res; - - assert(res > 0); + int2 res = make_int2(background_light->map_resolution, background_light->map_resolution/2); + /* If the resolution isn't set manually, try to find an environment texture. */ + if (res.x == 0) { + Shader *shader = (scene->background->shader) ? scene->background->shader : scene->default_background; + foreach(ShaderNode *node, shader->graph->nodes) { + if(node->type == EnvironmentTextureNode::node_type) { + EnvironmentTextureNode *env = (EnvironmentTextureNode*) node; + ImageMetaData metadata; + if(env->image_manager && env->image_manager->get_image_metadata(env->slot, metadata)) { + res.x = max(res.x, metadata.width); + res.y = max(res.y, metadata.height); + } + } + } + if (res.x > 0 && res.y > 0) { + VLOG(2) << "Automatically set World MIS resolution to " << res.x << " by " << res.y << "\n"; + } + } + /* If it's still unknown, just use the default. */ + if (res.x == 0 || res.y == 0) { + res = make_int2(1024, 512); + VLOG(2) << "Setting World MIS resolution to default\n"; + } + kintegrator->pdf_background_res_x = res.x; + kintegrator->pdf_background_res_y = res.y; vector pixels; - shade_background_pixels(device, dscene, res, pixels, progress); + shade_background_pixels(device, dscene, res.x, res.y, pixels, progress); if(progress.get_cancel()) return; /* build row distributions and column distribution for the infinite area environment light */ - int cdf_count = res + 1; - float2 *marg_cdf = dscene->light_background_marginal_cdf.alloc(cdf_count); - float2 *cond_cdf = dscene->light_background_conditional_cdf.alloc(cdf_count * cdf_count); + int cdf_width = res.x+1; + float2 *marg_cdf = dscene->light_background_marginal_cdf.alloc(res.y + 1); + float2 *cond_cdf = dscene->light_background_conditional_cdf.alloc(cdf_width * res.y); double time_start = time_dt(); - if(res < 512) { + if(max(res.x, res.y) < 512) { /* Small enough resolution, faster to do single-threaded. */ - background_cdf(0, res, res, cdf_count, &pixels, cond_cdf); + background_cdf(0, res.x, res.x, res.y, &pixels, cond_cdf); } else { /* Threaded evaluation for large resolution. */ const int num_blocks = TaskScheduler::num_threads(); - const int chunk_size = res / num_blocks; + const int chunk_size = res.y / num_blocks; int start_row = 0; TaskPool pool; for(int i = 0; i < num_blocks; ++i) { const int current_chunk_size = (i != num_blocks - 1) ? chunk_size - : (res - i * chunk_size); + : (res.y - i * chunk_size); pool.push(function_bind(&background_cdf, start_row, start_row + current_chunk_size, - res, - cdf_count, + res.x, + res.y, &pixels, cond_cdf)); start_row += current_chunk_size; @@ -588,22 +610,22 @@ void LightManager::device_update_background(Device *device, } /* marginal CDFs (column, V direction, sum of rows) */ - marg_cdf[0].x = cond_cdf[res].x; + marg_cdf[0].x = cond_cdf[res.x].x; marg_cdf[0].y = 0.0f; - for(int i = 1; i < res; i++) { - marg_cdf[i].x = cond_cdf[i * cdf_count + res].x; - marg_cdf[i].y = marg_cdf[i - 1].y + marg_cdf[i - 1].x / res; + for(int i = 1; i < res.y; i++) { + marg_cdf[i].x = cond_cdf[i * cdf_width + res.x].x; + marg_cdf[i].y = marg_cdf[i - 1].y + marg_cdf[i - 1].x / res.y; } - float cdf_total = marg_cdf[res - 1].y + marg_cdf[res - 1].x / res; - marg_cdf[res].x = cdf_total; + float cdf_total = marg_cdf[res.y - 1].y + marg_cdf[res.y - 1].x / res.y; + marg_cdf[res.y].x = cdf_total; if(cdf_total > 0.0f) - for(int i = 1; i < res; i++) + for(int i = 1; i < res.y; i++) marg_cdf[i].y /= cdf_total; - marg_cdf[res].y = 1.0f; + marg_cdf[res.y].y = 1.0f; VLOG(2) << "Background MIS build time " << time_dt() - time_start << "\n";