Inside the companion
Light Transport
Build a diffuse CPU path renderer and inspect its sampling, geometry, and tests.
Project files 11
Render the diffuse study
Parse the renderer options, produce an image, and report failures.
#include "diffuse.hpp"
#include <array>
#include <charconv>
#include <iostream>
#include <stdexcept>
#include <string_view>
#include <system_error>
int main(int argc,char* argv[]) {
try {
if (argc<2) throw std::runtime_error("Usage: lantern-render --trace | --constant | output.ppm [0..8 bounces]");
const std::string_view command{argv[1]};
if ((command=="--constant"||command=="--trace")&&argc!=2)
throw std::runtime_error("This diagnostic takes no extra arguments.");
if (command=="--constant") {
const std::array<lantern::Surface,1> surface{{{{7,{0,0,0},1},{0.6,0.3,0.15},{}}}};
const auto scene=lantern::DiffuseScene::build(surface,{2,1,0.5});
if (!scene) throw std::runtime_error(scene.error().message);
lantern::Rng rng{47};
const auto trace=scene->trace({{0,0,3},{0,0,-1}},rng,1);
if (!trace) throw std::runtime_error(trace.error().message);
std::cout<<"Expected linear RGB: 1.2 0.3 0.075\nObserved linear RGB: "
<<trace->radiance.x<<' '<<trace->radiance.y<<' '<<trace->radiance.z<<'\n';
return 0;
}
const auto scene=lantern::study_scene();
if (!scene) throw std::runtime_error(scene.error().message);
if (command=="--trace") {
lantern::Rng rng{47};
const auto trace=scene->trace({{-0.9,0,5},{0,0,-1}},rng,4);
if (!trace) throw std::runtime_error(trace.error().message);
for (std::size_t i=0;i<trace->count;++i) {
const auto& s=trace->steps[i];
std::cout<<"visit "<<i<<" object=";
if (s.object) std::cout<<*s.object; else std::cout<<"environment";
std::cout<<" local_cos="<<s.cosine<<" pdf="<<s.pdf
<<" beta="<<s.throughput.x<<','<<s.throughput.y<<','<<s.throughput.z
<<" L="<<s.radiance.x<<','<<s.radiance.y<<','<<s.radiance.z<<'\n';
}
const auto end=trace->end==lantern::PathEnd::environment?"environment":
trace->end==lantern::PathEnd::depth_limit?"depth limit":"absorbed";
std::cout<<"Path ended: "<<end<<" after "<<trace->count<<" visits.\n";
return 0;
}
if (command.starts_with("--")) throw std::runtime_error("Unknown diagnostic option.");
lantern::RenderSettings settings;
if (argc>3) throw std::runtime_error("Too many arguments.");
if (argc==3) {
const std::string_view text{argv[2]};
const auto [end,error]=std::from_chars(text.data(),text.data()+text.size(),settings.max_bounces);
if (error!=std::errc{}||end!=text.data()+text.size()||settings.max_bounces>8)
throw std::runtime_error("Bounce limit must be an integer in 0..8.");
}
const auto image=lantern::render_diffuse(*scene,settings);
if (!image) throw std::runtime_error(image.error().message);
if (const auto saved=lantern::write_ppm(*image,argv[1]); !saved) throw std::runtime_error(saved.error().message);
std::cout<<"Wrote "<<settings.width<<'x'<<settings.height<<", "<<settings.samples
<<" samples/pixel, at most "<<settings.max_bounces<<" scatters; seed "<<settings.seed<<".\n";
return 0;
} catch (const std::exception& error) { std::cerr<<error.what()<<'\n'; return 1; }
}
This is the file included with the course. Copy and download give you its complete source.
Taking your work offline? Download all course companions
