diff --git a/CHANGELOG.md b/CHANGELOG.md
index fbacd7a..8625d81 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,16 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [3.0.4] - 2024-05-22
+### Changed
+- Prevent Unity from throwing an exception when using the file explorer to change the output file path in a recorder preset.
+- Disallow `ActiveCamera` from being selected with SRP-based projects as a camera source.
+- Display an error message and prevent Recorder from starting if MainCamera is selected while the main camera is missing from the project.
+- Better handle error messages when using a Tagged Camera while the tag is missing from the project or not assigned to the camera.
+
+### Fixed
+- Ensure that camera's rotation is retained when recording monoscopic 360 views.
+- Ensure an alpha channel is added when the Editor targets a mobile platform.
## [3.0.3] - 2021-11-08
### Added
diff --git a/Documentation~/InclCaptureOptionsTargetedCamera.md b/Documentation~/InclCaptureOptionsTargetedCamera.md
index e1c7318..b90056b 100644
--- a/Documentation~/InclCaptureOptionsTargetedCamera.md
+++ b/Documentation~/InclCaptureOptionsTargetedCamera.md
@@ -1,5 +1,8 @@
These properties appear when you set **Source** to **Targeted Camera**.
+>[!NOTE]
+>* You can't capture images from a Targeted Camera if you are using a URP 2D Renderer, due to a [known limitation](KnownIssues.md#targeted-camera-recording-is-not-available-with-urp-2d-renderer). As an alternative, you can capture from the Game View or from a Render Texture Asset.
+
![](Images/CaptureOptionsTargetedCamera.png)
|Property||Function|
diff --git a/Documentation~/KnownIssues.md b/Documentation~/KnownIssues.md
index e653b1d..b0895c4 100644
--- a/Documentation~/KnownIssues.md
+++ b/Documentation~/KnownIssues.md
@@ -8,6 +8,11 @@ This page lists some known issues and limitations that you might experience with
**Workaround:** The recommended use case is to limit yourself to one Movie recording at a time. Ensure that you have only one active Movie Recorder in the Recorder window and no Movie Recorder Clips in Timeline, or vice-versa. If you need to keep concurrent recordings for some reason, you can still set up lower resolutions or try different encoders (for instance, the MP4 encoding step is much faster than the ProRes one).
+#### Targeted Camera recording is not available with URP 2D Renderer
+ **Limitation:** Recorder cannot capture images from a Targeted Camera in URP 2D projects. A capture pass that Recorder requires is missing in the renderer.
+
+ **Workaround:** As an alternative, you can capture from the Game View or from a Render Texture Asset.
+
#### ActiveCamera recording not available with SRPs
**Limitation:** The use of a Scriptable Render Pipeline ([SRP](https://docs.unity3d.com/Manual/ScriptableRenderPipeline.html)) in your project prevents you from setting ActiveCamera as the source of the recording in the [Movie Recorder](RecorderMovie.md#targeted-camera-source-properties) and the [Image Sequence Recorder](RecorderImage.md#targeted-camera-source-properties). This render pipeline limitation applies to all SRPs including Unity's High Definition Render Pipeline ([HDRP](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@latest)) and Universal Render Pipeline ([URP](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest)). For the same reason, the [AOV Recorder](RecorderAOV.md#source-camera), which requires HDRP, doesn't include the ActiveCamera option by design.
@@ -36,6 +41,14 @@ This page lists some known issues and limitations that you might experience with
**Workaround:** If you need to record a Beauty pass with TAA enabled on your recording camera, you should record it through its own recording session, separately from any other AOVs.
+#### Recorder does not capture any custom cursors
+
+**Known issue:** When you record a video where you use a custom cursor set with [Cursor.SetCursor](https://docs.unity3d.com/ScriptReference/Cursor.SetCursor.html), the cursor doesn't appear in the recordings.
+
+**Workaround:** To make sure that the Recorder captures your custom cursor, you have to:
+- Set the **Input Source** to **GameView**.
+- Call `Cursor.SetCursor` with [`CursorMode.ForceSoftware`](https://docs.unity3d.com/ScriptReference/CursorMode.ForceSoftware.html).
+
#### 360 View recording issues and limitations
@@ -46,3 +59,16 @@ The Recorder doesn't fully support 360 View recording. Here is a list of known i
* If you record a 360 View through a Physical Camera, the rendered image is not equirectangular, which makes the output media unusable. To work around the issue, use a regular Camera for the recording.
* The Recorder doesn't support stereoscopic recording in projects that use any Scriptable Render Pipelines (SRPs). The **Stereo Separation** property has no effect on the recorded views, which makes the rendering identical for both eyes.
+
+#### Different Cameras with different Display targets lead to black output
+
+**Known issue:** Certain camera outputs are black when cameras have different TargetDisplays. This occurs when you set up recorders with **Targeted Camera** as the **Input Source**.
+The Game view triggers the render loop of all cameras that target the same Display number. If no Game view is configured for a specific Display number that is set on a Camera, it does not render.
+
+**Workaround:** Open another Game view and set it to the Display number that you need to capture.
+
+#### Impossible to capture multiple Game views at the same time
+
+**Known issue:** Recorder does not handle a configuration with mutiple Game views as **Input Source**. Recorder assumes that there is only one Game view and gets the one that has the focus at the moment the Editor enters the Play Mode.
+
+**Workaround:** Set the **Input Source** to **Targeted Camera** or **Render Texture Asset**.
diff --git a/Editor/Sources/OutputPathDrawer.cs b/Editor/Sources/OutputPathDrawer.cs
index 10abee1..1513b1d 100644
--- a/Editor/Sources/OutputPathDrawer.cs
+++ b/Editor/Sources/OutputPathDrawer.cs
@@ -55,12 +55,36 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
var fullPath = OutputPath.GetFullPath((OutputPath.Root)m_RootProperty.intValue, m_LeafProperty.stringValue,
m_AbsolutePathProperty.stringValue);
- if (!target.forceAssetsFolder)
+ var tooltip = "Select the output location through your file browser";
+ var folder = fullPath;
+
+ if (target.forceAssetsFolder)
+ {
+ tooltip = "Select the output location in Unity Assets through your file browser";
+ folder = Application.dataPath;
+ }
+
+ var folderPanelBtnClicked = GUI.Button(btnRect, new GUIContent("...", tooltip));
+
+ if (pathType == OutputPath.Root.Absolute && m_AbsolutePathProperty.stringValue == "")
+ {
+ // Empty absolute path: force absolute path root to the first drive found
+ m_AbsolutePathProperty.stringValue = DriveInfo.GetDrives().First().RootDirectory.FullName;
+ }
+
+ EditorGUI.indentLevel = indent;
+ EditorGUI.EndProperty();
+
+ if (folderPanelBtnClicked)
{
- if (GUI.Button(btnRect, new GUIContent("...", "Select the output location through your file browser")))
+ var newPath = EditorUtility.OpenFolderPanel("Select output location", folder, "");
+ if (!string.IsNullOrEmpty(newPath))
{
- var newPath = EditorUtility.OpenFolderPanel("Select output location", fullPath, "");
- if (!string.IsNullOrEmpty(newPath))
+ if (target.forceAssetsFolder && !newPath.Contains(Application.dataPath))
+ EditorUtility.DisplayDialog("Invalid Path",
+ "Selected path " + newPath + " must be in the Unity Assets directory",
+ "Ok");
+ else
{
var newValue = OutputPath.FromPath(newPath);
m_RootProperty.intValue = (int)newValue.root;
@@ -70,40 +94,13 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
m_LeafProperty.stringValue = newValue.leaf;
}
}
- }
- else
- {
- if (GUI.Button(btnRect, new GUIContent("...", "Select the output location in Unity Assets through your file browser")))
- {
- var newPath = EditorUtility.OpenFolderPanel("Select output location",
- Application.dataPath, "");
- if (!string.IsNullOrEmpty(newPath))
- {
- if (!newPath.Contains(Application.dataPath))
- EditorUtility.DisplayDialog("Invalid Path",
- "Selected path " + newPath + " must be in the Unity Assets directory",
- "Ok");
- else
- {
- var newValue = OutputPath.FromPath(newPath);
- m_RootProperty.intValue = (int)newValue.root;
- if (newValue.root == OutputPath.Root.Absolute)
- m_AbsolutePathProperty.stringValue = newValue.leaf;
- else
- m_LeafProperty.stringValue = newValue.leaf;
- }
- }
- }
- }
- if (pathType == OutputPath.Root.Absolute && m_AbsolutePathProperty.stringValue == "")
- {
- // Empty absolute path: force absolute path root to the first drive found
- m_AbsolutePathProperty.stringValue = DriveInfo.GetDrives().First().RootDirectory.FullName;
- }
+ m_RootProperty.serializedObject.ApplyModifiedProperties();
+ m_AbsolutePathProperty.serializedObject.ApplyModifiedProperties();
+ m_LeafProperty.serializedObject.ApplyModifiedProperties();
- EditorGUI.indentLevel = indent;
- EditorGUI.EndProperty();
+ GUIUtility.ExitGUI();
+ }
}
}
}
diff --git a/Editor/Sources/Recorders/_Inputs/Camera/CameraInput.cs b/Editor/Sources/Recorders/_Inputs/Camera/CameraInput.cs
index e94fe4e..ee1e8e3 100644
--- a/Editor/Sources/Recorders/_Inputs/Camera/CameraInput.cs
+++ b/Editor/Sources/Recorders/_Inputs/Camera/CameraInput.cs
@@ -298,21 +298,13 @@ protected internal override void NewFrameStarting(RecordingSession session)
{
var tag = ((CameraInputSettings)settings).CameraTag;
- try
- {
- var objs = GameObject.FindGameObjectsWithTag(tag);
+ var objs = GameObject.FindGameObjectsWithTag(tag);
- var cams = objs.Select(obj => obj.GetComponent()).Where(c => c != null);
- if (cams.Count() > 1)
- Debug.LogWarning("More than one camera has the requested target tag '" + tag + "'");
+ var cams = objs.Select(obj => obj.GetComponent()).Where(c => c != null);
+ if (cams.Count() > 1)
+ Debug.LogWarning("More than one camera has the requested target tag '" + tag + "'");
- TargetCamera = cams.FirstOrDefault();
- }
- catch (UnityException)
- {
- Debug.LogWarning("No camera has the requested target tag '" + tag + "'");
- TargetCamera = null;
- }
+ TargetCamera = cams.FirstOrDefault();
break;
}
diff --git a/Editor/Sources/Recorders/_Inputs/Camera/CameraInputSettings.cs b/Editor/Sources/Recorders/_Inputs/Camera/CameraInputSettings.cs
index 3c6da5b..0bdcb11 100644
--- a/Editor/Sources/Recorders/_Inputs/Camera/CameraInputSettings.cs
+++ b/Editor/Sources/Recorders/_Inputs/Camera/CameraInputSettings.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;
@@ -13,6 +14,8 @@ namespace UnityEditor.Recorder.Input
[Serializable]
public class CameraInputSettings : StandardImageInputSettings
{
+ internal const string k_UnsupportedCameraSourceError = "ActiveCamera is only compatible with the Legacy Render Pipeline.";
+
///
/// Indicates the Camera input type.
///
@@ -73,8 +76,35 @@ protected internal override Type InputType
protected internal override void CheckForErrors(List errors)
{
base.CheckForErrors(errors);
- if (Source == ImageSource.TaggedCamera && string.IsNullOrEmpty(CameraTag))
- errors.Add("Missing tag for camera selection");
+
+ if (Source == ImageSource.TaggedCamera)
+ {
+ if (string.IsNullOrEmpty(CameraTag))
+ errors.Add("Missing tag for camera selection");
+ else
+ {
+ try
+ {
+ var objs = GameObject.FindGameObjectsWithTag(CameraTag);
+ var cams = objs.Select(obj => obj.GetComponent()).Where(c => c != null);
+
+ if (cams.Count() == 0)
+ errors.Add("No camera has the requested target tag '" + CameraTag + "'");
+ }
+ catch (UnityException)
+ {
+ errors.Add("The requested target tag '" + CameraTag + "' does not exist in the project");
+ }
+ }
+ }
+ else if (Source == ImageSource.MainCamera && Camera.main == null)
+ {
+ errors.Add("There is no MainCamera in the project");
+ }
+ else if (Source == ImageSource.ActiveCamera && !UnityHelpers.UsingLegacyRP())
+ {
+ errors.Add(k_UnsupportedCameraSourceError);
+ }
}
}
}
diff --git a/Editor/Sources/Recorders/_Inputs/Camera/CameraInputSettingsPropertyDrawer.cs b/Editor/Sources/Recorders/_Inputs/Camera/CameraInputSettingsPropertyDrawer.cs
index 58ba011..f43a67f 100644
--- a/Editor/Sources/Recorders/_Inputs/Camera/CameraInputSettingsPropertyDrawer.cs
+++ b/Editor/Sources/Recorders/_Inputs/Camera/CameraInputSettingsPropertyDrawer.cs
@@ -40,7 +40,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
{
--EditorGUI.indentLevel;
Initialize(property);
- if (UnityHelpers.UsingHDRP())
+ if (!UnityHelpers.UsingLegacyRP())
{
m_SupportedSources = ImageSource.MainCamera | ImageSource.TaggedCamera;
}
diff --git a/Editor/Sources/Recorders/_Inputs/Camera360/Camera360Input.cs b/Editor/Sources/Recorders/_Inputs/Camera360/Camera360Input.cs
index 2e3c6e6..b902d40 100644
--- a/Editor/Sources/Recorders/_Inputs/Camera360/Camera360Input.cs
+++ b/Editor/Sources/Recorders/_Inputs/Camera360/Camera360Input.cs
@@ -118,7 +118,8 @@ protected internal override void NewFrameReady(RecordingSession session)
}
else
{
- targetCamera.RenderToCubemap(m_Cubemap1, 63, Camera.MonoOrStereoscopicEye.Mono);
+ targetCamera.stereoSeparation = 0;
+ targetCamera.RenderToCubemap(m_Cubemap1, 63, Camera.MonoOrStereoscopicEye.Left);
m_Cubemap1.ConvertToEquirect(OutputRenderTexture);
}
diff --git a/Editor/Sources/Recorders/_Inputs/RenderTextureSampler/RenderTextureSampler.cs b/Editor/Sources/Recorders/_Inputs/RenderTextureSampler/RenderTextureSampler.cs
index c0ef096..d2aad41 100644
--- a/Editor/Sources/Recorders/_Inputs/RenderTextureSampler/RenderTextureSampler.cs
+++ b/Editor/Sources/Recorders/_Inputs/RenderTextureSampler/RenderTextureSampler.cs
@@ -25,6 +25,9 @@ class RenderTextureSampler : BaseRenderTextureInput
Material m_accumulateMaterial;
Material m_normalizeMaterial;
+ RenderTextureFormat m_InternalGraphicsFormat;
+ RenderTextureReadWrite m_InternalGraphicsReadWrite;
+
class HookedCamera
{
public Camera camera;
@@ -163,12 +166,23 @@ protected internal override void BeginRecording(RecordingSession session)
m_normalizeMaterial = new Material(normalizeShader) { hideFlags = HideFlags.DontSave };
- m_renderRT = new RenderTexture(m_renderWidth, m_renderHeight, 24, RenderTextureFormat.DefaultHDR,
- RenderTextureReadWrite.Linear) { wrapMode = TextureWrapMode.Clamp };
+ m_InternalGraphicsReadWrite = RenderTextureReadWrite.sRGB;
+ m_InternalGraphicsFormat = RenderTextureFormat.ARGBHalf;
+ ImageRecorderSettings s = session.settings as ImageRecorderSettings;
+ if (s != null && s.CanCaptureHDRFrames() && s.CaptureHDR)
+ {
+ m_InternalGraphicsReadWrite = RenderTextureReadWrite.Linear;
+ m_InternalGraphicsFormat = RenderTextureFormat.DefaultHDR;
+ }
+
+ m_renderRT = new RenderTexture(m_renderWidth, m_renderHeight, 24, m_InternalGraphicsFormat, m_InternalGraphicsReadWrite)
+ {
+ wrapMode = TextureWrapMode.Clamp
+ };
for (int i = 0; i < 2; ++i)
{
- m_accumulateRTs[i] = new RenderTexture(m_renderWidth, m_renderHeight, 0, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear)
+ m_accumulateRTs[i] = new RenderTexture(m_renderWidth, m_renderHeight, 0, m_InternalGraphicsFormat, m_InternalGraphicsReadWrite)
{
wrapMode = TextureWrapMode.Clamp
};
@@ -176,7 +190,7 @@ protected internal override void BeginRecording(RecordingSession session)
m_accumulateRTs[i].Create();
}
- var rt = new RenderTexture(OutputWidth, OutputHeight, 0, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear);
+ var rt = new RenderTexture(OutputWidth, OutputHeight, 0, m_InternalGraphicsFormat, m_InternalGraphicsReadWrite);
rt.Create();
OutputRenderTexture = rt;
m_samples = new Vector2[(int)rtsSettings.SuperSampling];
@@ -213,7 +227,7 @@ protected internal override void NewFrameStarting(RecordingSession session)
continue;
hookedCam = new HookedCamera() { camera = cam, textureBackup = cam.targetTexture };
- var camRT = new RenderTexture((int)(m_renderWidth * cam.rect.width), (int)(m_renderHeight * cam.rect.height), 24, RenderTextureFormat.DefaultHDR, RenderTextureReadWrite.Linear);
+ var camRT = new RenderTexture((int)(m_renderWidth * cam.rect.width), (int)(m_renderHeight * cam.rect.height), 24, m_InternalGraphicsFormat, m_InternalGraphicsReadWrite);
cam.targetTexture = camRT;
m_hookedCameras.Add(hookedCam);
sort = true;
diff --git a/package.json b/package.json
index 891d916..b331618 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "com.unity.recorder",
"displayName": "Recorder",
- "version": "3.0.3",
+ "version": "3.0.4",
"unity": "2019.4",
"unityRelease": "1f1",
"dependencies": {
@@ -23,15 +23,19 @@
"layer"
],
"relatedPackages": {
- "com.unity.recorder.tests": "3.0.3"
+ "com.unity.recorder.tests": "3.0.4"
+ },
+ "_upm": {
+ "changelog": "### Changed\n- Prevent Unity from throwing an exception when using the file explorer to change the output file path in a recorder preset.\n- Disallow `ActiveCamera` from being selected with SRP-based projects as a camera source.\n- Display an error message and prevent Recorder from starting if MainCamera is selected while the main camera is missing from the project.\n- Better handle error messages when using a Tagged Camera while the tag is missing from the project or not assigned to the camera.\n\n### Fixed\n- Ensure that camera's rotation is retained when recording monoscopic 360 views.\n- Ensure an alpha channel is added when the Editor targets a mobile platform."
},
"upmCi": {
- "footprint": "9aa807f7e2d8b874343dfcb9574166ea011e5327"
+ "footprint": "f528a67a783618bb1d186559abc2aef4e9e6bbd5"
},
+ "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.recorder@3.0/manual/index.html",
"repository": {
"url": "https://github.cds.internal.unity3d.com/unity/com.unity.recorder.git",
"type": "git",
- "revision": "52d2b7e77e6832f279e4de2172226475c5465729"
+ "revision": "4c9f2aee426214deb37c7d9defbdbed235e3f3c1"
},
"samples": [
{