Search Unity

Question Render material twice in a frame to different targets or with different keywords

Discussion in 'Shaders' started by Curwen, Jul 19, 2022.

  1. Curwen

    Curwen

    Joined:
    Jan 12, 2015
    Posts:
    13
    Hello,

    I'm using URP and trying to find a clever way to create a (uber) material that can be rendered twice differently each frame. Specifically, I want to apply a fullscreen effect (e.g. outlines) but not to 'detail' textures (e.g. hatching overlay textures applied on top of the main textures).

    Things I've already discarded or trying to avoid having to do:
    * I can't simply use the Render Objects (Experimental)'s feature material override, as I need to preserve the properties of each material (e.g. the hatching can have different properties per material)
    * Duplicating meshes with a different material/shader and assigning them to a different layer (programmatically or manually like games sometimes use decal meshes on top of the main mesh), which gets rendered by another Renderer Feature via a Layer Mask -- that is too much trouble to do manually, and doing it programmatically would be a last resort solution as it'd double all geometry, and may become pretty tricky for anything that's animated.

    I've been digging in the forums for a couple days but couldn't find a solution that seems really appropriate for my setup.

    Current (unsuccessful) attempt:
    So I thought I would try to be clever and use a global shader keyword (or property) to force the engine to use a different shader variant. I'd let Unity render my opaques once normally and by default my shader would not render those detail textures. I created a Renderer Feature that I can add to the queue that calls Shader.SetGlobalKeyword to set a keyword (e.g. _DETAILS_ENABLED) to true/false.

    The queue of Renderer Features in my Universal Renderer Data looks like (all after rendering opaques):
    • Copy Camera Color to texture ID '_Original'
    • Enable Global Keyword _DETAILS_ENABLED
    • Render Objects (Experimental) (hoping that the keyword changes which variant of the shader is used)
    • Copy Camera Color to texture ID '_Details'
    • ...
    • Calculate outlines from _Original texture and save them to texture ID '_Outlines'
    • Blend _Original, _Details and _Outlines textures to Camera Color
    • Disable Global Keyword _DETAILS_ENABLED (so it's off for next frame's opaque render)
    Sadly this doesn't work. If I disable my last feature so _DETAILS_ENABLED stays on forever, I can confirm that the quick test shader I built works correctly, it changes when that _DETAILS_ENABLED is true. But changing the keyword doesn't take effect within the frame, it only seems to apply to the next ones. I also tried with a global float property (Shader.SetGlobalFloat) and a branch in the shader but same result.

    Questions:
    Can anyone with a deeper understanding of the rendering pipeline explain why that doesn't seem to be a viable solution, or if I'm simply missing something to getting it to work like that?

    Alternately, is there any other way to achieve this goal with URP that doesn't rely on duplicating geometry on another layer? Like creating different passes in the shader, or adding material IDs on the meshes and rendering them to different target textures, not as part of the main opaque render pass? Based on my searches, it seems like there's a few of ways to add passes when rendering a mesh, but not much to split the output of those passes across different targets.

    I don't mind having to programmatically create/manage a set of matching material instances that have slightly different properties so I can override all materials in my scene, but I haven't found a way to perform a replacement of most materials via a Renderer Feature that's more granular than applying the same exact material to all meshes, and it doesn't seem like such a great idea to iterate on all my meshes' renderers each frame to change their assigned material twice, if that'd even work mid-frame like that.


    I'm a beginner with shaders in general, some of this may be completely off, I've just been digging into it all for a few weeks trying to get to the visual style I want and I'm sure I'm getting some stuff mixed up because it's been a lot of opening dozens of forum tabs and piecing information together.

    Thanks for any insight you can share!