#include "pathtracer.h" #include #include #include namespace glare::raytrace { Pathtracer::Pathtracer() : Pathtracer(RayGeneratorType::eTrace) { } Pathtracer::Pathtracer(std::shared_ptr graph_root) : Pathtracer(std::make_shared(graph_root)) { } Pathtracer::Pathtracer(std::shared_ptr graph_root, RayGeneratorType ray_generator) : Pathtracer(std::make_shared(graph_root), ray_generator) { } Pathtracer::Pathtracer(RayGeneratorType ray_generator) : Pathtracer(std::shared_ptr(nullptr), ray_generator) { } Pathtracer::Pathtracer(std::shared_ptr collector) : Pathtracer(collector, RayGeneratorType::eTrace) { } Pathtracer::Pathtracer(std::shared_ptr collector, RayGeneratorType ray_generator) { m_bounce_count_thresholds[static_cast(EffectType::eDiffusion)] = 4; m_bounce_count_thresholds[static_cast(EffectType::eReflection)] = 6; m_bounce_count_thresholds[static_cast(EffectType::eTransmission)] = 12; m_bounce_count_thresholds[static_cast(EffectType::eEmission)] = 1; m_ls_bounce_count_thresholds[static_cast(EffectType::eDiffusion)] = 1; m_ls_bounce_count_thresholds[static_cast(EffectType::eReflection)] = 3; m_ls_bounce_count_thresholds[static_cast(EffectType::eTransmission)] = 3; m_ls_bounce_count_thresholds[static_cast(EffectType::eEmission)] = 6; m_linespace_config.distance_threshold = 8.f; m_render_config.bounces = 16; Log_Hint << "Pathtracing Shader will now be compiled. This might take a couple of seconds."; m_render_shader = std::make_unique(files::shader("/pathtracer/pathtracer.comp")); m_generator_type = ray_generator; m_trace_buffer = std::make_unique(); registerForMessage(tags::scene); if (collector) initialize(collector); } Pathtracer::~Pathtracer() { if (core::Context::hasCurrentContext() && !core::Context::current().window().isClosing()) unregisterForMessage(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_count_thresholds[static_cast(EffectType::eDiffusion)] = static_cast(bounces_child.find_child_by_attribute("item", "name", "diffusion").attribute("value").as_uint(2)); m_bounce_count_thresholds[static_cast(EffectType::eReflection)] = static_cast(bounces_child.find_child_by_attribute("item", "name", "reflection").attribute("value").as_uint(4)); m_bounce_count_thresholds[static_cast(EffectType::eTransmission)] = static_cast(bounces_child.find_child_by_attribute("item", "name", "transmission").attribute("value").as_uint(4)); m_bounce_count_thresholds[static_cast(EffectType::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_ls_bounce_count_thresholds[static_cast(EffectType::eDiffusion)] = static_cast(ls_thresholds_child.find_child_by_attribute("item", "name", "diffusion").attribute("value").as_uint(2)); m_ls_bounce_count_thresholds[static_cast(EffectType::eReflection)] = static_cast(ls_thresholds_child.find_child_by_attribute("item", "name", "reflection").attribute("value").as_uint(4)); m_ls_bounce_count_thresholds[static_cast(EffectType::eTransmission)] = static_cast(ls_thresholds_child.find_child_by_attribute("item", "name", "transmission").attribute("value").as_uint(4)); m_ls_bounce_count_thresholds[static_cast(EffectType::eEmission)] = static_cast(ls_thresholds_child.find_child_by_attribute("item", "name", "emission").attribute("value").as_uint(8)); m_linespace_config.accuracy_threshold = ls_thresholds_child.find_child_by_attribute("item", "name", "accuracy").attribute("value").as_float(0.5f); m_linespace_config.shadow_threshold = ls_thresholds_child.find_child_by_attribute("item", "name", "shadow").attribute("value").as_float(0.5f); m_linespace_config.distance_threshold = ls_thresholds_child.find_child_by_attribute("item", "name", "distance").attribute("value").as_float(10.f); m_render_config.clamp_direct = pt_child.find_child_by_attribute("item", "name", "clamp-direct").attribute("value").as_float(8); m_render_config.clamp_indirect = pt_child.find_child_by_attribute("item", "name", "clamp-indirect").attribute("value").as_float(8); } void Pathtracer::reset() { m_render_config.current_sample = 0; m_collector->apply(*m_render_shader); m_render_shader->uniform("u_render_config.num_bounces", m_render_config.bounces); m_render_shader->uniform("u_render_config.clamp_direct", m_render_config.clamp_direct); m_render_shader->uniform("u_render_config.clamp_indirect", m_render_config.clamp_indirect); m_render_shader->uniform("u_linespace_properties.accuracy_quality", m_linespace_config.accuracy_threshold); m_render_shader->uniform("u_linespace_properties.shadow_quality", m_linespace_config.shadow_threshold); m_render_shader->uniform("u_linespace_properties.distance_threshold", m_linespace_config.distance_threshold); unsigned bounce_thresholds = 0; for (int i = 0; i < 4; ++i) { bounce_thresholds |= (m_bounce_count_thresholds[i] & 0xff) << (8 * i); } m_render_shader->uniform("u_render_config.bounce_thresholds", bounce_thresholds); m_linespace_config.bounce_thresholds = 0; for (int i = 0; i < 4; ++i) { m_linespace_config.bounce_thresholds |= (m_ls_bounce_count_thresholds[i] & 0xff) << (8 * i); } m_render_shader->uniform("u_linespace_properties.bounce_thresholds", m_linespace_config.bounce_thresholds); 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::TextureRGBA_32F &Pathtracer::render(uint32_t width, uint32_t height) { static auto color_storage = std::make_unique(width, height); if (width != m_last_width || height != m_last_height) { m_last_width = width; m_last_height = height; m_trace_buffer->reserve(width * height, gl::BufferUsage::eDynamicCopy); m_render_target = std::make_unique(width, height); color_storage = std::make_unique(width, height); m_render_shader->uniform("u_render_target", m_render_target->makeImageResident(gl::Access::eReadWrite)); m_render_shader->uniform("u_color_store", color_storage->makeImageResident(gl::Access::eReadWrite)); m_render_shader->storageBuffer("trace_buffer", *m_trace_buffer); m_render_config.current_sample = 0; } if (m_render_config.current_sample == 0) { gl::clearTextureImage(color_storage->id(), 0, gl::TextureFormat::eRGBA, gl::Type::eFloat, glm::value_ptr(glm::vec4(0))); } if (m_skybox && m_skybox->isInitialized()) { m_render_shader->uniform("u_environment.cubemap", m_skybox->makeResident()); m_render_shader->uniform("u_environment.has_cubemap", 1); } else { m_render_shader->uniform("u_environment.color", glm::vec4(0.7f, 0.9f, 0.97f, 1)); m_render_shader->uniform("u_environment.has_cubemap", 0); } for (unsigned sample = 0; sample < m_render_config.samples_per_frame; sample++) { if (m_render_config.current_sample >= m_render_config.max_samples) return *m_render_target; if (debug.print_render_times) core::ClockGL::instance().start(); const static std::unique_ptr ray_generator = std::make_unique(); ray_generator->generate(*this); if (debug.print_render_times) Log_Hint << "RENDER TIME [Generate Primary Rays]: " << core::ClockGL::instance().end() << " ns"; if (debug.print_render_times) core::ClockGL::instance().start(); m_render_shader->use(); m_render_shader->uniform("u_render_config.current_sample", m_render_config.current_sample); m_render_shader->uniform("random_seed", ray_generator->randomSeed()); m_render_shader->dispatch2d(width, 16, height, 16); if (debug.print_render_times) Log_Hint << "RENDER TIME [Trace and Bounce]: " << core::ClockGL::instance().end() << " ns"; ++m_render_config.current_sample; } 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(fs::path target) const { core::textures::saveTexture(target, *m_render_target); } void Pathtracer::setGenerator(RayGeneratorType generator) { m_generator_type = generator; reset(); } RayGeneratorType Pathtracer::getGenerator() const { return m_generator_type; } void Pathtracer::initialize(std::shared_ptr collector) { m_collector = collector; m_collector->collect(); m_skybox = core::Context::current().skybox(); reset(); } void Pathtracer::reload(std::shared_ptr graph_root) { if (!m_collector) { m_collector = std::make_shared(graph_root); } else { m_collector->setDirty(DirtyFlags::eAll); m_collector->collect(graph_root); } m_skybox = core::Context::current().skybox(); reset(); } void Pathtracer::handle(messaging::message_t& message) { switch (message.name) { case tags::scene: reset(); break; default: break; } } const core::Program& Pathtracer::shader() const { return *m_render_shader; } uint32_t Pathtracer::width() const { return m_last_width; } uint32_t Pathtracer::height() const { return m_last_height; } core::Buffer& Pathtracer::traceBuffer() const { return *m_trace_buffer; } const core::TextureRGBA_32F& Pathtracer::renderTarget() const { return *m_render_target; } std::shared_ptr Pathtracer::collector() const { return m_collector; } unsigned Pathtracer::currentSample() const { return m_render_config.current_sample; } void Pathtracer::setEffectBounces(EffectType type, uint8_t bounces) { uint8_t clamped = bounces & 0xff; if (clamped != bounces) { Log_Warn << bounces << " - value larger than 15 (0xf). Result of " << clamped << " may be unintended."; } m_bounce_count_thresholds[static_cast(type)] = clamped; reset(); } uint8_t Pathtracer::getEffectBounces(EffectType type) { return m_bounce_count_thresholds[static_cast(type)]; } void Pathtracer::setLinespaceBounceThresholds(EffectType type, uint8_t bounces) { uint8_t clamped = bounces & 0xff; if (clamped != bounces) { Log_Warn << bounces << " - value larger than 15 (0xf). Result of " << clamped << " may be unintended."; } m_ls_bounce_count_thresholds[unsigned(type)] = clamped; reset(); } uint8_t Pathtracer::getLinespaceBounceThreshold(EffectType type) { return m_ls_bounce_count_thresholds[static_cast(type)]; } void Pathtracer::setSkybox(std::shared_ptr skybox) { m_skybox = skybox; reset(); } std::shared_ptr Pathtracer::getSkybox() const { return m_skybox; } void Pathtracer::setBounces(unsigned count) { reset(); m_render_config.bounces = count; } unsigned Pathtracer::getBounces() const { return m_render_config.bounces; } void Pathtracer::setMaxSamples(unsigned count) { m_render_config.max_samples = count; } unsigned Pathtracer::getMaxSamples() const { return m_render_config.max_samples; } void Pathtracer::setSamplesPerFrame(unsigned count) { m_render_config.samples_per_frame = count; } unsigned Pathtracer::getSamplesPerFrame() const { return m_render_config.samples_per_frame; } void Pathtracer::setClampDirect(float clamp_direct) { m_render_config.clamp_direct = clamp_direct; reset(); } float Pathtracer::getClampDirect() const { return m_render_config.clamp_direct; } void Pathtracer::setClampIndirect(float clamp_indirect) { m_render_config.clamp_indirect = clamp_indirect; reset(); } float Pathtracer::getClampIndirect() const { return m_render_config.clamp_indirect; } void Pathtracer::setAccuracyThreshold(float accuracy_threshold) { m_linespace_config.accuracy_threshold = accuracy_threshold; reset(); } float Pathtracer::getAccuracyThreshold() const { return m_linespace_config.accuracy_threshold; } void Pathtracer::setShadowThreshold(float shadow_threshold) { m_linespace_config.shadow_threshold = shadow_threshold; reset(); } float Pathtracer::getShadowThreshold() const { return m_linespace_config.shadow_threshold; } void Pathtracer::setDistanceThreshold(float distance_threshold) { m_linespace_config.distance_threshold = distance_threshold; reset(); } float Pathtracer::getDistanceThreshold() const { return m_linespace_config.distance_threshold; } }