Search Unity

What is a Render Pass in Simple Words?

Discussion in 'Shaders' started by Koval331, Jul 23, 2019.

  1. Koval331

    Koval331

    Joined:
    Feb 3, 2019
    Posts:
    114
    What is a render pass and how does it work in combination with render paths, and render pipeline?

    p.s. If I remember correctly, when I worked in HLSL long time ago there was no such thing as pass so that's why I am confused.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    First, render path vs render pipeline. The terminology here is being used to differentiate between the two built in rendering systems, the Forward Rendering Path and Deferred Rendering Path, and the newer Scriptable Render Pipeline (SRP) based rendering systems, the Lightweight Pipeline (aka LWRP, recently renamed the Universal Pipeline) and High Definition Pipeline (aka HDRP). The main difference is the built in rendering paths are black box implementations outside of their shader code, where as the render pipelines are written in c# and can be directly modified if wanted.

    There are two different things that are commonly referred to as a “render pass” in Unity.

    The most common one is a shader pass. In Shader Lab, the propriety language used to define shaders for Unity, you can define multiple passes. Each pass is an HLSL vertex and fragment (aka pixel) shader pair, with optional geometry and / or tessellation related shader stage functions, along with basic render state and other information about when Unity should use that pass stored in a Tags block. For example, a lit shader will usually have a “LightMode”=“ForwardBase” pass to denote the main pass used for the rendering the object when using the forward rendering path, a “LightMode”=“Deferred” pass for rendering the object for deferred gbuffers for the deferred rendering path, and “LightMode”=“ShadowCaster” for the shadow map rendering of both rendering paths. There are also multi-pass shaders which have multiple passes defined, usually without any light mode, which for the Forward rendering path Unity will render a mesh multiple times, once for each pass, on top of any forward LightMode passes. If a shader has both deferred and forward passes, only the passes appropriate to the current rendering path will be used. Similarly, the SRPs have their own specific shader pass tags, so technically a single .shader file could have implementations for the forward and deferred path, and multiple SRPs.

    The other way the term render pass is used is a render of some view of the scene. For example, when using the forward rendering path, Unity will usually generate a camera depth texture by rendering all opaque objects in the scene from the point of view of the main camera using those objects’ shadow caster shader pass. Then again for each shadow casting light to generate the shadow maps. Then separately rendering the scene again with the un-tagged and forward shader passes to produce the image you see on screen. Those are multiple separate render passes. There’s also the concept of a replacement shader pass, which renders the scene from a camera view with all shaders in the scene overridden by a new one. It can also be used to describe something being rendered manually into a render texture use manual DrawMesh commands or command buffers, or even to something like Unity’s UI systems which can render separately from the main scene.
     
  3. Koval331

    Koval331

    Joined:
    Feb 3, 2019
    Posts:
    114
    Thanks for your answer!

    So shader pass is a collection of settings / functions to configure shader how / when to draw stuff.

    And render pass - is an act of actually calling graphics API and drawing stuff somewhere (e.g. on a frame buffer).

    Do I understand everything correclty?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    That's mostly accurate. I would also add that "shader pass" might be used to refer to the Pass {} block within the shader file, or the entire act of rendering that pass for a mesh including the API calls through to writing out to the current render target, but for a single mesh.
    Unity's sub meshes mess with this definition a bit as a single mesh object may actually require multiple draw calls, but this is generally hidden from the user unless you dig deep into the API calls with an external program.

    I'd probably differentiate render pass a bit more than just "the actual API calls" to include the option for multiple meshes and potentially multiple shader passes. My mental model of a "render pass" is everything from the time a render target is set to the last thing that is rendered to it and that render buffer is no longer the target. The forward rendering path is a great example of this as a lot of things happen in the "forward render pass", like drawing all opaque objects using the any forward base shader passes, forward add shader passes, unlit shader passes, then rendering the skybox, potentially doing some mid-render pass post processing (like ambient occlusion), then rendering all transparent objects using those forward base, forward add, and unlit shader passes. I kind of see that as one render pass, though a lot of different things happen. Other stuff like additional post processing and UI may be rendered to the same buffer afterwards, but I see that as separate render passes (unless the UI is rendered in-camera or in-world rather than after post processing). It's all a bit arbitrary though, so don't get hung up on the definitions too much.
     
    Koval331 likes this.
  5. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    @bgolus - is it correct to assume (can we rely upon it?) that a shader with passes:

    A: Pass { Tags { "LightMode" = "ForwardBase" } }
    B: Pass { Tags { "LightMode" = "ForwardAdd" } }
    C: Pass { Tags { /* nothing */ } }
    D: Pass { Tags { "LightMode" = "ShadowCaster" } }

    ... will render precisely:

    1. Pass D (once only; builds _CameraDepthTexture)
    2. Pass A (once only; main light for scene, plus (optional) SH if enabled)
    3. ... optional: Pass B (can be zero, or more, depends on scene)
    4. ... optional: Pass B (can be zero, or more, depends on scene)
    5. ... optional: Pass B (can be zero, or more, depends on scene)
    6. ... optional: Pass B (can be zero, or more, depends on scene)
    7. ...
    8. Pass C (once only)
    ?

    (asking because: if I understand this correctly, I think it's useful to record the above as a corrollary of what you describe, helps plan passes. And thinking about this myself because: I need to delay all light calculations until ALL lights have been sent to the shader. So My Pass A and B will be nearly empty (just grabbing a little data for later), and my "real" lighting will happen in Pass C. This seems like it should work fine, and doesn't violate any expectations of Unity)
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Yes, and no.

    On the yes side, above is mostly correct, apart from the shadow caster pass also being used for, well, shadows, and will be run up to 4 times for the main directional light (cascades), 6 times for point lights, and once for each shadow casting spot and additional directional light.

    On the no side, there’s a bug with Unity’s dynamic batching where sometimes the passes on some objects will be run out of order. This can be fixed by disabling dynamic batching, either for the project for this shader specifically. There’s also the issue that passes of other meshes may be intertwined with these passes. That doesn’t happen often, and may be related to the same dynamic batching issue, but I don’t know for sure.

    However my question would be, why not use the LWRP/URP? Both of those are single pass renderers where all lights are already fed into the “base” pass. You can do whatever you need to the lighting information in the base shader without any issues.
     
  7. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    As a side-note: I am coming to the point where I disable dynamic batching on all shaders as a matter of course. It causes way too many problems, breaks too many things, comes with lots of weirdness, and after all that provides very little benefit (the cases I've had where it would genuinely make a big difference ... I've always found it easier to do GPU-side instancing instead!)

    Eventually, yes!

    But not while almost 50% of people are on 2018 or less: https://forum.unity.com/threads/edi...3-2019-now-only-spenders-are-included.485317/

    ...and I'm waiting for the last few question-marks on core features in URP to be added/resolved, including the all-important code-sharing ones :).