This folder contains a custom projection mapping system for correcting visual distortions in cone-shaped VR projection screens. This advanced rendering technique enables accurate visual presentation in cylindrical or conical projection setups commonly used in rodent VR systems.
ConeProjection/
├── Scripts/
│ ├── MakeConeProjection.cs # Main projection controller with optimization
│ ├── MakeCubeMap.cs # Cube map generation from camera
│ └── ProjectionOptimizer.cs # Gradient descent projection calibration
├── Shaders/
│ ├── ConeProjection.compute # GPU compute shader for cone mapping
│ ├── CubeMapReadoutShader_LEFT.shader # Left eye projection shader
│ └── CubeMapReadoutShader_RIGHT.shader # Right eye projection shader
├── Materials/
│ ├── LeftProjectionMaterial.mat # Left eye material
│ └── RightProjectionMaterial.mat # Right eye material
└── RenderTexturesForConeProjection/
├── CubeMapLeft.renderTexture # Cube map render target
└── EquirectangularLeft.renderTexture # Equirectangular render target
When projecting VR content onto curved or conical screens, standard perspective projection creates severe visual distortions. This system:
- Captures the 3D scene using cube map rendering
- Computes ray-cone intersections to determine what the mouse sees from inside the cone
- Applies geometric corrections using GPU compute shaders
- Optimizes projection parameters to match physical projector placement
This ensures that the virtual environment appears geometrically correct from the mouse's perspective, even on a curved projection surface.
Main projection controller and calibration system
Key Features:
- Real-time projection parameter adjustment (10 DOF)
- Gradient descent optimization for calibration
- Compute shader integration for GPU acceleration
- UV marker-based alignment system
Configurable Parameters:
projector_distance // Distance from origin to projector (meters)
projector_theta_1 // Horizontal angle (degrees)
projector_theta_2 // Vertical tilt adjustment (degrees)
projector_phi // Azimuth rotation (degrees)
projector_tau // Roll/twist angle (degrees)
projector_scale // Zoom/magnification factor
projector_height // Height above ground plane (meters)
cone_distance_to_top_row // Cone apex distance (meters)
cone_dinstance_to_second_row // Cone base distance (meters)
cone_half_angle // Cone opening half-angle (degrees)Optimization System:
- Marker-based calibration: Place world markers at known 3D positions on cone surface
- UV correspondence: Match markers to their 2D projector coordinates
- Automatic parameter tuning: Gradient descent minimizes reprojection error
- Real-time feedback: Visual overlay shows calibration accuracy
Usage:
// Attach to Camera GameObject
// Assign ComputeShader, CubeMap, Material, and output_normals in Inspector
// Configure projection parameters
// Call optimizationStep() to auto-calibrateEnvironment capture using cube map rendering
Purpose: Converts the Unity camera view into a cube map texture that can be sampled from any direction.
Key Features:
- Real-time cube map generation (6 faces)
- Executes in Edit Mode for setup
- Provides omnidirectional view for projection mapping
Technical Details:
- Renders 63 faces (full coverage)
- MonoOrStereoscopicEye.Left for left eye rendering
- Updates every frame for dynamic scenes
Gradient descent optimization for projection calibration
Purpose: Automatically finds optimal projection parameters by minimizing the error between expected and actual marker positions.
Algorithm:
- Forward projection: World markers → Expected UV coordinates
- Loss computation: Sum of squared distances between expected and actual UV
- Gradient calculation: Analytical derivatives w.r.t. all 10 parameters
- Parameter update: Gradient descent with weighted learning rates
Key Features:
- Analytical gradient computation (no numerical approximation)
- Importance weighting for critical markers
- Automatic learning rate scheduling
- 1M iteration optimization with periodic logging
Gradient Weights:
gradient_weights = {
1f, // theta_1 (horizontal angle)
1f, // theta_2 (vertical tilt)
1f, // phi (azimuth)
10.0f, // tau (roll) - more sensitive
10.0f, // distance - more sensitive
1f, // height
10f, // scale - more sensitive
1f, // cone_half_angle
1f, // distance_to_top
0f // distance_to_bottom - fixed
};GPU compute shader for ray-cone intersection
Algorithm:
- For each pixel (u,v) in projector space:
- Construct ray from projector origin through pixel
- Solve quadratic equation for ray-cone intersection
- Compute 3D point on cone surface
- Calculate surface normal and lighting
- Handle edge cases (no intersection, behind cone)
Mathematical Foundation:
Ray: P = X + λY (X = projector position, Y = ray direction)
Cone: x² + y² = (z·tan(α))² (α = half-angle)
Quadratic: aλ² + bλ + c = 0
a = Y.x² + Y.y² - (tan α)²·Y.z²
b = 2(X·Y)x,y - 2(tan α)²·(X·Y)z
c = X.x² + X.y² - (tan α)²·X.z²
Output:
- RGB: 3D point on cone surface (scaled)
- Alpha: cos(angle) for lighting/fade effects
Final rendering shaders with cube map sampling
Purpose: Samples the cube map using the computed 3D directions to display the correct texture on the cone projection.
Key Features:
- Mouse position-based alignment
- Height-based fadeout (configurable min/max)
- Alignment image overlay (for calibration)
- Separate shaders for left/right eye rendering
Properties:
_MainTex // Normal map from compute shader
_Cube // Cube map texture
_MousePositionLEFT // Mouse position for dynamic adjustment
_AlignmentImageLEFT // Calibration overlay toggle
_MaxHeight // Upper fade boundary
_MinHeight // Lower fade boundary
_fadeOut // Fade intensity1. Create two GameObjects:
- "CubemapCamera" (for environment capture)
- "ProjectionCamera" (for final projection)
2. Attach Scripts:
- MakeCubeMap.cs → CubemapCamera
- MakeConeProjection.cs → ProjectionCamera
3. Create RenderTextures:
- CubeMapLeft (Cube type, 1024x1024)
- EquirectangularLeft (2D, 1980x1020)
4. Assign in Inspector:
- MakeCubeMap: CubeMap = CubeMapLeft
- MakeConeProjection:
- ComputeShader = ConeProjection.compute
- CubeMap = CubeMapLeft
- Material = LeftProjectionMaterial
- output_normals = EquirectangularLeft
1. Physical Markers:
- Place calibration markers on cone surface
- Measure 3D positions (angle, height)
- Record in worldPointData array
2. UV Markers:
- Create GameObjects at corresponding projector coordinates
- Scale by 9.0 (Unity units)
- Assign to UVMarkers array
3. Optimization:
- Set learning_rate (start with 0.001)
- Set importance weights (higher for critical points)
- Call optimizationStep() in Inspector
- Monitor loss value (should decrease)
4. Fine-tuning:
- Manually adjust parameters for final tweaks
- Check estimatedUVMarkers overlay
- Verify alignment across entire cone surface
Once calibrated, the system runs automatically:
- CubemapCamera captures environment
- Compute shader projects onto cone
- Shader renders corrected output
- Mouse sees geometrically accurate VR world
Resolution: 1980x1020 pixels (custom aspect ratio) Compute Threads: 16x16 blocks (GPU optimized) Optimization: 1M iterations max (early stopping available) Update Rate: Real-time (Unity frame rate)
Supported Projector Types:
- Standard DLP/LCD projectors
- Short-throw projectors
- Arbitrary mounting angles
Supported Screen Geometries:
- Cones (arbitrary half-angle)
- Cylinders (90° half-angle)
- Truncated cones
This projection system is critical for:
- Accurate Visual Stimuli: Ensures grating frequencies, object sizes, and distances are perceived correctly
- Spatial Navigation: Prevents geometric distortions that could confuse place cell recordings
- Optokinetic Testing: Maintains constant velocity across visual field
- Depth Cue Research: Preserves perspective and parallax cues
Without correction: Objects appear stretched, velocities non-uniform, distances distorted With correction: Geometrically accurate presentation matching real-world optics
The system implements a full perspective projection with arbitrary projector placement:
Basis Vectors:
U = horizontal projector axis (right)
V = vertical projector axis (up)
F = forward projector axis (optical axis)
X = projector position in world space
Ray Construction:
Y(u,v) = β·(aspect·(u-0.5)·U + (v-0.5)·V) + F
Ray = X + λY(u,v)
Optimization Objective:
L = Σ importance[i] · ||UV_estimated[i] - UV_actual[i]||²
Minimized using analytical gradient descent with respect to all 10 projection parameters.
Cylinder:
cone_half_angle = 90f; // Vertical wallsNarrow Cone:
cone_half_angle = 30f; // Steeper coneTruncated Cone:
cone_distance_to_top_row = 2.0f; // Top radius
cone_dinstance_to_second_row = 3.0f; // Height differenceLower Resolution:
x_resolution = 1280;
y_resolution = 720;Reduce Iterations:
for (int step_number = 0; step_number < 100000; step_number++)This projection correction technique is based on principles from:
- Computer Graphics: Perspective projection and ray tracing
- Camera Calibration: Zhang's method and bundle adjustment
- VR Systems: Curved display warping and pre-distortion
Similar techniques used in:
- Planetarium dome projection
- Flight simulator curved screens
- Immersive cave systems (CAVE)
Adapted specifically for rodent VR trackball systems with conical projection screens.
Required Unity Components:
- Compute Shader support (Unity 2018.1+)
- RenderTexture API
- Camera.RenderToCubemap()
Required Scripts:
- None (standalone system)
Recommended Resolution: 1920x1080 minimum projector output
Black output:
- Check compute shader assignment
- Verify RenderTexture formats (ARGBFloat)
- Ensure cube map is populated
Distorted projection:
- Recalibrate using marker system
- Check cone parameters (half-angle, distances)
- Verify projector mounting matches parameters
Low performance:
- Reduce x_resolution/y_resolution
- Optimize compute shader thread groups
- Disable real-time optimization during experiments
When describing this system:
✅ "Developed custom GPU-accelerated projection mapping system using compute shaders and ray-tracing for geometrically accurate VR display on curved screens"
✅ "Implemented automatic calibration using gradient descent optimization with 10 degrees of freedom for arbitrary projector placement"
✅ "Created analytical gradient computation for real-time parameter optimization minimizing reprojection error across cone surface"