{"slug": "show-hn-i-wrote-a-c-ray-tracer-from-scratch-without-ai", "title": "Show HN: I wrote a C++ ray tracer from scratch without AI", "summary": "A developer released Luz, a C++20 path tracer built from scratch with zero third-party dependencies, supporting Monte Carlo path tracing, global illumination, BVH acceleration, and more. The open-source project includes a Blender exporter and runs on macOS, Linux, and Windows.", "body_md": "**Luz** is a C++20 Path Tracer developed from scratch with zero third-party dependencies.\n\nIt supports Monte Carlo path tracing, global illumination, BVH acceleration, adaptive sampling, denoising, atmospheric scattering, custom scene files, and a Blender-to-Luz exporter.\n\n- Monte Carlo path tracing\n- Global illumination\n- Multithreaded CPU rendering\n- Adaptive sampling\n- Denoiser (NFOR-style)\n- Spheres, planes, rectangles, triangles, cubes, volumes, and OBJ meshes\n- Lambertian, metal, dielectric, emissive, and isotropic materials\n- Area, point, sphere and directional lights\n- Custom\n`.luz`\n\nscene files - .blend to .luz converter\n- Fully customizable render parameters via CLI or scene file\n- Importance sampling with PDFs\n- BVH acceleration, including packed mesh BVHs with binned SAH construction and near-first traversal\n- Atmospheric simulation w/ scattering\n- Depth of field, antialiasing, exposure, contrast, tone mapping, gamma correction, and bloom\n- BMP and TIFF output\n- Deterministic benchmark harness with render, denoise, post-process, and score breakdowns\n\n- C++20 compiler\n- Make or CMake 3.16+\n- Python 3, only for optional tools/scripts\n\nBuild with the Makefile:\n\n```\nmake\n```\n\nRender a bundled example scene:\n\n```\n./Luz --file examples/scenes/blender_monkey.luz --samples 50 --resolution 300x300\n```\n\nThe default output is `render.bmp`\n\n. Scene files can set `outputfilename=...`\n\n, and the CLI can override common render settings.\n\nRun the test suite:\n\n```\nmake test\n```\n\nLuz includes deterministic benchmarks for render, denoise, post-process, and overall score comparisons.\n\n```\nmake benchmark BENCH_CPUS=1 BENCH_THREADS=1 > before.csv\nmake benchmark BENCH_CPUS=1 BENCH_THREADS=1 > after.csv\nmake benchmark-compare BEFORE=before.csv AFTER=after.csv\n```\n\nFor details, see [ docs/benchmarks.md](/themartiano/luz/blob/main/docs/benchmarks.md).\n\nA CMake build is also available:\n\n```\ncmake -S . -B build\ncmake --build build\nctest --test-dir build\n```\n\nSupported platforms:\n\n- macOS\n- Linux\n- Windows\n\nOn macOS and Linux, the Makefile is the primary path. On Windows, use CMake with MSVC or the MinGW-based Makefile target:\n\n```\nmake windows\n```\n\nWSL is also supported as a Linux build environment.\n\nRelease builds are tuned for the machine doing the build by default. The\nMakefile enables `-O3`\n\n, native CPU tuning with `-march=native`\n\n, and link-time\noptimization with `-flto`\n\n. It also enables a fast floating-point mode where the\ncompiler/platform supports it. CMake uses the same release intent: `-O3`\n\n, native\nCPU tuning, and interprocedural optimization/LTO when supported.\n\nThese defaults produce faster local renders, but binaries built with\n`-march=native`\n\nmay not run on older or different CPUs, and LTO can expose\ntoolchain-specific linker issues. If you hit an illegal-instruction crash,\nlinker error, or need a more portable binary, disable the aggressive options and\nrebuild from clean objects:\n\n```\nmake clean\nmake NATIVE=0 LTO=0\n```\n\nFor CMake builds, configure with the optimization toggles off:\n\n```\ncmake -S . -B build -DLUZ_NATIVE_OPTIMIZATIONS=OFF -DLUZ_ENABLE_LTO=OFF\ncmake --build build --clean-first\nUsage: ./Luz [options]\n\n  -f, --file PATH             Load a .luz scene file\n  -r, --resolution WxH        Override render resolution\n  -s, --samples N             Override samples per pixel\n  --adaptive [true|false]     Enable adaptive per-pixel sampling\n  --no-adaptive               Disable adaptive sampling\n  --adaptive-min-samples N    Minimum samples before adaptive stopping\n  --adaptive-threshold F      Relative adaptive noise threshold\n  --adaptive-check-interval N Adaptive convergence check interval\n  -mlb, --maxLightBounces N   Override maximum light bounces\n      --max-light-bounces N   Alias for --maxLightBounces\n  -t, --threads N             Render with N worker threads\n  --seed N                    Seed random sampling\n  --gamma true|false          Toggle gamma correction\n  -tm, --tonemapping true|false  Toggle tone mapping\n  --bloom true|false          Toggle bloom\n  --exposure EV              Exposure compensation in stops\n  --contrast F               Display contrast multiplier\n  --denoise [true|false]      Write a denoised companion render\n  --no-denoise                Disable denoising\n  -o, --output PATH           Override render output path\n  --denoise-output PATH       Override denoised output path\n  --render-times              Write renderTime.bmp\n  --benchmark                 Run the built-in benchmark scene\n  --benchmark-case NAME       Benchmark case: default, many-objects, mesh-bvh, diffuse, postprocess, atmosphere, lights, emissive-geometry, primitives-materials, volumes, obj-mesh\n```\n\n`--adaptive`\n\ntreats `--samples`\n\nas the maximum samples per pixel. Each pixel\nuses a progressive per-pixel sample sequence, renders at least\n`--adaptive-min-samples`\n\n, then periodically checks luminance and RGB confidence\nintervals. Very dark pixels use a conservative minimum before they can stop, so\nrare light contributions are less likely to be mistaken for converged black.\n\nLower thresholds keep more detail and cost more time. For final renders, start with a high max sample count and tune with values like:\n\n```\n./Luz --file exports/stormtroopers.luz --samples 4096 --adaptive --adaptive-min-samples 512 --adaptive-check-interval 64 --adaptive-threshold 0.005 --denoise\n```\n\n`--denoise`\n\nenables Luz's NFOR-style feature-buffer denoiser and writes a\nseparate companion image. By default, `render.bmp`\n\nbecomes\n`render_denoised.bmp`\n\n; use `--denoise-output PATH`\n\nto choose the exact path.\n\nThe denoiser has no hard minimum resolution or sample count, but it needs enough signal to estimate useful color and feature statistics. One sample per pixel is mainly a stress test: there is no per-pixel variance estimate, so the denoised image can look almost unchanged or can smooth the wrong details. Use at least a few samples per pixel for previews, and prefer roughly 16+ samples per pixel when judging denoiser quality. Very low resolutions also make evaluation misleading because each local filter window covers too much of the image.\n\nExample scenes live in `examples/scenes/`\n\n. Mesh assets live in `assets/objects/`\n\n. The scene-file format is documented in [ docs/scene-files.md](/themartiano/luz/blob/main/docs/scene-files.md).\n\nObject paths in `.luz`\n\nfiles are resolved relative to the scene file first, then relative to the current working directory, then under `assets/objects/`\n\n. This means `examples/scenes/blender_monkey.luz`\n\ncan reference `../../assets/objects/blender_monkey.obj`\n\nand still run from the repository root.\n\nOBJ meshes can also be offset and assigned a scene material:\n\n```\nobj=mesh.obj,(x,y,z),material[\nmetal=(0.8,0.8,0.8),0.1\n]\n```\n\nBlender scenes can be exported through Blender's Python API:\n\n```\n\"/Applications/Blender.app/Contents/MacOS/Blender\" -b scene.blend --python tools/blender_export_luz.py -- --output exports/scene.luz\n./Luz --file exports/scene.luz --threads 8\n```\n\nThe exporter writes a `.luz`\n\nfile plus OBJ meshes. Usage and current fidelity\nlimits are documented in [ docs/blender-exporter.md](/themartiano/luz/blob/main/docs/blender-exporter.md).\n\n```\ninclude/luz/       Public headers\nsrc/core/          Math, geometry, materials, image, and sampling code\nsrc/renderer/      Rendering implementation\nsrc/scene/         Scene model and scene helpers\nsrc/io/            Scene-file, OBJ, BMP, and TIFF loading/writing\nsrc/cli/           Command-line entry point and flags\nexamples/scenes/   Example .luz scene files\nassets/objects/    OBJ assets used by examples\ndocs/images/       Compressed showcase images\ntools/             Export and utility scripts\ntests/             Standard-library-only test program\ndocker/            Benchmark container\n```\n\nSpecial thanks to the [Ray Tracing in One Weekend](https://github.com/RayTracing/raytracing.github.io) book series. It was a great source of inspiration and information during a big part of the development of Luz, specially since those were times before AI.\n\nStormtrooper Scene by @[ScottGraham](https://blendswap.com/profile/120125) on [BlendSwap](https://blendswap.com/blend/13953).\n\nBust Statue by @[geoffreymarchal](https://blendswap.com/profile/180520) on [BlendSwap](https://blendswap.com/blend/21704).\n\nMIT. See `LICENSE`\n\n.", "url": "https://wpnews.pro/news/show-hn-i-wrote-a-c-ray-tracer-from-scratch-without-ai", "canonical_source": "https://github.com/themartiano/luz", "published_at": "2026-06-15 09:34:10+00:00", "updated_at": "2026-06-15 09:42:03.011043+00:00", "lang": "en", "topics": ["computer-vision", "developer-tools"], "entities": ["Luz", "Blender", "C++20", "Make", "CMake", "Python", "Windows", "Linux"], "alternates": {"html": "https://wpnews.pro/news/show-hn-i-wrote-a-c-ray-tracer-from-scratch-without-ai", "markdown": "https://wpnews.pro/news/show-hn-i-wrote-a-c-ray-tracer-from-scratch-without-ai.md", "text": "https://wpnews.pro/news/show-hn-i-wrote-a-c-ray-tracer-from-scratch-without-ai.txt", "jsonld": "https://wpnews.pro/news/show-hn-i-wrote-a-c-ray-tracer-from-scratch-without-ai.jsonld"}}