A 116-second, twelve-scene Jet cinematic, followed by a one-second black hold and an intentional ESP32 restart. The completed film was reviewed on S3; demanding shots can drop below the 60-field/s target. See the validation record for measured performance.
The original low wedge coupe, compact streets and framing follow the car design and one concept reference per shot. These generated references guide composition and proportions; they are not screenshots or promises of ESP32 image quality. The former NASCAR model is not used or bundled in this project.
| Time | Shot | Reference |
|---|---|---|
| 0–12 | River skyline, fade-in, searching sky beams, floating holograms | 01 |
| 12–22 | Descending camera in a rainy narrow street; mirrored shopfronts | 02 |
| 22–29 | Oblique overhead courier and tyre spray | 03 |
| 29–38 | Close three-quarter orbit around the front of the car | 04 |
| 38–47 | Angled driver-height cockpit, bonnet and red scanner | 05 |
| 47–53 | Oblique instrument close-up: 70 to 88 MPH | 06 |
| 53–64 | Hard braking, faster sideways skid, close pass-by, then tracking in traffic | 07 |
| 64–78 | Police close from established distant positions, overtakes and camera cuts | 08 |
| 78–88 | Wheels slow and hinge downward; cyan hub glow builds | 09 |
| 88–93 | Pitched climb, forward flight and frozen police | 10 |
| 93–101 | Lower fly-by over the city, fade to black | 11 |
| 101–116 | Waterfront pan, closing credits and shared fade to black | 01 |
Camera translation is linear within each tracking shot; the front orbit uses constant angular progression. Smooth lane changes describe vehicle steering, not camera easing. The wheels provide the entire hover conversion: no wings or deployed rear engine. The launch has no flash: the car retains its forward speed of 88 MPH while climbing at 650 world units/s. Its nose follows the flight direction, including the hinged wheels, window reflections and glow sprites. The final camera waits ahead of its flight path, letting the coupe pass close to the lens before tilting down over the city. After the fly-by fades to black, the original waterfront composition returns with its linear pan. It fades in over two seconds; the credits appear from 103–105 seconds, remain fully visible for eight seconds, then fade with the scene from 113–116 seconds. Road sections recycle around the tracking camera. The framing uses offset subjects and diagonal street lines, with lenses chosen per cut. Linear field-of-view changes tighten the 70–88 MPH gauge insert from 62° to 46°, widen the launch from 54° to 70°, and pull the final approach from 34° to 68° before the car passes the lens. These projection changes add no blur pass or depth-of-field buffer.
The 570-unit coupe represents a 4.5-metre car: 70 MPH is 6.95 car lengths per second and 88 MPH is 8.74. Road parallax and wheel rotation use the same conversion. The tight right turn slows to roughly 25 MPH, then cuts back to the faster boulevard tracking rig. The speedometer keeps its 70–88 MPH climb.
Street draw distance is 9,000 world units. Recycled streets provide sixteen blocks and put their longer extent in the viewing direction, including behind the car for front tracking. The fixed rain street uses simpler distant facades; the boulevard exit recycles sixteen blocks along its eastbound axis. Painter sorting uses 128 depth buckets to preserve nearby surface ordering at this range: still one byte per triangle, plus 512 bytes of sorting stack versus the default 64 buckets. Jet's default configuration remains unchanged.
At 3,200 units, grouped storefronts switch to six-triangle alternatives that retain their shop artwork and facade planes. Four box-filtered 32×32 facade mipmaps occupy 8 KiB of internal DRAM; the cache is initialized once and remains immutable during rendering. Distant lamp meshes are culled separately.
The colour grade increases contrast by 25% around an 80/255 pivot, deepening shadows and lifting highlights. It is applied to authored material colours and baked texture RGB565 values, with no per-pixel postprocess. Additive glow textures retain their original falloff.
- Shared ESP32 runtime: 480×320 output, half-width RGB565, alternating fields, overlapped DMA scanout and two raster workers on the S3. At 60 fields/s an individual physical LCD row refreshes at 30 Hz.
- Painter sorting without a depth allocation, with exact mean-depth refinement inside buckets touched by vehicle meshes. Opaque Phong body, unlit tyres and scenery, nearest environment mapping on the separately modelled canopy.
- Previous-field river reflection; inverted mesh reflections under the rainy street; translucent additive beams and full-resolution sprite halos.
- Bevelled/sloping skyline, shop textures, sloped awnings, projecting signs, spinning wheels, flickering lamps and fast rain streaks in every exterior shot. The narrow street also has short, independently scattered road splashes; interiors remain dry.
- Scene-local geometry/materials are owned by a bank and released at cuts. Persistent immutable textures remain valid for concurrent scanout snapshots. Large scene allocations prefer PSRAM; internal RAM is reserved for live transforms and runtime work. Adjacent shop details share transform work.
- Neon tubes widen to maintain at least two projected pixels in the slow skyline pan. The same ribbon treatment covers shop trims and city ground lines; far shop LOD uses broader trim bands. Searchlights have brighter core beams and wider, faster sweeps. Fine texture details can still alias.
- Closed traffic wheel wells and a recessed chassis floor prevent the body from intersecting the wheels. Suspension pitches/rolls the hero body while its road wheels stay planted.
- Closed hero body, shared canopy/glass boundaries, inner wheel-well walls, a continuous chassis floor and capped wheels that remain sealed in hover mode.
- Enclosed cockpit with sloping windscreen pillars, roof header and door trim. One low dashboard holds two flush displays and a slim red scanner; its open centre makes the autonomous layout clear. The driver camera stays at road height. The speed display updates a scene-local 16 KiB RGB565 texture only when its integer value changes; the allocation prefers PSRAM on the S3.
- Only the rounded FPS number appears at the absolute top-right on hardware. Full timing and triangle information remains in serial diagnostics.
Assets are original procedural geometry and artwork in main/Vehicle.hpp,
main/City.hpp and tools/prepare_assets.py. Bitmap concept art was made with
the built-in image generation tool; the exact prompts are in
references/prompts.json. The small firmware artwork
is generated with Pillow; rebuilding does not need image generation services.
The 17 static 3D textures use byte indices into small RGB565 palettes in internal RAM. Their source artwork retains every original RGB565 colour and resolution; indices remain in flash. The four filtered facade mipmaps stay RGB565 in 8 KiB of DRAM. Glow sprites, credits and the dynamic speed display also remain RGB565.
On the reference S3 and 80 MHz ST7796 display, repeated 36-view comparisons reduced mean render time from 23.550 to 21.578 ms (8.37%) on top of the earlier engine optimisations. A control using identical renderer code confirmed 8.55% less render time and 12.48% less raster time, with identical field hashes and triangle counts. These are fixed-view timings, not moving-film average FPS.
Texture storage falls from 323,584 to 164,448 bytes including palettes. Linked benchmark firmware is 155,864 bytes smaller, at a cost of 5,800 bytes of static internal memory: 2,984 bytes of IRAM instructions and 2,816 bytes of palette data including alignment. This saves flash/cache footprint rather than PSRAM. See Jet's measurement and compatibility notes.
The generator grades and converts to RGB565 before indexing, checks the 256-colour limit and verifies a lossless round trip. Indexed artwork is sampled with nearest filtering, including in desktop-quality builds; Sprite2D requires RGB565 textures. Normal builds use the checked-in arrays and do not regenerate the font-dependent artwork.
Initialize the shared submodules from the repository root, then use an ESP-IDF 6.0.1 environment in this directory:
idf.py -B build-s3 -DIDF_TARGET=esp32s3 -DSDKCONFIG=sdkconfig.s3 build
idf.py -B build-s3 -DIDF_TARGET=esp32s3 -DSDKCONFIG=sdkconfig.s3 -p COM6 flash monitorP4 configuration is included but this film has only been run on S3 hardware. The complete sequence must be watched before judging resets: restarting after the final black hold is intentional; an earlier restart is a fault.
Native validation and video export (C++17, CMake, Python, ffmpeg):
cmake -S tests -B build-preview -DCMAKE_BUILD_TYPE=Release
cmake --build build-preview --config Release
ctest --test-dir build-preview -C Release --output-on-failure
python tools/render_video.py preview.mp4 --renderer build-preview/film_video.exeUse the executable path generated by your build system; multi-configuration
builds may put it in build-preview/Release. --ffmpeg accepts a custom encoder
path; --start and --seconds export an excerpt. The exporter streams RGB24
directly into ffmpeg and writes no individual frame files. Its 60 fps video
reconstructs the same alternating packed fields, including previous-field water
and scanline sprite compositing. It omits the hardware FPS overlay: native
export speed is not an S3 measurement. Silent video is for visual review.
A separate desktop quality target renders full 2880×1920 RGB565 colour and depth buffers at every frame, with perspective mapping and full-detail meshes. RGB565 textures can use bilinear filtering; the lossless indexed artwork remains nearest-sampled. The export downsamples to 1920×1280 at 60 fps using Lanczos filtering for spatial anti-aliasing. It has no packed fields, scanline reconstruction or hardware counters. Geometry, artwork and camera paths are shared with the S3 build; it remains an actual Jet render, not a ray-traced remake.
cmake -S tests/quality -B build-quality -DCMAKE_BUILD_TYPE=Release
cmake --build build-quality --config Release
ctest --test-dir build-quality -C Release --output-on-failure
python tools/render_video.py quality.mp4 --qualityFor multi-configuration generators, also pass --renderer with the executable
under build-quality/Release. Both export modes write render and encoding logs
next to the MP4. The quality build enables Jet's optional
JET_HIGH_PRECISION_UVS: large projected triangles use full-width edge weights
and double reciprocal depths to prevent texture overflow. The quality build also
enables JET_PERSPECTIVE_DEPTH, interpolating reciprocal
Z so large sloping panels occlude correctly. Both options remain disabled on S3
and add no firmware pixel cost.
The checked-in main/CreditMask.hpp contains the closing typography at native
and desktop resolution. It needs no runtime font library. To replace its typeface,
run python tools/prepare_credits.py --font regular.ttf --bold-font bold.ttf.
The embedded mask occupies 7,560 flash bytes and expands into approximately
118 KiB of PSRAM only in the final scene.
See VALIDATION.md for checks and known limitations.
