VR Development¶
This guide consolidates all VR-specific knowledge from VPW table builds. VR introduces rendering, performance, and design challenges that do not exist in desktop or cabinet modes.
VR Room Creation¶
A VR room is a 3D environment surrounding the virtual pinball cabinet, visible when the player looks up or around in their headset. Rooms range from simple static enclosures to elaborate thematic environments with animation.
Mesh Format and Performance Budget¶
VR room meshes should be low-poly with baked textures. The room is peripheral scenery, not the player's focus, so detail can be minimal. Fish Tales' VR room reached 75MB due to animated water frames and baked lighting textures (learned from Fish Tales). CFTBL's underwater room featured animated swimming creatures and bubbles (learned from Creature from the Black Lagoon).
Room lighting should complement the table lighting rather than competing with it. Do not add dynamic lights to the VR room that could conflict with the table's GI system. Use baked ambient lighting in the room model. The table's GI spill onto nearby room surfaces is handled by VPX's reflection system, not by room lights (learned from Indiana Jones).
VR Room Script Configuration¶
The VRRoom script variable controls room mode. Common pattern (learned from Big Bang Bar):
VRRoom = 0: Desktop/cabinet, no room loadedVRRoom = 1: Standard VR roomVRRoom = 2: VR room with backglass positioning
When VR mode is active, hide desktop-specific elements: Textbox1.visible = false and Table.BackdropImage_DT = Empty. The room auto-detects VR mode in newer implementations, eliminating the need for manual script toggles.
RGB and Dynamic Lighting in VR Rooms¶
Big Bang Bar implemented an RGB VR room with script-controlled lighting that responds to table state (learned from Big Bang Bar). The SetRoomBrightness function syncs room brightness with table events, and UpdateBallBrightness syncs ball appearance with room lighting. Even enabling the reflection feature (with all objects set as non-reflective) may cause slowdown, so reflections are left off by default as a user option.
VR-Specific Rendering Bugs¶
Render Probe Roughness Causes Ghosting and White Flashing¶
Non-zero roughness on playfield reflection probes causes two serious VR artifacts (first identified on Bad Cats, confirmed on Breakshot):
- White flashing on the playfield surface, intermittent and distracting
- Ghosting (double-image effect when moving the head), most noticeable during ROM state changes
The fix is to set playfield reflection probe roughness to 0 for VR mode. The recommended approach is to create two render probes: one with roughness for desktop and one with zero roughness for VR. Switch to the zero-roughness probe when VR mode is detected. Example implementation exists in Fish Tales. As apophis79 rationalized: "It's more realistic for there to be no roughness if the table was recently clear coated."
Removing roughness from render probes can yield up to 40% FPS improvement even on desktop (learned from Fish Tales). The F12 options menu should expose probe settings so users can adjust for their hardware.
VR Mode Left On Causes Desktop Performance Drop¶
VR room primitives being loaded (VRRoom script variable set to 1 instead of 0) causes a 20-40 FPS loss even in desktop mode (learned from Batman DE). Always ensure VRRoom = 0 for desktop releases. The VR collection primitives add overhead even when not visually needed.
Wall Collidable in VR but Not Desktop¶
Some VPX elements render differently in VR versus desktop. Insert normal map reflections look good in VR (showing spoke reflections naturally) but produce distracting single bright pixels in desktop mode at certain POV angles. Solution: add a script option to toggle normal maps, disabled by default, enabled only when VR mode is active (learned from Batman DE, originally demonstrated in TFTC).
TransmissionScale and Glass in VR¶
High transmit values on VPX lights cause light to "pass through" objects, creating floating light artifacts visible in VR that desktop mode hides (learned from Hook). Reduce transmit values for lights near physical surfaces. Materials with transparency settings can cause light to bleed through in VR where it would not be visible in desktop mode; adjust the material's transparency amount in the VPX material editor (learned from Dr. Who).
Insert lights transmit straight through wire ramp meshes in VR. Fix: set "Disable Lig.f.Below" (DisableLightingFromBelow) to 1 on all wire ramp mesh primitives (learned from F-14 Tomcat). This setting prevents lights positioned below the ramp from bleeding through in VR specifically.
Shadow-Casting Light Height for VR¶
GI bulb light center points must be positioned at the physical top of the bulb, not adjusted to look correct from a specific camera angle. Moving light centers to match a POV causes them to appear incorrectly from any other angle, including VR and different desktop POVs (learned from Cactus Canyon). Secondary bloom lights should be hidden in VR where they cause artifacts.
Backglass and Depth Ordering in VR¶
VR backglass elements require specific depth bias values to layer correctly. For Blood Machines: BGBright (lit layer) = -500, BGDark (unlit layer) = 100 (learned from Blood Machines). Values that work in desktop may not work in VR, so always test depth bias in the VR headset.
VR backglass flasher implementation uses flasher and GI lights on a dedicated layer (e.g., layer 10) with naming conventions like BGFL# for flashers and BGGIBulb# for GI (learned from Batman DE). Avoid doubling solenoid callbacks between backglass flasher subs and table SetLamp calls.
For realistic VR backglass lighting, use flasher objects positioned behind the backglass image to simulate the actual lamp layout. This is preferred over B2S for VR since some users do not run B2S (learned from Creature from the Black Lagoon).
VLM can bake segment displays (score displays) directly into the VR backglass, eliminating the need for separate score display rendering in VR (learned from Flash Gordon).
Sideblades and DisableLightingFromBelow¶
Z-fighting on sideblades is fixed by setting DisableLightingFromBelow to 1 on the sideblade primitives (learned from Ghostbusters). Bakemap reflections must be set to "dynamic" (not "static only") for objects to appear in mirrored sideblades (learned from Bad Cats).
VR Blocker Walls¶
VR blocker walls prevent players from seeing through the cabinet to the VR room floor through gaps like the plunger lane. The technique: copy the cabinet primitive, make it fractionally smaller, and flip the normals so they face inward. This creates an inner shell that blocks line-of-sight through gaps. Purely visual, not collidable. First implemented on Breakshot, became a standard technique (learned from Breakshot, Countdown).
VR Testing Methodology¶
VR testing is tedious (constant headset on/off), but most VR debugging can be done without removing the headset (learned from Big Bang Bar). Key testing targets:
- Ramp heights: VR immediately exposes ramp geometry errors that are invisible in desktop mode. The Goonies' wire ramp went uphill toward the front of the table, completely unnoticeable in 2D desktop play but "hilariously wrong" in VR (learned from Goonies).
- Insert z-fighting: In VR, insert ON and OFF primitives flicker at shallow viewing angles that desktop's fixed camera angle masks (learned from Indiana Jones).
- Cabinet bottom visibility: Through transparent insert areas, VR players can see the underside of the cabinet model. Fix with a dark material or opaque blocker primitive below the playfield.
Fake Reflections in VR¶
Sixtoe's definitive statement: "Fake reflections look bloody awful in VR, always" (learned from Haunted House, Indiana Jones). Prefer material quality (proper roughness, subtle dirt/wear textures) over reflection effects. VR players notice material authenticity more than reflection accuracy because they can lean in close to examine surfaces.
A dust/dirt flasher layer with opacity around 200-250 sells glass material presence without relying on reflections (learned from Haunted House).
SteamVR vs OpenXR Configuration¶
VPX supports OpenGL for VR rendering. Per Niwak, OpenGL is recommended for both desktop and VR play. OpenGL provides hardware-level adaptive sync, MSAA anti-aliasing, and stereo rendering. "If it says 144Hz, it really runs at exactly 144Hz" (learned from Guns N' Roses).
VR Cabinet Mode¶
When VR players can walk behind the cabinet, they see objects placed behind it. Use "hider primitives" to block the view of mirrored/duplicate game elements (learned from Die Hard Trilogy). VR-specific objects (prefixed with VR naming conventions) can be toggled visible separately from desktop objects.
Frame Pacing and Performance in VR¶
For VR, 90 Hz is standard on Oculus headsets. The minimum GPU recommended is a GTX 1060, with a GTX 1660 handling everything satisfactorily (learned from Dr. Who). Stable frame pacing (consistent time between frames) matters more than raw FPS. A flat 110 FPS with consistent timing feels smoother than fluctuating 130-150 FPS.
Key VR performance optimizations:
- Timer consolidation: Too many separate timers hurt performance, especially in VR. Consolidate multiple timers into one "frametimer" or hijack an existing global timer (learned from Austin Powers).
- Post-processing AA off:
quality_smaaenabled by default caused VR choppiness on capable systems. Disable SMAA for VR builds (learned from Cactus Canyon). - Overlapping refractions: More impactful than single refractions. Reduce the "thickness" value for refractions to reduce stutter. Tables without refractions had no performance issues (learned from Bad Cats).
Timer -1 Extra Execution (Worse at 90Hz VR)¶
VPX timers with interval set to -1 (run once per frame) execute one additional time after being disabled. The disable takes effect on the next screen refresh, not immediately. This manifests more frequently in VR (90 Hz) than desktop (144 Hz) because each frame represents a larger time step at lower refresh rates. Fix: account for the extra execution in stopping logic, or use saturating position checks that clamp to the target (learned from Fish Tales).
Ball Reflections and PlayfieldReflectionScale¶
If no ball image is selected, VPX defaults to a built-in ball environment, not the table's environment image. The ball image should ideally be the same as the table's environment image for physically correct reflections (learned from Breakshot). The old "spherical map" checkbox on ball images produces incorrect mapping; equirectangular ball images are the modern standard.
Ball reflections on the underside appear black if no image is assigned to the VPX playfield object, even when the playfield mesh is invisible in baked tables. Fix: assign the playfield image to the playfield object regardless of mesh visibility (learned from Godzilla).
PlayfieldReflectionScale = 0 disables shiny reflections, useful for marble or non-glossy ball textures (learned from Haunted House).
F12 Options Menu for VR-Specific Settings¶
The F12 options menu should expose VR-relevant settings so users can adjust without editing script. Key options to include:
- Render probe roughness toggle (0 for VR, configurable for desktop)
- Refraction enable/disable (significant GPU impact)
- Glass scratches toggle
- LUT selection (note: any LUT, even 1:1 passthrough, has a performance cost at VR resolution; undefined LUT slots cause VR head tracking to freeze -- VPX bug #1741) (learned from VPW Example Table)
- VR room brightness
The F12 menu can cause the backglass to disappear in VR on some tables. Test menu interactions in the headset (learned from Big Bang Bar).
VR Mode Script Switch Pattern¶
VR mode implemented as a script switch that swaps primitive images, materials, and positions at runtime. Pattern: If VRRoom = True Then block enables VR-specific primitives via collection, repositions flashers, and swaps textures. Use a "playmode" option (0=Desktop, 1=Cabinet, 2=VR) to prevent incompatible combinations. Ramp textures need separate VR and cabinet versions because pre-rendered transparency effects look different in VR (learned from Austin Powers).
FlexDMD in VR¶
To see FlexDMD in VR: press F1 on table load and enable "External DMD." Ensure the VR preview window size does not overlap the DMD capture area. May need a second screen or dummy plug for the preview window (learned from Defender).
In VR, any flasher with "Use DMD script" checked projects the DMD content. Multiple instances of FlexDMD cannot be displayed simultaneously in VR (learned from Game of Thrones).