#include "diffuse.hpp" #include #include #include #include namespace { std::size_t checks{}; void require(bool okay,const char* message) { ++checks; if (!okay) throw std::runtime_error(message); } bool near(double a,double b) { return std::abs(a-b)<1e-10; } bool near(lantern::Vec3 a,lantern::Vec3 b) { return near(a.x,b.x)&&near(a.y,b.y)&&near(a.z,b.z); } double dot(lantern::Vec3 a,lantern::Vec3 b) { return a.x*b.x+a.y*b.y+a.z*b.z; } } int main() { try { using namespace lantern; for (const auto n:std::array{{{0,0,1},{0,0,-1},{1,0,0},{0,0.6,0.8}}}) { const auto f=frame_from_normal(n); require(f&&near(dot(f->tangent,f->normal),0)&&near(dot(f->bitangent,f->normal),0)&& near(dot(f->tangent,f->bitangent),0)&&near(dot(f->tangent,f->tangent),1)&& near(dot(f->bitangent,f->bitangent),1),"frame is orthonormal including both poles"); require(near(to_world(*f,{0,0,1}),n),"local north maps to the stated normal"); } require(!frame_from_normal({0,0,0})&&!frame_from_normal({0,0,2}),"nonunit frames rejected"); std::array surfaces{{{{7,{0,0,0},1},{0.6,0.3,0.15},{}}}}; const auto constant=DiffuseScene::build(surfaces,{2,1,0.5}); require(constant.has_value(),"constant-environment scene builds"); for (std::uint64_t seed=0;seed<64;++seed) { Rng rng{seed}; const auto t=constant->trace({{0,0,3},{0,0,-1}},rng,1); require(t&&near(t->radiance,{1.2,0.3,0.075})&&t->count==2&&t->end==PathEnd::environment, "every one-bounce sample equals reflectance times constant environment"); } Rng rng{47}; const auto zero=constant->trace({{0,0,3},{0,0,-1}},rng,0); require(zero&&near(zero->radiance,{})&&zero->count==1&&zero->end==PathEnd::depth_limit,"zero scatters at nonemissive surface is black"); const auto miss=constant->trace({{0,0,3},{0,0,1}},rng,0); require(miss&&near(miss->radiance,{2,1,0.5}),"zero scatter miss still sees environment"); require(!constant->trace({{0,0,3},{0,0,-2}},rng,1),"invalid initial ray rejected"); require(!constant->trace({{0,0,3},{0,0,-1}},rng,9),"path work cap rejected"); surfaces[0].sphere.radius=8; surfaces[0].reflectance={0.5,0.5,0.5}; surfaces[0].emission={0.1,0.2,0.3}; const auto enclosure=DiffuseScene::build(surfaces,{16,16,16}); const auto bounded=enclosure->trace({{0,0,0},{0,0,1}},rng,4); require(bounded&&bounded->count==5&&bounded->end==PathEnd::depth_limit&& near(bounded->radiance,{0.19375,0.3875,0.58125}),"enclosure adds five known geometric-series emission terms and no environment"); const auto maximum=enclosure->trace({{0,0,0},{0,0,1}},rng,8); require(maximum&&maximum->count==9&&maximum->end==PathEnd::depth_limit&& near(maximum->radiance,{0.199609375,0.39921875,0.598828125}),"maximum eight scatters use exactly nine owned visits and known emission terms"); surfaces[0].reflectance={0,0,0}; const auto emitter=DiffuseScene::build(surfaces,{}); const auto absorbed=emitter->trace({{0,0,0},{0,0,1}},rng,4); require(absorbed&&near(absorbed->radiance,{0.1,0.2,0.3})&&absorbed->count==1&&absorbed->end==PathEnd::absorbed,"black emitter adds emission before termination"); surfaces[0].reflectance.x=1.1; require(!DiffuseScene::build(surfaces,{}),"energy-gaining reflectance rejected"); require(!DiffuseScene::build({}, {std::numeric_limits::infinity(),0,0}),"nonfinite environment rejected"); const auto empty=DiffuseScene::build({}, {0.1,0.2,0.3}); const auto image=render_diffuse(*empty,{4,3,2,1,47}); require(image&&image->linear.size()==12,"owned image has validated extent"); for (const auto color:image->linear) require(near(color,{0.1,0.2,0.3}),"every empty-scene pixel has analytically known radiance"); require(!render_diffuse(*empty,{0,3,2,1,47})&&!render_diffuse(*empty,{512,512,128,1,47}),"invalid dimensions and camera-ray budget rejected"); const auto scene=study_scene(); require(scene.has_value(),"study scene is valid"); const auto first=render_diffuse(*scene,{8,6,2,4,47}), second=render_diffuse(*scene,{8,6,2,4,47}); bool equal=first&&second; if (equal) for (std::size_t i=0;ilinear.size();++i) equal &= near(first->linear[i],second->linear[i]); require(equal,"fixed image/settings replay on this implementation"); std::cout<