Search Unity

HDRP 10: API for rendering an inactive camera to another one

Discussion in 'High Definition Render Pipeline' started by slime73, Sep 17, 2020.

  1. slime73

    slime73

    Joined:
    May 14, 2017
    Posts:
    107
    HDRP 10.0's changelog has a section saying there's now the ability to render the contents of an inactive camera via some Custom Pass mechanism: https://docs.unity3d.com/Packages/c....0/manual/whats-new-10-0.html#custom-pass-api

    However I can't find much information about this. I found this PR which was merged: https://github.com/Unity-Technologies/Graphics/pull/170 but according to the PR comments and commits, the API is internal and not usable in client code yet.

    Is the feature meant to be usable yet? If it's not actually available in 10.0 and the changelog is wrong, is there a plan or timeline for when it will be stabilized and made public?
     
    Egad_McDad likes this.
  2. slime73

    slime73

    Joined:
    May 14, 2017
    Posts:
    107
    Answering my own question, I've found a public CustomPassUtils.RenderFromCamera function. :)
     
    Egad_McDad likes this.
  3. Lunatix

    Lunatix

    Joined:
    Dec 28, 2012
    Posts:
    11
    May I ask if you had any luck using this?
    I'm trying to render weapon and arms in a fist person shooter with a fixed FoV but I can't get it to work.

    The basic idea was:
    * A main camera which renders the whole scene except weapon and arms
    * A second camera (disabled) with a lower FoV attached as a child to the first one
    * A custom pass which only renders the weapons and arms in the "CharacterFirstPersonFoV" layer

    The problem is, I have experimented with this and can't get it to run because there is basically zero documentation for this.

    Do I have to call anything other than `CustomPassUtils.RenderFromCamera` in my custom pass?
    Can I directly render to the display or do I have to use a render target?

    This is what I have for now:
    upload_2020-10-25_15-57-34.png

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering.HighDefinition;
    3. using UnityEngine.Rendering;
    4. using UnityEngine.Experimental.Rendering;
    5.  
    6. class ManipulateFoVPass : CustomPass {
    7.     [SerializeField]
    8.     private LayerMask layerMask = 1;
    9.     [SerializeField]
    10.     private CustomPass.RenderQueueType renderQueueFilter = CustomPass.RenderQueueType.AllOpaque;
    11.     [SerializeField]
    12.     private Camera camera;
    13.  
    14.     protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) {
    15.  
    16.     }
    17.  
    18.     protected override void AggregateCullingParameters(ref ScriptableCullingParameters cullingParameters, HDCamera hdCamera) {
    19.         cullingParameters.cullingMask |= (uint)(int)layerMask;
    20.     }
    21.  
    22.     protected override void Execute(CustomPassContext ctx) {
    23.         CustomPassUtils.RenderFromCamera(ctx, this.camera, this.layerMask);
    24.     }
    25.  
    26.     protected override void Cleanup() {
    27.  
    28.     }
    29. }
     
  4. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    264
    Hello, by looking at the code you have, it should work.

    I made something similar a while ago, I'll drop the code here in case it's useful for you:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering.HighDefinition;
    3. using UnityEngine.Rendering;
    4. using UnityEngine.Experimental.Rendering;
    5.  
    6. #if UNITY_EDITOR
    7.  
    8. using UnityEditor.Rendering.HighDefinition;
    9.  
    10. [CustomPassDrawer(typeof(FPSForeground))]
    11. class FPSForegroundEditor : CustomPassDrawer
    12. {
    13.     protected override PassUIFlag commonPassUIFlags => PassUIFlag.Name;
    14. }
    15.  
    16. #endif
    17.  
    18. class FPSForeground : CustomPass
    19. {
    20.     public float        fov = 45;
    21.     public LayerMask    foregroundMask;
    22.     Camera              foregroundCamera;
    23.     const string        kCameraTag = "_FPSForegroundCamera";
    24.  
    25.     protected override void AggregateCullingParameters(ref ScriptableCullingParameters cullingParameters, HDCamera hdCamera)
    26.         => cullingParameters.cullingMask |= (uint)foregroundMask.value;
    27.  
    28.     protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
    29.     {
    30.         // Hidden foreground camera:
    31.         var cam = GameObject.Find(kCameraTag);
    32.         if (cam == null)
    33.         {
    34.             cam = new GameObject(kCameraTag) { hideFlags = HideFlags.DontSave | HideFlags.NotEditable };
    35.             cam.AddComponent<Camera>();
    36.         }
    37.         foregroundCamera = cam.GetComponent<Camera>();
    38.     }
    39.  
    40.     protected override void Execute(CustomPassContext ctx)
    41.     {
    42.         // Disable it for scene view because it's horrible
    43.         if (ctx.hdCamera.camera.cameraType == CameraType.SceneView)
    44.             return;
    45.         var currentCam = ctx.hdCamera.camera;
    46.         // Copy settings of our current camera
    47.         foregroundCamera.transform.SetPositionAndRotation(currentCam.transform.position, currentCam.transform.rotation);
    48.         foregroundCamera.CopyFrom(ctx.hdCamera.camera);
    49.         // Make sure the camera is disabled, we don't want it to render anything.
    50.         foregroundCamera.enabled = false;
    51.         foregroundCamera.fieldOfView = fov;
    52.         foregroundCamera.cullingMask = foregroundMask;
    53.         var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    54.         {
    55.             depthState = new DepthState(true, CompareFunction.Always),
    56.         };
    57.         CustomPassUtils.RenderFromCamera(ctx, foregroundCamera, ctx.cameraColorBuffer, ctx.cameraDepthBuffer, ClearFlag.None, foregroundMask, overrideRenderState: depthTestOverride);
    58.     }
    59.  
    60.     protected override void Cleanup()
    61.     {
    62.         CoreUtils.Destroy(foregroundCamera.gameObject);
    63.     }
    64. }
    I'll make a cleaner version available on the custom pass repository sample when I upgrade to HDRP 10.x.

    Note that the script above require the objects in the foreground layer to be excluded from the camera culling mask (to avoid rendering the object twice)
     
    PavelDubovik likes this.
  5. KCGames47

    KCGames47

    Joined:
    Nov 3, 2014
    Posts:
    39
    Here ^ the documentation that doesn't even work :) I guess now we're all set
     
  6. PavelDubovik

    PavelDubovik

    Joined:
    Jun 29, 2021
    Posts:
    3
    antoinel_unity Hi!
    I wanna to do independent fov for first person arms and items, like described in this thread. So I implemented my CustomPass, based on your code. All work well for 3d objects, but the problem is that in my game some items use world space UI. It seems, that camera override doesn't work with UI properly, because in my case UI elements take fov from main camera. I also have been changing first person camera position for test. As a result, 3d objects changed their position in game view according position of first person camera, but UI stayed at same place, like it use position matrix from main camera. Could you look at my code of Custom Pass, please? Maybe I miss something.
    I tested it on HDRP 12.1.1 and HDRP 13.1.7.
    Code (CSharp):
    1.  
    2. using System;
    3. using UnityEngine.Assertions;
    4. using UnityEngine.Rendering;
    5. using UnityEngine.Rendering.HighDefinition;
    6. using Object = UnityEngine.Object;
    7.  
    8. namespace DCD.Client.Simulation
    9. {
    10.     [Serializable]
    11.     public class FirstPersonObjectsPass: DrawRenderersCustomPass
    12.     {
    13.         private FirstPersonCamera _firstPersonCamera;
    14.  
    15.         /// <inheritdoc />
    16.         protected override bool executeInSceneView => false;
    17.  
    18.         /// <inheritdoc />
    19.         protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
    20.         {
    21.             base.Setup(renderContext, cmd);
    22.  
    23.             _firstPersonCamera = Object.FindObjectOfType<FirstPersonCamera>();
    24.             Assert.IsNotNull(_firstPersonCamera);
    25.         }
    26.  
    27.         /// <inheritdoc />
    28.         protected override void Execute(CustomPassContext ctx)
    29.         {
    30.             var currentCam = ctx.hdCamera.camera;
    31.  
    32.             _firstPersonCamera.transform.SetPositionAndRotation(currentCam.transform.position, currentCam.transform.rotation);
    33.             _firstPersonCamera.Camera.CopyFrom(ctx.hdCamera.camera);
    34.             _firstPersonCamera.Camera.enabled = false;
    35.  
    36.             _firstPersonCamera.Camera.fieldOfView = _firstPersonCamera.FOV;
    37.             _firstPersonCamera.Camera.cullingMask = _firstPersonCamera.CullingMask;
    38.  
    39.             CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ctx.cameraDepthBuffer, clearFlags);
    40.  
    41.             using (new CustomPassUtils.OverrideCameraRendering(ctx, _firstPersonCamera.Camera))
    42.             {
    43.                 base.Execute(ctx);
    44.             }
    45.         }
    46.     }
    47. }
    48.  
    and here my settings for 3d objects
    upload_2022-3-28_10-49-55.png
    and for UI
    upload_2022-3-28_10-51-21.png
    I'm sure, than UI use this pass, because changing near clipping plane on first person camera takes affect on UI.
     
  7. olavrv

    olavrv

    Joined:
    May 26, 2015
    Posts:
    515
    Is there a cheap way (low performance impact) to use custompass to get a disabled (?) camera which only renders UIelements to a rendertexture?

    Not sure I understand this custom pass business :D

    Appreciate any help!
     
  8. gewl

    gewl

    Joined:
    May 19, 2016
    Posts:
    95
    I'd check out https://github.com/alelievr/HDRP-UI-Camera-Stacking
     
    LumaPxxx likes this.
  9. LumaPxxx

    LumaPxxx

    Joined:
    Oct 3, 2010
    Posts:
    339
  10. olavrv

    olavrv

    Joined:
    May 26, 2015
    Posts:
    515
    @gewl I am using that now, works well - but does not significantly reduce camera CPU overhead.
     
  11. olavrv

    olavrv

    Joined:
    May 26, 2015
    Posts:
    515
    I am using custom pass to render from a disabled camera. This works perfect for opaque and transparent geometry, but it doesn't render UI elements. Any help with this would be appreciated!

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering.HighDefinition;
    3. using UnityEngine.Rendering;
    4. using UnityEngine.Experimental.Rendering;
    5. using System.Reflection;
    6.  
    7. #if UNITY_EDITOR
    8.  
    9. using UnityEditor.Rendering.HighDefinition;
    10.  
    11. [CustomPassDrawer(typeof(RenderFromCamera))]
    12. class FPSForegroundEditor : CustomPassDrawer
    13. {
    14.     protected override PassUIFlag commonPassUIFlags => PassUIFlag.Name;
    15. }
    16.  
    17. #endif
    18.  
    19. class RenderFromCamera : CustomPass
    20. {
    21.  
    22.     public LayerMask foregroundMask;
    23.     public Camera UICamera;
    24.     public RenderTexture targetRenderTexture;
    25.     public Material overrideMaterial;
    26.     public ClearFlag clearFlag = ClearFlag.None;
    27.     public RenderQueueType renderQue = RenderQueueType.All;
    28.     public CompareFunction depthState = CompareFunction.NotEqual;
    29.     protected override bool executeInSceneView => false;
    30.     FieldInfo cullingResultField, hdCamField;
    31.  
    32.  
    33.     protected override void AggregateCullingParameters(ref ScriptableCullingParameters cullingParameters, HDCamera hdCamera)
    34.         => cullingParameters.cullingMask |= (uint)foregroundMask.value;
    35.  
    36.     protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
    37.     {
    38.         cullingResultField = typeof(CustomPassContext).GetField(nameof(CustomPassContext.cullingResults));
    39.     }
    40.  
    41.     protected override void Execute(CustomPassContext ctx)
    42.     {
    43.         if (ctx.hdCamera.camera.cameraType == CameraType.SceneView)
    44.             return;
    45.  
    46.         UICamera.enabled = false;
    47.  
    48.         var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    49.         {
    50.             depthState = new DepthState(true, depthState),
    51.         };
    52.  
    53.         UICamera.TryGetCullingParameters(out var cullingParams);
    54.  
    55.         cullingResultField.SetValueDirect(__makeref(ctx), ctx.renderContext.Cull(ref cullingParams));
    56.  
    57.         CustomPassUtils.RenderFromCamera(ctx, UICamera, /*targetRenderTexture*/ctx.cameraColorBuffer, clearFlag, foregroundMask, renderQue, overrideRenderState: depthTestOverride);
    58.     }
    59.  
    60.     protected override void Cleanup()
    61.     {
    62.     }
    63. }