From a36de40bfcc7608cb228f55d93d7bb532bc3c267 Mon Sep 17 00:00:00 2001 From: "T.C. Chang" Date: Wed, 4 Sep 2019 02:58:45 +0800 Subject: [PATCH] [bugfix] not processing non-updated region of frame; otherwise, other parts of the frame got tone-mapped multiple times in asynchronous render result queries --- Engine/Include/ph_core.h | 5 +- Engine/Source/Core/Engine.cpp | 12 +- Engine/Source/Core/Engine.h | 4 +- Engine/Source/Core/Filmic/HdrRgbFilm.cpp | 1 + Engine/Source/Core/Renderer/Renderer.cpp | 3 +- .../Source/Frame/Operator/JRToneMapping.cpp | 7 +- Engine/Source/Frame/Operator/JRToneMapping.h | 2 + PhotonCLI/ProcessedArguments.cpp | 126 +------------- PhotonCLI/ProcessedArguments.h | 159 ++++++++++++++++-- PhotonCLI/StaticImageRenderer.cpp | 52 +++--- PhotonCLI/StaticImageRenderer.h | 12 +- PhotonCLI/main.cpp | 16 +- 12 files changed, 224 insertions(+), 175 deletions(-) diff --git a/Engine/Include/ph_core.h b/Engine/Include/ph_core.h index ac6da8ef7..35b06cb34 100644 --- a/Engine/Include/ph_core.h +++ b/Engine/Include/ph_core.h @@ -10,8 +10,8 @@ To correctly use Photon-v2 API, please read the following notes: should be called on the same thread. - phCreate/Delete() and phAsync() functions can be used in a multithreaded - environment, namely, they are thread-safe. An exception would be that create & - delete should be on the same thread. + environment, namely, they are thread-safe. An exception would be that create + and delete should be called from the same thread. - Resources created by phCreate() cannot be manipulated concurrently. Any function requiring some resource ID inputs (except phAsync() functions) is @@ -150,6 +150,7 @@ extern PH_API void phGetObservableRenderData( extern PH_API void phDeleteEngine(PHuint64 engineId); extern PH_API void phSetWorkingDirectory(PHuint64 engineId, const PHchar* workingDirectory); +// REFACTOR: rename aquire to retrieve extern PH_API void phAquireFrame(PHuint64 engineId, PHuint64 channelIndex, PHuint64 frameId); extern PH_API void phAquireFrameRaw(PHuint64 engineId, PHuint64 channelIndex, PHuint64 frameId); diff --git a/Engine/Source/Core/Engine.cpp b/Engine/Source/Core/Engine.cpp index 967877342..a644a4b65 100644 --- a/Engine/Source/Core/Engine.cpp +++ b/Engine/Source/Core/Engine.cpp @@ -30,8 +30,8 @@ void Engine::update() m_data.update(0.0_r); // HACK - m_id = m_frameProcessor.addPipeline(); - m_frameProcessor.getPipeline(m_id)->appendOperator(std::make_unique()); + //m_id = m_frameProcessor.addPipeline(); + //m_frameProcessor.getPipeline(m_id)->appendOperator(std::make_unique()); /*m_filmSet.setProcessor(EAttribute::LIGHT_ENERGY, processor); m_filmSet.setProcessor(EAttribute::NORMAL, processor);*/ @@ -55,7 +55,9 @@ void Engine::retrieveFrame( if(applyPostProcessing) { - m_frameProcessor.process(out_frame, m_id); + //m_frameProcessor.process(out_frame, m_id); + // HACK + JRToneMapping().operate(out_frame); } } @@ -91,7 +93,9 @@ void Engine::asyncPeekFrame( if(applyPostProcessing) { - m_frameProcessor.process(out_frame, m_id); + //m_frameProcessor.process(out_frame, m_id); + // HACK + JRToneMapping().operateLocal(out_frame, TAABB2D(region)); } } diff --git a/Engine/Source/Core/Engine.h b/Engine/Source/Core/Engine.h index 6f2545576..61720b1e3 100644 --- a/Engine/Source/Core/Engine.h +++ b/Engine/Source/Core/Engine.h @@ -56,9 +56,9 @@ class Engine final std::shared_ptr m_renderer; uint32 m_numRenderThreads; - FrameProcessor m_frameProcessor; + //FrameProcessor m_frameProcessor; // TODO: associate each attribute with a pipeline - FrameProcessor::PipelineId m_id; + //FrameProcessor::PipelineId m_id; }; // In-header Implementations: diff --git a/Engine/Source/Core/Filmic/HdrRgbFilm.cpp b/Engine/Source/Core/Filmic/HdrRgbFilm.cpp index 6091b006e..a5b1ad110 100644 --- a/Engine/Source/Core/Filmic/HdrRgbFilm.cpp +++ b/Engine/Source/Core/Filmic/HdrRgbFilm.cpp @@ -153,6 +153,7 @@ void HdrRgbFilm::developRegion(HdrRgbFrame& out_frame, const TAABB2D& reg float64 reciWeight; std::size_t fx, fy, filmIndex; + // FIXME: we should iterate in frameIndexBound only for(int64 y = 0; y < getActualResPx().y; y++) { for(int64 x = 0; x < getActualResPx().x; x++) diff --git a/Engine/Source/Core/Renderer/Renderer.cpp b/Engine/Source/Core/Renderer/Renderer.cpp index b1f170954..277f737b1 100644 --- a/Engine/Source/Core/Renderer/Renderer.cpp +++ b/Engine/Source/Core/Renderer/Renderer.cpp @@ -123,6 +123,7 @@ SdlTypeInfo Renderer::ciTypeInfo() return SdlTypeInfo(ETypeCategory::REF_RENDERER, "renderer"); } -void Renderer::ciRegister(CommandRegister& cmdRegister) {} +void Renderer::ciRegister(CommandRegister& cmdRegister) +{} }// end namespace ph \ No newline at end of file diff --git a/Engine/Source/Frame/Operator/JRToneMapping.cpp b/Engine/Source/Frame/Operator/JRToneMapping.cpp index f0fae4457..276c592a0 100644 --- a/Engine/Source/Frame/Operator/JRToneMapping.cpp +++ b/Engine/Source/Frame/Operator/JRToneMapping.cpp @@ -14,7 +14,12 @@ JRToneMapping::JRToneMapping() : void JRToneMapping::operate(HdrRgbFrame& frame) const { - frame.forEachPixel([this](const HdrRgbFrame::Pixel& pixel) + operateLocal(frame, {{0, 0}, frame.getSizePx()}); +} + +void JRToneMapping::operateLocal(HdrRgbFrame& frame, const TAABB2D& region) const +{ + frame.forEachPixel(region, [this](const HdrRgbFrame::Pixel& pixel) { HdrRgbFrame::Pixel color = pixel; color.mulLocal(m_exposure); diff --git a/Engine/Source/Frame/Operator/JRToneMapping.h b/Engine/Source/Frame/Operator/JRToneMapping.h index 9cf3e2e49..080604fbc 100644 --- a/Engine/Source/Frame/Operator/JRToneMapping.h +++ b/Engine/Source/Frame/Operator/JRToneMapping.h @@ -2,6 +2,7 @@ #include "Frame/Operator/FrameOperator.h" #include "Common/primitive_type.h" +#include "Core/Bound/TAABB2D.h" namespace ph { @@ -21,6 +22,7 @@ class JRToneMapping : public FrameOperator void operate(HdrRgbFrame& frame) const override; + void operateLocal(HdrRgbFrame& frame, const TAABB2D& region) const; void setExposure(real exposure); private: diff --git a/PhotonCLI/ProcessedArguments.cpp b/PhotonCLI/ProcessedArguments.cpp index 90ccdd13b..64a378e8a 100644 --- a/PhotonCLI/ProcessedArguments.cpp +++ b/PhotonCLI/ProcessedArguments.cpp @@ -7,19 +7,14 @@ PH_CLI_NAMESPACE_BEGIN -namespace -{ - constexpr std::string_view DEFAULT_SCENE_FILE_PATH = "./scene.p2"; - constexpr std::string_view DEFAULT_DEFAULT_IMAGE_FILE_PATH = "./rendered_scene.png"; -} - ProcessedArguments::ProcessedArguments(int argc, char* argv[]) : ProcessedArguments(CommandLineArguments(argc, argv)) {} ProcessedArguments::ProcessedArguments(CommandLineArguments arguments) : - m_sceneFilePath (DEFAULT_SCENE_FILE_PATH), - m_imageFilePath (DEFAULT_DEFAULT_IMAGE_FILE_PATH), + m_sceneFilePath ("./scene.p2"), + m_imageOutputPath ("./rendered_scene"), + m_imageFileFormat ("png"), m_numRenderThreads (1), m_isPostProcessRequested (true), m_isHelpMessageRequested (false), @@ -28,9 +23,9 @@ ProcessedArguments::ProcessedArguments(CommandLineArguments arguments) : m_wildcardFinish (""), m_outputPercentageProgress(std::numeric_limits::max()), + // HACK m_isFrameDiagRequested(false) { - std::string imageFileFormat; while(!arguments.isEmpty()) { const std::string argument = arguments.retrieveOne(); @@ -41,11 +36,11 @@ ProcessedArguments::ProcessedArguments(CommandLineArguments arguments) : } else if(argument == "-o") { - m_imageFilePath = arguments.retrieveOne(); + m_imageOutputPath = arguments.retrieveOne(); } else if(argument == "-of") { - imageFileFormat = arguments.retrieveOne(); + m_imageFileFormat = arguments.retrieveOne(); } else if(argument == "-t") { @@ -113,115 +108,6 @@ ProcessedArguments::ProcessedArguments(CommandLineArguments arguments) : std::cerr << "warning: unknown command <" << argument << "> specified, ignoring" << std::endl; } }// end while more arguments exist - - // possibly override image format if a more specific order is given - if(!imageFileFormat.empty()) - { - m_imageFilePath += "." + imageFileFormat; - } - - // TODO: check arguments -} - -std::string ProcessedArguments::getSceneFilePath() const -{ - return m_sceneFilePath; -} - -std::string ProcessedArguments::getImageFilePath() const -{ - return m_imageFilePath; -} - -int ProcessedArguments::getNumRenderThreads() const -{ - return m_numRenderThreads; -} - -bool ProcessedArguments::isPostProcessRequested() const -{ - return m_isPostProcessRequested; -} - -bool ProcessedArguments::isHelpMessageRequested() const -{ - return m_isHelpMessageRequested; -} - -bool ProcessedArguments::isImageSeriesRequested() const -{ - return m_isImageSeriesRequested; -} - -std::string ProcessedArguments::wildcardStart() const -{ - return m_wildcardStart; -} - -std::string ProcessedArguments::wildcardFinish() const -{ - return m_wildcardFinish; -} - -float ProcessedArguments::getOutputPercentageProgress() const -{ - return m_outputPercentageProgress; -} - -void ProcessedArguments::printHelpMessage() -{ - std::cout << R"( -=============================================================================== --s - -Specify path to scene file. To render an image series, you can specify -"myScene*.p2" as where * is a wildcard for any string (--series is -required in this case). (default path: "./scene.p2") -=============================================================================== --o - -Specify image output path. This should be a filename for single image and a -directory for image series. (default path: "./rendered_scene.png") -=============================================================================== --of - -Specify the format of output image. Supported formats are: png, jpg, bmp, tga, -hdr, exr. If this option is omitted, format is deduced from filename extension. -=============================================================================== --t - -Set number of threads used for rendering. (default: single thread) -=============================================================================== --p - -Output an intermediate image whenever the specified has passed, -e.g., write 2.3% to output whenever the rendering has progressed 2.3 percent; -or write 7s to output every 7 seconds. Specify as true will -make the program overwrite previous intermediate image; false for the -opposite effect. -=============================================================================== ---raw - -Do not perform any post-processing. (default: perform post-processing) -=============================================================================== ---help - -Print this help message then exit. -=============================================================================== ---series - -Render an image series. The order for rendering will be lexicographical order -of the wildcarded string. Currently only .png is supported. -=============================================================================== ---start <*> - -Render image series starting from a specific wildcarded string. -=============================================================================== ---finish <*> - -Render image series until a specific wildcarded string is matched. (inclusive) -=============================================================================== - )" << std::endl; } PH_CLI_NAMESPACE_END diff --git a/PhotonCLI/ProcessedArguments.h b/PhotonCLI/ProcessedArguments.h index bfb6f2fac..6ac056a8e 100644 --- a/PhotonCLI/ProcessedArguments.h +++ b/PhotonCLI/ProcessedArguments.h @@ -5,6 +5,7 @@ #include #include +#include PH_CLI_NAMESPACE_BEGIN @@ -17,21 +18,28 @@ class ProcessedArguments ProcessedArguments(int argc, char* argv[]); explicit ProcessedArguments(CommandLineArguments arguments); - std::string getSceneFilePath() const; - std::string getImageFilePath() const; - int getNumRenderThreads() const; - bool isPostProcessRequested() const; - bool isHelpMessageRequested() const; - bool isImageSeriesRequested() const; - std::string wildcardStart() const; - std::string wildcardFinish() const; + std::string getSceneFilePath() const; + std::string getImageOutputPath() const; + std::string getImageFilePath() const; + std::string getImageFileFormat() const; + int getNumRenderThreads() const; + bool isPostProcessRequested() const; + bool isHelpMessageRequested() const; + bool isImageSeriesRequested() const; + std::string wildcardStart() const; + std::string wildcardFinish() const; float getOutputPercentageProgress() const; + void setSceneFilePath(const std::string& sceneFilePath); + void setImageOutputPath(const std::string& imageOutputPath); + + // TODO: other setters + + // HACK bool isFrameDiagRequested() const { return m_isFrameDiagRequested; } - std::string getFramePathA() const { return m_framePathA; @@ -43,8 +51,8 @@ class ProcessedArguments private: std::string m_sceneFilePath; - std::string m_imageFilePath; - std::string m_intermediateImageFileFormat; + std::string m_imageOutputPath; + std::string m_imageFileFormat; int m_numRenderThreads;// FIXME: use unsigned integer bool m_isPostProcessRequested; bool m_isHelpMessageRequested; @@ -53,9 +61,138 @@ class ProcessedArguments std::string m_wildcardFinish; float m_outputPercentageProgress; + // HACK bool m_isFrameDiagRequested; std::string m_framePathA; std::string m_framePathB; }; +// In-header Implementations: + +inline std::string ProcessedArguments::getSceneFilePath() const +{ + return m_sceneFilePath; +} + +inline std::string ProcessedArguments::getImageOutputPath() const +{ + return m_imageOutputPath; +} + +inline std::string ProcessedArguments::getImageFilePath() const +{ + return m_imageOutputPath + "." + m_imageFileFormat; +} + +inline std::string ProcessedArguments::getImageFileFormat() const +{ + return m_imageFileFormat; +} + +inline int ProcessedArguments::getNumRenderThreads() const +{ + return m_numRenderThreads; +} + +inline bool ProcessedArguments::isPostProcessRequested() const +{ + return m_isPostProcessRequested; +} + +inline bool ProcessedArguments::isHelpMessageRequested() const +{ + return m_isHelpMessageRequested; +} + +inline bool ProcessedArguments::isImageSeriesRequested() const +{ + return m_isImageSeriesRequested; +} + +inline std::string ProcessedArguments::wildcardStart() const +{ + return m_wildcardStart; +} + +inline std::string ProcessedArguments::wildcardFinish() const +{ + return m_wildcardFinish; +} + +inline float ProcessedArguments::getOutputPercentageProgress() const +{ + return m_outputPercentageProgress; +} + +inline void ProcessedArguments::setSceneFilePath(const std::string& sceneFilePath) +{ + m_sceneFilePath = sceneFilePath; +} + +inline void ProcessedArguments::setImageOutputPath(const std::string& imageOutputPath) +{ + m_imageOutputPath = imageOutputPath; +} + +inline void ProcessedArguments::printHelpMessage() +{ + std::cout << R"( +=============================================================================== +-s + +Specify path to scene file. To render an image series, you can specify +"myScene*.p2" as where * is a wildcard for any string (--series is +required in this case). +(default path: "./scene.p2") +=============================================================================== +-o + +Specify image output path. This should be a filename (without extension) for +single image or a directory for image series. +(default path: "./rendered_scene") +=============================================================================== +-of + +Specify the format of output image. Supported formats are: png, jpg, bmp, tga, +hdr, exr. +(default format: png) +=============================================================================== +-t + +Set number of threads used for rendering. +(default: single thread) +=============================================================================== +-p + +Output an intermediate image whenever the specified has passed, +e.g., write 2.3% to output whenever the rendering has progressed 2.3 percent; +or write 7s to output every 7 seconds. Specify as true will +make the program overwrite previous intermediate image; false for the +opposite effect. +=============================================================================== +--raw + +Do not perform any post-processing. +(default: perform post-processing) +=============================================================================== +--help + +Print this help message then exit. +=============================================================================== +--series + +Render an image series. The order for rendering will be lexicographical order +of the wildcarded string. Currently only .png is supported. +=============================================================================== +--start <*> + +Render image series starting from a specific wildcarded string. +=============================================================================== +--finish <*> + +Render image series until a specific wildcarded string is matched. (inclusive) +=============================================================================== + )" << std::endl; +} + PH_CLI_NAMESPACE_END \ No newline at end of file diff --git a/PhotonCLI/StaticImageRenderer.cpp b/PhotonCLI/StaticImageRenderer.cpp index e0e15a631..c7667468e 100644 --- a/PhotonCLI/StaticImageRenderer.cpp +++ b/PhotonCLI/StaticImageRenderer.cpp @@ -19,15 +19,9 @@ PH_CLI_NAMESPACE_BEGIN StaticImageRenderer::StaticImageRenderer(const ProcessedArguments& args) : m_engineId(0), - m_sceneFilePath(), - m_imageFilePath(args.getImageFilePath()), - m_numRenderThreads(args.getNumRenderThreads()), - m_isPostProcessRequested(args.isPostProcessRequested()), - m_outputPercentageProgress(args.getOutputPercentageProgress()) + m_args(args) { - phCreateEngine(&m_engineId, static_cast(m_numRenderThreads)); - - setSceneFilePath(args.getSceneFilePath()); + phCreateEngine(&m_engineId, static_cast(args.getNumRenderThreads())); } StaticImageRenderer::~StaticImageRenderer() @@ -35,8 +29,10 @@ StaticImageRenderer::~StaticImageRenderer() phDeleteEngine(m_engineId); } -void StaticImageRenderer::render() const +void StaticImageRenderer::render() { + setSceneFilePath(m_args.getSceneFilePath()); + if(!loadCommandsFromSceneFile()) { return; @@ -76,19 +72,34 @@ void StaticImageRenderer::render() const << "samples/sec: " << samplesPerSecond << std::endl; } - if(currentProgress - lastOutputProgress > m_outputPercentageProgress) + if(currentProgress - lastOutputProgress > m_args.getOutputPercentageProgress()) { PHuint32 qx, qy, qw, qh; int regionStatus = phAsyncPollUpdatedFrameRegion(m_engineId, &qx, &qy, &qw, &qh); if(regionStatus != PH_FILM_REGION_STATUS_INVALID) { + std::cerr << "x=" << qx << ", y=" << qy << ", qw=" << qw << ", qh=" << qh << std::endl; phAsyncPeekFrame(m_engineId, 0, qx, qy, qw, qh, queryFrameId); + const auto intermediateImageFilePath = + m_args.getImageOutputPath() + + "_intermediate_" + std::to_string(currentProgress) + "%" + + "." + m_args.getImageFileFormat(); phSaveFrame( queryFrameId, - (m_imageFilePath + "_" + std::to_string(currentProgress) + "%.png").c_str()); + intermediateImageFilePath.c_str()); } + /*phAsyncPeekFrame(m_engineId, 0, 0, 0, filmWpx, filmHpx, queryFrameId); + + const auto intermediateImageFilePath = + m_args.getImageOutputPath() + + "_intermediate_" + std::to_string(currentProgress) + "%" + + "." + m_args.getImageFileFormat(); + phSaveFrame( + queryFrameId, + intermediateImageFilePath.c_str());*/ + lastOutputProgress = currentProgress; } @@ -102,7 +113,7 @@ void StaticImageRenderer::render() const PHuint64 frameId; phCreateFrame(&frameId, filmWpx, filmHpx); - if(m_isPostProcessRequested) + if(m_args.isPostProcessRequested()) { phAquireFrame(m_engineId, 0, frameId); } @@ -111,7 +122,7 @@ void StaticImageRenderer::render() const phAquireFrameRaw(m_engineId, 0, frameId); } - save_frame_with_fail_safe(frameId, m_imageFilePath); + save_frame_with_fail_safe(frameId, m_args.getImageFilePath()); phDeleteFrame(frameId); @@ -120,8 +131,9 @@ void StaticImageRenderer::render() const void StaticImageRenderer::setSceneFilePath(const std::string& path) { - m_sceneFilePath = path; + m_args.setSceneFilePath(path); +// REFACTOR: use a get directory-from-file-path function #ifndef __APPLE__ namespace fs = std::experimental::filesystem; const std::string sceneDirectory = fs::path(path).parent_path().string(); @@ -140,23 +152,25 @@ void StaticImageRenderer::setSceneFilePath(const std::string& path) #endif } -void StaticImageRenderer::setImageFilePath(const std::string& path) +void StaticImageRenderer::setImageOutputPath(const std::string& path) { - m_imageFilePath = path; + m_args.setImageOutputPath(path); } bool StaticImageRenderer::loadCommandsFromSceneFile() const { + const auto sceneFilePath = m_args.getSceneFilePath(); + std::ifstream sceneFile; - sceneFile.open(m_sceneFilePath, std::ios::in); + sceneFile.open(sceneFilePath, std::ios::in); if(!sceneFile.is_open()) { - std::cerr << "warning: scene file <" << m_sceneFilePath << "> opening failed" << std::endl; + std::cerr << "warning: scene file <" << sceneFilePath << "> opening failed" << std::endl; return false; } else { - std::cerr << "loading scene file <" << m_sceneFilePath << ">" << std::endl; + std::cerr << "loading scene file <" << sceneFilePath << ">" << std::endl; std::string lineCommand; while(sceneFile.good()) diff --git a/PhotonCLI/StaticImageRenderer.h b/PhotonCLI/StaticImageRenderer.h index 97ea288f2..44468a88b 100644 --- a/PhotonCLI/StaticImageRenderer.h +++ b/PhotonCLI/StaticImageRenderer.h @@ -15,18 +15,14 @@ class StaticImageRenderer final explicit StaticImageRenderer(const ProcessedArguments& args); ~StaticImageRenderer(); - void render() const; + void render(); void setSceneFilePath(const std::string& path); - void setImageFilePath(const std::string& path); + void setImageOutputPath(const std::string& path); private: - PHuint64 m_engineId; - std::string m_sceneFilePath; - std::string m_imageFilePath; - int m_numRenderThreads; - bool m_isPostProcessRequested; - float m_outputPercentageProgress; + PHuint64 m_engineId; + ProcessedArguments m_args; bool loadCommandsFromSceneFile() const; }; diff --git a/PhotonCLI/main.cpp b/PhotonCLI/main.cpp index 07112d7e3..ec501e9db 100644 --- a/PhotonCLI/main.cpp +++ b/PhotonCLI/main.cpp @@ -48,6 +48,7 @@ int main(int argc, char* argv[]) // begin engine operations + // HACK if(args.isFrameDiagRequested()) { std::cout << "begin frame diag" << std::endl; @@ -104,6 +105,7 @@ void renderImageSeries(const ProcessedArguments& args) const std::string sceneFilenameStar = fs::path(args.getSceneFilePath()).filename().string(); const std::string sceneFilenameBase = sceneFilenameStar.substr(0, sceneFilenameStar.find('*')); + // REFACTOR: get rid of pair, name the parameters typedef std::pair StringPair; std::vector sceneFiles; for(const auto& directory : fs::directory_iterator(sceneDirectory)) @@ -121,14 +123,14 @@ void renderImageSeries(const ProcessedArguments& args) continue; } - const std::size_t stringSize = filename.size() - filenameBase.size() - 3; - const std::string string = filename.substr(filenameBase.size(), stringSize); + const std::size_t wildcardedStringSize = filename.size() - filenameBase.size() - 3; + const std::string wildcardedString = filename.substr(filenameBase.size(), wildcardedStringSize); - sceneFiles.push_back({string, path.string()}); + sceneFiles.push_back({wildcardedString, path.string()}); } std::sort(sceneFiles.begin(), sceneFiles.end()); - fs::create_directories(args.getImageFilePath()); + fs::create_directories(args.getImageOutputPath()); auto sceneBegin = std::find_if(sceneFiles.begin(), sceneFiles.end(), [&args](const std::pair& sceneFile) @@ -157,9 +159,9 @@ void renderImageSeries(const ProcessedArguments& args) StaticImageRenderer renderer(args); renderer.setSceneFilePath(sceneFile.second); - const std::string imageFilename = sceneFile.first + ".png"; - const fs::path imageFilePath = fs::path(args.getImageFilePath()) / imageFilename; - renderer.setImageFilePath(imageFilePath.string()); + const std::string imageFilename = sceneFile.first; + const fs::path imageFilePath = fs::path(args.getImageOutputPath()) / imageFilename; + renderer.setImageOutputPath(imageFilePath.string()); renderer.render(); }