agent: implement a commandline interface for render parameters except model loading

This commit is contained in:
orchestrator-bot
2026-08-09 14:43:21 +02:00
parent fb6b84a7b7
commit 325ec6bfdd
6 changed files with 519 additions and 14 deletions
+45 -9
View File
@@ -2,32 +2,63 @@
#include "cpu/CPURenderer.h"
#include "metal/MetalRenderer.h"
#include "util/ModelLoader.h"
#include "util/CLI.h"
#include <imgui.h>
int main()
int main(int argc, const char* argv[])
{
// Parse command-line arguments
CLIArgs cliArgs = parseCLIArgs(argc, argv);
// Show help if requested
if (cliArgs.help)
{
printUsage(argv[0]);
return 0;
}
// Create renderer
std::unique_ptr<Renderer> renderer = std::make_unique<MetalRenderer>();
renderer->addDirectionalLight(DirectionalLight{
.direction = glm::normalize(glm::vec3(-0.4f, -0.3f, -0.2f)),
.color = glm::vec3(1, 1, 1),
});
renderer->addPointLight(PointLight{});
// Add lights from CLI arguments, or use defaults if none specified
if (cliArgs.pointLights.empty() && cliArgs.directionalLights.empty())
{
// Use default lights when no CLI lights are specified
renderer->addDirectionalLight(DirectionalLight{
.direction = glm::normalize(glm::vec3(-0.4f, -0.3f, -0.2f)),
.color = glm::vec3(1, 1, 1),
});
renderer->addPointLight(PointLight{});
}
else
{
// Apply lights from CLI arguments
applyCLIToScene(*renderer, cliArgs);
}
renderer->addModels(ModelLoader::loadModel("../res/models/stanford-bunny.obj"),
glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 1.0f, 0.0f),
glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)));
renderer->generate();
// Initialize camera and render parameters with defaults
Camera camera = Camera{
.position = glm::vec3(2, 1, 2),
.target = glm::vec3(0, 0, 0),
.f = 0,
.A = 0,
.S_O = 6,
.f = 0.7f,
.A = 0.35f,
.S_O = 20.0f,
};
RenderParameter render = RenderParameter{
.width = 1920,
.height = 1080,
.numSamples = 10000,
};
// Apply CLI arguments (overrides defaults for specified values)
applyCLIToCamera(camera, cliArgs);
applyCLIToRenderParams(render, cliArgs);
renderer->startRender(camera, render);
while (true)
@@ -39,6 +70,11 @@ int main()
ImGui::InputFloat("Focal Length", &camera.f);
ImGui::InputFloat("Aperture", &camera.A);
ImGui::InputFloat("S_O", &camera.S_O);
ImGui::InputFloat2("Sensor Size", &camera.sensorSize.x);
ImGui::Text("Material Parameters");
ImGui::InputFloat3("Fog Emissive", &camera.fogEmm.x);
ImGui::InputFloat("Specular Coeff", &camera.ks);
ImGui::InputFloat("Ambient Coeff", &camera.ka);
ImGui::Text("Render Parameters");
ImGui::InputInt2("Dimensions", (int*)&render.width);
ImGui::InputInt("Samples", (int*)&render.numSamples);
+3 -3
View File
@@ -195,10 +195,10 @@ void MetalRenderer::render(Camera camera, RenderParameter parameter)
.f = camera.f,
.cameraForward = camera.target - camera.position,
.S_O = camera.S_O,
.fogEmm = glm::vec3(0, 0, 0),
.ks = 0,
.fogEmm = camera.fogEmm,
.ks = camera.ks,
.A = camera.A,
.ka = 0,
.ka = camera.ka,
.sensorSize = camera.sensorSize,
.width = parameter.width,
.height = parameter.height,
+403
View File
@@ -0,0 +1,403 @@
#include "CLI.h"
#include "scene/Renderer.h"
#include <iostream>
#include <string>
#include <cstdlib>
void printUsage(const char* programName)
{
std::cout << "Usage: " << programName << " [options]\n"
<< "\n"
<< "Camera Parameters:\n"
<< " --pos <x> <y> <z> Camera position (default: 2 1 2)\n"
<< " --target <x> <y> <z> Camera target (default: 0 0 0)\n"
<< " --focal <f> Focal length in meters (default: 0.7)\n"
<< " --aperture <A> Aperture radius in meters (default: 0.35)\n"
<< " --subject-dist <d> Subject distance S_O in meters (default: 20)\n"
<< " --sensor <w> <h> Sensor size in meters (default: 0.036 0.024)\n"
<< "\n"
<< "Material Parameters:\n"
<< " --fog <r> <g> <b> Fog emissive color (default: 0 0 0)\n"
<< " --specular <s> Specular coefficient (default: 0)\n"
<< " --ambient <a> Ambient coefficient (default: 0)\n"
<< "\n"
<< "Light Parameters:\n"
<< " --point-light <x> <y> <z> <r> <g> <b> [attenuation]\n"
<< " Add a point light (default attenuation: 1.0)\n"
<< " --dir-light <x> <y> <z> <r> <g> <b>\n"
<< " Add a directional light\n"
<< "\n"
<< "Render Parameters:\n"
<< " --width <w> Image width in pixels (default: 1920)\n"
<< " --height <h> Image height in pixels (default: 1080)\n"
<< " --samples <n> Number of samples per pixel (default: 10000)\n"
<< "\n"
<< "Other:\n"
<< " -h, --help Show this help message\n"
<< "\n"
<< "Examples:\n"
<< " " << programName << " --width 1280 --height 720 --samples 1000\n"
<< " " << programName << " --pos 3 2 3 --focal 0.5\n"
<< " " << programName << " --fog 0.1 0.1 0.2 --specular 0.5 --ambient 0.1\n"
<< " " << programName << " --point-light 0 5 0 1 1 1 --dir-light -1 -1 -1 1 0.8 0.6\n"
<< std::endl;
}
static bool parseFloat(const char* str, float& out)
{
try
{
out = std::stof(str);
return true;
}
catch (...)
{
return false;
}
}
static bool parseUint(const char* str, uint32_t& out)
{
try
{
out = static_cast<uint32_t>(std::stoul(str));
return true;
}
catch (...)
{
return false;
}
}
static bool parseVec3(const char* xStr, const char* yStr, const char* zStr, glm::vec3& out)
{
float x, y, z;
if (!parseFloat(xStr, x) || !parseFloat(yStr, y) || !parseFloat(zStr, z))
{
return false;
}
out = glm::vec3(x, y, z);
return true;
}
static bool parseVec2(const char* xStr, const char* yStr, glm::vec2& out)
{
float x, y;
if (!parseFloat(xStr, x) || !parseFloat(yStr, y))
{
return false;
}
out = glm::vec2(x, y);
return true;
}
CLIArgs parseCLIArgs(int argc, const char* argv[])
{
CLIArgs args;
for (int i = 1; i < argc; ++i)
{
std::string arg = argv[i];
if (arg == "-h" || arg == "--help")
{
args.help = true;
continue;
}
if (arg == "--pos" && i + 3 < argc)
{
glm::vec3 pos;
if (parseVec3(argv[i + 1], argv[i + 2], argv[i + 3], pos))
{
args.cameraPosition = pos;
i += 3;
}
else
{
std::cerr << "Error: Invalid camera position values\n";
std::exit(1);
}
continue;
}
if (arg == "--target" && i + 3 < argc)
{
glm::vec3 target;
if (parseVec3(argv[i + 1], argv[i + 2], argv[i + 3], target))
{
args.cameraTarget = target;
i += 3;
}
else
{
std::cerr << "Error: Invalid camera target values\n";
std::exit(1);
}
continue;
}
if (arg == "--focal" && i + 1 < argc)
{
float f;
if (parseFloat(argv[i + 1], f))
{
args.focalLength = f;
++i;
}
else
{
std::cerr << "Error: Invalid focal length value\n";
std::exit(1);
}
continue;
}
if (arg == "--aperture" && i + 1 < argc)
{
float A;
if (parseFloat(argv[i + 1], A))
{
args.aperture = A;
++i;
}
else
{
std::cerr << "Error: Invalid aperture value\n";
std::exit(1);
}
continue;
}
if (arg == "--subject-dist" && i + 1 < argc)
{
float d;
if (parseFloat(argv[i + 1], d))
{
args.subjectDistance = d;
++i;
}
else
{
std::cerr << "Error: Invalid subject distance value\n";
std::exit(1);
}
continue;
}
if (arg == "--sensor" && i + 2 < argc)
{
glm::vec2 sensor;
if (parseVec2(argv[i + 1], argv[i + 2], sensor))
{
args.sensorSize = sensor;
i += 2;
}
else
{
std::cerr << "Error: Invalid sensor size values\n";
std::exit(1);
}
continue;
}
if (arg == "--fog" && i + 3 < argc)
{
glm::vec3 fog;
if (parseVec3(argv[i + 1], argv[i + 2], argv[i + 3], fog))
{
args.fogEmissive = fog;
i += 3;
}
else
{
std::cerr << "Error: Invalid fog emissive values\n";
std::exit(1);
}
continue;
}
if (arg == "--specular" && i + 1 < argc)
{
float s;
if (parseFloat(argv[i + 1], s))
{
args.specularCoeff = s;
++i;
}
else
{
std::cerr << "Error: Invalid specular coefficient value\n";
std::exit(1);
}
continue;
}
if (arg == "--ambient" && i + 1 < argc)
{
float a;
if (parseFloat(argv[i + 1], a))
{
args.ambientCoeff = a;
++i;
}
else
{
std::cerr << "Error: Invalid ambient coefficient value\n";
std::exit(1);
}
continue;
}
if (arg == "--point-light" && i + 5 < argc)
{
glm::vec3 position, color;
if (parseVec3(argv[i + 1], argv[i + 2], argv[i + 3], position) &&
parseVec3(argv[i + 4], argv[i + 5], argv[i + 6], color))
{
PointLight light;
light.position = position;
light.color = color;
light.attenuation = 1.0f;
if (i + 7 < argc)
{
if (!parseFloat(argv[i + 7], light.attenuation))
{
std::cerr << "Error: Invalid point light attenuation value\n";
std::exit(1);
}
i += 7;
}
else
{
i += 6;
}
args.pointLights.push_back(light);
}
else
{
std::cerr << "Error: Invalid point light values (expected: <x> <y> <z> <r> <g> <b> [attenuation])\n";
std::exit(1);
}
continue;
}
if (arg == "--dir-light" && i + 5 < argc)
{
glm::vec3 direction, color;
if (parseVec3(argv[i + 1], argv[i + 2], argv[i + 3], direction) &&
parseVec3(argv[i + 4], argv[i + 5], argv[i + 6], color))
{
DirectionalLight light;
light.direction = direction;
light.color = color;
i += 6;
args.directionalLights.push_back(light);
}
else
{
std::cerr << "Error: Invalid directional light values (expected: <x> <y> <z> <r> <g> <b>)\n";
std::exit(1);
}
continue;
}
if (arg == "--width" && i + 1 < argc)
{
uint32_t w;
if (parseUint(argv[i + 1], w))
{
args.width = w;
++i;
}
else
{
std::cerr << "Error: Invalid width value\n";
std::exit(1);
}
continue;
}
if (arg == "--height" && i + 1 < argc)
{
uint32_t h;
if (parseUint(argv[i + 1], h))
{
args.height = h;
++i;
}
else
{
std::cerr << "Error: Invalid height value\n";
std::exit(1);
}
continue;
}
if (arg == "--samples" && i + 1 < argc)
{
uint32_t n;
if (parseUint(argv[i + 1], n))
{
args.numSamples = n;
++i;
}
else
{
std::cerr << "Error: Invalid samples value\n";
std::exit(1);
}
continue;
}
std::cerr << "Error: Unknown argument '" << arg << "'\n";
std::cerr << "Use --help for usage information\n";
std::exit(1);
}
return args;
}
void applyCLIToCamera(Camera& camera, const CLIArgs& args)
{
if (args.cameraPosition)
camera.position = *args.cameraPosition;
if (args.cameraTarget)
camera.target = *args.cameraTarget;
if (args.focalLength)
camera.f = *args.focalLength;
if (args.aperture)
camera.A = *args.aperture;
if (args.subjectDistance)
camera.S_O = *args.subjectDistance;
if (args.sensorSize)
camera.sensorSize = *args.sensorSize;
if (args.fogEmissive)
camera.fogEmm = *args.fogEmissive;
if (args.specularCoeff)
camera.ks = *args.specularCoeff;
if (args.ambientCoeff)
camera.ka = *args.ambientCoeff;
}
void applyCLIToRenderParams(RenderParameter& params, const CLIArgs& args)
{
if (args.width)
params.width = *args.width;
if (args.height)
params.height = *args.height;
if (args.numSamples)
params.numSamples = *args.numSamples;
}
void applyCLIToScene(Scene& scene, const CLIArgs& args)
{
for (const auto& light : args.pointLights)
{
scene.addPointLight(light);
}
for (const auto& light : args.directionalLights)
{
scene.addDirectionalLight(light);
}
}
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include "Camera.h"
#include "Scene.h"
#include <string>
#include <vector>
#include <optional>
#include <cstdint>
// Forward declaration - defined in scene/Renderer.h
struct RenderParameter;
/// Parsed command-line arguments for render parameters
struct CLIArgs
{
// Camera parameters
std::optional<glm::vec3> cameraPosition;
std::optional<glm::vec3> cameraTarget;
std::optional<float> focalLength;
std::optional<float> aperture;
std::optional<float> subjectDistance;
std::optional<glm::vec2> sensorSize;
std::optional<glm::vec3> fogEmissive;
std::optional<float> specularCoeff;
std::optional<float> ambientCoeff;
// Light parameters
std::vector<PointLight> pointLights;
std::vector<DirectionalLight> directionalLights;
// Render parameters
std::optional<uint32_t> width;
std::optional<uint32_t> height;
std::optional<uint32_t> numSamples;
// Flags
bool help = false;
};
/// Parse command-line arguments
/// @param argc Argument count
/// @param argv Argument values
/// @return Parsed CLIArgs structure
CLIArgs parseCLIArgs(int argc, const char* argv[]);
/// Apply CLI arguments to camera, using defaults for unspecified values
/// @param camera Camera to modify
/// @param args Parsed CLI arguments
void applyCLIToCamera(Camera& camera, const CLIArgs& args);
/// Apply CLI arguments to render parameters, using defaults for unspecified values
/// @param params RenderParameter to modify
/// @param args Parsed CLI arguments
void applyCLIToRenderParams(RenderParameter& params, const CLIArgs& args);
/// Apply CLI light arguments to scene
/// @param scene Scene to add lights to
/// @param args Parsed CLI arguments
void applyCLIToScene(Scene& scene, const CLIArgs& args);
/// Print usage/help information
void printUsage(const char* programName);
+3 -1
View File
@@ -3,6 +3,8 @@ target_sources(RayTracer
BRDF.h
BRDF.cpp
Camera.h
CLI.h
CLI.cpp
Material.h
Material.cpp
Model.h
@@ -14,4 +16,4 @@ target_sources(RayTracer
Texture.cpp
TextureLoader.h
TextureLoader.cpp
)
)
+4 -1
View File
@@ -9,4 +9,7 @@ struct Camera
float S_O = 20;
float f = 0.7;
float A = 0.35;
};
glm::vec3 fogEmm = glm::vec3(0, 0, 0);
float ks = 0;
float ka = 0;
};