# Parallax native workshop A standard-library-only C++23 graphics reference with three small executables: - `prism-render`: perspective sphere/ground rendering with ambient + Lambert lighting and directional sphere shadows. - `prism-raster`: screen-space triangles with barycentric color interpolation and a depth buffer. - `prism-memory`: a bounded PMR allocator and ownership experiment. Scenes are typed C++ values authored in `src/scene.cpp` and `src/raster_lab.cpp`. Every public rendering boundary validates those values before arithmetic, allocation, or pixel access. There are no external dependencies or input parsers. The browser's interactive models are teaching tools; these sources produce real PPM images. ## Build and run Requires a C++23 compiler/standard library and CMake 3.25 or later. Verified with Apple Clang 21 and CMake 4.4.4 on macOS. The sources target standard C++ on macOS and Linux; Linux has not been verified in this environment. The standard library must supply `` as well as the compiler accepting `-std=c++23`; a language-mode switch alone does not install library features. From this `native` directory: ```sh cmake --preset clang-debug cmake --build --preset clang-debug ctest --preset clang-debug ./build/clang-debug/prism-render studio.ppm ./build/clang-debug/prism-raster triangles.ppm ./build/clang-debug/prism-memory ``` These presets select Ninja and Clang, so both must be on your tool path. The website's **Workshop → Build your studio** primer explains each stage from the beginning. `clang-release` uses a separate Release tree; configure, build, and test it with the same three commands and that preset name. For AddressSanitizer and UndefinedBehaviorSanitizer with the Clang preset: ```sh cmake --preset clang-sanitize cmake --build --preset clang-sanitize ctest --preset clang-sanitize ``` For a different compiler, use a new build tree and explicitly select the compiler and generator. The equivalent sanitizer option is `-DPRISM_SANITIZERS=ON` for Clang/GCC. Do not change compilers inside an existing configured tree. Personal preset overrides belong in the untracked `CMakeUserPresets.json`. Both image executables accept exactly one output path; `--help` prints usage. Exit 0 means success; exit 1 means an argument, validation, allocation, or I/O failure. Output files are replaced when valid rendering completes and writing starts. A write failure can leave an incomplete output: this reference does not claim atomic publication. Choose an output path you can replace. ## Follow the owners `include/prism/scene.hpp` defines `Scene`, `Vec3`, `Color`, and error-returning validation. `demo_scene()` returns an independent owner of its title and sphere storage. Change that typed scene, then rebuild to run your experiment. `src/renderer.cpp` constructs a perspective camera, intersects spheres and a checker ground plane, shades direct light, and applies an explicit gamma-2 display transform. Its `Image` owns one RGB vector. `pixels()` returns a borrowed const span only from an existing owner; requesting it from a temporary is rejected at compile time. Pixel-center sampling and fixed inputs make repeated renders deterministic for a given build. Cross-platform floating-point bit identity is not promised. The P6 header always uses ungrouped ASCII numbers, regardless of the application's global locale. Its gamma-2 samples are a documented PPM variant, not the exact BT.709 transfer specified by Netpbm or the exact sRGB transfer. `src/raster_lab.cpp` starts with **already projected screen-space vertices**. Each triangle owns three vertices with screen X/Y, depth, and linear RGB. The rasterizer allocates one RGB image and one depth vector; edge functions produce barycentric weights, and those weights interpolate color and depth. Smaller depth wins independently of submission order; exact depth ties keep the first sample. Both windings work. Degenerate triangles cover nothing. The pixel loop is restricted to the triangle's box intersected with the viewport before any index is formed. All edges are inclusive for this isolated-triangle reference; a watertight mesh renderer needs a deliberately tested top-left edge rule. `src/memory_lab.cpp` instruments a PMR interface with `CountingResource`. A 512-byte `monotonic_buffer_resource` with `null_memory_resource()` upstream cannot grow onto the heap. A failed reserve preserves existing vector elements. PMR containers die before `release()`, while an ordinary owning string remains valid afterward. No invalidated view is read. Counters measure logical live requested bytes, not metadata or arena physical usage. This teaching resource is single-threaded; sharing it would require synchronization. The ownership design applies the supplied C++ Core Guidelines' R.1 (RAII), R.5 (scoped objects), I.4 (typed interfaces), and ES.103 (avoid overflow). It uses no owning raw pointers or manual `new`/`delete` in the renderer or PMR implementation. The locale regression transfers a test facet directly to its standard-library locale owner. Output streams close through RAII, and explicit close errors are checked. These are selected design practices, not a claim of complete Guidelines conformance. `Scene`, `Image`, and `RasterScene` are public candidate aggregates; their cross-field invariants are checked at operation boundaries rather than preserved by private class state (see C.2/C.40). ## Coordinate and budget contracts Ray scenes use a right-handed world with Y up. The camera looks from `position` toward `look_at` and cannot point directly parallel to world up. `light_direction` points from a surface toward the light. Colors are linear RGB in 0–1. Sphere radii are 0.001–100, world coordinates/ground height are finite within ±1,000, and vertical field of view is 10–120 degrees. Raster vertices use +X right and +Y down. Pixel centers are `(x + 0.5, y + 0.5)`. Coordinates must be finite within ±8,192; partly off-screen triangles are allowed. Depth must be finite in 0–1, with 0 near and 1 far. RGB is interpolated in screen space: this is not a perspective-correct texture interpolator. | Resource | Limit | | --- | --- | | Width and height | 1–2,048 each | | Total pixels | 2,097,152 | | Ray scene spheres | 32 | | Ray pixels × spheres | 33,554,432 (shadows add at most the same sphere work again) | | Raster triangles | 64 | | Raster pixels × triangles | 16,777,216 | | Scene title | 1–80 bytes, no ASCII controls | Validation divides before multiplying to check combined work budgets. Validated pixel counts bound RGB storage; raster depth storage is also bounded by the same pixel count. These are deliberately small teaching budgets, not universal engine limits. The `expected` return values cover ordinary validation and reported I/O failures; they do not make these functions `noexcept`. Standard-library allocation and diagnostic construction can still throw. The command-line entry points supply the final exception boundary. The fixed ray epsilon and near-degenerate raster threshold are teaching policies, not proofs of robust predicates at every scale. ## Check the published C++ examples The native build above needs no JavaScript runtime. For an additional curriculum check from the **project root**, use the website's existing Bun installation and choose a disposable output directory: ```sh bun native/tests/verify_lesson_examples.ts clang++ ../../work/first-light-audit/examples ``` This compiles all 15 published First Light C++ examples directly from the lesson source with C++23 and warnings treated as errors. The compiler's standard library must implement `std::expected`. It supplies the explicitly named declarations for fragments, runs each example, checks the gradient's independent expected values, exercises normalization failures, and checks the published error producer and caller against known channel bytes, non-finite and out-of-range inputs, error indices, and unchanged pixels after failure. It also verifies that a temporary image cannot supply a borrowed view. GLSL and the annotated ray equation are reviewed separately; they are not C++ translation units. ## Extension exercises The reference implements shadows, raster coverage, interpolated depth, and RGB. Reflections, a 3D raster camera, homogeneous clipping, perspective-correct UVs, textures, GPU execution, and asynchronous tile scheduling are extension exercises. A reflected-ray implementation needs a fixed bounce budget and tested secondary ray offsets. A parallel renderer needs explicit task ownership, cancellation, joining, and resource lifetime rules before sharing memory. The synchronous references remain the correctness baseline. The earlier optional JSON/Glaze experiment was archived outside this delivered project when the course scope was corrected. It is not a dependency or part of this graphics reference. Original downloaded attachments were left untouched.