#include "CLI.h" #include #include namespace AppCLI { void printHelp() { std::cout << R"( Usage: RayTracer [options] Render Parameters: -w, --width Image width (default: 1920) --height Image height (default: 1080) -s, --samples Number of samples per pixel (default: 10000) -b, --bounces Maximum ray bounces (default: 4) --bg-color Background/sky color (default: 0.05 0.05 0.1) --throughput Minimum throughput threshold (default: 0.01) -o, --output Output image file path (PGM format) Camera Parameters: --cam-pos Camera position (default: 2 1 2) --cam-target Camera target (default: 0 0 0) --cam-focal Focal length (default: 0.7) --cam-aperture Aperture size (default: 0.35) --cam-distance Subject distance S_O (default: 20) --cam-sensor Sensor size (default: 0.036 0.024) Camera Shading Parameters: --fog-emission Fog emission color (default: 0 0 0) --specular-coeff Specular coefficient ks (default: 0) --ambient-coeff Ambient coefficient ka (default: 0) Light Parameters: --dir-light Add directional light (direction + color) --point-light Add point light (position + color + attenuation) Other: -h, --help Show this help message Examples: RayTracer -w 1280 --height 720 -s 1000 RayTracer --cam-pos 3 2 3 --cam-focal 0.5 RayTracer --dir-light -0.4 -0.3 -0.2 1 1 1 RayTracer --point-light 0 5 0 1 0.5 0 0.5 RayTracer --fog-emission 0.1 0.2 0.3 --specular-coeff 0.5 --ambient-coeff 0.2 RayTracer -b 8 --bg-color 0.1 0.1 0.2 -o output.pgm )"; } CLIOptions parseArgs(int argc, const char* argv[]) { CLIOptions options; ::CLI::App app{"RayTracer - A Path Tracing Renderer"}; app.set_help_flag("-h,--help", "Show this help message"); // Render parameters app.add_option_group("Render Parameters")->add_option( "-w,--width", options.width, "Image width"); app.add_option("--height", options.height, "Image height"); app.add_option("-s,--samples", options.numSamples, "Number of samples per pixel"); app.add_option("-b,--bounces", options.maxBounces, "Maximum ray bounces"); app.add_option("--throughput", options.throughputThreshold, "Minimum throughput threshold"); app.add_option("-o,--output", options.outputFile, "Output image file path"); // Background color (3 floats) std::vector bgColor(3, 0.0f); app.add_option("--bg-color", bgColor, "Background/sky color (r g b)") ->expected(3); // Camera parameters std::vector camPos(3, 0.0f); app.add_option("--cam-pos", camPos, "Camera position (x y z)") ->expected(3); std::vector camTarget(3, 0.0f); app.add_option("--cam-target", camTarget, "Camera target (x y z)") ->expected(3); app.add_option("--cam-focal", options.camFocalLength, "Focal length"); app.add_option("--cam-aperture", options.camAperture, "Aperture size"); app.add_option("--cam-distance", options.camDistance, "Subject distance S_O"); std::vector camSensor(2, 0.0f); app.add_option("--cam-sensor", camSensor, "Sensor size (w h)") ->expected(2); // Camera shading parameters std::vector fogEmission(3, 0.0f); app.add_option("--fog-emission", fogEmission, "Fog emission color (r g b)") ->expected(3); app.add_option("--specular-coeff", options.specularCoeff, "Specular coefficient ks"); app.add_option("--ambient-coeff", options.ambientCoeff, "Ambient coefficient ka"); // Light parameters - using TakeAll policy to allow multiple occurrences std::vector> dirLightArgs; app.add_option("--dir-light", dirLightArgs, "Add directional light (dx dy dz r g b)") ->expected(6) ->multi_option_policy(::CLI::MultiOptionPolicy::TakeAll); std::vector> pointLightArgs; app.add_option("--point-light", pointLightArgs, "Add point light (x y z r g b attenuation)") ->expected(7) ->multi_option_policy(::CLI::MultiOptionPolicy::TakeAll); // Parse arguments bool helpRequested = false; try { app.parse(argc, argv); } catch (const ::CLI::CallForHelp& e) { helpRequested = true; } catch (const ::CLI::CallForAllHelp& e) { helpRequested = true; } catch (const ::CLI::ParseError& e) { std::cerr << e.what() << std::endl; return options; } // Check if help was requested if (helpRequested || app.get_help_ptr()->count() > 0) { options.showHelp = true; printHelp(); return options; } // Process background color if (bgColor.size() == 3) { options.backgroundColor = glm::vec3(bgColor[0], bgColor[1], bgColor[2]); } // Process camera position if (camPos.size() == 3) { options.camPosition = glm::vec3(camPos[0], camPos[1], camPos[2]); } // Process camera target if (camTarget.size() == 3) { options.camTarget = glm::vec3(camTarget[0], camTarget[1], camTarget[2]); } // Process camera sensor size if (camSensor.size() == 2) { options.camSensorSize = glm::vec2(camSensor[0], camSensor[1]); } // Process fog emission if (fogEmission.size() == 3) { options.fogEmission = glm::vec3(fogEmission[0], fogEmission[1], fogEmission[2]); } // Process directional lights for (const auto& args : dirLightArgs) { if (args.size() == 6) { DirectionalLight light; light.direction = glm::vec3(args[0], args[1], args[2]); light.color = glm::vec3(args[3], args[4], args[5]); options.directionalLights.push_back(light); } } // Process point lights for (const auto& args : pointLightArgs) { if (args.size() == 7) { PointLight light; light.position = glm::vec3(args[0], args[1], args[2]); light.color = glm::vec3(args[3], args[4], args[5]); light.attenuation = args[6]; options.pointLights.push_back(light); } } return options; } Camera toCamera(const CLIOptions& opts) { return Camera{ .position = opts.camPosition, .target = opts.camTarget, .sensorSize = opts.camSensorSize, .S_O = opts.camDistance, .f = opts.camFocalLength, .A = opts.camAperture, .fogEmm = opts.fogEmission, .ks = opts.specularCoeff, .ka = opts.ambientCoeff }; } RenderParameter toRenderParameter(const CLIOptions& opts) { return RenderParameter{ .width = opts.width, .height = opts.height, .numSamples = opts.numSamples }; } void applyLights(Renderer* renderer, const CLIOptions& opts) { for (const auto& dirLight : opts.directionalLights) { renderer->addDirectionalLight(dirLight); } for (const auto& pointLight : opts.pointLights) { renderer->addPointLight(pointLight); } } }