diff options
author | Brecht Van Lommel <brecht@blender.org> | 2021-10-24 15:19:19 +0300 |
---|---|---|
committer | Brecht Van Lommel <brecht@blender.org> | 2021-10-26 16:36:39 +0300 |
commit | d7d40745fa09061a3117bd3669c5a46bbf611eae (patch) | |
tree | 8dbaca086ecbb09aad62c25e9ece66332fe79af3 /intern/cycles/scene/shader.cpp | |
parent | b698fe1e047e56e8ed67ba47464c0017d9c50eea (diff) |
Cycles: changes to source code folders structure
* Split render/ into scene/ and session/. The scene/ folder now contains the
scene and its nodes. The session/ folder contains the render session and
associated data structures like drivers and render buffers.
* Move top level kernel headers into new folders kernel/camera/, kernel/film/,
kernel/light/, kernel/sample/, kernel/util/
* Move integrator related kernel headers into kernel/integrator/
* Move OSL shaders from kernel/shaders/ to kernel/osl/shaders/
For patches and branches, git merge and rebase should be able to detect the
renames and move over code to the right file.
Diffstat (limited to 'intern/cycles/scene/shader.cpp')
-rw-r--r-- | intern/cycles/scene/shader.cpp | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp new file mode 100644 index 00000000000..6b464a77401 --- /dev/null +++ b/intern/cycles/scene/shader.cpp @@ -0,0 +1,876 @@ +/* + * 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 "scene/background.h" +#include "scene/camera.h" +#include "scene/colorspace.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/osl.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/svm.h" +#include "scene/tables.h" + +#include "util/util_foreach.h" +#include "util/util_murmurhash.h" +#include "util/util_task.h" +#include "util/util_transform.h" + +#ifdef WITH_OCIO +# include <OpenColorIO/OpenColorIO.h> +namespace OCIO = OCIO_NAMESPACE; +#endif + +CCL_NAMESPACE_BEGIN + +thread_mutex ShaderManager::lookup_table_mutex; +vector<float> 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<double> slope_x(DATA_TMP_SIZE); + vector<double> 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<float> &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); + + SOCKET_INT(pass_id, "Pass ID", 0); + + return type; +} + +Shader::Shader() : Node(get_node_type()) +{ + pass_id = 0; + + graph = NULL; + + has_surface = false; + has_surface_transparent = false; + has_surface_emission = false; + has_surface_raytrace = 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; + + need_update_uvs = true; + need_update_attribute = true; + need_update_displacement = 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::get_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->get_color() * node->get_strength(); + } + else if (surf->link->parent->type == BackgroundNode::get_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->get_color() * node->get_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_displacement = 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 */ + tag_modified(); + + scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED); + + /* 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->tag_update(scene, LightManager::SHADER_MODIFIED); + + /* 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->tag_update(scene, LightManager::SHADER_MODIFIED); + } + } + + /* 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) { + if (displacement_method == DISPLACE_BOTH) { + attributes.add(ATTR_STD_POSITION_UNDISPLACED); + } + if (displacement_method_is_modified()) { + need_update_displacement = true; + scene->geometry_manager->tag_update(scene, GeometryManager::SHADER_DISPLACEMENT_MODIFIED); + scene->object_manager->need_flags_update = true; + } + } + + /* compare if the attributes changed, mesh manager will check + * need_update_attribute, update the relevant meshes and clear it. */ + if (attributes.modified(prev_attributes)) { + need_update_attribute = true; + scene->geometry_manager->tag_update(scene, GeometryManager::SHADER_ATTRIBUTE_MODIFIED); + scene->procedural_manager->tag_update(); + } + + 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 (!reference_count()) { + tag_modified(); + /* We do not reference here as the shader will be referenced when added to a socket. */ + scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED); + } +} + +bool Shader::need_update_geometry() const +{ + return need_update_uvs || need_update_attribute || need_update_displacement; +} + +/* Shader Manager */ + +ShaderManager::ShaderManager() +{ + update_flags = UPDATE_ALL; + beckmann_table_offset = TABLE_OFFSET_INVALID; + + init_xyz_transforms(); +} + +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::device_update(Device *device, + DeviceScene *dscene, + Scene *scene, + Progress &progress) +{ + if (!need_update()) { + return; + } + + uint id = 0; + foreach (Shader *shader, scene->shaders) { + shader->id = id++; + } + + /* Those shaders should always be compiled as they are used as fallback if a shader cannot be + * found, e.g. bad shader index for the triangle shaders on a Mesh. */ + assert(scene->default_surface->reference_count() != 0); + assert(scene->default_light->reference_count() != 0); + assert(scene->default_background->reference_count() != 0); + assert(scene->default_empty->reference_count() != 0); + + device_update_specific(device, dscene, scene, progress); +} + +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->get_use_mis()) + flag |= SD_USE_MIS; + if (shader->has_surface_emission) + flag |= SD_HAS_EMISSION; + if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) + flag |= SD_HAS_TRANSPARENT_SHADOW; + if (shader->has_surface_raytrace) + flag |= SD_HAS_RAYTRACE; + 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->get_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 (shader->get_volume_sampling_method() == VOLUME_SAMPLING_EQUIANGULAR) + flag |= SD_VOLUME_EQUIANGULAR; + if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_MULTIPLE_IMPORTANCE) + flag |= SD_VOLUME_MIS; + if (shader->get_volume_interpolation_method() == VOLUME_INTERPOLATION_CUBIC) + flag |= SD_VOLUME_CUBIC; + if (shader->has_bump) + flag |= SD_HAS_BUMP; + if (shader->get_displacement_method() != DISPLACE_BUMP) + flag |= SD_HAS_DISPLACEMENT; + + /* constant emission check */ + float3 constant_emission = zero_float3(); + 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->get_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 = graph->create_node<DiffuseBsdfNode>(); + diffuse->set_color(make_float3(0.8f, 0.8f, 0.8f)); + graph->add(diffuse); + + graph->connect(diffuse->output("BSDF"), graph->output()->input("Surface")); + + Shader *shader = scene->create_node<Shader>(); + shader->name = "default_surface"; + shader->set_graph(graph); + shader->reference(); + scene->default_surface = shader; + shader->tag_update(scene); + } + + /* default volume */ + { + ShaderGraph *graph = new ShaderGraph(); + + PrincipledVolumeNode *principled = graph->create_node<PrincipledVolumeNode>(); + graph->add(principled); + + graph->connect(principled->output("Volume"), graph->output()->input("Volume")); + + Shader *shader = scene->create_node<Shader>(); + shader->name = "default_volume"; + shader->set_graph(graph); + scene->default_volume = shader; + shader->tag_update(scene); + /* No default reference for the volume to avoid compiling volume kernels if there are no actual + * volumes in the scene */ + } + + /* default light */ + { + ShaderGraph *graph = new ShaderGraph(); + + EmissionNode *emission = graph->create_node<EmissionNode>(); + emission->set_color(make_float3(0.8f, 0.8f, 0.8f)); + emission->set_strength(0.0f); + graph->add(emission); + + graph->connect(emission->output("Emission"), graph->output()->input("Surface")); + + Shader *shader = scene->create_node<Shader>(); + shader->name = "default_light"; + shader->set_graph(graph); + shader->reference(); + scene->default_light = shader; + shader->tag_update(scene); + } + + /* default background */ + { + ShaderGraph *graph = new ShaderGraph(); + + Shader *shader = scene->create_node<Shader>(); + shader->name = "default_background"; + shader->set_graph(graph); + shader->reference(); + scene->default_background = shader; + shader->tag_update(scene); + } + + /* default empty */ + { + ShaderGraph *graph = new ShaderGraph(); + + Shader *shader = scene->create_node<Shader>(); + shader->name = "default_empty"; + shader->set_graph(graph); + shader->reference(); + scene->default_empty = shader; + shader->tag_update(scene); + } +} + +uint ShaderManager::get_graph_kernel_features(ShaderGraph *graph) +{ + uint kernel_features = 0; + + foreach (ShaderNode *node, graph->nodes) { + kernel_features |= node->get_feature(); + if (node->special_type == SHADER_SPECIAL_TYPE_CLOSURE) { + BsdfBaseNode *bsdf_node = static_cast<BsdfBaseNode *>(node); + if (CLOSURE_IS_VOLUME(bsdf_node->get_closure_type())) { + kernel_features |= KERNEL_FEATURE_NODE_VOLUME; + } + else if (CLOSURE_IS_PRINCIPLED(bsdf_node->get_closure_type())) { + kernel_features |= KERNEL_FEATURE_PRINCIPLED; + } + } + if (node->has_surface_bssrdf()) { + kernel_features |= KERNEL_FEATURE_SUBSURFACE; + } + if (node->has_surface_transparent()) { + kernel_features |= KERNEL_FEATURE_TRANSPARENT; + } + } + + return kernel_features; +} + +uint ShaderManager::get_kernel_features(Scene *scene) +{ + uint kernel_features = KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION; + for (int i = 0; i < scene->shaders.size(); i++) { + Shader *shader = scene->shaders[i]; + if (!shader->reference_count()) { + continue; + } + + /* Gather requested features from all the nodes from the graph nodes. */ + kernel_features |= get_graph_kernel_features(shader->graph); + ShaderNode *output_node = shader->graph->output(); + if (output_node->input("Displacement")->link != NULL) { + kernel_features |= KERNEL_FEATURE_NODE_BUMP; + if (shader->get_displacement_method() == DISPLACE_BOTH) { + kernel_features |= KERNEL_FEATURE_NODE_BUMP_STATE; + } + } + /* On top of volume nodes, also check if we need volume sampling because + * e.g. an Emission node would slip through the KERNEL_FEATURE_NODE_VOLUME check */ + if (shader->has_volume_connected) { + kernel_features |= KERNEL_FEATURE_VOLUME; + } + } + + return kernel_features; +} + +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<ustring, ustringHash> 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; +} + +void ShaderManager::tag_update(Scene * /*scene*/, uint32_t /*flag*/) +{ + /* update everything for now */ + update_flags = ShaderManager::UPDATE_ALL; +} + +bool ShaderManager::need_update() const +{ + return update_flags != UPDATE_NONE; +} + +#ifdef WITH_OCIO +static bool to_scene_linear_transform(OCIO::ConstConfigRcPtr &config, + const char *colorspace, + Transform &to_scene_linear) +{ + OCIO::ConstProcessorRcPtr processor; + try { + processor = config->getProcessor(OCIO::ROLE_SCENE_LINEAR, colorspace); + } + catch (OCIO::Exception &) { + return false; + } + + if (!processor) { + return false; + } + + OCIO::ConstCPUProcessorRcPtr device_processor = processor->getDefaultCPUProcessor(); + if (!device_processor) { + return false; + } + + to_scene_linear = transform_identity(); + device_processor->applyRGB(&to_scene_linear.x.x); + device_processor->applyRGB(&to_scene_linear.y.x); + device_processor->applyRGB(&to_scene_linear.z.x); + to_scene_linear = transform_transposed_inverse(to_scene_linear); + return true; +} +#endif + +void ShaderManager::init_xyz_transforms() +{ + /* Default to ITU-BT.709 in case no appropriate transform found. + * Note XYZ here is defined as having a D65 white point. */ + 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 + /* Get from OpenColorO config if it has the required roles. */ + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + if (!(config && config->hasRole(OCIO::ROLE_SCENE_LINEAR))) { + return; + } + + Transform xyz_to_rgb; + + if (config->hasRole("aces_interchange")) { + /* Standard OpenColorIO role, defined as ACES2065-1. */ + const Transform xyz_E_to_aces = make_transform(1.0498110175f, + 0.0f, + -0.0000974845f, + 0.0f, + -0.4959030231f, + 1.3733130458f, + 0.0982400361f, + 0.0f, + 0.0f, + 0.0f, + 0.9912520182f, + 0.0f); + const Transform xyz_D65_to_E = make_transform( + 1.0521111f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.9184170f, 0.0f); + + Transform aces_to_rgb; + if (!to_scene_linear_transform(config, "aces_interchange", aces_to_rgb)) { + return; + } + + xyz_to_rgb = aces_to_rgb * xyz_E_to_aces * xyz_D65_to_E; + } + else if (config->hasRole("XYZ")) { + /* Custom role used before the standard existed. */ + if (!to_scene_linear_transform(config, "XYZ", xyz_to_rgb)) { + return; + } + } + else { + /* No reference role found to determine XYZ. */ + return; + } + + xyz_to_r = float4_to_float3(xyz_to_rgb.x); + xyz_to_g = float4_to_float3(xyz_to_rgb.y); + xyz_to_b = float4_to_float3(xyz_to_rgb.z); + + const Transform rgb_to_xyz = transform_inverse(xyz_to_rgb); + rgb_to_y = float4_to_float3(rgb_to_xyz.y); +#endif +} + +CCL_NAMESPACE_END |