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); + } + } } }