Search Unity

(Entities prev 11) Frustum & Culling any examples?

Discussion in 'Entity Component System' started by Antypodish, Sep 4, 2018.

  1. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,780
    Looking for correct Frustum & Culling solutions in Entities preview 11.

    Trying make something of it, but I think there were many changes recently. Now I don't know, which approach should be considered. What is official take on it?

    [pure ECS] Code example for .AddComponent(entities, new MeshCulledComponent()) ?
    https://forum.unity.com/threads/pur...-new-meshculledcomponent.543738/#post-3631360

    This approach appears to be obsolete.

    But in preview 11, I don't see MeshFrustumCullingSystem.cs. Instead I found in preview 5,6,8.

    upload_2018-9-4_7-5-13.png

    MeshFrustumCullingSystem.cs. from preview 8
    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Entities;
    3. using Unity.Jobs;
    4. using Unity.Burst;
    5. using Unity.Mathematics;
    6. using Unity.Transforms;
    7. using UnityEditor;
    8. using UnityEngine;
    9.  
    10. namespace Unity.Rendering
    11. {
    12.     [UnityEngine.ExecuteInEditMode]
    13.     [UpdateAfter(typeof(MeshFrustumCullingSystem))]
    14.     public class MeshCullingBarrier : BarrierSystem
    15.     {
    16.     }
    17.  
    18.     [UnityEngine.ExecuteInEditMode]
    19.     public class MeshFrustumCullingSystem : JobComponentSystem
    20.     {
    21.         struct BoundingSphere
    22.         {
    23.             public ComponentDataArray<MeshCullingComponent> sphere;
    24.             public ComponentDataArray<TransformMatrix> transform;
    25.             public EntityArray entities;
    26.             public readonly int Length;
    27.         }
    28.  
    29.         [BurstCompile]
    30.         struct TransformCenterJob : IJobParallelForBatch
    31.         {
    32.             [NativeDisableParallelForRestriction]
    33.             public NativeArray<float4> output;
    34.             [NativeDisableParallelForRestriction]
    35.             public NativeArray<float4> oldCullStatus;
    36.  
    37.             [ReadOnly]
    38.             public ComponentDataArray<MeshCullingComponent> sphere;
    39.             [ReadOnly]
    40.             public ComponentDataArray<TransformMatrix> transform;
    41.             public void Execute(int start, int count)
    42.             {
    43.                 float4 x = (float4)0.0f;
    44.                 float4 y = (float4)0.0f;
    45.                 float4 z = (float4)0.0f;
    46.                 float4 r = (float4)0.0f;
    47.                 float4 cull = (float4)0.0f;
    48.                 for (int i = 0; i < count; ++i)
    49.                 {
    50.                     var center = math.mul(transform[start+i].Value, new float4(sphere[start+i].BoundingSphereCenter, 1.0f)).xyz;
    51.                     x[i] = center.x;
    52.                     y[i] = center.y;
    53.                     z[i] = center.z;
    54.                     float scale = math.max(math.max(transform[start + i].Value.c0.x, transform[start + i].Value.c1.y),
    55.                         transform[start + i].Value.c2.z);
    56.                     r[i] = sphere[start + i].BoundingSphereRadius * scale;
    57.                     cull[i] = sphere[start + i].CullStatus;
    58.                 }
    59.                 output[start] = x;
    60.                 output[start + 1] = y;
    61.                 output[start + 2] = z;
    62.                 output[start + 3] = r;
    63.                 oldCullStatus[start/4] = cull;
    64.             }
    65.         }
    66.  
    67.         struct FrustumPlanes
    68.         {
    69.             public float4 leftX;
    70.             public float4 leftY;
    71.             public float4 leftZ;
    72.             public float4 leftDist;
    73.             public float4 rightX;
    74.             public float4 rightY;
    75.             public float4 rightZ;
    76.             public float4 rightDist;
    77.             public float4 topX;
    78.             public float4 topY;
    79.             public float4 topZ;
    80.             public float4 topDist;
    81.             public float4 bottomX;
    82.             public float4 bottomY;
    83.             public float4 bottomZ;
    84.             public float4 bottomDist;
    85.         }
    86.         [BurstCompile]
    87.         struct FrustumCullJob : IJobParallelFor
    88.         {
    89.             [DeallocateOnJobCompletion][ReadOnly] public NativeArray<float4> center;
    90.             public NativeArray<float4> culled;
    91.  
    92.             [DeallocateOnJobCompletion][ReadOnly] public NativeArray<FrustumPlanes> planes;
    93.             public void Execute(int i)
    94.             {
    95.                 var x = center[i * 4];
    96.                 var y = center[i * 4 + 1];
    97.                 var z = center[i * 4 + 2];
    98.                 var r = center[i * 4 + 3];
    99.  
    100.                 float4 cullDist = float.MinValue;
    101.                 for (int p = 0; p < planes.Length; ++p)
    102.                 {
    103.                     var leftDist = planes[p].leftX * x + planes[p].leftY * y + planes[p].leftZ * z - planes[p].leftDist + r;
    104.                     var rightDist = planes[p].rightX * x + planes[p].rightY * y + planes[p].rightZ * z - planes[p].rightDist + r;
    105.                     var topDist = planes[p].topX * x + planes[p].topY * y + planes[p].topZ * z - planes[p].topDist + r;
    106.                     var bottomDist = planes[p].bottomX * x + planes[p].bottomY * y + planes[p].bottomZ * z - planes[p].bottomDist + r;
    107.  
    108.                     var newCullDist = leftDist;
    109.                     newCullDist = math.min(newCullDist, rightDist);
    110.                     newCullDist = math.min(newCullDist, topDist);
    111.                     newCullDist = math.min(newCullDist, bottomDist);
    112.                     cullDist = math.max(cullDist, newCullDist);
    113.                 }
    114.  
    115.                 // set to 1 if culled - 0 if visible
    116.                 culled[i] = math.select((float4) 1f, (float4) 0.0f, cullDist >= (float4) 0.0f);;
    117.             }
    118.         }
    119.  
    120.         struct CullStatusUpdatejob : IJob
    121.         {
    122.             public EntityCommandBuffer commandBuffer;
    123.             [ReadOnly] public EntityArray entities;
    124.             public ComponentDataArray<MeshCullingComponent> spheres;
    125.             [DeallocateOnJobCompletion][ReadOnly] public NativeArray<float4> cullStatus;
    126.             [DeallocateOnJobCompletion][ReadOnly] public NativeArray<float4> oldCullStatus;
    127.             public void Execute()
    128.             {
    129.                 // Check for meshes which changed culling status, 4 at a time
    130.                 for (int i = 0; i < spheres.Length / 4; ++i)
    131.                 {
    132.                     if (math.any(oldCullStatus[i] != cullStatus[i]))
    133.                     {
    134.                         for (int j = 0; j < 4; ++j)
    135.                         {
    136.                             if (oldCullStatus[i][j] != cullStatus[i][j])
    137.                             {
    138.                                 var temp = spheres[i * 4 + j];
    139.                                 temp.CullStatus = cullStatus[i][j];
    140.                                 spheres[i * 4 + j] = temp;
    141.                                 if (cullStatus[i][j] == 0.0f)
    142.                                     commandBuffer.RemoveComponent<MeshCulledComponent>(entities[i*4 + j]);
    143.                                 else
    144.                                     commandBuffer.AddComponent(entities[i*4+j], new MeshCulledComponent());
    145.                             }
    146.                         }
    147.                     }
    148.                 }
    149.                 if ((spheres.Length & 3) != 0)
    150.                 {
    151.                     int baseIndex = spheres.Length / 4;
    152.                     for (int i = 0; i < (spheres.Length & 3); ++i)
    153.                     {
    154.                         if (oldCullStatus[baseIndex][i] != cullStatus[baseIndex][i])
    155.                         {
    156.                             var temp = spheres[baseIndex * 4 + i];
    157.                             temp.CullStatus = cullStatus[baseIndex][i];
    158.                             spheres[baseIndex * 4 + i] = temp;
    159.                             if (cullStatus[baseIndex][i] == 0.0f)
    160.                                 commandBuffer.RemoveComponent<MeshCulledComponent>(entities[baseIndex*4 + i]);
    161.                             else
    162.                                 commandBuffer.AddComponent(entities[baseIndex*4 + i], new MeshCulledComponent());
    163.                         }
    164.                     }
    165.                 }
    166.             }
    167.         }
    168.  
    169.         FrustumPlanes generatePlane(Camera cam)
    170.         {
    171.             GeometryUtility.CalculateFrustumPlanes(cam, cameraPlanes);
    172.             float3 leftPlaneNormal = cameraPlanes[0].normal;
    173.             float leftPlaneDist = -cameraPlanes[0].distance;
    174.             float3 rightPlaneNormal = cameraPlanes[1].normal;
    175.             float rightPlaneDist = -cameraPlanes[1].distance;
    176.             float3 bottomPlaneNormal = cameraPlanes[2].normal;
    177.             float bottomPlaneDist = -cameraPlanes[2].distance;
    178.             float3 topPlaneNormal = cameraPlanes[3].normal;
    179.             float topPlaneDist = -cameraPlanes[3].distance;
    180.  
    181.             return new FrustumPlanes
    182.             {
    183.                 leftX = leftPlaneNormal.xxxx,
    184.                 leftY = leftPlaneNormal.yyyy,
    185.                 leftZ = leftPlaneNormal.zzzz,
    186.                 leftDist = new float4(leftPlaneDist),
    187.                 rightX = rightPlaneNormal.xxxx,
    188.                 rightY = rightPlaneNormal.yyyy,
    189.                 rightZ = rightPlaneNormal.zzzz,
    190.                 rightDist = new float4(rightPlaneDist),
    191.                 topX = topPlaneNormal.xxxx,
    192.                 topY = topPlaneNormal.yyyy,
    193.                 topZ = topPlaneNormal.zzzz,
    194.                 topDist = new float4(topPlaneDist),
    195.                 bottomX = bottomPlaneNormal.xxxx,
    196.                 bottomY = bottomPlaneNormal.yyyy,
    197.                 bottomZ = bottomPlaneNormal.zzzz,
    198.                 bottomDist = new float4(bottomPlaneDist),
    199.             };
    200.         }
    201.         [Inject] private BoundingSphere boundingSpheres;
    202.         [Inject] private MeshCullingBarrier barrier;
    203.         private Plane[] cameraPlanes;
    204.         protected override void OnCreateManager(int capacity)
    205.         {
    206.             cameraPlanes = new Plane[6];
    207.         }
    208.  
    209.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    210.         {
    211.             int numCameras = Camera.allCamerasCount;
    212. #if UNITY_EDITOR
    213.             if (EditorApplication.isPlayingOrWillChangePlaymode)
    214.                 numCameras += SceneView.GetAllSceneCameras().Length;
    215.             else
    216.                 numCameras = SceneView.GetAllSceneCameras().Length;
    217. #endif
    218.             if (numCameras == 0)
    219.                 return inputDeps;
    220.             var planes = new NativeArray<FrustumPlanes>(numCameras, Allocator.TempJob);
    221.  
    222. #if UNITY_EDITOR
    223.             for (int cam = 0; cam < SceneView.GetAllSceneCameras().Length; ++cam)
    224.                 planes[cam] = generatePlane(SceneView.GetAllSceneCameras()[cam]);
    225.  
    226.             if (EditorApplication.isPlayingOrWillChangePlaymode)
    227. #endif
    228.             {
    229.                 for (int i = 0; i < Camera.allCamerasCount; ++i)
    230.                     planes[numCameras-Camera.allCamerasCount+i] = generatePlane(Camera.allCameras[i]);
    231.             }
    232.  
    233.             var centers = new NativeArray<float4>((boundingSpheres.Length + 3) & ~3, Allocator.TempJob);
    234.             var cullStatus = new NativeArray<float4>((boundingSpheres.Length + 3) & ~3, Allocator.TempJob);
    235.             var oldCullStatus = new NativeArray<float4>((boundingSpheres.Length + 3) & ~3, Allocator.TempJob);
    236.             var transJob = new TransformCenterJob {output = centers, oldCullStatus = oldCullStatus, sphere = boundingSpheres.sphere, transform = boundingSpheres.transform};
    237.             var cullJob = new FrustumCullJob
    238.             {
    239.                 center = centers, culled = cullStatus,
    240.                 planes = planes
    241.             };
    242.  
    243.             // First run a job which calculates the center positions of the meshes and stores them as float4(x1,x2,x3,x4), float4(y1,y2,y3,y4), ..., float4(x5, x6, x7, x8)
    244.             var trans = transJob.ScheduleBatch(boundingSpheres.Length, 4, inputDeps);
    245.             // Check four meshes at a time agains the plains, possible since we changed how positions are stored in the previous job
    246.             var cullHandle = cullJob.Schedule((boundingSpheres.Length + 3) / 4, 1, trans);
    247.  
    248.             var cullStatusUpdateJob = new CullStatusUpdatejob
    249.             {
    250.                 commandBuffer = barrier.CreateCommandBuffer(),
    251.                 entities = boundingSpheres.entities,
    252.                 spheres = boundingSpheres.sphere,
    253.                 cullStatus = cullStatus,
    254.                 oldCullStatus = oldCullStatus
    255.             };
    256.            
    257.             return cullStatusUpdateJob.Schedule(cullHandle);
    258.         }
    259.     }
    260. }
    261.  

    Is MeshCulledComponent.cs valid to use? Or is just remains, as well as MeshCulledComponent?
    Code (CSharp):
    1. using Unity.Mathematics;
    2. using Unity.Entities;
    3.  
    4. namespace Unity.Rendering
    5. {
    6.     public struct MeshCulledComponent : IComponentData
    7.     {
    8.     }
    9.  
    10.     public struct MeshCullingComponent : IComponentData
    11.     {
    12.         public float3 BoundingSphereCenter;
    13.         public float BoundingSphereRadius;
    14.         public float CullStatus;
    15.     }
    16. }

    This is a list of files searched by word conentent = culling
    upload_2018-9-4_7-10-50.png

    And here by frustum
    upload_2018-9-4_7-11-29.png

    I am missing something, or there is no more FrustumCullingSystem in entities prev 11?

    I know camera culling frustum works, when not looking at entities meshes. So rendering is not utilized. But I would like make additional culling mechanics, if I can?
     
    Last edited: Sep 4, 2018
  2. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    Prev 11 removed per instance culling and does a per chunk frustum culling inside of MeshInstanceRendererSystem. It seems like you currently have to attach a WorldMeshRenderBounds for it to be used. A system updating WorldMeshRenderBounds from MeshRenderBounds seems to be missing yet. Looks like this will change alot more in the next releases.
    Per chunk culling is probably a good idea most of the time but an optional per instance culling for objects with expensive vertex processing might still be useful.
     
    mannyhams and Antypodish like this.
  3. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,780
    Thx,

    This is what I gather so far, with some oddities.

    I just tried to add WorldMeshRenderBounds to my mesh.
    It does cull it. But I can not figure out the corresponding center and radius, to mesh located at 0.
    It feels completely of tangent

    Code (CSharp):
    1. commandsBuffer.AddComponent ( entity, new WorldMeshRenderBounds { Center = new float3 ( 1,1,1) * -9.5f, Radius = 3.5f } ) ;  
    Blue cubes (100 of them in a row) are culled by (apparently) coordinates and radius, (same as dummy GameObject sphere).
    upload_2018-9-4_15-57-57.png

    And none culled

    Code (CSharp):
    1. commandsBuffer.AddComponent ( entity, new WorldMeshRenderBounds { Center = new float3 ( 1,1,1) * -5.5f, Radius = 3.5f } ) ;  
    Only Center multiplier is different (-5.5 instead, -9.5)
    upload_2018-9-4_16-0-20.png

    I would expect, that sphere region should overlap with entities meshes? In my case, I move center only by Vector3.ones * -4, while cubes are "running" for 100 units. No overlapping happens here. Or is something else ...

    Assuming the culling is per chunk indeed, thats what makes all blocks disappear at once.
     
  4. meanmonkey

    meanmonkey

    Joined:
    Nov 13, 2014
    Posts:
    148
    Are there any news regarding frustum culling? I don't really get it...when I attach WorldMeshRenderBounds there is some culling done, but not correctly (e.g. entities are only visible if camera looks down by 90 degrees). Do I have to set the bounds ? If yes, how ?
     
    Antypodish likes this.
  5. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    You need to set the values.

    What it looks like for my voxel chunks.

    entityCommandBuffer.SetComponent(new WorldMeshRenderBounds { Center = point + this.halfChunk, Radius = this.radius });
     
    Antypodish and meanmonkey like this.
  6. meanmonkey

    meanmonkey

    Joined:
    Nov 13, 2014
    Posts:
    148
    thanks! at least I get some better results now, still behaving kinda weird though, but I guess I'll have to tweak my values (I read them from gameobject prefabs in my own template system)
     
  7. meanmonkey

    meanmonkey

    Joined:
    Nov 13, 2014
    Posts:
    148
    do you set localtoworld value too ?
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    LocalToWorld is set by TransformSystem and shouldn't be modified
     
    meanmonkey likes this.
  9. meanmonkey

    meanmonkey

    Joined:
    Nov 13, 2014
    Posts:
    148
    allright thx