Changeset View
Changeset View
Standalone View
Standalone View
intern/cycles/scene/light.cpp
| Show All 11 Lines | |||||
| #include "scene/scene.h" | #include "scene/scene.h" | ||||
| #include "scene/shader.h" | #include "scene/shader.h" | ||||
| #include "scene/shader_graph.h" | #include "scene/shader_graph.h" | ||||
| #include "scene/shader_nodes.h" | #include "scene/shader_nodes.h" | ||||
| #include "scene/stats.h" | #include "scene/stats.h" | ||||
| #include "integrator/shader_eval.h" | #include "integrator/shader_eval.h" | ||||
| #include "lpe/dump.h" | |||||
| #include "lpe/lpeparse.h" | |||||
| #include "util/foreach.h" | #include "util/foreach.h" | ||||
| #include "util/hash.h" | #include "util/hash.h" | ||||
| #include "util/log.h" | #include "util/log.h" | ||||
| #include "util/path.h" | #include "util/path.h" | ||||
| #include "util/progress.h" | #include "util/progress.h" | ||||
| #include "util/task.h" | #include "util/task.h" | ||||
| CCL_NAMESPACE_BEGIN | CCL_NAMESPACE_BEGIN | ||||
| ▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | NODE_DEFINE(Light) | ||||
| SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", true); | SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", true); | ||||
| SOCKET_BOOLEAN(is_portal, "Is Portal", false); | SOCKET_BOOLEAN(is_portal, "Is Portal", false); | ||||
| SOCKET_BOOLEAN(is_enabled, "Is Enabled", true); | SOCKET_BOOLEAN(is_enabled, "Is Enabled", true); | ||||
| SOCKET_NODE(shader, "Shader", Shader::get_node_type()); | SOCKET_NODE(shader, "Shader", Shader::get_node_type()); | ||||
| SOCKET_STRING(lightgroup, "Light Group", ustring()); | SOCKET_STRING(lightgroup, "Light Group", ustring()); | ||||
| SOCKET_STRING(lpe_tag, "Light Path Expression Tag", ustring()); | |||||
| return type; | return type; | ||||
| } | } | ||||
| Light::Light() : Node(get_node_type()) | Light::Light() : Node(get_node_type()) | ||||
| { | { | ||||
| dereference_all_used_nodes(); | dereference_all_used_nodes(); | ||||
| } | } | ||||
| Show All 22 Lines | |||||
| /* Light Manager */ | /* Light Manager */ | ||||
| LightManager::LightManager() | LightManager::LightManager() | ||||
| { | { | ||||
| update_flags = UPDATE_ALL; | update_flags = UPDATE_ALL; | ||||
| need_update_background = true; | need_update_background = true; | ||||
| last_background_enabled = false; | last_background_enabled = false; | ||||
| last_background_resolution = 0; | last_background_resolution = 0; | ||||
| initialize_symbol_to_index_map(); | |||||
| } | } | ||||
| LightManager::~LightManager() | LightManager::~LightManager() | ||||
| { | { | ||||
| foreach (IESSlot *slot, ies_slots) { | foreach (IESSlot *slot, ies_slots) { | ||||
| delete slot; | delete slot; | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 582 Lines • ▼ Show 20 Lines | foreach (Light *light, scene->lights) { | ||||
| if (!light->is_shadow_catcher) { | if (!light->is_shadow_catcher) { | ||||
| shader_id |= SHADER_EXCLUDE_SHADOW_CATCHER; | shader_id |= SHADER_EXCLUDE_SHADOW_CATCHER; | ||||
| } | } | ||||
| klights[light_index].type = light->light_type; | klights[light_index].type = light->light_type; | ||||
| klights[light_index].strength[0] = light->strength.x; | klights[light_index].strength[0] = light->strength.x; | ||||
| klights[light_index].strength[1] = light->strength.y; | klights[light_index].strength[1] = light->strength.y; | ||||
| klights[light_index].strength[2] = light->strength.z; | klights[light_index].strength[2] = light->strength.z; | ||||
| klights[light_index].lpe_tag_index = get_lpe_tag_index(light->get_lpe_tag()); | |||||
| if (light->light_type == LIGHT_POINT) { | if (light->light_type == LIGHT_POINT) { | ||||
| shader_id &= ~SHADER_AREA_LIGHT; | shader_id &= ~SHADER_AREA_LIGHT; | ||||
| float radius = light->size; | float radius = light->size; | ||||
| float invarea = (radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) : 1.0f; | float invarea = (radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) : 1.0f; | ||||
| if (light->use_mis && radius > 0.0f) | if (light->use_mis && radius > 0.0f) | ||||
| ▲ Show 20 Lines • Show All 217 Lines • ▼ Show 20 Lines | if (need_update_background) { | ||||
| if (progress.get_cancel()) | if (progress.get_cancel()) | ||||
| return; | return; | ||||
| } | } | ||||
| device_update_ies(dscene); | device_update_ies(dscene); | ||||
| if (progress.get_cancel()) | if (progress.get_cancel()) | ||||
| return; | return; | ||||
| device_update_lpe(dscene, scene, progress); | |||||
| if (progress.get_cancel()) { | |||||
| return; | |||||
| } | |||||
| update_flags = UPDATE_NONE; | update_flags = UPDATE_NONE; | ||||
| need_update_background = false; | need_update_background = false; | ||||
| } | } | ||||
| void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_background) | void LightManager::device_free(Device *, DeviceScene *dscene, const bool free_background) | ||||
| { | { | ||||
| dscene->light_distribution.free(); | dscene->light_distribution.free(); | ||||
| dscene->lights.free(); | dscene->lights.free(); | ||||
| if (free_background) { | if (free_background) { | ||||
| dscene->light_background_marginal_cdf.free(); | dscene->light_background_marginal_cdf.free(); | ||||
| dscene->light_background_conditional_cdf.free(); | dscene->light_background_conditional_cdf.free(); | ||||
| } | } | ||||
| dscene->ies_lights.free(); | dscene->ies_lights.free(); | ||||
| free_light_path_automata(dscene); | |||||
| } | } | ||||
| void LightManager::tag_update(Scene * /*scene*/, uint32_t flag) | void LightManager::tag_update(Scene * /*scene*/, uint32_t flag) | ||||
| { | { | ||||
| update_flags |= flag; | update_flags |= flag; | ||||
| } | } | ||||
| bool LightManager::need_update() const | bool LightManager::need_update() const | ||||
| ▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | for (int i = 0; i < ies_slots.size(); i++) { | ||||
| data[i] = __int_as_float(-1); | data[i] = __int_as_float(-1); | ||||
| } | } | ||||
| } | } | ||||
| dscene->ies_lights.copy_to_device(); | dscene->ies_lights.copy_to_device(); | ||||
| } | } | ||||
| } | } | ||||
| static vector<Pass *> get_lpe_passes(Scene *scene) | |||||
| { | |||||
| vector<Pass *> result; | |||||
| for (Pass *pass : scene->passes) { | |||||
| if (pass->get_type() != PASS_AOV_LPE) { | |||||
| continue; | |||||
| } | |||||
| if (pass->get_light_path_expression().empty()) { | |||||
| continue; | |||||
| } | |||||
| result.push_back(pass); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| void LightManager::initialize_symbol_to_index_map() | |||||
| { | |||||
| symbol_to_index_map.clear(); | |||||
| symbol_to_index_map[LPE::Labels::CAMERA] = PATH_EVENT_CAMERA; | |||||
| symbol_to_index_map[LPE::Labels::TRANSMIT] = PATH_EVENT_TRANSMIT; | |||||
| symbol_to_index_map[LPE::Labels::REFLECT] = PATH_EVENT_REFLECT; | |||||
| symbol_to_index_map[LPE::Labels::VOLUME] = PATH_EVENT_VOLUME_SCATTER; | |||||
| symbol_to_index_map[LPE::Labels::BACKGROUND] = PATH_EVENT_BACKGROUND; | |||||
| symbol_to_index_map[LPE::Labels::LIGHT] = PATH_EVENT_LIGHT; | |||||
| symbol_to_index_map[LPE::Labels::OBJECT] = PATH_EVENT_EMISSION_OBJECT; | |||||
| symbol_to_index_map["A"] = PATH_EVENT_ALBEDO; | |||||
| symbol_to_index_map[LPE::Labels::DIFFUSE] = PATH_EVENT_SCATTERING_DIFFUSE; | |||||
| symbol_to_index_map[LPE::Labels::GLOSSY] = PATH_EVENT_SCATTERING_GLOSSY; | |||||
| symbol_to_index_map[LPE::Labels::SINGULAR] = PATH_EVENT_SCATTERING_SINGULAR; | |||||
| symbol_to_index_map[LPE::Labels::STRAIGHT] = PATH_EVENT_SCATTERING_STRAIGHT; | |||||
| symbol_to_index_map["_"] = PATH_EVENT_WILDCARD; | |||||
| symbol_to_index_map[LPE::Labels::STOP] = PATH_EVENT_STOP; | |||||
| num_lpe_tags = NUM_DEFAULT_PATH_EVENTS; | |||||
| } | |||||
| int LightManager::get_lpe_tag_index(const ustring &lpe_tag_name) | |||||
| { | |||||
| thread_scoped_lock lock(symbol_to_index_map_mutex); | |||||
| if (lpe_tag_name == "") { | |||||
| return PATH_EVENT_LABEL_NONE; | |||||
| } | |||||
| auto iter = symbol_to_index_map.find(lpe_tag_name.c_str()); | |||||
| if (iter != symbol_to_index_map.end()) { | |||||
| return iter->second; | |||||
| } | |||||
| int index = num_lpe_tags++; | |||||
| symbol_to_index_map.insert({lpe_tag_name.c_str(), index}); | |||||
| return index; | |||||
| } | |||||
| void LightManager::device_update_lpe(DeviceScene *dscene, Scene *scene, Progress &progress) | |||||
| { | |||||
| const vector<Pass *> lpe_passes = get_lpe_passes(scene); | |||||
| if (lpe_passes.empty()) { | |||||
| return; | |||||
| } | |||||
| vector<LPE::LPexp *> lpes(lpe_passes.size()); | |||||
| /* TODO(kevindietrich) : support labels. */ | |||||
| /* TODO(kevindietrich) : user_events, user_scatterings. */ | |||||
| /* https://docs.arnoldrenderer.com/display/A5ARP/Light+Path+Expression+AOVs | |||||
| * https://www.sidefx.com/docs/houdini/render/lpe.html | |||||
| * https://clarissewiki.com/5.0/light-path-expressions.html | |||||
| * https://clarissewiki.com/5.0/lpes-token-reference.html | |||||
| * https://rmanwiki.pixar.com/display/REN24/Light+Path+Expressions | |||||
| * "A" : albedo (arnold) | |||||
| */ | |||||
| std::vector<std::string> userEvents = {"A"}; | |||||
| std::vector<std::string> userScatterings = {}; | |||||
| LPE::Parser parser{&userEvents}; | |||||
| size_t lpe_index = 0; | |||||
| for (Pass *pass : lpe_passes) { | |||||
| LPE::LPexp *compiled = parser.parse(pass->get_light_path_expression().c_str()); | |||||
| if (parser.error()) { | |||||
| std::cerr << "[Light Path Expression] Parse error : " << parser.getErrorMsg() << " at char " | |||||
| << parser.getErrorPos() << '\n'; | |||||
| progress.set_cancel("Unable to parse a light path expression, see console for details..."); | |||||
| return; | |||||
| } | |||||
| lpes[lpe_index++] = compiled; | |||||
| } | |||||
| #if 0 | |||||
| { | |||||
| LPE::dump_json( | |||||
| "/tmp/lpe.json", | |||||
| lpes, | |||||
| [&](int index) -> std::string { return lpe_passes[index]->get_name().c_str(); }, | |||||
| [&](int index) -> std::string { | |||||
| return lpe_passes[index]->get_light_path_expression().c_str(); | |||||
| }); | |||||
| } | |||||
| #endif | |||||
| /* Generate state machine. */ | |||||
| LPE::NdfAutomata ndfautomata; | |||||
| for (size_t i = 0; i < lpes.size(); ++i) { | |||||
| Pass *pass = lpe_passes[i]; | |||||
| /* Set the pass offset as the last state. */ | |||||
| generateAutomata(ndfautomata, *lpes[i], Pass::get_offset(scene->passes, pass)); | |||||
| } | |||||
| LPE::DfAutomata dfautomata; | |||||
| ndfautoToDfauto(ndfautomata, dfautomata); | |||||
| update_light_path_automata(dscene, dfautomata); | |||||
| } | |||||
| /* Code adapted from OSL's OptAutomata. | |||||
| * Main difference is that we use indices for symbols instead of strings, so we stay GPU friendly. | |||||
| */ | |||||
| void LightManager::update_light_path_automata(DeviceScene *dscene, | |||||
| const LPE::DfAutomata &dfautomata) | |||||
| { | |||||
| const size_t num_states = dfautomata.size(); | |||||
| size_t num_transitions = 0; | |||||
| size_t num_lpes = 0; | |||||
| for (size_t s = 0; s < num_states; ++s) { | |||||
| num_transitions += dfautomata.getState(s)->getSymbolTransitions().size(); | |||||
| num_lpes += dfautomata.getState(s)->getLpeIndexSet().size(); | |||||
| } | |||||
| KernelLightPathTransition *ktransitions = dscene->light_path_transitions.alloc(num_transitions); | |||||
| KernelLightPathState *kstates = dscene->light_path_states.alloc(num_states); | |||||
| uint *kactive_aovs = dscene->light_path_aovs.alloc(num_lpes); | |||||
| size_t trans_offset = 0; | |||||
| size_t lpe_indices_offset = 0; | |||||
| for (size_t s = 0; s < num_states; ++s) { | |||||
| const LPE::DfAutomata::State *state = dfautomata.getState(s); | |||||
| const LPE::SymbolToInt &symbol_transitions = state->getSymbolTransitions(); | |||||
| const LPE::LpeIndexSet &lpe_index_set = state->getLpeIndexSet(); | |||||
| kstates[s].start_transition = trans_offset; | |||||
| kstates[s].start_aovs = lpe_indices_offset; | |||||
| kstates[s].num_transitions = symbol_transitions.size(); | |||||
| kstates[s].num_aovs = lpe_index_set.size(); | |||||
| kstates[s].wildcard_transition = state->getWildCardTransition(); | |||||
| for (const LPE::LpeIndexSet::value_type &i : lpe_index_set) { | |||||
| kactive_aovs[lpe_indices_offset++] = i; | |||||
| } | |||||
| for (const LPE::SymbolToInt::value_type &i : symbol_transitions) { | |||||
| ktransitions[trans_offset].symbol = symbol_to_index_map.find(i.first)->second; | |||||
| ktransitions[trans_offset].state = i.second; | |||||
| trans_offset++; | |||||
| } | |||||
| std::sort(ktransitions + kstates[s].start_transition, | |||||
| ktransitions + kstates[s].start_transition + kstates[s].num_transitions, | |||||
| [](const KernelLightPathTransition &a, const KernelLightPathTransition &b) { | |||||
| return a.symbol < b.symbol; | |||||
| }); | |||||
| } | |||||
| dscene->data.has_lpes = true; | |||||
| dscene->light_path_transitions.copy_to_device(); | |||||
| dscene->light_path_states.copy_to_device(); | |||||
| dscene->light_path_aovs.copy_to_device(); | |||||
| } | |||||
| void LightManager::free_light_path_automata(DeviceScene *dscene) | |||||
| { | |||||
| dscene->data.has_lpes = false; | |||||
| dscene->light_path_transitions.free(); | |||||
| dscene->light_path_states.free(); | |||||
| dscene->light_path_aovs.free(); | |||||
| } | |||||
| CCL_NAMESPACE_END | CCL_NAMESPACE_END | ||||