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

Threaded Occlusion Culling Help

Discussion in 'Scripting' started by XJDHDR, Apr 26, 2022.

  1. XJDHDR

    XJDHDR

    Joined:
    Mar 31, 2020
    Posts:
    19
    Edit: This was originally a post in this thread in reply to this post.

    At the risk of going further off-topic, I would like to share an example of something I would like to be able to do with multithreading. I want to create an Occlusion Culling system for my game using BoxCollider2Ds or something similar to create rectangles that occlude anything behind them. Before anyone asks, my game creates it's scenes at runtime and so, I can't use Unity's built-in OC system or anything that requires baking.

    This is a basic outline of the sort of code I would like to run in the OC manager script:
    Code (CSharp):
    1. // All the models that must be tested for occlusion
    2. internal static MeshRenderer[] OccludableMeshes;
    3.  
    4. // Corners of the BoxCollider2D or model used for the OC plane
    5. internal static Vector3[] OccluderCorners;        // 0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left
    6.  
    7. // Camera that will be tested for occlusion.
    8. internal static Camera TargetCamera;
    9.  
    10. private void Update()
    11. {
    12.     Vector3 cameraPos = TargetCamera.transform.position;
    13.  
    14.     Plane cameraNearPlane = new Plane(
    15.         // Plane's right and left sides are inverted from the screen's perspective.
    16.         TargetCamera.ScreenToWorldPoint(new Vector3(0, TargetCamera.pixelHeight, TargetCamera.nearClipPlane)),    // Top-right corner, looking from the front.
    17.         TargetCamera.ScreenToWorldPoint(new Vector3(0, 0, TargetCamera.nearClipPlane)),                            // Bottom-right corner, looking from the front.
    18.         TargetCamera.ScreenToWorldPoint(new Vector3(TargetCamera.pixelWidth, 0, TargetCamera.nearClipPlane))        // Bottom-left corner, looking from the front.
    19.     );
    20.     Plane cameraFarPlane = new Plane(
    21.         TargetCamera.ScreenToWorldPoint(new Vector3(0, 0, TargetCamera.farClipPlane)),
    22.         TargetCamera.ScreenToWorldPoint(new Vector3(TargetCamera.pixelWidth, TargetCamera.pixelHeight, TargetCamera.farClipPlane)),
    23.         TargetCamera.ScreenToWorldPoint(new Vector3(TargetCamera.pixelWidth, 0, TargetCamera.farClipPlane))
    24.     );
    25.  
    26.     Plane[] occlusionPlanes = new Plane[6];
    27.     occlusionPlanes[0] = new Plane(cameraPos, OccluderCorners[0], OccluderCorners[3]);
    28.     occlusionPlanes[1] = new Plane(cameraPos, OccluderCorners[2], OccluderCorners[1]);
    29.     occlusionPlanes[2] = new Plane(cameraPos, OccluderCorners[3], OccluderCorners[2]);
    30.     occlusionPlanes[3] = new Plane(cameraPos, OccluderCorners[1], OccluderCorners[0]);
    31.     occlusionPlanes[4] = cameraNearPlane;
    32.     occlusionPlanes[5] = cameraFarPlane;
    33.  
    34.     ConcurrentBag<MeshRenderer> occludedModels = new ConcurrentBag<MeshRenderer>();
    35.     uint occlusionLayer = 0x0000000f;
    36.  
    37.     Parallel.For(0, OccludableMeshes.Length, (i, state) =>
    38.     {
    39.         if (GeometryUtility.TestPlanesAABB(occlusionPlanes, OccludableMeshes[i].bounds))
    40.         {
    41.             // Add the occluded mesh to a ConcurrentBag to disable later in the main thread.
    42.             occludedModels.Add(OccludableMeshes[i]);
    43.  
    44.             // Alternatively, if it is possible to disable MeshRenderers or change layer from worker threads, do one of these instead.
    45.             //OccludableMeshes[i].enabled = false;
    46.             //OccludableMeshes[i].renderingLayerMask = occlusionLayer;
    47.         }
    48.     });
    49.  
    50.     while (!occludedModels.IsEmpty)
    51.     {
    52.         occludedModels.TryTake(out MeshRenderer retrieved);
    53.  
    54.         if ((bool)retrieved)
    55.             retrieved.renderingLayerMask = occlusionLayer;
    56.             //retrieved.enabled = false;
    57.     }
    58. }

    I haven't tested this but I don't believe it's possible to run the TestPlanesAABB method from a worker thread. Nor is it possible to disable a MeshRenderer or change it's rendering layer from anything other than the main thread. (Edit: I do understand the reasoning behind this. It would be bad if a thread altered the layer or enable state of a mesh while the main thread was doing something with it. I am curious if TestPlanesAABB is indeed blocked and, if so, the reason it must run from the main thread.) The issue I have is that these worker threads are being run from a Parallel For loop, which as Neto_Kokku said, pauses the main thread until every worker thread is finished.

    That said, I do recognise that it's likely very difficult/impossible to tell whether a worker thread is being run from a Parallel For/ForEach loop, or whether it's a thread that can run at the same time as the main thread.
     
    Last edited: Apr 28, 2022
    Saniell and julienkay like this.
  2. XJDHDR

    XJDHDR

    Joined:
    Mar 31, 2020
    Posts:
    19
    Also, despite what the moderator claimed in the title, this not a request for help. It's a suggestion for improving Unity.