{"id":28191716,"url":"https://github.com/4rknova/xtracer","last_synced_at":"2026-04-12T12:09:31.156Z","repository":{"id":1152602,"uuid":"1037499","full_name":"4rknova/xtracer","owner":"4rknova","description":"Experimental rendering framework written in C/C++ with a shared core (xtcore) and multiple frontends (CLI, Web, WASM runtime).","archived":false,"fork":false,"pushed_at":"2026-04-09T18:17:21.000Z","size":9319,"stargazers_count":31,"open_issues_count":11,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-09T20:18:02.315Z","etag":null,"topics":["computer-graphics","path-tracing","ray-tracing","renderer","rendering-engine"],"latest_commit_sha":null,"homepage":"https://www.artstation.com/artwork/xkaGO","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/4rknova.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["4rknova"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2010-10-30T14:18:08.000Z","updated_at":"2026-04-04T03:33:39.000Z","dependencies_parsed_at":"2025-05-16T11:11:05.830Z","dependency_job_id":"1ddb2b03-c456-4483-b4db-1df69504d36c","html_url":"https://github.com/4rknova/xtracer","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/4rknova/xtracer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4rknova%2Fxtracer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4rknova%2Fxtracer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4rknova%2Fxtracer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4rknova%2Fxtracer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/4rknova","download_url":"https://codeload.github.com/4rknova/xtracer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4rknova%2Fxtracer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31714089,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-12T06:22:27.080Z","status":"ssl_error","status_checked_at":"2026-04-12T06:21:52.710Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["computer-graphics","path-tracing","ray-tracing","renderer","rendering-engine"],"created_at":"2025-05-16T11:10:38.497Z","updated_at":"2026-04-12T12:09:31.150Z","avatar_url":"https://github.com/4rknova.png","language":"C++","funding_links":["https://github.com/sponsors/4rknova"],"categories":["C++"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/4rknova/xtracer/develop/res/logo.svg\" alt=\"xtracer\" width=\"400\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://hub.docker.com/r/4rknova/xtracer\"\u003e\u003cimg src=\"https://img.shields.io/docker/pulls/4rknova/xtracer?label=Docker%20pulls\" alt=\"Docker Pulls\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://hub.docker.com/r/4rknova/xtracer\"\u003e\u003cimg src=\"https://img.shields.io/docker/v/4rknova/xtracer?sort=semver\u0026label=Docker%20Hub\" alt=\"Docker Hub\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/4rknova/xtracer/pkgs/container/xtracer\"\u003e\u003cimg src=\"https://img.shields.io/badge/ghcr.io-available-blue?logo=github\" alt=\"GitHub Container Registry\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nExperimental rendering framework written in C/C++ with a shared core (`xtcore`) and multiple frontends (CLI, Web, WASM runtime).\n\n`xtracer` is a physically-based ray/path tracing engine built for exploration and experimentation. The core library (`xtcore`) handles scene parsing, ray-geometry intersection, shading, and tone mapping — and is consumed by three independent frontends:\n\n- **CLI** (`xtracer_cli`) — offline renderer that writes images to disk; supports PNG output with full control over integrator, resolution, samples, and anti-aliasing.\n- **Web server** (`xtracer_web`) — HTTP API server with a job queue, progressive preview streaming, and a browser-based SPA for scene selection, rendering, log inspection, and image export in multiple formats.\n- **WASM runtime** (`xtracer_wasm`) — WebAssembly build for in-browser rendering without a server.\n\nThe engine supports a range of integrators from simple Whitted-style ray tracing to MIS path tracing with area-light and environment sampling, plus photon mapping, ambient occlusion, and several debug views. Scenes are described in a custom `.scn` format covering procedural and mesh geometry, analytic cameras (thin-lens with polygonal bokeh, ODS, ERP, cubemap), and environment types including Rayleigh sky.\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/4rknova/xtracer/develop/src/frontend/web-client/res/ftue.png\" alt=\"preview\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n## Quick Start (Install + Run)\n\nInstall dependencies (Debian/Ubuntu):\n\n```bash\nsudo apt update\nsudo apt install -y build-essential cmake pkg-config libomp-dev zlib1g-dev\n```\n\nConfigure + build:\n\n```bash\ncmake -S . -B build/intermediate/build -DXTRACER_ENABLE_WEB=ON\ncmake --build build/intermediate/build -j\n```\n\nRun web server:\n\n```bash\n./build/intermediate/build/xtracer_web --host 127.0.0.1 --port 8080 --scene-dir scene --web-root src/frontend/web-client --max-concurrent-renders 1 --render-reserve-threads 1 --verbose\n```\n\nOpen: `http://127.0.0.1:8080`\n\nUI showcase: `http://127.0.0.1:8080/showcase.html`\n\nRun CLI:\n\n```bash\n./build/intermediate/build/xtracer_cli scene/lab-camera-modes-showcase.scn -renderer pathtracer_mis -res 1280x720 -samples 4 -aa 2\n```\n\nWith scene variant:\n\n```bash\n./build/intermediate/build/xtracer_cli scene/lab-camera-modes-showcase.scn -variant night -renderer pathtracer_mis -res 1280x720 -samples 4 -aa 2\n```\n\n## Repository Layout\n\n| Area | Path | Purpose |\n|---|---|---|\n| Core renderer | `src/xtcore/` | Scene parsing, render context, integrators, tone mapping |\n| CLI frontend | `src/frontend/cli/` | Command-line scene rendering |\n| Web frontend backend | `src/frontend/web-server/` | HTTP API, job manager, log stream |\n| Frontend shared code | `src/frontend/common/` | Shared render service + integrator metadata |\n| Web static app | `src/frontend/web-client/` | SPA for Scene / Render / Editor / Workspaces / Gallery / Settings / Logs / About, including runtime JSON config in `app/data/` |\n| Scenes | `scene/` | Example scene files (`.scn`) |\n| Supporting libs | `lib/` | Internal libraries (`nimg`, `nmesh`, `nmath`, etc.) |\n| Third-party deps | `ext/` | Vendored external dependencies (registry: `docs/DEPENDENCIES.md`) |\n\n## Feature Matrix\n\n### Frontends\n\n| Capability | CLI (`xtracer_cli`) | Web (`xtracer_web`) | WASM (`xtracer_wasm`) |\n|---|---:|---:|---:|\n| Load `.scn` scenes | Yes | Yes | Yes |\n| Select camera | Yes (`-cam`) | Yes | Yes |\n| Select scene variant | Yes (`-variant`) | Yes (`variant` API param) | Via backend API |\n| Integrator selection | Yes (`-renderer`) | Yes (`/api/integrators`) | Yes (through web app adapter) |\n| Progressive updates | Terminal progress | Job progress + preview API | Progressive snapshots |\n| Image export | PNG | PNG/JPG/BMP/TGA/HDR/EXR + Raygraph PLY (`/api/jobs/{id}/export`) | PNG snapshots (adapter flow) |\n| HTTP API | No | Yes | No |\n\n### Integrators\n\n| Integrator ID | Type | Exposed In `/api/integrators` |\n|---|---|---:|\n| `raytracer` | Whitted-style | Yes |\n| `pathtracer` | Brute-force path tracing | Yes |\n| `pathtracer_mis` | MIS diffuse path tracing | Yes |\n| `pathtracer_mis_full` | MIS path tracing with emissive-area and environment sampling | Yes |\n| `pathtracer_bdpt` | Experimental bidirectional path tracing with MIS path connection | Yes |\n| `photon_mapping` | Photon mapping | Yes |\n| `ao` | Ambient occlusion | Yes |\n| `debug_views` | Multi-mode debug integrator | Yes |\n| `depth` | Debug depth alias | No (alias accepted in `/api/render`) |\n| `stencil` | Debug stencil alias | No (alias accepted in `/api/render`) |\n| `normal` | Debug normal alias | No (alias accepted in `/api/render`) |\n| `uv` | Debug UV alias | No (alias accepted in `/api/render`) |\n| `emission` | Debug emission alias | No (alias accepted in `/api/render`) |\n\n### Scene Schema Support (`.scn`)\n\n#### Environment Types\n\n| Type | Notes |\n|---|---|\n| `gradient` | Uses `config.a`, `config.b` colors |\n| `color` | Uses `config.value` |\n| `cubemap` | Uses face sources: `posx/posy/posz/negx/negy/negz` |\n| `erp` | Uses panoramic source texture |\n| `rayleigh_sky` | Procedural sky using `sun_direction`, `sun_intensity`, `beta_rayleigh`, `ground_color`, `density`, `horizon_falloff`, `sun_disk_radius`, `sun_disk_intensity`, `sun_glow_radius`, `sun_glow_intensity`, `sun_glow_falloff` |\n\n#### Camera Types\n\n| Type | Notes |\n|---|---|\n| `thin-lens` | Position/target/up/fov/flength/aperture (+ optional aperture shape controls) |\n| `ods` | Omni-directional stereo camera |\n| `erp` | Equirectangular camera |\n| `cubemap` | Cubemap camera |\n\n`thin-lens` optional aperture-shape fields:\n- `aperture_blades` (integer): `0` or `\u003c3` keeps circular aperture; `\u003e=3` uses an N-gon aperture for polygonal bokeh.\n- `aperture_rotation` (float, degrees): rotates the polygonal aperture when `aperture_blades \u003e= 3`.\n\n#### Geometry Types\n\n| Type | Notes |\n|---|---|\n| `plane` | Analytic plane |\n| `sphere` | Analytic sphere |\n| `point` | Parsed as epsilon-radius sphere |\n| `triangle` | Triangle via `vecdata.v0/v1/v2` |\n| `menger_sponge` | Implicit fractal surface (`position`, `radius`, `resolution`) |\n| `sierpinski_tetrahedron` | Implicit fractal surface (`position`, `radius`, `resolution`) |\n| `mandelbulb` | Implicit fractal surface (`position`, `radius`, `resolution`, `power`, `bailout`) |\n| `julia` | Implicit fractal surface (`position`, `radius`, `resolution`, `power`, `bailout`, `julia_c`) |\n| `csg` | Constructive solid geometry tree (`op`, `left`, `right`) |\n| `mesh` | External OBJ/FBX/glTF or procedural generator |\n\n#### CSG (`geometry.type = csg`)\n\nCSG geometry uses binary tree nodes:\n- `op`: `union`, `soft_union` (`smooth_union` alias), `intersection`, or `difference`\n- `left`: nested node\n- `right`: nested node\n- `smoothness` (optional): soft union blend width (`\u003e 0`, default `0.15`), used only with `soft_union`\n\n`left`/`right` can be:\n- another CSG node (contains `op`, `left`, `right`), or\n- a supported leaf geometry definition (v1): `sphere`, `point`, `plane`, `menger_sponge`, `sierpinski_tetrahedron`, `mandelbulb`, `julia`.\n\nExample:\n\n```scn\ngeometry = {\n  shape = {\n    type = csg\n    op = difference\n    left = { type = sphere, position = vec3(0,0,0), radius = 1.0 }\n    right = { type = sphere, position = vec3(0.4,0,0), radius = 0.6 }\n  }\n}\n```\n\n#### Procedural Mesh Generators (`geometry.type = mesh`, `source = gen(...)`)\n\n| Generator | Generator | Generator | Generator |\n|---|---|---|---|\n| `plane` | `icosahedron` | `tetrahedron` | `cube` |\n| `hexahedron` | `octahedron` | `dodecahedron` | `capsule` |\n| `cylinder` | `capped_cylinder` | `cone` | `truncated_cone` |\n| `ring` | `rounded_ring` | `torus_knot` | `icosphere` |\n| `geodesic_dome` | `icosa_cage` | `hemisphere` | `disc` |\n| `menger_sponge` | `sierpinski_tetrahedron` | `menger_sponge_implicit` | `sierpinski_tetrahedron_implicit` |\n| `mobius_strip` | `klein_bottle` | `superellipsoid` | `hairball` |\n| `shell_spiral` | `rock` | `chain_link` | `lathe` |\n| `snowflake` | `pyramid` | `gear` | `spring` |\n| `star` | `crystal` | `tree` | `coral` |\n| `terrain` | `draped_cloth_strip` | `city` |  |\n\nNote: `menger_sponge` and `sierpinski_tetrahedron` can be used either as:\n- `geometry.type` values (native implicit xtcore surfaces), or\n- `gen(...)` mesh generators (triangulated geometry).\n\nSizing note:\n- `gen(menger_sponge)` and `gen(menger_sponge_implicit)` are normalized to unit canonical bounds (`[-0.5, 0.5]` per axis) before scene modifiers.\n\n`gen(pyramid)` supports:\n- `base_size` (float, `\u003e 0`, default `1.0`)\n- `height` (float, `\u003e 0`, default `1.0`)\n\n`gen(mobius_strip)` supports:\n- `resolution` (integer, `\u003e= 24`, default `64`): strip tessellation around the loop and across the band\n- `radius` (float, `\u003e 0`, default `1.0`): major loop radius from the strip centerline\n- `width` (float, `\u003e 0`, default `0.64`): full strip width across the band\n\n`gen(ring)` supports:\n- `radius` (float, `\u003e 0`): outer radius\n- `height` (float, `\u003e 0`): ring height\n- `thickness` (float, `\u003e 0`): wall thickness as `outer_radius - inner_radius`\n- `height_resolution` (integer, `\u003e= 1`): vertical side tessellation\n\n`gen(rounded_ring)` supports:\n- `radius` (float, `\u003e 0`): outer radius\n- `height` (float, `\u003e 0`): rounded profile height\n- `thickness` (float, `\u003e 0`): ring thickness as `outer_radius - inner_radius`\n- `profile_resolution` (integer, `\u003e= 8`): cross-section tessellation for the rounded profile\n\n`gen(terrain)` supports:\n- `resolution` (integer, `\u003e= 2`): grid resolution\n- `dimensions` (`vec3`): terrain width, height amplitude, and depth\n- `height_sampler` (group): sampler used to drive terrain displacement; defaults to `type = scenery_heightfield`\n\n`gen(draped_cloth_strip)` supports:\n- `resolution` (integer, `\u003e= 8`): strip tessellation\n- `dimensions` (`vec3`): strip width, sag amplitude, and length\n- `folds` (float): longitudinal fold count\n- `edge_lift` (float): raises or lowers the strip edges relative to the center\n- `curl` (float): adds a gentle twist/curl along the strip\n- `taper` (float): narrows or widens the strip toward the free end\n- `sway` (float): shifts the hanging strip laterally for a wind/pull feel\n- `asymmetry` (float): biases the drape so one side hangs differently from the other\n- `pinned` (float `[0,1]`): blends between freer hanging and more pinned-end draping\n\n`scenery_heightfield` sampler supports:\n- `seed` (integer)\n- `scale` (float)\n- `octaves` (integer, `\u003e= 1`)\n- `lacunarity` (float, `\u003e 1`)\n- `gain` (float, `(0,1)`)\n- `ridge_strength` (float)\n- `mountain_strength` (float)\n- `valley_strength` (float)\n\n#### Seeded Random Generators (`random`)\n\nScenes may define a top-level `random` group to synthesize deterministic, repeatable scene content.\nEach random entry must provide an explicit `seed`.\n\nSupported generator types:\n\n- `sphere_grid`: creates many sphere objects with seeded random placement/material assignment.\n\n`sphere_grid` properties:\n- `seed` (integer, required)\n- `prefix` (string, optional): name prefix for generated geometry/material/object ids\n- `x_min`, `x_max`, `z_min`, `z_max` (integers): loop bounds (`[min, max)`)\n- `y` (float): sphere center Y\n- `radius` (float, `\u003e 0`)\n- `jitter` (float): per-cell random XY jitter magnitude\n- `avoid_center` (`vec3`) and `avoid_radius` (float): exclusion sphere\n- `lambert_ratio` (float `[0,1]`): diffuse probability threshold\n- `metal_ratio` (float `[lambert_ratio,1]`): metal threshold (`\u003e metal_ratio` becomes glass)\n- `metal_fuzz_min`, `metal_fuzz_max` (float `[0,1]`): seeded fuzz range for generated metals\n- `ior` (float `\u003e= 1`): generated glass index of refraction\n- `glass_reflectance` (float `[0,1]`): generated glass reflection probability\n\n#### Material Types\n\n| Type |\n|---|\n| `lambert` |\n| `phong` |\n| `blinn_phong` |\n| `emissive` |\n| `dielectric` |\n| `principled` |\n| `rough_dielectric` |\n| `thin_dielectric` |\n| `subsurface` |\n| `sheen` |\n| `thin_translucent` |\n| `boundary` |\n\n`boundary` is an interface-only material. It does not add surface shading and forwards rays through the hit point unchanged (useful for object-local medium boundaries).\n\nModern PBR-oriented material notes:\n- `principled`: GGX metal/roughness material. Common inputs are `samplers.base_color`, optional grayscale `samplers.roughness` / `samplers.metallic`, and scalars `metallic`, `roughness`, `anisotropy`, `anisotropy_rotation`, `ior`, `clearcoat`, `clearcoat_roughness`. `clearcoat` acts as a visible dielectric top layer over the base lobe, while `clearcoat_roughness` controls the sharpness of that coat highlight. Falls back to `diffuse` if `base_color` is omitted.\n- `rough_dielectric`: rough transmissive dielectric for frosted glass/acrylic style surfaces. Common inputs are `samplers.transmission`, optional grayscale `samplers.roughness`, optional `samplers.normal`, optional `samplers.absorption_color`, and scalars `roughness`, `ior`, `transparency`, `absorption_distance`. Absorption is applied as a slab-style Beer-Lambert approximation along transmissive events.\n- `thin_dielectric`: thin-sheet transmissive dielectric for windows, visors, and packaging films. Common inputs are `samplers.transmission`, optional `samplers.normal`, and scalars `roughness`, `ior`, `transparency`. It keeps the path in the current medium instead of treating the surface as a solid volume boundary, and rough sheets participate in the BSDF/MIS path while near-ideal sheets stay on the delta path.\n- `subsurface`: pragmatic diffuse-plus-transmission material for wax, jade, soap, and resin-style surfaces. Common inputs are `samplers.base_color` or `samplers.diffuse`, optional `samplers.subsurface_color`, optional `samplers.subsurface_radius`, optional `samplers.normal`, and scalars `subsurface` plus `thickness`. `subsurface_radius` and `thickness` drive a thickness-aware exit tint on the transmitted lobe.\n- `sheen`: cloth-like diffuse material with a bounded retroreflective edge tint. Common inputs are `samplers.base_color` or `samplers.diffuse`, optional `samplers.sheen_color`, optional `samplers.normal`, and scalar `sheen`.\n- `thin_translucent`: thin-sheet diffuse transmission material for leaves, paper, curtains, and lampshades. Common inputs are `samplers.base_color` or `samplers.diffuse`, optional `samplers.translucency_color` (or `transmission`), optional `samplers.normal`, and scalars `translucency` plus `thickness`.\n\n#### Object-Local Participating Media (v1)\n\nParticipating media are defined in a top-level `medium` group and referenced by object.\n\nIf an object has no `medium` reference, the renderer uses vacuum behavior.\n\nCurrent v1 constraints:\n- medium types: `homogeneous`, `heterogeneous_noise`\n- intended boundary material: `boundary`\n- currently applied in path tracers (`pathtracer`, `pathtracer_mis`, `pathtracer_mis_full`)\n- inline `object.medium = { ... }` blocks are not supported\n\nExample:\n\n```text\nmedium = {\n  fog_main = {\n    type = homogeneous\n    sigma_a = col3(0.06,0.04,0.03)\n    sigma_s = col3(0.24,0.20,0.14)\n    g = 0.15\n    emission = col3(0.00,0.00,0.00)\n  }\n}\n\nobject = {\n  fog_shell = {\n    geometry = fog_volume\n    material = boundary_shell\n    medium = fog_main\n  }\n}\n```\n\n`heterogeneous_noise` supports these extra parameters (all optional):\n- `density` (default `1.0`)\n- `noise_scale` (default `1.0`)\n- `noise_min` (default `0.25`)\n- `noise_max` (default `1.0`)\n- `octaves` (default `4`)\n- `lacunarity` (default `2.0`)\n- `gain` (default `0.5`)\n- `seed` (default `1337`)\n\n#### External Object Import (`object.source`)\n\n`object.source` can import external mesh assets directly and auto-create runtime objects and materials from the file contents.\n\nSupported formats:\n- `.obj`\n- `.fbx`\n- `.gltf`\n- `.glb`\n\nBehavior:\n- `object.source = ...` creates one or more runtime objects from the external asset\n- material assignments are taken from the imported file\n- the generated materials do not need explicit `material = ...` entries in the `.scn`\n- `prefix` is used when generating runtime geometry/material/object ids\n\nExample:\n\n```scn\nobject = {\n  imported = {\n    source = assets/fbx/blender_272_cube_7400_binary.fbx\n    prefix = imported_\n  }\n}\n```\n\nNotes for FBX:\n- current support is for static mesh import\n- node transforms are flattened during import\n- importer reads base color, emissive, normal, roughness, and metallic data when available\n- both referenced texture files and embedded texture blobs are supported\n- animation, skinning, and advanced material graphs are not imported yet\n\nNotes for glTF:\n- current support is for static mesh import from `.gltf` and `.glb`\n- node transforms are flattened during import\n- importer maps glTF PBR materials into xtracer `principled` materials where possible\n- external texture files and GLB-embedded images are supported\n- animation, skinning, morph targets, and advanced extensions are not imported yet\n\n#### Sampler Types\n\n| Type | Notes |\n|---|---|\n| `color` | Solid color |\n| `texture` | 2D texture |\n| `cubemap` | Cubemap texture |\n| `erp` | Equirectangular texture |\n| `gradient` | Gradient sampler |\n| `graphpaper` | Procedural graph paper |\n| `checker` | Procedural checker |\n| `weave` | Procedural weave |\n| `fbm_marble` | Procedural marble |\n| `voronoi_normal` | Procedural Voronoi tangent-space normal map |\n\nLambert, `principled`, `rough_dielectric`, `thin_dielectric`, `subsurface`, `sheen`, and `thin_translucent` support an optional sampler named `normal` for tangent-space normal mapping.\nSupported normal sampler types:\n- `texture` (normal-map texture)\n- `voronoi_normal` (procedural normal generator)\n\n`voronoi_normal` parameters:\n- `cells` (integer, `\u003e= 1`): Number of Voronoi cells.\n- `max_deviation` (float, degrees, clamped to `[0, 89]`): Maximum angular deviation from tangent-space +Z.\n- `seed` (integer): Deterministic random seed.\n\n#### Mesh Modifiers\n\n| Modifier |\n|---|\n| `rotation` |\n| `scale` |\n| `translation` |\n| `flip_normals` |\n| `extrude` |\n\n#### Scene Variants\n\nScenes can define optional overlays under `variants`:\n\n```scn\nvariants = {\n  hide_base = true\n\n  base = {\n    name        = Baseline\n    description = Uses the unmodified scene definition.\n  }\n\n  night = {\n    name        = Night Lighting\n    description = Dims emissive and swaps to night camera.\n    remove = {\n      object = {\n        light_fill = {}\n      }\n    }\n    set = {\n      default_camera = cam_night\n      material = {\n        lamp = {\n          properties = {\n            samplers = {\n              emissive = { type = color, value = col3(0.03,0.03,0.06) }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\nRules:\n- `remove`: presence-based nested groups (`name = {}`) remove matching properties/groups in the base scene.\n- `set`: deep-merge overlay; properties overwrite and groups merge recursively.\n- `hide_base` (optional bool): when `true`, hides the `(base)` option in the web variant picker.\n- `name` / `description`: optional metadata for frontend variant pickers.\n- `variants.base`: optional metadata for the base (no variant) selection.\n- Apply order: `remove` -\u003e `set` -\u003e CLI `-mod` overrides.\n- Requesting a missing variant returns a scene-load error.\n\n## Web App + API\n\n### Web App Tabs\n\n| Tab | Key Capabilities |\n|---|---|\n| Scene | File-manager-style scene browser plus fixed-size camera/variant cards (with variant name + description metadata), active selection panels, single-click selection, double-click activation, and scene file right-click actions (`Set Active`, `Delete`) |\n| Render | Scene/camera/integrator selection, render settings, a square preview container that fills the render pane as the largest square that fits, tile-size presets (`8`, `32`, `64`, `Auto` where auto derives a square tile from frame size and effective thread count), a preview-toolbar export format dropdown + live format-aware save button, preview sampling toggle, in-flight abort support (render action toggles `Render`/`Abort`), render modes (`Direct`, `Progressive`, `Incremental`, `Interactive`) with `Progressive` as the default frontend mode, interactive camera controls/ramping, plus post-filter stack controls (enable/disable + chain) applied to preview/export |\n| Editor | Switchable `3D View` / `Graph` / `Text Editor` modes, scene source editor, create geometry, mesh translate/rotate/scale controls, 3D scene scale multiplier, click-select + Ctrl-drag move, `F` focus shortcut, visual viewport integration, scene save |\n| Gallery | Cached render browser with card grid, detail view, pass thumbnails for progressive/incremental renders, refresh, and delete |\n| Settings | Theme mode + light/dark palette selection, frontend behavior toggles, render polling controls, and first-time tutorial reset/start controls |\n| Logs | Backend log stream with wait-based incremental updates and level filters |\n| About | Build/backend metadata, project license text, and third-party dependency notices including usage/location |\n\nShared sidebar jobs card:\n- Shows a live 1 minute thread-usage graph plus current active/queued jobs, queue ordering controls, and abort actions wherever the card is enabled.\n\nFirst-time use tutorial (FTUE):\n- On first launch, the web app opens a guided tutorial for scene selection, rendering, and scene editing flow.\n- In `Settings`, enable `Show tutorial on next launch` to reset onboarding state for the next app start.\n- In `Settings`, use `Start Tutorial Now` to reopen the tutorial immediately.\n- Tutorial steps are config-driven via `src/frontend/web-client/app/data/ftue_steps.json` (`steps[]` entries support `title`, `body`, `target_selector`, `placement`, `tab`, `editor_view`, `open_cards`, and optional `focus_selector`).\n\nPost-filter stack:\n- Current filters: `desaturate`; `chromatic_aberration` (`amount`, `center_x`, `center_y`, `falloff`); `vignette` (`strength`, `radius`, `softness`, `center_x`, `center_y`); `film_grain` (`amount`, `size`, `seed`, `luma_weighted`); `denoise` (bilateral: `strength`, `radius`, `sigma`); `fxaa` (`subpix`, `edge_threshold`, `edge_threshold_min`); `sharpen` (`amount`, `radius`, `threshold`); `brightness` (`amount`); `contrast` (`amount`, `pivot`); `raindrops_lens` (`density`, `size`, `distortion`, `seed`).\n\nRender preview interactions:\n- Mouse wheel zooms the preview image.\n- Drag pans the preview while zoomed.\n- Double-click or `Reset View` resets preview pan/zoom.\n- Clicking or dragging on the preview minimap recenters the current zoom on that region.\n- The preview toolbar includes export format + save controls and a two-icon sampling switch (`Smooth`, `Nearest`).\n\nInteractive preview controls (Render tab, with `Render Mode = Interactive`):\n- `Left drag`: look around\n- `Middle/Right drag` or `Shift + Left drag`: pan\n- `Wheel` or touch pinch: zoom\n- `W/A/S/D`: move forward/left/back/right\n- `Q/E`: move down/up\n- `Shift`: speed boost\n- `Interactive Speed` slider: scales fly movement speed\n- `Save Interactive Camera`: appends a new `camera` entry to the active scene from the current interactive pose and saves it\n- Preview HUD: shows mode, speed, and current interactive quality stage\n- Moving quality auto-adapts toward a low-latency frame-time target before settle refinement\n- During active movement, interactive mode temporarily uses a low-cost navigation profile (raytracer + lightweight settings), then restores settle refinement\n\n### Web API Endpoints\n\n| Method | Endpoint | Purpose |\n|---|---|---|\n| GET | `/api/health` | Health probe |\n| GET | `/api/about` | Backend/app metadata, runtime capacity stats, and license/third-party notice fields including dependency usage/location |\n| GET | `/api/scenes` | List available scenes |\n| GET | `/api/scenes/{scene}/cameras` | List cameras in scene (optional `variant=\u003cname\u003e`; includes `camera_entries` with `name` + `type`; returns `202` while async scene load is in progress) |\n| GET | `/api/scenes/{scene}/source` | Fetch scene source |\n| GET | `/api/scenes/{scene}/geometry` | Extract mesh geometry payload (optional `variant=\u003cname\u003e`; returns `202` while async scene load is in progress) |\n| GET | `/api/scenes/{scene}/runtime_graph` | Fetch runtime-resolved scene graph (objects/surfaces/materials/media/cameras; object entries may include `medium`) (optional `variant=\u003cname\u003e`; returns `202` while async scene load is in progress) |\n| GET | `/api/scenes/{scene}/runtime_texture?material=...\u0026sampler=...` | Fetch a runtime material texture preview PNG, including embedded imported textures (optional `variant=\u003cname\u003e`; returns `202` while async scene load is in progress) |\n| GET | `/api/scenes/{scene}/camera_resolve` | Resolve active camera metadata (optional `variant=\u003cname\u003e`; returns `202` while async scene load is in progress) |\n| GET | `/api/scenes/load_jobs/{id}` | Poll async scene load job status |\n| GET | `/api/scenes/{scene}/asset?path=...` | Fetch referenced scene asset |\n| GET | `/api/scenes/template/empty` | Empty scene template |\n| POST | `/api/scenes/save` | Save scene source |\n| POST | `/api/scenes/delete` | Delete a scene file by name |\n| GET | `/api/workspaces?client_id={id}` | List workspaces + active workspace + workspace-scoped settings snapshot |\n| POST | `/api/workspaces` | Create workspace for client context (returns `409` when the retained workspace cap is saturated by active workspaces) |\n| POST | `/api/workspaces/active` | Switch active workspace for client |\n| POST | `/api/workspaces/delete` | Delete workspace |\n| POST | `/api/workspaces/scene_draft` | Save workspace-local scene draft (returns `413` when the draft payload exceeds the backend limit; the backend retains up to 16 drafts per workspace) |\n| POST | `/api/workspaces/settings` | Save workspace UI settings (quality/frame/integrator/tone mapping/preview/post-filters; returns `413` when `settings_json` exceeds the backend limit) |\n| GET | `/api/integrators` | List backend integrator metadata + controls |\n| GET | `/api/post_filters` | List backend post-filter metadata, stage support, and parameter schema |\n| GET | `/api/resolutions` | Resolution presets |\n| POST | `/api/render` | Create render job (optional `variant=\u003cname\u003e`, optional `render_mode={direct,progressive,incremental,interactive}`; legacy `normal` is also accepted, optional interactive camera override: `cam_px/cam_py/cam_pz`, `cam_tx/cam_ty/cam_tz`, `cam_upx/cam_upy/cam_upz`, `cam_hfov`; returns `503` when the bounded server queue is full) |\n| GET | `/api/jobs/active` | Server-authoritative list of active jobs (`jobs[]`, running first then queued) |\n| POST | `/api/jobs/abort/{id}` | Abort explicit job id |\n| GET | `/api/jobs/{id}` | Job status snapshot |\n| GET | `/api/jobs/{id}/image` | PNG preview/final image (supports tone mapping + optional post-filter query params) |\n| GET | `/api/jobs/{id}/image_delta?since={n}\u0026limit={m}` | Incremental preview tiles since tile index `n` (binary packet, supports tone mapping + optional post-filter query params) |\n| GET | `/api/jobs/{id}/export?format={png,jpg,bmp,tga,exr,hdr}` | Download final export (supports optional post-filter query params) |\n| GET | `/api/jobs/{id}/photons` | Photon debug points |\n| GET | `/api/gallery` | List cached gallery entries (newest first) |\n| GET | `/api/gallery/{id}/image` | Fetch the latest cached render image |\n| GET | `/api/gallery/{id}/pass/{n}/image` | Fetch a cached pass image for progressive/incremental renders |\n| DELETE | `/api/gallery/{id}` | Delete a cached gallery entry |\n| GET | `/api/logs?since={id}` | Incremental backend logs |\n| GET | `/api/logs/wait?since={id}\u0026timeout_ms={n}` | Wait for new backend logs (long-poll) |\n\n## Build\n\n### Prerequisites (Debian/Ubuntu)\n\n```bash\nsudo apt update\nsudo apt install -y build-essential cmake pkg-config libomp-dev zlib1g-dev\n```\n\nWASM toolchain (`XTRACER_ENABLE_WASM=ON`):\n\n```bash\nsudo apt install -y emscripten\n```\n\n### Recommended Native Build (Out-of-Tree)\n\n```bash\ncmake -S . -B build/intermediate/build -DXTRACER_ENABLE_WEB=ON\ncmake --build build/intermediate/build -j\n```\n\n\u003e Note: native binaries are configured to output under the chosen CMake build directory (for example `build/intermediate/build/`).\n\n### CMake Options\n\n| Option | Default | Description |\n|---|---:|---|\n| `XTRACER_ENABLE_WEB` | `ON` | Build HTTP web frontend |\n| `XTRACER_ENABLE_WASM` | `OFF` | Build standalone WASM runtime |\n| `XTRACER_ENABLE_WASM_DIST` | `OFF` | Build/package standalone WASM dist during native build |\n| `XTRACER_ENABLE_VIZ` | `OFF` | Build OpenGL sampling visualization tool (`xtracer_viz_sampling`) |\n| `XTRACER_ENABLE_NMATH_SIMD` | `ON` | Enable x86 SSE2 SIMD fast-paths for `nmath` double-precision vector and matrix operations |\n| `XTRACER_ENABLE_NMATH_SIMD_AVX` | `ON` | Use AVX path for `nmath` SIMD (`XTRACER_ENABLE_NMATH_SIMD` must be `ON`) |\n\nNotes:\n- `XTRACER_ENABLE_NMATH_SIMD` currently targets native x86/x86_64 builds and is ignored for Emscripten.\n- SIMD paths are used only when `nmath` is built in double precision (default configuration); scalar fallback remains available.\n- `XTRACER_ENABLE_NMATH_SIMD_AVX` enables AVX codegen and runtime AVX instructions for supported hosts.\n\n## Run\n\n### CLI\n\n```bash\n./build/intermediate/build/xtracer_cli scene/lab-camera-modes-showcase.scn -renderer pathtracer_mis -res 1280x720 -samples 4 -aa 2\n```\n\n### Web Server\n\n```bash\n./build/intermediate/build/xtracer_web --host 127.0.0.1 --port 8080 --scene-dir scene --web-root src/frontend/web-client --max-concurrent-renders 1 --render-reserve-threads 1 --verbose\n```\n\nOpen: `http://127.0.0.1:8080`\n\n`--gallery-dir` controls where cached gallery renders and per-pass previews are stored (default: `gallery`).\n`--max-concurrent-renders` controls how many render jobs execute simultaneously (default: `1`).\n`--render-reserve-threads` controls how many threads auto-render mode keeps free for server responsiveness (default: `1`).\nWhen `/api/render` uses `threads=0`, backend auto mode resolves to `max(1, runtime_threads - reserve_threads)` (single-core hosts still render with `1` thread).\nThe web backend also bounds pending render backlog to `32` queued jobs; extra `/api/render` requests return `503` instead of accumulating unbounded queued state.\nWorkspace state is also bounded: the backend retains at most `32` workspaces, evicts orphaned idle workspaces after `60` minutes, and returns `409` from `/api/workspaces` if all retained slots are still active.\nStartup prints an ASCII banner with runtime info (host/port, paths, concurrency, and detected core/thread limits).\n\n### Docker Deployment\n\nBuild and start on a host machine:\n\n```bash\ncp .env.example .env\ndocker compose up --build -d\n```\n\nCheck container status:\n\n```bash\ndocker compose ps\ndocker compose logs -f xtracer-web\n```\n\nOpen: `http://127.0.0.1:${XTRACER_PORT:-8080}`\n\nNotes:\n\n- Scene files are mounted from `./scene` into the container at `/app/scene`.\n- Set `XTRACER_OMP_NUM_THREADS` in `.env` to control OpenMP worker count.\n\nStop:\n\n```bash\ndocker compose down\n```\n\n### Math Microbenchmark\n\n```bash\n./build/intermediate/build/bench_nmath\n```\n\n### Mesh Intersection Benchmark\n\n```bash\n./build/intermediate/build/bench_mesh_intersection\n```\n\nOptional tuning:\n\n```bash\n./build/intermediate/build/bench_mesh_intersection --resolution 128 --width 512 --height 512 --passes 8\n```\n\n### Sampling Visualization Tool\n\nBuild with visualization enabled:\n\n```bash\ncmake -S . -B build/intermediate/build-viz -DXTRACER_ENABLE_VIZ=ON\ncmake --build build/intermediate/build-viz -j --target xtracer_viz_sampling\n```\n\nRun:\n\n```bash\n./build/intermediate/build-viz/xtracer_viz_sampling\n```\n\n### WASM Runtime Build\n\n```bash\nemcmake cmake -S . -B build/intermediate/build-wasm \\\n  -DXTRACER_ENABLE_WEB=OFF \\\n  -DXTRACER_ENABLE_WASM=ON\ncmake --build build/intermediate/build-wasm -j --target xtracer_wasm\n```\n\nExpected output:\n\n- `src/frontend/web-client/xtracer_wasm.js`\n- `src/frontend/web-client/xtracer_wasm.wasm`\n- copied self-contained scenes from `scene/` into build output `\u003cbuild-dir\u003e/scenes/` (for example `build/intermediate/build-wasm/scenes/`)\n\nOptional static packaging:\n\n```bash\n./util/package_wasm_standalone.sh\n```\n\n## Test Targets\n\n| Test Name (CTest) | Binary |\n|---|---|\n| `colorspace::roundtrip` | `\u003cbuild-dir\u003e/test/test_nimg_colorspace` |\n| `colorspace::vectors` | `\u003cbuild-dir\u003e/test/test_nimg_colorspace_vectors` |\n| `xtcore::tile` | `\u003cbuild-dir\u003e/test/test_xtcore_tile` |\n| `xtcore::context` | `\u003cbuild-dir\u003e/test/test_xtcore_context` |\n| `xtcore::sphere` | `\u003cbuild-dir\u003e/test/test_xtcore_sphere` |\n| `xtcore::triangle` | `\u003cbuild-dir\u003e/test/test_xtcore_triangle` |\n| `xtcore::csg` | `\u003cbuild-dir\u003e/test/test_xtcore_csg` |\n| `xtcore::fbx_import` | `\u003cbuild-dir\u003e/test/test_xtcore_fbx_import` |\n| `xtcore::gltf_import` | `\u003cbuild-dir\u003e/test/test_xtcore_gltf_import` |\n| `xtcore::boundary_material` | `\u003cbuild-dir\u003e/test/test_xtcore_boundary_material` |\n| `xtcore::white_furnace` | `\u003cbuild-dir\u003e/test/test_xtcore_white_furnace` |\n| `xtcore::raytracer_emissive` | `\u003cbuild-dir\u003e/test/test_xtcore_raytracer_emissive` |\n| `xtcore::object_medium_parse` | `\u003cbuild-dir\u003e/test/test_xtcore_object_medium_parse` |\n| `cli::setup_parse` | `\u003cbuild-dir\u003e/test/test_xtracer_cli_setup` |\n| `ncf::inline_and_utf8` | `\u003cbuild-dir\u003e/test/test_ncf_parser` |\n| `scene::validate_all` | `\u003cbuild-dir\u003e/test/test_xtcore_scene_validator` |\n| `nmath::sampling` | `\u003cbuild-dir\u003e/test/test_nmath_sampling` |\n| `nmath::simd` | `\u003cbuild-dir\u003e/test/test_nmath_simd` |\n| `nmath::simd_perf_compare` | `\u003cbuild-dir\u003e/test/test_nmath_simd_perf_compare` |\n| `cli::stencil_smoke` | `\u003cbuild-dir\u003e/xtracer_cli` smoke render |\n\nRun all tests:\n\n```bash\nctest --test-dir build/intermediate/build --output-on-failure\n```\n\nConvenience one-liners:\n\n```bash\nmake check\nmake perf\n```\n\n- `make check`: configures/builds `build/intermediate/build` and runs all tests.\n- `make perf`: configures/builds `build/perf-release` and runs `nmath::simd_perf_compare` via `nmath_perf_check`.\n\n## Third-Party Dependencies\n\n| Name | License | URL |\n|---|---|---|\n| cgltf | MIT | https://github.com/jkuhlmann/cgltf |\n| cpp-httplib | MIT | https://github.com/yhirose/cpp-httplib |\n| STB | Public Domain / MIT | https://github.com/nothings/stb |\n| strpool | Public Domain / MIT | https://github.com/mattiasgustavsson/libs |\n| Three.js | MIT | https://github.com/mrdoob/three.js |\n| TinyEXR | BSD-3-Clause | https://github.com/syoyo/tinyexr |\n| TinyObjLoader | MIT | https://github.com/tinyobjloader/tinyobjloader |\n| ufbx | MIT | https://github.com/ufbx/ufbx |\n\n## License\n\nBSD 3-Clause. See `LICENSE`.\n\nCopyright (c) 2010-present Nikolaos Papadopoulos.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4rknova%2Fxtracer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F4rknova%2Fxtracer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4rknova%2Fxtracer/lists"}