[Custom Pass] Any way to render object further than far clip plane of the camera?

So I have a space game with Solar System in real scale in it. I need to render very far distant objects, like other planets of Solar System. For example, here I render the Moon using Draw Renderers Custom Pass with Before Rendering injection point:

This works great because the Moon is relatively close to the Earth (300 000 km). But other planets is far far away. I tried to use different scale for them, like 1/60000, but then I have a problem that the Moon will render in front of Earth. I could not use second camera, because it is very expensive. So I need a way to render them in Custom Pass, even if they are further than far clip plane of the camera. Is this possible with Custom Pass? Could you please point me the direction, how can I solve this?

I think it is not possible, the frustum culling should always occur, even when using a custom pass.

One solution for your problem might be to render distant objects into the sky cubemap and display them that way.

Hey,

Right now I don’t think it’s possible without hacking the camera matrices manually to change the camera position when rendering the custom pass (Which is what i do here: GitHub - alelievr/HDRP-Custom-Passes: A bunch of custom passes made for HDRP but it only works in after post process right now).
We’re working on a more complete custom pass API which will allow you to render objects from any point of view in your scene during the rendering of your camera, I think this can solve your issue even though it may not be optimal because rendering thousands of these object will be costly.

Thanks! This would be great solution. Also there will be less than 100 such objects, each of them is a simple sphere with the material.

Hey @antoinel_unity I recently updated to Unity 2020.2 and hdrp 10.
I saw new utility functions like CustomPassUtils.RenderFromCamera and saw FPSForeground sample. But I still quite not understand, how to render anything beyond the far clip plane of the first camera?
My results so far looks like this:
Here is the first camera (clip planes 0.1 - 15)

Here is my result with custom pass whuch is using camera with clip planes 15-55:

Here is how I want it to be:

Here is current code and it is wrong, but I don’t understand where to start from:

class DistantGeometryPass : CustomPass {
    [SerializeField] Camera viewCam = null;
    [SerializeField] LayerMask layerMask = 1;
    [SerializeField] bool overrideDepthState = false;
    [SerializeField] CompareFunction depthCompareFunction = CompareFunction.LessEqual;
    [SerializeField] bool depthOnly = false;
    protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) {
        // Setup code here
    }

    protected override void Execute(CustomPassContext ctx) {
        // Disable it for scene view because it's horrible
        if (ctx.hdCamera.camera.cameraType == CameraType.SceneView)
            return;

        var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth) {
            depthState = new DepthState(overrideDepthState, depthCompareFunction),
        };
        CustomPassUtils.RenderFromCamera(ctx, viewCam, ctx.cameraColorBuffer, ctx.cameraDepthBuffer, ClearFlag.None, layerMask, overrideRenderState: depthTestOverride);
    }

    protected override void Cleanup() {
        // Cleanup code
    }
}

So, I’ve found this thread: CustomPass RenderDepthFromCamera
And modified code like this:

using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering;
using System.Reflection;

class DistantGeometryPass : CustomPass {
    [SerializeField] Camera viewCam = null;
    [SerializeField] LayerMask layerMask = 1;
    [SerializeField] bool overrideDepthState = false;
    [SerializeField] CompareFunction depthCompareFunction = CompareFunction.LessEqual;
    [SerializeField] bool depthOnly = false;

    FieldInfo cullingResultField;

    protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) {
        cullingResultField = typeof(CustomPassContext).GetField(nameof(CustomPassContext.cullingResults));
    }

    protected override void Execute(CustomPassContext ctx) {
        // Disable it for scene view because it's horrible
        if (ctx.hdCamera.camera.cameraType == CameraType.SceneView)
            return;

        viewCam.TryGetCullingParameters(out var cullingParams);

        // Assign the custom culling result to the context
        // so it'll be used for the following operations
        cullingResultField.SetValueDirect(__makeref(ctx), ctx.renderContext.Cull(ref cullingParams));

        var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth) {
            depthState = new DepthState(overrideDepthState, depthCompareFunction),
        };
        CustomPassUtils.RenderFromCamera(ctx, viewCam, ctx.cameraColorBuffer, ctx.cameraDepthBuffer, ClearFlag.None, layerMask, overrideRenderState: depthTestOverride);
    }

    protected override void Cleanup() {
        // Cleanup code
    }
}

The result looks like this:

Which is better, but still not the refernce.

Maybe this could help: https://docs.unity3d.com/ScriptReference/Camera-layerCullDistances.html

Cheers

Thanks for advice. Unfortunately, it will not solve original problem with planets, because there are huge distances between them. And there will be depth buffer precision issues and other stuff like missing cascade shadows, if the camera has big far clip plane value and a small near clip plane at the same time.

Ok, I think I solved it. Firstly, I render objects from other camera into custom color/depth using above pass. Then copy data from custom color buffer to camera buffer using second pass and LEqual ZTest. At the same time I am writing to camera depth buffer values just above far clip plane on that pixels.
It will obviously work only for Before Rendering and After Opaque injection points.
I don’t know if it is the best solution, but at least it is working.

Result:

Custom Pass Setup:

(As you can see there is no shadows on the second half of the image, but it is fine for me, becausue I am rendering very distant objects).

1 Like

This looks like a good solution to a similar situation I’m trying to address. I understand how the first pass copies the secondary camera’s color/depth buffers to the respective custom buffers. But I’m a bit new to SRP/HDRP, and am stuck trying to figure out how to replicate the function of the second pass.

I can’t find any really useful examples of combining the custom color/depth buffers with an existing camera’s buffers. Does anyone know any basic tutorials or docs that explain this?

in your custom pass inspector, set buffer to custom

then in the C# of that custom pass, set the target to cameraColorBuffer like this:

 CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer);
        CoreUtils.DrawFullScreen(ctx.cmd, customPassMaterial, ctx.propertyBlock, 0);

then in your customPassMaterial, load the custom buffer in the shader:

float4 bufferColor = LoadCustomColor(pcs);
return bufferColor;
1 Like

Thanks @robot-ink_1 ! I’ll give this a try