A lightweight, high-performance standalone XR application for real-time stereo camera passthrough to VR headsets. Designed for the Bigscreen Beyond 2e with dual ELP CC01 USB cameras.
This application provides minimal-latency video passthrough from USB cameras directly to a VR headset compositor, bypassing the overhead of game engines like Unity. Built with OpenXR and DirectX 11 for maximum performance and battery efficiency.
- Low Latency: Direct camera-to-compositor pipeline (1-2 frame latency)
- Efficient: ~90% less CPU/GPU usage compared to Unity-based solution
- Auto-Reconnect: Automatic camera reconnection on disconnect
- Configurable Rotation: 90° increment rotation (0°, 90°, 180°, 270°)
- Debug Overlay: Real-time performance metrics via ImGui
- Dual Logging: Console and file-based logging
- VR Headset: Bigscreen Beyond 2e (or any OpenXR-compatible headset)
- Cameras: 2x ELP CC01 USB cameras (1920x1080@50fps, YUY2/MJPEG)
- PC: Windows 10/11, USB 3.0 ports on separate controllers
- Graphics: DirectX 11 compatible GPU
- Windows 10/11
- SteamVR with OpenXR runtime
- Visual Studio 2022 (for building)
- CMake 3.20+
┌─────────────────┐
│ DirectShow │ ──> Capture 2 camera streams (YUY2/MJPEG)
│ Camera Capture │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Frame Processor│ ──> YUY2→RGBA conversion (GPU compute shader)
│ │ + Rotation (90° increments)
└────────┬────────┘
│
▼
┌─────────────────┐
│ OpenXR Runtime │ ──> Submit to VR compositor
│ (SteamVR) │ Left camera → Left eye
└─────────────────┘ Right camera → Right eye
- Camera Manager (
camera_manager.cpp): DirectShow camera enumeration, capture graph setup, auto-reconnect - XR Session (
xr_session.cpp): OpenXR lifecycle, D3D11 swapchain management, frame timing - Frame Processor (
frame_processor.cpp): YUY2→RGBA conversion, rotation, texture rendering - Main Loop (
main.cpp): Event polling, frame synchronization, reconnection logic - Debug Overlay (
debug_ui.cpp): ImGui-based performance metrics
VRPassthrough/
├── src/
│ ├── main.cpp # Entry point and main loop
│ ├── camera_manager.h/cpp # DirectShow camera handling
│ ├── xr_session.h/cpp # OpenXR lifecycle
│ ├── frame_processor.h/cpp # GPU texture processing
│ ├── debug_ui.h/cpp # ImGui debug overlay
│ ├── logger.h/cpp # Logging system
│ └── config.h # Configuration structure
│
├── shaders/
│ ├── yuv_to_rgba.hlsl # YUY2 → RGBA conversion (compute)
│ └── rotate_texture.hlsl # Rotation shader (vertex + pixel)
│
├── external/ # Third-party dependencies (gitignored)
│ ├── openxr/ # OpenXR SDK
│ └── imgui/ # Dear ImGui
│
├── build/ # CMake build output (gitignored)
├── CMakeLists.txt # Build configuration
├── config.ini # Runtime configuration
├── DESIGN.md # Detailed design documentation
└── README.md # This file
Edit config.ini to customize behavior:
[Camera]
NameFilter=3.0 USB Camera
Width=1920
Height=1080
FPS=50
Format=YUY2
Rotation=180
[Display]
# EyeWidth=2560 # Optional: override auto-detection
# EyeHeight=2560
[Advanced]
AutoReconnect=true
MaxReconnectAttempts=0 # 0 = infinite
ReconnectDelay=2.0
DebugLogging=false
DebugOverlay=true- Install Visual Studio 2022 with C++ Desktop Development workload
- Install CMake 3.20+ (or use VS2022's built-in CMake)
- Install SteamVR
# Clone repository
cd VRPassthrough
# Download dependencies (see DESIGN.md for details)
git submodule update --init --recursive
# Create build directory
mkdir build
cd build
# Configure CMake
cmake .. -G "Visual Studio 17 2022" -A x64
# Build
cmake --build . --config Release
# Run
cd Release
./VRPassthrough.exe- Connect both ELP cameras to separate USB 3.0 controllers
- Start SteamVR
- Run
VRPassthrough.exe - Put on headset - you should see stereo passthrough
- Press
F1to toggle debug overlay (shows FPS, latency, camera status)
| Metric | Unity Version | Standalone C++ |
|---|---|---|
| CPU Usage | 30-40% | 5-10% |
| GPU Usage | 40-50% | 15-25% |
| RAM Usage | 1.5-2 GB | 200-300 MB |
| Frame Latency | 3-5 frames (40-66ms) | 1-2 frames (13-26ms) |
| Battery Life | ~1-2 hours | ~3-4 hours (est) |
- Ensure cameras are on separate USB controllers (check Device Manager)
- Try different USB ports (opposite sides of laptop)
- Check
NameFilterin config.ini matches camera name
- USB bandwidth issue - disconnect other USB devices
- Lower resolution/FPS in config.ini
- Switch from YUY2 to MJPEG format
- Check Windows power plan (High Performance mode)
- Ensure laptop stays awake with lid closed (see CLAUDE.md)
- Auto-reconnect should handle this automatically
- Check USB cable connections
- Verify
AutoReconnect=truein config.ini
See DESIGN.md for detailed architecture documentation.
- OpenXR 1.0: Cross-runtime XR API
- DirectX 11: Graphics API for texture management
- DirectShow: Windows camera capture API
- HLSL: Shader language for GPU processing
- Dear ImGui: Debug UI framework
This project is part of a werewolf costume VR passthrough system. See CLAUDE.md for full project history.
- OpenXR Working Group for the OpenXR specification
- Omar Cornut for Dear ImGui
- Bigscreen for the Beyond 2e VR headset