Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Question Implemeting color-based object selection in URP

Discussion in 'Universal Render Pipeline' started by CarePackage17, Jun 16, 2023.

  1. CarePackage17

    CarePackage17

    Joined:
    Dec 6, 2014
    Posts:
    21
    I've started to experiment with URP and I'm trying to implement something like this: https://www.cs.cornell.edu/courses/cs4620/2017sp/cs4621/lecture08/exhibit03.html

    I'm pretty much a beginner when it comes to URP and modifying the way Unity renders a frame.
    Conceptually, I know that I need to add a second render target where every mesh renderer is rendered with a unique ID and write that ID in the fragment shader; however, I'm not sure how to accomplish that within the framework that URP offers (or if it's even possible at all without creating an entirely custom render pipeline).

    I started with creating a new renderer feature and I can set an additional render target in there and clear it in order to start understanding how URP/SRP works. That's not directly helpful to achieve my goal as I need to add an extra render target to the existing DrawOpaqueObjects pass, not to an extra pass that I created myself.

    I guess then I'd also need to modify the builtin URP shaders to write my object ID to the extra render target, so it looks like I need to modify URP internals? Is that the only way to achieve that?

    I'd appreciate some help. :)
     
  2. CarePackage17

    CarePackage17

    Joined:
    Dec 6, 2014
    Posts:
    21
    Alright, I have now figured out how to have multiple render targets. Here's what I did:

    Inside DrawObjectsPass.OnCameraSetup, I added the following code:
    Code (CSharp):
    1. m_rtIds[0] = renderingData.cameraData.renderer.cameraColorTarget;
    2.  
    3. RenderTextureDescriptor desc = renderingData.cameraData.cameraTargetDescriptor;
    4. desc.depthBufferBits = 0;
    5.  
    6. int selectBufId = Shader.PropertyToID("_SelectBuf");
    7. cmd.GetTemporaryRT(selectBufId, desc);
    8. m_rtIds[1] = new RenderTargetIdentifier(selectBufId);
    9.  
    10. //Alright, so now we have multipe render targets showing up in the frame debugger.
    11. ConfigureTarget(m_rtIds, renderingData.cameraData.renderer.cameraDepthTarget);
    Then in LitForwardPass.hlsl, I modified the LitPassFragment function to return a struct like this (analogous to what the deferred path is doing):
    Code (csharp):
    1. struct FragmentOutput
    2. {
    3.     half4 color : SV_Target0;
    4.     half4 id : SV_Target1;
    5. };
    After writing a constant color to id, I can see it showing up in the frame debugger, which is great!

    Now, my next problem is actually getting a per-renderer or per-object ID that I can use for the color.
    Does such a thing exist somewhere? The DrawObjectsPass calls ScriptableRenderContext.DrawRenderers, but I guess which renderers exactly is figured out by the C++ engine after passing in culling results.
     
  3. kpresnakov

    kpresnakov

    Joined:
    Mar 31, 2021
    Posts:
    5
    For custom per-draw/per-renderer data see my post. It's the correct way and least wasteful way for what you try to achieve, but it's not easily doable in URP. It could be done by a massive hack, but that opens an entirely new world of pain. The other (very wasteful) way I can think of (and I've done that before, so I know it could work), is to put the object id in a custom uv coordinate for each vertex. This means you need to preprocess your meshes and add a uv coordinate in one of the TEXCOORD channels you don't use in the shaders. Or as a third coordinate in the main uv channel. Or in some of the POSITION, NORMAL, TANGENT channels, if they have unused slots, etc. When passing that id from vert to frag make sure interpolation won't cause bad things if the id is a float. It tends to do that - interpolating between 3 equal float numbers could result to a number that's off by something like +/-0.0000001 and this can cause headaches if you try to directly cast it to an int/uint. One way is to do some safeguard rounding in frag, another - to use nointerpolation modifier for your varying, but I don't know which platforms support it, yet another - to pass the bits of the int/uint id in the float coordinate in C# code (BitConverter has some useful methods for that), then reinterpret cast the bits in the float to int/uint (asint/asuint intrinsics in HLSL do the trick) in vert and pass that int/uint to frag.
     
  4. CarePackage17

    CarePackage17

    Joined:
    Dec 6, 2014
    Posts:
    21
    Lots of interesting stuff in your response, thank you :)

    I thought about abusing some of the existing but unused vertex attributes like you describe, but it's probably not worth the pain. (Also, I wondered what SH was standing for, good to know it's spherical harmonics).

    The unused UV channel/something else approach might be interesting; since my use case (if this worked in the end) would mostly be runtime-loaded meshes, I might be able to add that data during loading.
     
  5. CarePackage17

    CarePackage17

    Joined:
    Dec 6, 2014
    Posts:
    21
    I started reading about BatchRendererGroup and DOTS stuff, and this seems to already exist?
    https://github.com/Unity-Technologi...pipelines.universal/Shaders/BRGPicking.shader

    Not sure if this is editor-only or if I can have it in builds too, but I'm tempted to give it a try.
    The only potential drawback is that it requires 2022.3, but this is more of a research project now anyway, so it's not that much of a problem.