diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md
index 4778b6811cc..e67e9b5026f 100644
--- a/com.unity.render-pipelines.high-definition/CHANGELOG.md
+++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md
@@ -118,6 +118,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added a DisplayInfo attribute to specify a name override and a display order for Volume Component fields (used only in default inspector for now).
- Added Min distance to contact shadows.
- Added support for Depth of Field in path tracing (by sampling the lens aperture).
+- Added an API in HDRP to override the camera within the rendering of a frame (mainly for custom pass).
### Fixed
- Fix when rescale probe all direction below zero (1219246)
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs
index 74ad3623b32..1af0a0628af 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/Camera/HDCamera.cs
@@ -200,6 +200,9 @@ internal struct ShadowHistoryUsage
internal SkyUpdateContext m_LightingOverrideSky = new SkyUpdateContext();
+ /// Mark the HDCamera as persistant so it won't be destroyed if the camera is disabled
+ internal bool isPersistent = false;
+
// VisualSky is the sky used for rendering in the main view.
// LightingSky is the sky used for lighting the scene (ambient probe and sky reflection)
// It's usually the visual sky unless a sky lighting override is setup.
@@ -375,7 +378,7 @@ internal bool IsVolumetricReprojectionEnabled()
// That way you will never update an HDCamera and forget to update the dependent system.
// NOTE: This function must be called only once per rendering (not frame, as a single camera can be rendered multiple times with different parameters during the same frame)
// Otherwise, previous frame view constants will be wrong.
- internal void Update(FrameSettings currentFrameSettings, HDRenderPipeline hdrp, MSAASamples newMSAASamples, XRPass xrPass)
+ internal void Update(FrameSettings currentFrameSettings, HDRenderPipeline hdrp, MSAASamples newMSAASamples, XRPass xrPass, bool allocateHistoryBuffers = true)
{
// Inherit animation settings from the parent camera.
Camera aniCam = (parentCamera != null) ? parentCamera : camera;
@@ -404,6 +407,7 @@ internal void Update(FrameSettings currentFrameSettings, HDRenderPipeline hdrp,
UpdateAntialiasing();
// Handle memory allocation.
+ if (allocateHistoryBuffers)
{
// Have to do this every frame in case the settings have changed.
// The condition inside controls whether we perform init/deinit or not.
@@ -457,7 +461,7 @@ internal void Update(FrameSettings currentFrameSettings, HDRenderPipeline hdrp,
}
else
{
- finalViewport = new Rect(camera.pixelRect.x, camera.pixelRect.y, camera.pixelWidth, camera.pixelHeight);
+ finalViewport = GetPixelRect();
}
actualWidth = Math.Max((int)finalViewport.size.x, 1);
@@ -493,12 +497,18 @@ internal void Update(FrameSettings currentFrameSettings, HDRenderPipeline hdrp,
RTHandles.SetReferenceSize(nonScaledViewport.x, nonScaledViewport.y, msaaSamples);
}
+ /// Set the RTHandle scale to the actual camera size (can be scaled)
+ internal void SetReferenceSize()
+ {
+ RTHandles.SetReferenceSize(actualWidth, actualHeight, msaaSamples);
+ m_HistoryRTSystem.SwapAndSetReferenceSize(actualWidth, actualHeight, msaaSamples);
+ }
+
// Updating RTHandle needs to be done at the beginning of rendering (not during update of HDCamera which happens in batches)
// The reason is that RTHandle will hold data necessary to setup RenderTargets and viewports properly.
internal void BeginRender(CommandBuffer cmd)
{
- RTHandles.SetReferenceSize(actualWidth, actualHeight, msaaSamples);
- m_HistoryRTSystem.SwapAndSetReferenceSize(actualWidth, actualHeight, msaaSamples);
+ SetReferenceSize();
m_RecorderCaptureActions = CameraCaptureBridge.GetCaptureActions(camera);
@@ -556,7 +566,7 @@ internal static void CleanUnused()
bool hasPersistentHistory = camera.m_AdditionalCameraData != null && camera.m_AdditionalCameraData.hasPersistentHistory;
// We keep preview camera around as they are generally disabled/enabled every frame. They will be destroyed later when camera.camera is null
- if (camera.camera == null || (!camera.camera.isActiveAndEnabled && camera.camera.cameraType != CameraType.Preview && !hasPersistentHistory))
+ if (camera.camera == null || (!camera.camera.isActiveAndEnabled && camera.camera.cameraType != CameraType.Preview && !hasPersistentHistory && !camera.isPersistent))
s_Cleanup.Add(key);
}
@@ -772,6 +782,9 @@ internal void UpdateCurrentSky(SkyManager skyManager)
}
}
}
+
+ internal void OverridePixelRect(Rect newPixelRect) => m_OverridePixelRect = newPixelRect;
+ internal void ResetPixelRect() => m_OverridePixelRect = null;
#endregion
#region Private API
@@ -803,6 +816,7 @@ public RTHandle Allocator(string id, int frameIndex, RTHandleSystem rtHandleSyst
IEnumerator> m_RecorderCaptureActions;
int m_RecorderTempRT = Shader.PropertyToID("TempRecorder");
MaterialPropertyBlock m_RecorderPropertyBlock = new MaterialPropertyBlock();
+ Rect? m_OverridePixelRect = null;
void SetupCurrentMaterialQuality(CommandBuffer cmd)
{
@@ -1237,6 +1251,14 @@ void ReleaseHistoryBuffer()
{
m_HistoryRTSystem.ReleaseAll();
}
+
+ Rect GetPixelRect()
+ {
+ if (m_OverridePixelRect != null)
+ return m_OverridePixelRect.Value;
+ else
+ return new Rect(camera.pixelRect.x, camera.pixelRect.y, camera.pixelWidth, camera.pixelHeight);
+ }
#endregion
}
}
diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs
index ba05ca5c744..2a8af992cc0 100644
--- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs
+++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs
@@ -290,6 +290,7 @@ internal int GetMaxScreenSpaceShadows()
bool m_ValidAPI; // False by default mean we render normally, true mean we don't render anything
bool m_IsDepthBufferCopyValid;
RenderTexture m_TemporaryTargetForCubemaps;
+ HDCamera m_CurrentHDCamera;
private CameraCache<(Transform viewer, HDProbe probe, int face)> m_ProbeCameraCache = new
CameraCache<(Transform viewer, HDProbe probe, int face)>();
@@ -1946,6 +1947,7 @@ AOVRequestData aovRequest
// Updates RTHandle
hdCamera.BeginRender(cmd);
+ m_CurrentHDCamera = hdCamera;
if (m_RayTracingSupported)
{
@@ -2607,6 +2609,8 @@ void Callback(CommandBuffer c, HDCamera cam)
// Otherwise command would not be rendered in order.
renderContext.ExecuteCommandBuffer(cmd);
cmd.Clear();
+
+ m_CurrentHDCamera = null;
}
struct BlitFinalCameraTextureParameters
@@ -4813,5 +4817,98 @@ void SendColorGraphicsBuffer(CommandBuffer cmd, HDCamera hdCamera)
VFXManager.SetCameraBuffer(hdCamera.camera, VFXCameraBufferTypes.Color, colorBuffer, 0, 0, hdCamera.actualWidth, hdCamera.actualHeight);
}
}
+
+ ///
+ /// Overrides the current camera, changing all the matrices and view parameters for the new one.
+ /// It allows you to render objects from another camera, which can be useful in custom passes for example.
+ ///
+ internal struct OverrideCameraRendering : IDisposable
+ {
+ CommandBuffer cmd;
+ Camera overrideCamera;
+ HDCamera overrideHDCamera;
+ float originalAspect;
+
+ ///
+ /// Overrides the current camera, changing all the matrices and view parameters for the new one.
+ ///
+ /// The current command buffer in use
+ /// The camera that will replace the current one
+ ///
+ ///
+ /// using (new HDRenderPipeline.OverrideCameraRendering(cmd, overrideCamera))
+ /// {
+ /// ...
+ /// }
+ ///
+ ///
+ public OverrideCameraRendering(CommandBuffer cmd, Camera overrideCamera)
+ {
+ this.cmd = cmd;
+ this.overrideCamera = overrideCamera;
+ this.overrideHDCamera = null;
+ this.originalAspect = 0;
+
+ if (!IsContextValid(overrideCamera))
+ return;
+
+ var hdrp = HDRenderPipeline.currentPipeline;
+ overrideHDCamera = HDCamera.GetOrCreate(overrideCamera);
+
+ // Mark the HDCamera as persistant so it's not deleted because it's camera is disabled.
+ overrideHDCamera.isPersistent = true;
+
+ // We need to patch the pixel rect of the camera because by default the camera size is synchronized
+ // with the game view and so it breaks in the scene view. Note that we can't use Camera.pixelRect here
+ // because when we assign it, the change is not instantaneous and is not reflected in pixelWidth/pixelHeight.
+ overrideHDCamera.OverridePixelRect(hdrp.m_CurrentHDCamera.camera.pixelRect);
+ // We also sync the aspect ratio of the camera, this time using the camera instead of HDCamera.
+ // This will update the projection matrix to match the aspect of the current rendering camera.
+ originalAspect = overrideCamera.aspect;
+ overrideCamera.aspect = (float)hdrp.m_CurrentHDCamera.camera.pixelRect.width / (float)hdrp.m_CurrentHDCamera.camera.pixelRect.height;
+
+ // Update HDCamera datas
+ overrideHDCamera.Update(overrideHDCamera.frameSettings, hdrp, hdrp.m_MSAASamples, hdrp.m_XRSystem.emptyPass, allocateHistoryBuffers: false);
+ // Reset the reference size as it could have been changed by the override camera
+ hdrp.m_CurrentHDCamera.SetReferenceSize();
+ overrideHDCamera.UpdateShaderVariablesGlobalCB(ref hdrp.m_ShaderVariablesGlobalCB, hdrp.m_FrameCount);
+
+ ConstantBuffer.PushGlobal(cmd, hdrp.m_ShaderVariablesGlobalCB, HDShaderIDs._ShaderVariablesGlobal);
+ }
+
+ bool IsContextValid(Camera overrideCamera)
+ {
+ var hdrp = HDRenderPipeline.currentPipeline;
+
+ if (hdrp.m_CurrentHDCamera == null)
+ {
+ Debug.LogError("OverrideCameraRendering can only be called inside the render loop !");
+ return false;
+ }
+
+ if (overrideCamera == hdrp.m_CurrentHDCamera.camera)
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// Reset the camera settings to the original camera
+ ///
+ void IDisposable.Dispose()
+ {
+ if (!IsContextValid(overrideCamera))
+ return;
+
+ overrideHDCamera.ResetPixelRect();
+ overrideCamera.aspect = originalAspect;
+
+ var hdrp = HDRenderPipeline.currentPipeline;
+ // Reset the reference size as it could have been changed by the override camera
+ hdrp.m_CurrentHDCamera.SetReferenceSize();
+ hdrp.m_CurrentHDCamera.UpdateShaderVariablesGlobalCB(ref hdrp.m_ShaderVariablesGlobalCB, hdrp.m_FrameCount);
+ ConstantBuffer.PushGlobal(cmd, hdrp.m_ShaderVariablesGlobalCB, HDShaderIDs._ShaderVariablesGlobal);
+ }
+ }
}
}