/* * Copyright 2011-2013 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "device/device.h" #include "render/background.h" #include "render/camera.h" #include "render/colorspace.h" #include "render/graph.h" #include "render/integrator.h" #include "render/light.h" #include "render/mesh.h" #include "render/nodes.h" #include "render/object.h" #include "render/osl.h" #include "render/scene.h" #include "render/shader.h" #include "render/svm.h" #include "render/tables.h" #include "util/util_foreach.h" #include "util/util_murmurhash.h" #include "util/util_task.h" #ifdef WITH_OCIO # include namespace OCIO = OCIO_NAMESPACE; #endif CCL_NAMESPACE_BEGIN thread_mutex ShaderManager::lookup_table_mutex; vector ShaderManager::beckmann_table; bool ShaderManager::beckmann_table_ready = false; /* Beckmann sampling precomputed table, see bsdf_microfacet.h */ /* 2D slope distribution (alpha = 1.0) */ static float beckmann_table_P22(const float slope_x, const float slope_y) { return expf(-(slope_x * slope_x + slope_y * slope_y)); } /* maximal slope amplitude (range that contains 99.99% of the distribution) */ static float beckmann_table_slope_max() { return 6.0; } /* MSVC 2015 needs this ugly hack to prevent a codegen bug on x86 * see T50176 for details */ #if defined(_MSC_VER) && (_MSC_VER == 1900) # define MSVC_VOLATILE volatile #else # define MSVC_VOLATILE #endif /* Paper used: Importance Sampling Microfacet-Based BSDFs with the * Distribution of Visible Normals. Supplemental Material 2/2. * * http://hal.inria.fr/docs/01/00/66/20/ANNEX/supplemental2.pdf */ static void beckmann_table_rows(float *table, int row_from, int row_to) { /* allocate temporary data */ const int DATA_TMP_SIZE = 512; vector slope_x(DATA_TMP_SIZE); vector CDF_P22_omega_i(DATA_TMP_SIZE); /* loop over incident directions */ for (int index_theta = row_from; index_theta < row_to; index_theta++) { /* incident vector */ const float cos_theta = index_theta / (BECKMANN_TABLE_SIZE - 1.0f); const float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta); /* for a given incident vector * integrate P22_{omega_i}(x_slope, 1, 1), Eq. (10) */ slope_x[0] = (double)-beckmann_table_slope_max(); CDF_P22_omega_i[0] = 0; for (MSVC_VOLATILE int index_slope_x = 1; index_slope_x < DATA_TMP_SIZE; ++index_slope_x) { /* slope_x */ slope_x[index_slope_x] = (double)(-beckmann_table_slope_max() + 2.0f * beckmann_table_slope_max() * index_slope_x / (DATA_TMP_SIZE - 1.0f)); /* dot product with incident vector */ float dot_product = fmaxf(0.0f, -(float)slope_x[index_slope_x] * sin_theta + cos_theta); /* marginalize P22_{omega_i}(x_slope, 1, 1), Eq. (10) */ float P22_omega_i = 0.0f; for (int j = 0; j < 100; ++j) { float slope_y = -beckmann_table_slope_max() + 2.0f * beckmann_table_slope_max() * j * (1.0f / 99.0f); P22_omega_i += dot_product * beckmann_table_P22((float)slope_x[index_slope_x], slope_y); } /* CDF of P22_{omega_i}(x_slope, 1, 1), Eq. (10) */ CDF_P22_omega_i[index_slope_x] = CDF_P22_omega_i[index_slope_x - 1] + (double)P22_omega_i; } /* renormalize CDF_P22_omega_i */ for (int index_slope_x = 1; index_slope_x < DATA_TMP_SIZE; ++index_slope_x) CDF_P22_omega_i[index_slope_x] /= CDF_P22_omega_i[DATA_TMP_SIZE - 1]; /* loop over random number U1 */ int index_slope_x = 0; for (int index_U = 0; index_U < BECKMANN_TABLE_SIZE; ++index_U) { const double U = 0.0000001 + 0.9999998 * index_U / (double)(BECKMANN_TABLE_SIZE - 1); /* inverse CDF_P22_omega_i, solve Eq.(11) */ while (CDF_P22_omega_i[index_slope_x] <= U) ++index_slope_x; const double interp = (CDF_P22_omega_i[index_slope_x] - U) / (CDF_P22_omega_i[index_slope_x] - CDF_P22_omega_i[index_slope_x - 1]); /* store value */ table[index_U + index_theta * BECKMANN_TABLE_SIZE] = (float)(interp * slope_x[index_slope_x - 1] + (1.0 - interp) * slope_x[index_slope_x]); } } } #undef MSVC_VOLATILE static void beckmann_table_build(vector &table) { table.resize(BECKMANN_TABLE_SIZE * BECKMANN_TABLE_SIZE); /* multithreaded build */ TaskPool pool; for (int i = 0; i < BECKMANN_TABLE_SIZE; i += 8) pool.push(function_bind(&beckmann_table_rows, &table[0], i, i + 8)); pool.wait_work(); } /* Shader */ NODE_DEFINE(Shader) { NodeType *type = NodeType::add("shader", create); SOCKET_BOOLEAN(use_mis, "Use MIS", true); SOCKET_BOOLEAN(use_transparent_shadow, "Use Transparent Shadow", true); SOCKET_BOOLEAN(heterogeneous_volume, "Heterogeneous Volume", true); static NodeEnum volume_sampling_method_enum; volume_sampling_method_enum.insert("distance", VOLUME_SAMPLING_DISTANCE); volume_sampling_method_enum.insert("equiangular", VOLUME_SAMPLING_EQUIANGULAR); volume_sampling_method_enum.insert("multiple_importance", VOLUME_SAMPLING_MULTIPLE_IMPORTANCE); SOCKET_ENUM(volume_sampling_method, "Volume Sampling Method", volume_sampling_method_enum, VOLUME_SAMPLING_MULTIPLE_IMPORTANCE); static NodeEnum volume_interpolation_method_enum; volume_interpolation_method_enum.insert("linear", VOLUME_INTERPOLATION_LINEAR); volume_interpolation_method_enum.insert("cubic", VOLUME_INTERPOLATION_CUBIC); SOCKET_ENUM(volume_interpolation_method, "Volume Interpolation Method", volume_interpolation_method_enum, VOLUME_INTERPOLATION_LINEAR); SOCKET_FLOAT(volume_step_rate, "Volume Step Rate", 1.0f); static NodeEnum displacement_method_enum; displacement_method_enum.insert("bump", DISPLACE_BUMP); displacement_method_enum.insert("true", DISPLACE_TRUE); displacement_method_enum.insert("both", DISPLACE_BOTH); SOCKET_ENUM(displacement_method, "Displacement Method", displacement_method_enum, DISPLACE_BUMP); return type; } Shader::Shader() : Node(node_type) { pass_id = 0; graph = NULL; has_surface = false; has_surface_transparent = false; has_surface_emission = false; has_surface_bssrdf = false; has_volume = false; has_displacement = false; has_bump = false; has_bssrdf_bump = false; has_surface_spatial_varying = false; has_volume_spatial_varying = false; has_volume_attribute_dependency = false; has_integrator_dependency = false; has_volume_connected = false; prev_volume_step_rate = 0.0f; displacement_method = DISPLACE_BUMP; id = -1; used = false; need_update = true; need_update_geometry = true; } Shader::~Shader() { delete graph; } bool Shader::is_constant_emission(float3 *emission) { /* If the shader has AOVs, they need to be evaluated, so we can't skip the shader. */ foreach (ShaderNode *node, graph->nodes) { if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) { return false; } } ShaderInput *surf = graph->output()->input("Surface"); if (surf->link == NULL) { return false; } if (surf->link->parent->type == EmissionNode::node_type) { EmissionNode *node = (EmissionNode *)surf->link->parent; assert(node->input("Color")); assert(node->input("Strength")); if (node->input("Color")->link || node->input("Strength")->link) { return false; } *emission = node->color * node->strength; } else if (surf->link->parent->type == BackgroundNode::node_type) { BackgroundNode *node = (BackgroundNode *)surf->link->parent; assert(node->input("Color")); assert(node->input("Strength")); if (node->input("Color")->link || node->input("Strength")->link) { return false; } *emission = node->color * node->strength; } else { return false; } return true; } void Shader::set_graph(ShaderGraph *graph_) { /* do this here already so that we can detect if mesh or object attributes * are needed, since the node attribute callbacks check if their sockets * are connected but proxy nodes should not count */ if (graph_) { graph_->remove_proxy_nodes(); if (displacement_method != DISPLACE_BUMP) { graph_->compute_displacement_hash(); } } /* update geometry if displacement changed */ if (displacement_method != DISPLACE_BUMP) { const char *old_hash = (graph) ? graph->displacement_hash.c_str() : ""; const char *new_hash = (graph_) ? graph_->displacement_hash.c_str() : ""; if (strcmp(old_hash, new_hash) != 0) { need_update_geometry = true; } } /* assign graph */ delete graph; graph = graph_; /* Store info here before graph optimization to make sure that * nodes that get optimized away still count. */ has_volume_connected = (graph->output()->input("Volume")->link != NULL); } void Shader::tag_update(Scene *scene) { /* update tag */ need_update = true; scene->shader_manager->need_update = true; /* if the shader previously was emissive, update light distribution, * if the new shader is emissive, a light manager update tag will be * done in the shader manager device update. */ if (use_mis && has_surface_emission) scene->light_manager->need_update = true; /* Special handle of background MIS light for now: for some reason it * has use_mis set to false. We are quite close to release now, so * better to be safe. */ if (this == scene->background->get_shader(scene)) { scene->light_manager->need_update_background = true; if (scene->light_manager->has_background_light(scene)) { scene->light_manager->need_update = true; } } /* quick detection of which kind of shaders we have to avoid loading * e.g. surface attributes when there is only a volume shader. this could * be more fine grained but it's better than nothing */ OutputNode *output = graph->output(); bool prev_has_volume = has_volume; has_surface = has_surface || output->input("Surface")->link; has_volume = has_volume || output->input("Volume")->link; has_displacement = has_displacement || output->input("Displacement")->link; /* get requested attributes. this could be optimized by pruning unused * nodes here already, but that's the job of the shader manager currently, * and may not be so great for interactive rendering where you temporarily * disconnect a node */ AttributeRequestSet prev_attributes = attributes; attributes.clear(); foreach (ShaderNode *node, graph->nodes) node->attributes(this, &attributes); if (has_displacement && displacement_method == DISPLACE_BOTH) { attributes.add(ATTR_STD_POSITION_UNDISPLACED); } /* compare if the attributes changed, mesh manager will check * need_update_geometry, update the relevant meshes and clear it. */ if (attributes.modified(prev_attributes)) { need_update_geometry = true; scene->geometry_manager->need_update = true; } if (has_volume != prev_has_volume || volume_step_rate != prev_volume_step_rate) { scene->geometry_manager->need_flags_update = true; scene->object_manager->need_flags_update = true; prev_volume_step_rate = volume_step_rate; } } void Shader::tag_used(Scene *scene) { /* if an unused shader suddenly gets used somewhere, it needs to be * recompiled because it was skipped for compilation before */ if (!used) { need_update = true; scene->shader_manager->need_update = true; } } /* Shader Manager */ ShaderManager::ShaderManager() { need_update = true; beckmann_table_offset = TABLE_OFFSET_INVALID; xyz_to_r = make_float3(3.2404542f, -1.5371385f, -0.4985314f); xyz_to_g = make_float3(-0.9692660f, 1.8760108f, 0.0415560f); xyz_to_b = make_float3(0.0556434f, -0.2040259f, 1.0572252f); rgb_to_y = make_float3(0.2126729f, 0.7151522f, 0.0721750f); #ifdef WITH_OCIO OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); if (config) { if (config->hasRole("XYZ") && config->hasRole("scene_linear")) { OCIO::ConstProcessorRcPtr to_rgb_processor = config->getProcessor("XYZ", "scene_linear"); OCIO::ConstProcessorRcPtr to_xyz_processor = config->getProcessor("scene_linear", "XYZ"); if (to_rgb_processor && to_xyz_processor) { float r[] = {1.0f, 0.0f, 0.0f}; float g[] = {0.0f, 1.0f, 0.0f}; float b[] = {0.0f, 0.0f, 1.0f}; to_xyz_processor->applyRGB(r); to_xyz_processor->applyRGB(g); to_xyz_processor->applyRGB(b); rgb_to_y = make_float3(r[1], g[1], b[1]); float x[] = {1.0f, 0.0f, 0.0f}; float y[] = {0.0f, 1.0f, 0.0f}; float z[] = {0.0f, 0.0f, 1.0f}; to_rgb_processor->applyRGB(x); to_rgb_processor->applyRGB(y); to_rgb_processor->applyRGB(z); xyz_to_r = make_float3(x[0], y[0], z[0]); xyz_to_g = make_float3(x[1], y[1], z[1]); xyz_to_b = make_float3(x[2], y[2], z[2]); } } } #endif } ShaderManager::~ShaderManager() { } ShaderManager *ShaderManager::create(int shadingsystem) { ShaderManager *manager; (void)shadingsystem; /* Ignored when built without OSL. */ #ifdef WITH_OSL if (shadingsystem == SHADINGSYSTEM_OSL) { manager = new OSLShaderManager(); } else #endif { manager = new SVMShaderManager(); } return manager; } uint ShaderManager::get_attribute_id(ustring name) { thread_scoped_spin_lock lock(attribute_lock_); /* get a unique id for each name, for SVM attribute lookup */ AttributeIDMap::iterator it = unique_attribute_id.find(name); if (it != unique_attribute_id.end()) return it->second; uint id = (uint)ATTR_STD_NUM + unique_attribute_id.size(); unique_attribute_id[name] = id; return id; } uint ShaderManager::get_attribute_id(AttributeStandard std) { return (uint)std; } int ShaderManager::get_shader_id(Shader *shader, bool smooth) { /* get a shader id to pass to the kernel */ int id = shader->id; /* smooth flag */ if (smooth) id |= SHADER_SMOOTH_NORMAL; /* default flags */ id |= SHADER_CAST_SHADOW | SHADER_AREA_LIGHT; return id; } void ShaderManager::update_shaders_used(Scene *scene) { if (!need_update) { return; } /* figure out which shaders are in use, so SVM/OSL can skip compiling them * for speed and avoid loading image textures into memory */ uint id = 0; foreach (Shader *shader, scene->shaders) { shader->used = false; shader->id = id++; } scene->default_surface->used = true; scene->default_light->used = true; scene->default_background->used = true; scene->default_empty->used = true; if (scene->background->shader) scene->background->shader->used = true; foreach (Geometry *geom, scene->geometry) foreach (Shader *shader, geom->used_shaders) shader->used = true; foreach (Light *light, scene->lights) if (light->shader) light->shader->used = true; } void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress & /*progress*/) { dscene->shaders.free(); if (scene->shaders.size() == 0) return; KernelShader *kshader = dscene->shaders.alloc(scene->shaders.size()); bool has_volumes = false; bool has_transparent_shadow = false; foreach (Shader *shader, scene->shaders) { uint flag = 0; if (shader->use_mis) flag |= SD_USE_MIS; if (shader->has_surface_transparent && shader->use_transparent_shadow) flag |= SD_HAS_TRANSPARENT_SHADOW; if (shader->has_volume) { flag |= SD_HAS_VOLUME; has_volumes = true; /* todo: this could check more fine grained, to skip useless volumes * enclosed inside an opaque bsdf. */ flag |= SD_HAS_TRANSPARENT_SHADOW; } /* in this case we can assume transparent surface */ if (shader->has_volume_connected && !shader->has_surface) flag |= SD_HAS_ONLY_VOLUME; if (shader->has_volume) { if (shader->heterogeneous_volume && shader->has_volume_spatial_varying) flag |= SD_HETEROGENEOUS_VOLUME; } if (shader->has_volume_attribute_dependency) flag |= SD_NEED_VOLUME_ATTRIBUTES; if (shader->has_bssrdf_bump) flag |= SD_HAS_BSSRDF_BUMP; if (device->info.has_volume_decoupled) { if (shader->volume_sampling_method == VOLUME_SAMPLING_EQUIANGULAR) flag |= SD_VOLUME_EQUIANGULAR; if (shader->volume_sampling_method == VOLUME_SAMPLING_MULTIPLE_IMPORTANCE) flag |= SD_VOLUME_MIS; } if (shader->volume_interpolation_method == VOLUME_INTERPOLATION_CUBIC) flag |= SD_VOLUME_CUBIC; if (shader->has_bump) flag |= SD_HAS_BUMP; if (shader->displacement_method != DISPLACE_BUMP) flag |= SD_HAS_DISPLACEMENT; /* constant emission check */ float3 constant_emission = make_float3(0.0f, 0.0f, 0.0f); if (shader->is_constant_emission(&constant_emission)) flag |= SD_HAS_CONSTANT_EMISSION; uint32_t cryptomatte_id = util_murmur_hash3(shader->name.c_str(), shader->name.length(), 0); /* regular shader */ kshader->flags = flag; kshader->pass_id = shader->pass_id; kshader->constant_emission[0] = constant_emission.x; kshader->constant_emission[1] = constant_emission.y; kshader->constant_emission[2] = constant_emission.z; kshader->cryptomatte_id = util_hash_to_float(cryptomatte_id); kshader++; has_transparent_shadow |= (flag & SD_HAS_TRANSPARENT_SHADOW) != 0; } dscene->shaders.copy_to_device(); /* lookup tables */ KernelTables *ktables = &dscene->data.tables; /* beckmann lookup table */ if (beckmann_table_offset == TABLE_OFFSET_INVALID) { if (!beckmann_table_ready) { thread_scoped_lock lock(lookup_table_mutex); if (!beckmann_table_ready) { beckmann_table_build(beckmann_table); beckmann_table_ready = true; } } beckmann_table_offset = scene->lookup_tables->add_table(dscene, beckmann_table); } ktables->beckmann_offset = (int)beckmann_table_offset; /* integrator */ KernelIntegrator *kintegrator = &dscene->data.integrator; kintegrator->use_volumes = has_volumes; /* TODO(sergey): De-duplicate with flags set in integrator.cpp. */ kintegrator->transparent_shadows = has_transparent_shadow; /* film */ KernelFilm *kfilm = &dscene->data.film; /* color space, needs to be here because e.g. displacement shaders could depend on it */ kfilm->xyz_to_r = float3_to_float4(xyz_to_r); kfilm->xyz_to_g = float3_to_float4(xyz_to_g); kfilm->xyz_to_b = float3_to_float4(xyz_to_b); kfilm->rgb_to_y = float3_to_float4(rgb_to_y); } void ShaderManager::device_free_common(Device *, DeviceScene *dscene, Scene *scene) { scene->lookup_tables->remove_table(&beckmann_table_offset); dscene->shaders.free(); } void ShaderManager::add_default(Scene *scene) { /* default surface */ { ShaderGraph *graph = new ShaderGraph(); DiffuseBsdfNode *diffuse = new DiffuseBsdfNode(); diffuse->color = make_float3(0.8f, 0.8f, 0.8f); graph->add(diffuse); graph->connect(diffuse->output("BSDF"), graph->output()->input("Surface")); Shader *shader = new Shader(); shader->name = "default_surface"; shader->set_graph(graph); scene->shaders.push_back(shader); scene->default_surface = shader; shader->tag_update(scene); } /* default volume */ { ShaderGraph *graph = new ShaderGraph(); PrincipledVolumeNode *principled = new PrincipledVolumeNode(); graph->add(principled); graph->connect(principled->output("Volume"), graph->output()->input("Volume")); Shader *shader = new Shader(); shader->name = "default_volume"; shader->set_graph(graph); scene->shaders.push_back(shader); scene->default_volume = shader; shader->tag_update(scene); } /* default light */ { ShaderGraph *graph = new ShaderGraph(); EmissionNode *emission = new EmissionNode(); emission->color = make_float3(0.8f, 0.8f, 0.8f); emission->strength = 0.0f; graph->add(emission); graph->connect(emission->output("Emission"), graph->output()->input("Surface")); Shader *shader = new Shader(); shader->name = "default_light"; shader->set_graph(graph); scene->shaders.push_back(shader); scene->default_light = shader; shader->tag_update(scene); } /* default background */ { ShaderGraph *graph = new ShaderGraph(); Shader *shader = new Shader(); shader->name = "default_background"; shader->set_graph(graph); scene->shaders.push_back(shader); scene->default_background = shader; shader->tag_update(scene); } /* default empty */ { ShaderGraph *graph = new ShaderGraph(); Shader *shader = new Shader(); shader->name = "default_empty"; shader->set_graph(graph); scene->shaders.push_back(shader); scene->default_empty = shader; shader->tag_update(scene); } } void ShaderManager::get_requested_graph_features(ShaderGraph *graph, DeviceRequestedFeatures *requested_features) { foreach (ShaderNode *node, graph->nodes) { requested_features->max_nodes_group = max(requested_features->max_nodes_group, node->get_group()); requested_features->nodes_features |= node->get_feature(); if (node->special_type == SHADER_SPECIAL_TYPE_CLOSURE) { BsdfBaseNode *bsdf_node = static_cast(node); if (CLOSURE_IS_VOLUME(bsdf_node->closure)) { requested_features->nodes_features |= NODE_FEATURE_VOLUME; } else if (CLOSURE_IS_PRINCIPLED(bsdf_node->closure)) { requested_features->use_principled = true; } } if (node->has_surface_bssrdf()) { requested_features->use_subsurface = true; } if (node->has_surface_transparent()) { requested_features->use_transparent = true; } if (node->has_raytrace()) { requested_features->use_shader_raytrace = true; } } } void ShaderManager::get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features) { requested_features->max_nodes_group = NODE_GROUP_LEVEL_0; requested_features->nodes_features = 0; for (int i = 0; i < scene->shaders.size(); i++) { Shader *shader = scene->shaders[i]; if (!shader->used) { continue; } /* Gather requested features from all the nodes from the graph nodes. */ get_requested_graph_features(shader->graph, requested_features); ShaderNode *output_node = shader->graph->output(); if (output_node->input("Displacement")->link != NULL) { requested_features->nodes_features |= NODE_FEATURE_BUMP; if (shader->displacement_method == DISPLACE_BOTH) { requested_features->nodes_features |= NODE_FEATURE_BUMP_STATE; requested_features->max_nodes_group = max(requested_features->max_nodes_group, NODE_GROUP_LEVEL_1); } } /* On top of volume nodes, also check if we need volume sampling because * e.g. an Emission node would slip through the NODE_FEATURE_VOLUME check */ if (shader->has_volume) requested_features->use_volume |= true; } } void ShaderManager::free_memory() { beckmann_table.free_memory(); #ifdef WITH_OSL OSLShaderManager::free_memory(); #endif ColorSpaceManager::free_memory(); } float ShaderManager::linear_rgb_to_gray(float3 c) { return dot(c, rgb_to_y); } string ShaderManager::get_cryptomatte_materials(Scene *scene) { string manifest = "{"; unordered_set materials; foreach (Shader *shader, scene->shaders) { if (materials.count(shader->name)) { continue; } materials.insert(shader->name); uint32_t cryptomatte_id = util_murmur_hash3(shader->name.c_str(), shader->name.length(), 0); manifest += string_printf("\"%s\":\"%08x\",", shader->name.c_str(), cryptomatte_id); } manifest[manifest.size() - 1] = '}'; return manifest; } CCL_NAMESPACE_END