Skip to content

Latest commit

 

History

History
63 lines (41 loc) · 5.22 KB

File metadata and controls

63 lines (41 loc) · 5.22 KB

AGENTS.md

This file provides guidance to AI agents when working with code in this repository.

What this is

A small GPU path tracer written in Rust + WGSL, using wgpu. It renders a fractal (a Mandelbulb-like distance-estimated field) via ray marching, accumulating samples across frames into an HDR texture. Runs natively (winit window) and is intended to also run in-browser via WebGPU/wasm (currently broken — see Known issues below).

Commands

Build and run natively (release mode is expected — debug is too slow for path tracing):

cargo run --release

Select a specific GPU interactively:

cargo run --release select

Select a GPU via environment variable (substring match on adapter name):

WGPU_ADAPTER_NAME=1080 cargo run --release

There are no automated tests in this repo, and no lint/format CI config — cargo check / cargo clippy can be used ad hoc but aren't wired into any workflow.

Build for web (wasm32 + wasm-bindgen, then serves via a local Python HTTP server on port 8000):

./build_and_run_web.sh

This requires rustup target add wasm32-unknown-unknown and a matching wasm-bindgen-cli version installed first (see comments at the top of the script for the exact version). Currently the web target is known not to run correctly (see readme).

Architecture

The rendering logic is split cleanly between "boilerplate" (GPU/window setup) and the actual path tracer:

  • src/compute.wgsl — the entire path tracer. Takes a pixel coordinate, ray-marches a distance-estimated fractal field (map()), and returns a color via Monte Carlo path tracing (diffuse/specular/Fresnel bounces, depth of field, anti-aliasing, "bloom" via Phong-distributed blur). This is the only file where rendering/graphics logic lives; everything else is infrastructure to get pixels on screen.
  • src/render.wgsl — trivial fullscreen-triangle shader that just blits the accumulated texture to the screen.
  • src/main.rs — high-level app state (Application, GameState, CameraUniform) and camera/input handling. Defines the CameraUniform struct that is uploaded to the GPU every frame and must stay byte-layout-identical to the CameraUniform struct in compute.wgsl (same field order, no implicit padding — see comments in both files about Vec3 alignment).
  • src/wgpu_boilerplate.rs — all direct wgpu API calls: adapter/device setup, buffer/texture/bind-group/pipeline creation, dispatching the compute pass + render pass each frame, and reading a texture back to a PNG. Exposes a Boilerplate trait implemented for WgpuDriver.
  • src/window_boilerplate.rswinit event loop plumbing (RenderLoop trait), shared between native and wasm targets via #[cfg(target_arch = "wasm32")] branches. Application in main.rs implements RenderLoop.

Frame accumulation (double-buffered textures)

The renderer accumulates one sample per pixel per frame into a running sum, rather than re-rendering from scratch. Because a compute shader cannot read and write the same storage texture, two textures are used in ping-pong fashion (images: [Texture; 2] in WgpuObjects): each frame reads the previous frame's texture and writes the new accumulated sum into the other one, alternating by frames_start % 2. render.wgsl and compute.wgsl bind groups are precomputed for both orderings (compute_bind_groups, render_bundles, each length 2) to avoid rebuilding them every frame. The image's 4th (alpha) channel is repurposed as a running ray/sample count, not transparency — division by this count happens at PNG export time (in read_texture_to_png) for on-screen accumulation is implicit since more samples just get brighter and are not normalized on-screen. Any change to texture format/usage or to this alternation logic needs to be mirrored in both init() and resize() in main.rs.

Resolution decoupling

IMAGE_RESOLUTION in main.rs can pin the render resolution independently of the window size (for rendering at higher quality than the display). When None, the image resolution tracks window resize events and all GPU resources (textures, bind groups, render bundles) are recreated in resize().

Live-tunable shader parameters

CameraUniform.v: Vec4 is a set of four free f32 slots read directly in compute.wgsl (cam.v.x/y/z/w) and used for ad hoc tuning of fractal coloring during development (see surface_info() in compute.wgsl). They're adjusted at runtime with the V/F, B/G, N/J, M/K key pairs in main.rs — this is a debug mechanism, not meaningful named config.

Key controls (native build)

W/A/S/D/Space/LShift move the camera, arrow keys rotate it, Q/E adjust focus distance, Z/C adjust aperture, Right Shift scales all movement to 20% for fine control, P saves the current accumulated image as a PNG (also auto-saves at power-of-2 frame counts once past frame 32), R resets state. See readme.md for the full table.

Known issues (don't try to "fix" without asking)

  • The program can crash on Intel integrated GPU drivers with a "parent device is lost" error — this is a driver bug, not something fixable in this codebase.
  • The wasm/WebGPU build does not currently run correctly in-browser, despite build_and_run_web.sh existing to build it.