#include "pathtracer.h" #include #include #include namespace glare::raytrace { Pathtracer::Pathtracer(std::shared_ptr graph_root, RayGenType ray_generator) : Pathtracer(std::make_shared(graph_root), ray_generator) { } Pathtracer::Pathtracer(RayGenType ray_generator) : Pathtracer(std::shared_ptr(nullptr), ray_generator) { } Pathtracer::Pathtracer(std::shared_ptr collector, RayGenType ray_generator) { Log_Hint << "Pathtracing Shader will now be compiled. This might take a couple of seconds."; if(!m_render_shader) m_render_shader = std::make_unique(files::shader("/pathtracer/pathtracer.comp")); m_ray_generator = ray_generator; core::Context::current().messages()->addObserver(this, tags::scene); m_render_target = std::make_unique(core::Texture::Target::e2D, core::Texture::StoreFormat::eRGBA32Float); m_color_storage = std::make_unique(core::Texture::Target::e2D, core::Texture::StoreFormat::eRGBA32Float); if (collector) initialize(collector); } Pathtracer::~Pathtracer() { if (core::Context::hasCurrentContext() && !core::Context::current().window().isClosing()) core::Context::current().messages()->removeObserver(this, tags::scene); } void Pathtracer::loadSettings(const fs::path &xml_file) { pugi::xml_document preferences_file; Log_Info << "Pathtracer settings file load status: " << preferences_file.load_file(xml_file.c_str()).description(); //Pathtracer const auto pt_child = preferences_file.child("pathtracer"); //bounces const auto bounces_child = pt_child.find_child_by_attribute("group", "name", "bounces"); m_bounce_thresholds[Effect::eDiffusion] = static_cast(bounces_child.find_child_by_attribute("item", "name", "diffusion").attribute("value").as_uint(2)); m_bounce_thresholds[Effect::eReflection] = static_cast(bounces_child.find_child_by_attribute("item", "name", "reflection").attribute("value").as_uint(4)); m_bounce_thresholds[Effect::eTransmission] = static_cast(bounces_child.find_child_by_attribute("item", "name", "transmission").attribute("value").as_uint(4)); m_bounce_thresholds[Effect::eEmission] = static_cast(bounces_child.find_child_by_attribute("item", "name", "emission").attribute("value").as_uint(8)); //ls thresholds const auto ls_thresholds_child = pt_child.find_child_by_attribute("group", "name", "ls-thresholds"); m_linespace_bounces[Effect::eDiffusion] = static_cast(ls_thresholds_child.find_child_by_attribute("item", "name", "diffusion").attribute("value").as_uint(2)); m_linespace_bounces[Effect::eReflection] = static_cast(ls_thresholds_child.find_child_by_attribute("item", "name", "reflection").attribute("value").as_uint(4)); m_linespace_bounces[Effect::eTransmission] = static_cast(ls_thresholds_child.find_child_by_attribute("item", "name", "transmission").attribute("value").as_uint(4)); m_linespace_bounces[Effect::eEmission] = static_cast(ls_thresholds_child.find_child_by_attribute("item", "name", "emission").attribute("value").as_uint(8)); m_linespace_distance = ls_thresholds_child.find_child_by_attribute("item", "name", "distance").attribute("value").as_float(10.f); m_linespace_accuracy = ls_thresholds_child.find_child_by_attribute("item", "name", "accuracy").attribute("value").as_float(0.5f); m_linespace_shadow = ls_thresholds_child.find_child_by_attribute("item", "name", "shadow").attribute("value").as_float(0.5f); m_clamp_direct = pt_child.find_child_by_attribute("item", "name", "clamp-direct").attribute("value").as_float(8); m_clamp_indirect = pt_child.find_child_by_attribute("item", "name", "clamp-indirect").attribute("value").as_float(8); } void Pathtracer::reset() { m_samples_current = 0; m_collector->apply(*m_render_shader); m_render_shader->uniform("u_render_config.num_bounces", m_bounce_count); m_render_shader->uniform("u_render_config.clamp_direct", m_clamp_direct); m_render_shader->uniform("u_render_config.clamp_indirect", m_clamp_indirect); m_render_shader->uniform("u_render_config.bounce_thresholds", uint32_t(m_bounce_thresholds)); m_render_shader->uniform("u_linespace_properties.accuracy_quality", m_linespace_accuracy); m_render_shader->uniform("u_linespace_properties.shadow_quality", m_linespace_shadow); m_render_shader->uniform("u_linespace_properties.distance_threshold", m_linespace_distance); m_render_shader->uniform("u_linespace_properties.bounce_thresholds", uint32_t(m_linespace_bounces)); m_render_shader->bindSubroutine(gl::ShaderType::eCompute, "u_light_sample[0]", "samplePoint"); m_render_shader->bindSubroutine(gl::ShaderType::eCompute, "u_light_sample[1]", "sampleSpot"); m_render_shader->bindSubroutine(gl::ShaderType::eCompute, "u_light_sample[2]", "sampleDirectional"); m_render_shader->bindSubroutine(gl::ShaderType::eCompute, "u_light_sample[3]", "sampleAmbient"); } const core::Texture &Pathtracer::render(uint32_t width, uint32_t height) { if (width != m_width || height != m_height) { m_width = width; m_height = height; m_trace_buffer.reserve(width * height, gl::BufferUsage::eDynamicCopy); m_render_target->set(core::Texture::SubTarget::e2D, core::Image({ static_cast(width), static_cast(height) }, 4)); m_color_storage->set(core::Texture::SubTarget::e2D, core::Image({ static_cast(width), static_cast(height) }, 4)); m_render_shader->uniform("u_render_target", m_render_target->imageAddress(gl::Access::eReadWrite)); m_render_shader->uniform("u_color_store", m_color_storage->imageAddress(gl::Access::eReadWrite)); m_render_shader->storageBuffer("trace_buffer", m_trace_buffer); m_samples_current = 0; } if (m_samples_current == 0) { m_collector->collect(); gl::clearTextureImage(m_color_storage->id(), 0, gl::TextureFormat::eRGBA, gl::Type::eFloat, glm::value_ptr(glm::vec4(0))); } for (unsigned sample = 0; sample < m_samples_per_frame; sample++) { if (m_samples_current >= m_samples_max) return *m_render_target; const static std::unique_ptr ray_generator = std::make_unique(); { LogTime("Generate Primary Rays", m_debug_times); ray_generator->generate(*this); } { LogTime("Trace and Bounce", m_debug_times); m_render_shader->use(); m_render_shader->uniform("u_render_config.current_sample", m_samples_current); m_render_shader->uniform("random_seed", ray_generator->randomSeed()); m_render_shader->dispatch2d(width, 16, height, 16); } ++m_samples_current; } return *m_render_target; } void Pathtracer::draw(std::shared_ptr node) { // If it's not manually initialized via the GUI button, initialize the Pathtracer here. if (!m_collector) reload(node); static const std::shared_ptr texture_renderer = core::DefaultTextureRenderers::makeSimpleRenderer(); texture_renderer->draw(render(core::Context::current().window().getWidth(), core::Context::current().window().getHeight())); } void Pathtracer::saveRender(const fs::path& target) const { if (target.extension() == ".hdr") { m_render_target->get().save(target); } else { m_render_target->get().save(target); } } Pathtracer::EffectArray::operator unsigned() { return *reinterpret_cast(m_data.data()); } uint8_t& Pathtracer::EffectArray::operator[](Effect effect) { return m_data[static_cast(effect)]; } const uint8_t& Pathtracer::EffectArray::operator[](Effect effect) const { return m_data[static_cast(effect)]; } Pathtracer::LogTime::LogTime(std::string label, bool enabled): m_label(std::move(label)), m_enabled(enabled) { core::ClockGL::instance().start(); } Pathtracer::LogTime::~LogTime() { if (m_enabled) Log_Hint << "Log Time for label=\"" << m_label << "\": " << core::ClockGL::instance().end() << " ns"; } void Pathtracer::initialize(std::shared_ptr collector) { assert(collector != nullptr); m_collector = collector; reset(); } void Pathtracer::reload(std::shared_ptr graph_root) { assert(graph_root != nullptr); if (!m_collector) m_collector = std::make_shared(); m_collector->setDirty(DirtyFlags::eAll); m_collector->collect(graph_root); reset(); } void Pathtracer::onNotify(msg::id_type id, msg::message_type message) { switch (id) { case tags::scene: reset(); break; default: break; } } const core::Program& Pathtracer::shader() const { return *m_render_shader; } uint32_t Pathtracer::width() const { return m_width; } uint32_t Pathtracer::height() const { return m_height; } core::Buffer& Pathtracer::traceBuffer() { return m_trace_buffer; } const core::Texture& Pathtracer::renderTarget() const { return *m_render_target; } std::shared_ptr Pathtracer::collector() const { return m_collector; } }