Production-ready scene capture system with multi-buffer support, batch processing, and 360° rotation capabilities for machine learning dataset generation.
- Multi-Channel Capture - Color, Depth, and Normal buffers
- Batch Processing - Capture multiple frames with rotation
- Random Scene Generation - Procedural object spawning for datasets
- Flexible Camera Setup - Use existing camera or create new one
- Custom Inspector - User-friendly Unity Editor integration
- 🤖 ML Dataset Generation
- 📊 Computer Vision Research
- 🎮 Synthetic Data Creation
- 🔬 Scene Analysis Tools
┌─────────────────────────────────────────────────┐
│ CaptureController (MonoBehaviour) │
│ - User-facing component │
│ - Inspector UI customization │
│ - Multi-capture orchestration │
└──────────────────┬──────────────────────────────┘
│ uses
▼
┌─────────────────────────────────────────────────┐
│ CaptureSession (IDisposable) │
│ - Core API logic │
│ - Factory pattern (3 Create overloads) │
│ - Channel management (bitwise flags) │
│ - Resource lifecycle management │
└──────────────────┬──────────────────────────────┘
│ uses
▼
┌─────────────────────────────────────────────────┐
│ Replacement Shaders │
│ - DepthGrayscale: Eye-space depth → grayscale │
│ - NormalsRGB: World-space normals → RGB │
└─────────────────────────────────────────────────┘
namespace SceneCapture
{
public class CaptureSession : IDisposable
{
// Factory methods
public static CaptureSession Create(Camera camera, ...);
public static CaptureSession Create(Transform transform, ...);
public static CaptureSession Create(Vector3 pos, Quaternion rot, ...);
// Properties
public CaptureChannels Channels { get; set; }
public Vector2Int Resolution { get; set; }
public float FieldOfView { get; set; }
// Methods
public void Capture(string name = null);
public void SetPosition(Vector3 pos, Quaternion rot);
public void Dispose();
}
}using SceneCapture;
// Create session from existing camera
using (var session = CaptureSession.Create(Camera.main, "Assets/Captures"))
{
session.Channels = CaptureSession.CaptureChannels.All;
session.Resolution = new Vector2Int(1920, 1080);
session.Capture("MyScene");
}// Attach CaptureController to GameObject
// Configure in Inspector:
// - Multi Capture Mode: ✓
// - Capture Count: 36
// - Rotate Around Target: ✓
// - Target Object: [Your Object]
// - Rotation Radius: 5
// Press Space or call from code:
captureController.TakeCapture();// Enable in Inspector:
// - Generate Random Objects: ✓
// - Object Count: 10
// - Spawn Area: 5
// - Size Range: 0.5 - 2.0
// Scene auto-generates on capture
// Press 'R' to regenerate scene manuallyInspector Properties:
| Section | Property | Description |
|---|---|---|
| Camera | Source Camera | Reference camera (optional) |
| Output | Save Path | Directory for captures |
| Base Name | Filename prefix | |
| Use Timestamp | Add timestamp to filenames | |
| Width/Height | Output resolution | |
| Channels | Capture Color | Enable color buffer |
| Capture Depth | Enable depth buffer | |
| Capture Normals | Enable normal buffer | |
| Controls | Capture Key | Trigger key (default: Space) |
| Capture On Start | Auto-capture on play | |
| Multi-Capture | Multi Capture Mode | Enable batch processing |
| Capture Count | Number of captures | |
| Rotation | Rotate Around Target | 360° rotation mode |
| Target Object | Object to orbit | |
| Rotation Radius | Orbit distance | |
| Rotation Height | Camera elevation | |
| Random Scene | Generate Random Objects | Procedural scene |
| Object Count | Number of objects (1-50) | |
| Spawn Area | Scene size | |
| Size Range | Object scale range |
Factory Methods:
// Use existing camera
CaptureSession.Create(Camera camera, string path, Vector2Int resolution);
// Use transform for position/rotation
CaptureSession.Create(Transform transform, string path, Vector2Int resolution);
// Create new camera at position
CaptureSession.Create(Vector3 pos, Quaternion rot, string path, Vector2Int resolution);Properties:
session.SavePath = "Assets/MyDataset";
session.Resolution = new Vector2Int(2048, 2048);
session.BaseName = "Frame";
session.UseTimestamp = false;
session.FieldOfView = 60f;
// Bitwise channel selection
session.Channels = CaptureChannels.Color | CaptureChannels.Depth;Methods:
// Capture single frame
session.Capture("custom_name");
// Set camera position
session.SetPosition(new Vector3(0, 5, -10), Quaternion.identity);
session.SetPosition(myTransform);
// Reset frame counter
session.ResetIndex();- Standard scene rendering
- ARGB32 format
- Includes lighting and materials
- Eye-space depth normalized by max scene depth
- White = near objects
- Black = far objects
- Grayscale PNG output
- Custom
_MaxDepthparameter (auto-calculated)
Shader Implementation:
// Shader: Custom/DepthGrayscale
COMPUTE_EYEDEPTH(o.depth);
float normalizedDepth = i.depth / _MaxDepth;
float depthValue = 1.0 - normalizedDepth; // Invert for better visualization- World-space surface normals
- RGB encoding: R=X, G=Y, B=Z
- Range mapped from [-1,1] to [0,1]
Shader Implementation:
// Shader: Custom/NormalsRGB
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 encodedNormal = normal * 0.5 + 0.5;Assets/Captures/
├── Capture_2025-01-17_14-23-45-123_Color.png
├── Capture_2025-01-17_14-23-45-123_Depth.png
└── Capture_2025-01-17_14-23-45-123_Normals.png
Assets/Captures/Batch_2025-01-17_14-25-30/
├── Frame_0000_Color.png
├── Frame_0000_Depth.png
├── Frame_0000_Normals.png
├── Frame_0001_Color.png
├── Frame_0001_Depth.png
├── ...
└── Frame_0035_Normals.png
using (var session = CaptureSession.Create(Camera.main, "Dataset"))
{
session.UseTimestamp = false;
session.Channels = CaptureChannels.Depth | CaptureChannels.Normals;
for (int i = 0; i < 100; i++)
{
// Position camera
float angle = i * Mathf.PI * 2f / 100f;
Vector3 pos = new Vector3(Mathf.Cos(angle) * 10f, 2f, Mathf.Sin(angle) * 10f);
session.SetPosition(pos, Quaternion.LookRotation(-pos));
// Capture
session.Capture($"orbit_{i:D3}");
// Modify scene between captures
RandomizeObjectPositions();
}
}// Using bitwise flags
var channels = CaptureSession.CaptureChannels.None;
if (needsColor) channels |= CaptureSession.CaptureChannels.Color;
if (needsDepth) channels |= CaptureSession.CaptureChannels.Depth;
if (needsNormals) channels |= CaptureSession.CaptureChannels.Normals;
session.Channels = channels;// Start with low res for testing
session.Resolution = new Vector2Int(512, 512);
session.Capture("test_lowres");
// Switch to high res for final dataset
session.Resolution = new Vector2Int(4096, 4096);
session.Capture("final_highres");Three overloaded Create() methods for flexible instantiation:
Create(Camera camera, ...) // Use existing camera
Create(Transform transform, ...) // Use transform position
Create(Vector3 pos, ...) // Create new cameraProper resource cleanup:
public void Dispose()
{
if (_ownsCamera && _cameraObj != null)
Object.Destroy(_cameraObj);
_camera = null;
_cameraObj = null;
}Flexible channel selection:
[Flags]
public enum CaptureChannels
{
None = 0,
Color = 1,
Depth = 2,
Normals = 4,
All = Color | Depth | Normals
}Enhanced Inspector UI:
#if UNITY_EDITOR
[CustomEditor(typeof(CaptureController))]
public class CaptureControllerEditor : Editor
{
public override void OnInspectorGUI()
{
// Custom folder picker button
// Runtime capture buttons
// Organized property groups
}
}
#endifFrame-by-frame multi-capture:
private IEnumerator MultiCapture()
{
for (int i = 0; i < captureCount; i++)
{
// Setup scene
yield return new WaitForEndOfFrame();
// Capture frame
session.Capture($"{baseName}_{i:D4}");
yield return new WaitForEndOfFrame();
}
}- Unity: 6.2 or higher (compatible with Unity 2020+)
- Render Pipeline: Built-in RP
- Dependencies: None (self-contained)
| Aspect | Value |
|---|---|
| Single Capture | <50ms @ 1920x1080 |
| Multi-Capture (36 frames) | ~2-3 seconds |
| Memory Usage | ~30MB temporary RT memory |
| Depth Calculation | O(n) renderers in scene |
Error: Missing shader: Custom/DepthGrayscale
Solution: Ensure shaders are in Assets/Shaders/ and named correctly.
Common causes:
- Camera culling mask excludes scene objects
- Objects outside camera frustum
- Shaders not assigned to materials
Check:
- Target Object is assigned
- Rotation Radius > 0
- Target Object is centered correctly
session.Resolution = new Vector2Int(512, 512); // Standard dataset size
session.UseTimestamp = false; // Sequential naming
session.Channels = CaptureChannels.All; // Capture all channelssession.Resolution = new Vector2Int(4096, 4096);
session.FieldOfView = 45f; // Narrower FOV for less distortion
camera.farClipPlane = 100f; // Limit depth range// Always use 'using' statement for automatic cleanup
using (var session = CaptureSession.Create(...))
{
// Captures here
} // Automatically disposes resources- Factory design pattern
- IDisposable pattern
- Bitwise enum flags
- Custom Editor scripting
- RenderTexture management
- Replacement shader rendering
- Coroutine-based workflows
Camera.RenderWithShader()RenderTexture.GetTemporary()FindObjectsByType<Renderer>()CustomEditorattributeEditorUtility.OpenFolderPanel()
Suggestions for improvements:
- Add segmentation mask channel
- Support URP/HDRP pipelines
- Export metadata (camera poses, scene info)
- Real-time preview window
- Video sequence capture
- Custom shader injection
MIT License - Free for personal and commercial use
For questions or collaboration, please open a GitHub issue.
Production-ready tool for computer vision and graphics research 🎯