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. Dismiss Notice

Question Drawing Isolated meshes without a camera in URP

Discussion in 'General Graphics' started by Sluggy, Jun 26, 2023.

  1. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    I'm trying to come up with a way to pre-render 3D meshes to render textures so that they can later be used for things like UI elements as well as imposters. However, it seems the use of commandbuffers is extremely limited in URP outside of some really hackish stuff that doesn't seem to work consistently(i.e. the pipeline beginCameraRendering event).

    Does anyone have suggestions. I've tried the above method with the pipeline even using both Graphics.DrawMesh and CommandBuffer.DrawMesh and they both have a myriad of issues. This really doesn't feel like it should be such a hard thing to do in a supposedly modern render pipeline. All I need is a one-time render of a mesh to a texture.
     
  2. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    817
    Why not have a camera, set a target texture and call render?

    Either way what you want should be possible, can you explain your myriad of issues and show some code?
     
  3. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    Basically that is what I am doing now. But it has a severe problem. Rendering on a camera is not immediate and so I cannot change the render target between each 'screen shot'. My workaround so far has been to simply store a queue of requests and do them one-by-one each frame. But in my test example with about 1600 characters that all need their imposters pre-rendered that takes nearly half a minute to process! And in my actual project I can expect that number to be bigger. Average-case could be 2x, worst case could be 100x.

    A potential workaround for this is to use fewer textures and atlas the imposters together but that's a whole world of headache I'm trying to avoid due to another similar system that would have to synchronize with it.

    As for command buffers.... honestly I can't even get them to render anything. At least anything meaningful. It just appears as some scrambled white pixels that occasionally pop in if I play with the model matrix's scale and rotation a bit. Maybe I'm just fundamentally misunderstanding something about how to setup the commands to handle the MVP matrix and materials?

    Code (CSharp):
    1.  
    2.                 var cmd = CommandBufferPool.Get();
    3.                 cmd.SetRenderTarget(com.Target);
    4.                 mat.SetPass(0);
    5.                 cmd.SetProjectionMatrix(Cam.projectionMatrix);
    6.                 cmd.SetViewMatrix(Cam.worldToCameraMatrix);
    7.                 cmd.DrawMesh(com.Mesh, meshW, mat, subIndex);
    8.                 context.ExecuteCommandBuffer(cmd);
    9.                 cmd.Release();
     
  4. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    Actually, looking at the raw numbers I think worst case scenario would be somewhere in the ballpark of 4 gigs of VRAM to cache all of that data. So it might not in fact even matter if I have to process them one-per-frame since it will likely all have to be streamed in rather than being pre-cached for the whole thing.
     
  5. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    817
    My experience is exclusively with the builtin pipeline, but doesnt URP support Camera.Render? We use this for directly this purpose.
     
    Last edited: Jun 26, 2023
  6. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    In URP all rendering is buffered until the rendering phase of the update cycle. This means there is no support for things like DrawMeshNow() and things like changing the target texture of a camera will only reflect the last value set. Even command buffers need to be used at specific points hence the reason I mentioned the RenderPipelineManager.beginCameraRendering event.

    If I could figure out how to at least get the command buffer in the above post to actually display anything other than some random scrambled pixels that would fix most of the issues but for now I'll just stick with what I have I guess.
     
  7. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    839
    So I spent some time scouring the internet and managed to find something that helped. In fact, DrawmeshNow CAN be used in URP but there are apparently some uhm, 'undocumented aspects' (which are definitely not bugs) with it that need to be accounted for. Ensuring the camera matrix isn't messed up due to timing issues as well as messing around with inverted culling are apparently things that need to happen. For anyone in the future that stumbles across this same issue here are a couple of helper functions to get you up and running..

    Code (CSharp):
    1.  
    2.         /// <summary>
    3.         /// Helper for drawing a one-off mesh to a render texture. This will render all submeshes and materials associated with it.
    4.         /// </summary>
    5.         /// <param name="rt"></param>
    6.         /// <param name="mesh"></param>
    7.         /// <param name="material"></param>
    8.         /// <param name="meshW"></param>
    9.         /// <param name="cameraVP"></param>
    10.         /// <param name="subMeshIndex"></param>
    11.         public static void BlitMesh(RenderTexture rt, Mesh mesh, Material[] materials, Matrix4x4 meshW, Matrix4x4 cameraVP)
    12.         {
    13.             //sometimes this will not be null and screw us.
    14.             //special thanks goes to a random github and someone by the name of @guycalledfrank on the internet for figuring this out
    15.             if (Camera.current != null)
    16.                 cameraVP *= Camera.current.worldToCameraMatrix.inverse;
    17.  
    18.             RenderTexture oldRt = RenderTexture.active;
    19.             RenderTexture.active = rt; bool oldCulling = GL.invertCulling;
    20.             GL.invertCulling = true; //another wonderful contribution from the internet!! no idea why but it works!!
    21.  
    22.             GL.Clear(true, true, Color.clear);
    23.             GL.PushMatrix();
    24.             GL.LoadProjectionMatrix(cameraVP);
    25.  
    26.             for (int i = 0; i < mesh.subMeshCount; i++)
    27.             {
    28.                 materials[i].SetPass(0);
    29.                 Graphics.DrawMeshNow(mesh, meshW, i);
    30.             }
    31.  
    32.             //now set everything back to the way it was before
    33.             GL.PopMatrix();
    34.             GL.invertCulling = oldCulling;
    35.             RenderTexture.active = oldRt;
    36.  
    37.         }
    38.  
    39.         /// <summary>
    40.         /// Helper for drawing a one-off mesh to a render texture.
    41.         /// </summary>
    42.         /// <param name="rt"></param>
    43.         /// <param name="mesh"></param>
    44.         /// <param name="material"></param>
    45.         /// <param name="meshW"></param>
    46.         /// <param name="cameraVP"></param>
    47.         /// <param name="subMeshIndex"></param>
    48.         public static void BlitMesh(RenderTexture rt, Mesh mesh, Material material, Matrix4x4 meshW, Matrix4x4 cameraVP, int subMeshIndex = 0)
    49.         {
    50.             //sometimes this will not be null and screw us.
    51.             //special thanks goes to a random github and someone by the name of @guycalledfrank on the internet for figuring this out
    52.             if (Camera.current != null)
    53.                 cameraVP *= Camera.current.worldToCameraMatrix.inverse;
    54.  
    55.             RenderTexture oldRt = RenderTexture.active;
    56.             RenderTexture.active = rt;
    57.             bool oldCulling = GL.invertCulling;
    58.             GL.invertCulling = true; //another wonderful contribution from the internet!! no idea why but it works!!
    59.  
    60.             GL.Clear(true, true, Color.clear);
    61.             GL.PushMatrix();
    62.             GL.LoadProjectionMatrix(cameraVP);
    63.  
    64.             material.SetPass(0);
    65.             Graphics.DrawMeshNow(mesh, meshW, subMeshIndex);
    66.  
    67.             //now set everything back to the way it was before
    68.             GL.PopMatrix();
    69.             GL.invertCulling = oldCulling;
    70.             RenderTexture.active = oldRt;
    71.  
    72.         }