Skip to content

Camera override API #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions com.unity.render-pipelines.high-definition/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ internal struct ShadowHistoryUsage

internal SkyUpdateContext m_LightingOverrideSky = new SkyUpdateContext();

/// <summary>Mark the HDCamera as persistant so it won't be destroyed if the camera is disabled</summary>
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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -493,12 +497,18 @@ internal void Update(FrameSettings currentFrameSettings, HDRenderPipeline hdrp,
RTHandles.SetReferenceSize(nonScaledViewport.x, nonScaledViewport.y, msaaSamples);
}

/// <summary>Set the RTHandle scale to the actual camera size (can be scaled)</summary>
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);

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -803,6 +816,7 @@ public RTHandle Allocator(string id, int frameIndex, RTHandleSystem rtHandleSyst
IEnumerator<Action<RenderTargetIdentifier, CommandBuffer>> m_RecorderCaptureActions;
int m_RecorderTempRT = Shader.PropertyToID("TempRecorder");
MaterialPropertyBlock m_RecorderPropertyBlock = new MaterialPropertyBlock();
Rect? m_OverridePixelRect = null;

void SetupCurrentMaterialQuality(CommandBuffer cmd)
{
Expand Down Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)>();
Expand Down Expand Up @@ -1946,6 +1947,7 @@ AOVRequestData aovRequest

// Updates RTHandle
hdCamera.BeginRender(cmd);
m_CurrentHDCamera = hdCamera;

if (m_RayTracingSupported)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -4813,5 +4817,98 @@ void SendColorGraphicsBuffer(CommandBuffer cmd, HDCamera hdCamera)
VFXManager.SetCameraBuffer(hdCamera.camera, VFXCameraBufferTypes.Color, colorBuffer, 0, 0, hdCamera.actualWidth, hdCamera.actualHeight);
}
}

/// <summary>
/// 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.
/// </summary>
internal struct OverrideCameraRendering : IDisposable
{
CommandBuffer cmd;
Camera overrideCamera;
HDCamera overrideHDCamera;
float originalAspect;

/// <summary>
/// Overrides the current camera, changing all the matrices and view parameters for the new one.
/// </summary>
/// <param name="cmd">The current command buffer in use</param>
/// <param name="overrideCamera">The camera that will replace the current one</param>
/// <example>
/// <code>
/// using (new HDRenderPipeline.OverrideCameraRendering(cmd, overrideCamera))
/// {
/// ...
/// }
/// </code>
/// </example>
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;
}

/// <summary>
/// Reset the camera settings to the original camera
/// </summary>
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);
}
}
}
}