├── .gitignore ├── Assets ├── FrustumCullSetupScript.cs ├── FrustumCullSetupScript.cs.meta ├── FrustumCullSetupScript.preset ├── UnityNativePlugin.dll ├── UnityNativePlugin.dll.meta └── main.unity ├── Packages └── manifest.json ├── Pictures └── Showcase.png ├── ProjectSettings └── ProjectVersion.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Unity Temp 2 | 3 | /Temp 4 | /Library 5 | -------------------------------------------------------------------------------- /Assets/FrustumCullSetupScript.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using UnityEngine; 3 | using UnityEngine.Rendering; 4 | 5 | using Unity.Collections; 6 | using Unity.Jobs; 7 | using Unity.Mathematics; 8 | 9 | 10 | public class FrustumCullSetupScript : MonoBehaviour 11 | { 12 | const float kMinExtendX = 0.1f; 13 | const float kMinExtendY = 0.1f; 14 | const float kMinExtendZ = 0.1f; 15 | 16 | public enum JobType 17 | { 18 | ReferenceSerialOnly, 19 | NativePluginSerialOnly, 20 | MathematicsNoBurst, 21 | MathematicsBurstOptimized 22 | }; 23 | 24 | struct PinnedArray 25 | { 26 | public T [] Array; 27 | public GCHandle Handle; 28 | 29 | public static implicit operator T[] (PinnedArray x) 30 | { 31 | return x.Array; 32 | } 33 | 34 | public System.IntPtr Address() 35 | { 36 | UnityEngine.Assertions.Assert.IsTrue(Handle.IsAllocated); 37 | return Handle.AddrOfPinnedObject(); 38 | } 39 | 40 | public void Reserve(int NumElements) 41 | { 42 | if (Array != null && Array.Length < NumElements) 43 | { 44 | UnityEngine.Assertions.Assert.IsTrue(Handle.IsAllocated); 45 | Handle.Free(); 46 | Array = null; 47 | } 48 | 49 | if (Array == null) 50 | { 51 | Array = new T[NumElements]; 52 | Handle = GCHandle.Alloc(Array, GCHandleType.Pinned); 53 | } 54 | } 55 | 56 | public void Release() 57 | { 58 | if (Array != null) 59 | { 60 | UnityEngine.Assertions.Assert.IsTrue(Handle.IsAllocated); 61 | Handle.Free(); 62 | Array = null; 63 | } 64 | } 65 | } 66 | 67 | public Mesh UnitCubeMesh; 68 | public Material DefaultMaterial; 69 | 70 | [Range(1.0f, 1000.0f)] 71 | public float RandomDistanceH = 1000.0f; 72 | 73 | [Range(1.0f, 1000.0f)] 74 | public float RandomDistanceV = 50.0f; 75 | 76 | [Range(kMinExtendX, 100.0f)] 77 | public float RandomExtentLimitX = 10.0f; 78 | 79 | [Range(kMinExtendY, 100.0f)] 80 | public float RandomExtentLimitY = 10.0f; 81 | 82 | [Range(kMinExtendZ, 100.0f)] 83 | public float RandomExtentLimitZ = 10.0f; 84 | 85 | public bool RandomizeBoxes = true; 86 | 87 | [Range(16, 8192 << 4)] 88 | public int NumMeshTranforms = 16; 89 | private int PrevNumMeshTransforms = 0; 90 | private Matrix4x4 [] MeshTransforms; 91 | 92 | private byte[] VisibilityMaskForParallel; 93 | private NativeArray BoxesMinMaxAoSoANative; 94 | 95 | public bool TestFrustumCorners = false; 96 | 97 | public int NumMeshesToDraw = 0; 98 | 99 | CullingJob_Parallel ParallelCullingJob; 100 | CullingJob_ParallelNoBurst ParallelCullingJobNoBurst; 101 | 102 | CullingJob_Serial SerialCullingJob; 103 | CullingJob_SerialNoBurst SerialCullingJobNoBurst; 104 | 105 | Unity.Jobs.JobHandle CullingJobHandle; 106 | 107 | PinnedArray FrustumData; 108 | PinnedArray BoxesMinMaxAoSoA; 109 | PinnedArray VisibilityMask; 110 | 111 | public JobType CullingJobType = JobType.ReferenceSerialOnly; 112 | public bool CullingJobParallel = false; 113 | 114 | private static Vector4 NormalizePlane(Vector4 Plane) 115 | { 116 | var LengthSquared = Plane.x * Plane.x + Plane.y * Plane.y + Plane.z * Plane.z; 117 | return Plane / LengthSquared; 118 | } 119 | 120 | private static Vector3 PointFrom3Planes(Vector4 Plane0, Vector4 Plane1, Vector4 Plane2) 121 | { 122 | Matrix4x4 M = Matrix4x4.identity; 123 | M.SetRow(0, (Vector4)(Vector3)Plane0); 124 | M.SetRow(1, (Vector4)(Vector3)Plane1); 125 | M.SetRow(2, (Vector4)(Vector3)Plane2); 126 | 127 | if (M.determinant != 0.0f) 128 | { 129 | var M2 = M.inverse; 130 | return M2.MultiplyVector(new Vector3(-Plane0.w, -Plane1.w, -Plane2.w)); 131 | } 132 | return Vector3.zero; 133 | } 134 | 135 | private static void DebugDrawCross(Vector3 Point, Vector3 LateralNormal, float Size) 136 | { 137 | var offset0 = LateralNormal * Size; 138 | var offset1 = Vector3.up * Size; 139 | Debug.DrawLine(Point, Point + offset0, Color.green); 140 | Debug.DrawLine(Point - offset1, Point + offset1, Color.green); 141 | } 142 | 143 | private static Vector3 RayDirFromPlanes(Vector4 Plane0, Vector4 Plane1) 144 | { 145 | return Vector3.Normalize(Vector3.Cross((Vector3)Plane0, (Vector3)Plane1)); 146 | } 147 | 148 | private Vector3 RandomCenter() 149 | { 150 | var center = Vector3.zero; 151 | center.x = UnityEngine.Random.value * RandomDistanceH * 2.0f - RandomDistanceH; 152 | center.z = UnityEngine.Random.value * RandomDistanceH * 2.0f - RandomDistanceH; 153 | center.y = UnityEngine.Random.value * RandomDistanceV * 2.0f - RandomDistanceV; 154 | return center; 155 | } 156 | 157 | private Vector3 RandomExtent() 158 | { 159 | var extent = Vector3.zero; 160 | extent.x = RandomExtentLimitX + UnityEngine.Random.value * (RandomExtentLimitX - kMinExtendX); 161 | extent.y = RandomExtentLimitY + UnityEngine.Random.value * (RandomExtentLimitY - kMinExtendY); 162 | extent.z = RandomExtentLimitZ + UnityEngine.Random.value * (RandomExtentLimitZ - kMinExtendZ); 163 | return extent; 164 | } 165 | 166 | struct FrustumCullingJobData 167 | { 168 | [ReadOnly] 169 | public NativeSlice MinXSlice; 170 | [ReadOnly] 171 | public NativeSlice MaxXSlice; 172 | [ReadOnly] 173 | public NativeSlice MinYSlice; 174 | [ReadOnly] 175 | public NativeSlice MaxYSlice; 176 | [ReadOnly] 177 | public NativeSlice MinZSlice; 178 | [ReadOnly] 179 | public NativeSlice MaxZSlice; 180 | 181 | [WriteOnly] 182 | public NativeArray VisibilityMask; 183 | 184 | public float4 Frustum_Plane0; 185 | public float4 Frustum_Plane1; 186 | public float4 Frustum_Plane2; 187 | public float4 Frustum_Plane3; 188 | public float4 Frustum_Plane4; 189 | public float4 Frustum_Plane5; 190 | public float4 Frustum_xXyY; 191 | public float4 Frustum_zZzZ; 192 | 193 | public bool TestFrustumCorners; 194 | 195 | static private float4 MaxDotProduct( 196 | float4 MinX, 197 | float4 MaxX, 198 | float4 MinY, 199 | float4 MaxY, 200 | float4 MinZ, 201 | float4 MaxZ, 202 | float4 Plane) 203 | { 204 | return math.max(MinX * Plane.x, MaxX * Plane.x) + 205 | math.max(MinY * Plane.y, MaxY * Plane.y) + 206 | math.max(MinZ * Plane.z, MaxZ * Plane.z) + 207 | Plane.wwww; 208 | } 209 | 210 | public void OnSchedule(ref NativeArray InBoxMinMaxAoSoA, ref Vector4[] InFrustumData, bool InTestCorners) 211 | { 212 | var BoxMinMaxAoSoASlice = InBoxMinMaxAoSoA.Slice().SliceConvert(); 213 | 214 | MinXSlice = BoxMinMaxAoSoASlice.SliceWithStride(0); 215 | MaxXSlice = BoxMinMaxAoSoASlice.SliceWithStride(16); 216 | MinYSlice = BoxMinMaxAoSoASlice.SliceWithStride(32); 217 | MaxYSlice = BoxMinMaxAoSoASlice.SliceWithStride(48); 218 | MinZSlice = BoxMinMaxAoSoASlice.SliceWithStride(64); 219 | MaxZSlice = BoxMinMaxAoSoASlice.SliceWithStride(80); 220 | 221 | Frustum_Plane0 = InFrustumData[0]; 222 | Frustum_Plane1 = InFrustumData[1]; 223 | Frustum_Plane2 = InFrustumData[2]; 224 | Frustum_Plane3 = InFrustumData[3]; 225 | Frustum_Plane4 = InFrustumData[4]; 226 | Frustum_Plane5 = InFrustumData[5]; 227 | Frustum_xXyY = InFrustumData[6]; 228 | Frustum_zZzZ = InFrustumData[7]; 229 | 230 | VisibilityMask = new NativeArray(BoxMinMaxAoSoASlice.Length, Allocator.TempJob); 231 | 232 | TestFrustumCorners = InTestCorners; 233 | } 234 | 235 | public void OnComplete(ref byte[] OutVisibilityMask) 236 | { 237 | UnityEngine.Assertions.Assert.IsTrue(VisibilityMask.IsCreated); 238 | VisibilityMask.CopyTo(OutVisibilityMask); 239 | VisibilityMask.Dispose(); 240 | } 241 | 242 | public void ComputeVisibility(int i) 243 | { 244 | var MinX = MinXSlice[i]; 245 | var MaxX = MaxXSlice[i]; 246 | var MinY = MinYSlice[i]; 247 | var MaxY = MaxYSlice[i]; 248 | var MinZ = MinZSlice[i]; 249 | var MaxZ = MaxZSlice[i]; 250 | 251 | uint4 Mask = 0; 252 | 253 | // box is fully outside of any frustum plane 254 | Mask |= math.asuint(MaxDotProduct(MinX, MaxX, MinY, MaxY, MinZ, MaxZ, Frustum_Plane0)); 255 | Mask |= math.asuint(MaxDotProduct(MinX, MaxX, MinY, MaxY, MinZ, MaxZ, Frustum_Plane1)); 256 | Mask |= math.asuint(MaxDotProduct(MinX, MaxX, MinY, MaxY, MinZ, MaxZ, Frustum_Plane2)); 257 | Mask |= math.asuint(MaxDotProduct(MinX, MaxX, MinY, MaxY, MinZ, MaxZ, Frustum_Plane3)); 258 | Mask |= math.asuint(MaxDotProduct(MinX, MaxX, MinY, MaxY, MinZ, MaxZ, Frustum_Plane4)); 259 | Mask |= math.asuint(MaxDotProduct(MinX, MaxX, MinY, MaxY, MinZ, MaxZ, Frustum_Plane5)); 260 | 261 | // frustum is fully outside of any box plane 262 | //if (TestFrustumCorners) 263 | { 264 | Mask |= math.asuint(Frustum_xXyY.yyyy - MinX); 265 | Mask |= math.asuint(Frustum_xXyY.wwww - MinY); 266 | Mask |= math.asuint(Frustum_zZzZ.yyyy - MinZ); 267 | Mask |= math.asuint(MaxX - Frustum_xXyY.xxxx); 268 | Mask |= math.asuint(MaxY - Frustum_xXyY.zzzz); 269 | Mask |= math.asuint(MaxZ - Frustum_zZzZ.xxxx); 270 | } 271 | 272 | /* 273 | Mask = Mask >> 31; 274 | VisibilityMask[i] = (byte)((Mask.w << 3) | (Mask.z << 2) | (Mask.y << 1) | (Mask.x)); 275 | /*/ 276 | Mask = Mask & 0x80000000; 277 | VisibilityMask[i] = (byte)((Mask.w >> 28) | (Mask.z >> 29) | (Mask.y >> 30) | (Mask.x >> 31)); 278 | /**/ 279 | } 280 | } 281 | 282 | public interface ICullingJob 283 | { 284 | Unity.Jobs.JobHandle Run(ref NativeArray InBoxMinMaxAoSoA, ref Vector4[] FrustumData, bool TestFrustumCorners); 285 | void Complete(Unity.Jobs.JobHandle Handle, ref byte[] VisibilityMask); 286 | } 287 | 288 | struct CullingJob_SerialNoBurst : Unity.Jobs.IJob, ICullingJob 289 | { 290 | public FrustumCullingJobData JobData; 291 | 292 | public Unity.Jobs.JobHandle Run(ref NativeArray InBoxMinMaxAoSoA, ref Vector4[] FrustumData, bool TestFrustumCorners) 293 | { 294 | JobData.OnSchedule(ref InBoxMinMaxAoSoA, ref FrustumData, TestFrustumCorners); 295 | return this.Schedule(); 296 | } 297 | 298 | public void Complete(Unity.Jobs.JobHandle Handle, ref byte[] VisibilityMask) 299 | { 300 | Handle.Complete(); 301 | UnityEngine.Assertions.Assert.IsTrue(Handle.IsCompleted); 302 | JobData.OnComplete(ref VisibilityMask); 303 | } 304 | 305 | public void Execute() 306 | { 307 | for (int i = 0; i < JobData.VisibilityMask.Length; ++i) 308 | { 309 | JobData.ComputeVisibility(i); 310 | } 311 | } 312 | } 313 | 314 | struct CullingJob_ParallelNoBurst : Unity.Jobs.IJobParallelFor, ICullingJob 315 | { 316 | public FrustumCullingJobData JobData; 317 | 318 | public Unity.Jobs.JobHandle Run(ref NativeArray InBoxMinMaxAoSoA, ref Vector4[] FrustumData, bool TestFrustumCorners) 319 | { 320 | JobData.OnSchedule(ref InBoxMinMaxAoSoA, ref FrustumData, TestFrustumCorners); 321 | return this.Schedule(JobData.VisibilityMask.Length, 32); 322 | } 323 | 324 | public void Complete(Unity.Jobs.JobHandle Handle, ref byte[] VisibilityMask) 325 | { 326 | Handle.Complete(); 327 | UnityEngine.Assertions.Assert.IsTrue(Handle.IsCompleted); 328 | JobData.OnComplete(ref VisibilityMask); 329 | } 330 | 331 | public void Execute(int i) 332 | { 333 | JobData.ComputeVisibility(i); 334 | } 335 | } 336 | 337 | [Unity.Burst.BurstCompile] 338 | struct CullingJob_Serial : Unity.Jobs.IJob, ICullingJob 339 | { 340 | public FrustumCullingJobData JobData; 341 | 342 | public Unity.Jobs.JobHandle Run(ref NativeArray InBoxMinMaxAoSoA, ref Vector4[] FrustumData, bool TestFrustumCorners) 343 | { 344 | JobData.OnSchedule(ref InBoxMinMaxAoSoA, ref FrustumData, TestFrustumCorners); 345 | return this.Schedule(); 346 | } 347 | 348 | public void Complete(Unity.Jobs.JobHandle Handle, ref byte[] VisibilityMask) 349 | { 350 | Handle.Complete(); 351 | UnityEngine.Assertions.Assert.IsTrue(Handle.IsCompleted); 352 | JobData.OnComplete(ref VisibilityMask); 353 | } 354 | 355 | public void Execute() 356 | { 357 | for (int i = 0; i < JobData.VisibilityMask.Length; ++i) 358 | { 359 | JobData.ComputeVisibility(i); 360 | } 361 | } 362 | } 363 | 364 | [Unity.Burst.BurstCompile] 365 | struct CullingJob_Parallel : Unity.Jobs.IJobParallelFor, ICullingJob 366 | { 367 | public FrustumCullingJobData JobData; 368 | 369 | public Unity.Jobs.JobHandle Run(ref NativeArray InBoxMinMaxAoSoA, ref Vector4[] FrustumData, bool TestFrustumCorners) 370 | { 371 | JobData.OnSchedule(ref InBoxMinMaxAoSoA, ref FrustumData, TestFrustumCorners); 372 | return this.Schedule(JobData.VisibilityMask.Length, 32); 373 | } 374 | 375 | public void Complete(Unity.Jobs.JobHandle Handle, ref byte[] VisibilityMask) 376 | { 377 | Handle.Complete(); 378 | UnityEngine.Assertions.Assert.IsTrue(Handle.IsCompleted); 379 | JobData.OnComplete(ref VisibilityMask); 380 | } 381 | 382 | public void Execute(int i) 383 | { 384 | JobData.ComputeVisibility(i); 385 | } 386 | } 387 | 388 | 389 | struct BoxMinMaxSoA 390 | { 391 | public Vector4 MinX; 392 | public Vector4 MaxX; 393 | public Vector4 MinY; 394 | public Vector4 MaxY; 395 | public Vector4 MinZ; 396 | public Vector4 MaxZ; 397 | 398 | public BoxMinMaxSoA(ref Vector4[] InBoxMinMaxAoSoA, int i) 399 | { 400 | MinX = InBoxMinMaxAoSoA[i * 6 + 0]; 401 | MaxX = InBoxMinMaxAoSoA[i * 6 + 1]; 402 | MinY = InBoxMinMaxAoSoA[i * 6 + 2]; 403 | MaxY = InBoxMinMaxAoSoA[i * 6 + 3]; 404 | MinZ = InBoxMinMaxAoSoA[i * 6 + 4]; 405 | MaxZ = InBoxMinMaxAoSoA[i * 6 + 5]; 406 | } 407 | 408 | public void SetFromVector4Array(ref Vector4[] InBoxMinMaxAoSoA, int i) 409 | { 410 | MinX = InBoxMinMaxAoSoA[i * 6 + 0]; 411 | MaxX = InBoxMinMaxAoSoA[i * 6 + 1]; 412 | MinY = InBoxMinMaxAoSoA[i * 6 + 2]; 413 | MaxY = InBoxMinMaxAoSoA[i * 6 + 3]; 414 | MinZ = InBoxMinMaxAoSoA[i * 6 + 4]; 415 | MaxZ = InBoxMinMaxAoSoA[i * 6 + 5]; 416 | } 417 | } 418 | 419 | static private int SetupTRS( 420 | int TransformIdx, 421 | ref Matrix4x4[] OutTransforms, 422 | Vector4 MinX, 423 | Vector4 MaxX, 424 | Vector4 MinY, 425 | Vector4 MaxY, 426 | Vector4 MinZ, 427 | Vector4 MaxZ, 428 | int ComponentIdx) 429 | { 430 | var Min = new Vector3(MinX[ComponentIdx], MinY[ComponentIdx], MinZ[ComponentIdx]); 431 | var Max = new Vector3(MaxX[ComponentIdx], MaxY[ComponentIdx], MaxZ[ComponentIdx]); 432 | OutTransforms[TransformIdx].SetTRS((Max + Min) * 0.5f, Quaternion.identity, (Max - Min) * 0.5f); 433 | return TransformIdx + 1; 434 | } 435 | 436 | static private int FilterOutCulledBoxes( 437 | ref Vector4[] InBoxesMinMaxAoSoA, 438 | ref byte[] InVisMask, 439 | ref Matrix4x4[] 440 | OutTransforms, bool BytePerPacket) 441 | { 442 | int NumTransforms = 0; 443 | var NumSoAPackets = InBoxesMinMaxAoSoA.Length / 6; 444 | 445 | 446 | if (BytePerPacket) 447 | UnityEngine.Assertions.Assert.AreEqual(NumSoAPackets, InVisMask.Length); 448 | else 449 | UnityEngine.Assertions.Assert.AreEqual((NumSoAPackets + 1) >> 1, InVisMask.Length); 450 | 451 | UnityEngine.Assertions.Assert.AreEqual(NumSoAPackets, OutTransforms.Length >> 2); 452 | 453 | for (int i = 0; i < NumSoAPackets; ++i) 454 | { 455 | var Byte = BytePerPacket ? InVisMask[i] : (InVisMask[i >> 1] >> ((i & 0x1) << 2)); 456 | 457 | if (Byte != 0xf) 458 | { 459 | var MinX = InBoxesMinMaxAoSoA[i * 6 + 0]; 460 | var MaxX = InBoxesMinMaxAoSoA[i * 6 + 1]; 461 | var MinY = InBoxesMinMaxAoSoA[i * 6 + 2]; 462 | var MaxY = InBoxesMinMaxAoSoA[i * 6 + 3]; 463 | var MinZ = InBoxesMinMaxAoSoA[i * 6 + 4]; 464 | var MaxZ = InBoxesMinMaxAoSoA[i * 6 + 5]; 465 | 466 | if ((Byte & 0x1) == 0) 467 | NumTransforms = SetupTRS(NumTransforms, ref OutTransforms, MinX, MaxX, MinY, MaxY, MinZ, MaxZ, 0); 468 | 469 | if ((Byte & 0x2) == 0) 470 | NumTransforms = SetupTRS(NumTransforms, ref OutTransforms, MinX, MaxX, MinY, MaxY, MinZ, MaxZ, 1); 471 | 472 | if ((Byte & 0x4) == 0) 473 | NumTransforms = SetupTRS(NumTransforms, ref OutTransforms, MinX, MaxX, MinY, MaxY, MinZ, MaxZ, 2); 474 | 475 | if ((Byte & 0x8) == 0) 476 | NumTransforms = SetupTRS(NumTransforms, ref OutTransforms, MinX, MaxX, MinY, MaxY, MinZ, MaxZ, 3); 477 | } 478 | } 479 | return NumTransforms; 480 | } 481 | 482 | 483 | private static Vector4 FrustumOutsideAABB(ref BoxMinMaxSoA Box, Vector3 FrustumAABBMin, Vector3 FrustumAABBMax) 484 | { 485 | var r0 = new Vector4(FrustumAABBMax.x, FrustumAABBMax.x, FrustumAABBMax.x, FrustumAABBMax.x) - Box.MinX; 486 | var r1 = new Vector4(FrustumAABBMax.y, FrustumAABBMax.y, FrustumAABBMax.y, FrustumAABBMax.y) - Box.MinY; 487 | var r2 = new Vector4(FrustumAABBMax.z, FrustumAABBMax.z, FrustumAABBMax.z, FrustumAABBMax.z) - Box.MinZ; 488 | var r3 = Box.MaxX - new Vector4(FrustumAABBMin.x, FrustumAABBMin.x, FrustumAABBMin.x, FrustumAABBMin.x); 489 | var r4 = Box.MaxY - new Vector4(FrustumAABBMin.y, FrustumAABBMin.y, FrustumAABBMin.y, FrustumAABBMin.y); 490 | var r5 = Box.MaxZ - new Vector4(FrustumAABBMin.z, FrustumAABBMin.z, FrustumAABBMin.z, FrustumAABBMin.z); 491 | return Vector4.Min(Vector4.Min(Vector4.Min(r0, r1), Vector4.Min(r2, r3)), Vector4.Min(r4, r5)); 492 | } 493 | 494 | private static Vector4 MaxDotProductPlaneAABB(ref BoxMinMaxSoA Box, Vector4 Plane) 495 | { 496 | return Vector4.Max(Box.MinX * Plane.x, Box.MaxX * Plane.x) + 497 | Vector4.Max(Box.MinY * Plane.y, Box.MaxY * Plane.y) + 498 | Vector4.Max(Box.MinZ * Plane.z, Box.MaxZ * Plane.z) + 499 | new Vector4(Plane.w, Plane.w, Plane.w, Plane.w); 500 | } 501 | 502 | static private void CullBoxesAoSoA_Default( 503 | ref byte[] OutVisibilityMask, 504 | ref Vector4[] InBoxesMinMaxAoSoA, 505 | ref Vector4[] InFrustumData, 506 | Vector3 FrustumAABBMax, 507 | Vector3 FrustumAABBMin, 508 | bool TestFrustumCorners) 509 | { 510 | int NumSoAPackets = InBoxesMinMaxAoSoA.Length / 6; 511 | 512 | for (int i = 0; i < NumSoAPackets; ++i) 513 | { 514 | var BoxMinMax = new BoxMinMaxSoA(ref InBoxesMinMaxAoSoA, i); 515 | 516 | var DotsL = MaxDotProductPlaneAABB(ref BoxMinMax, InFrustumData[0]); 517 | var DotsR = MaxDotProductPlaneAABB(ref BoxMinMax, InFrustumData[1]); 518 | var DotsT = MaxDotProductPlaneAABB(ref BoxMinMax, InFrustumData[2]); 519 | var DotsB = MaxDotProductPlaneAABB(ref BoxMinMax, InFrustumData[3]); 520 | var DotsN = MaxDotProductPlaneAABB(ref BoxMinMax, InFrustumData[4]); 521 | var DotsF = MaxDotProductPlaneAABB(ref BoxMinMax, InFrustumData[5]); 522 | 523 | var R = Vector4.Min(Vector4.Min(DotsL, DotsR), Vector4.Min(Vector4.Min(DotsT, DotsB), Vector4.Min(DotsF, DotsN))); 524 | 525 | if (TestFrustumCorners) 526 | { 527 | R = Vector4.Min(R, FrustumOutsideAABB(ref BoxMinMax, FrustumAABBMin, FrustumAABBMax)); 528 | } 529 | 530 | 531 | int Mask = (R.w < 0.0f ? 0x8 : 0x0) 532 | | (R.z < 0.0f ? 0x4 : 0x0) 533 | | (R.y < 0.0f ? 0x2 : 0x0) 534 | | (R.x < 0.0f ? 0x1 : 0x0); 535 | 536 | // init mask when index is even, update mask when it's odd 537 | if ((i & 0x1) == 0) 538 | OutVisibilityMask[i >> 1] = (byte)Mask; 539 | else 540 | OutVisibilityMask[i >> 1] |= (byte)(Mask << 4); 541 | } 542 | } 543 | 544 | [DllImport("UnityNativePlugin")] 545 | private static extern void UnityNativePlugin_CountCulledBoxesAoSoA( 546 | System.IntPtr OutVisibilityMask, 547 | System.IntPtr BoxesMinMaxAoSoA, 548 | System.IntPtr FrustumData, 549 | int count); 550 | 551 | [DllImport("UnityNativePlugin")] 552 | private static extern int UnitNativePluginSum(int a, int b); 553 | 554 | void Start() 555 | { 556 | FrustumData.Reserve(8); 557 | 558 | if (BoxesMinMaxAoSoANative.IsCreated) 559 | { 560 | Debug.Log("BoxesMinMaxAoSoANative: " + BoxesMinMaxAoSoANative); 561 | BoxesMinMaxAoSoANative.Dispose(); 562 | } 563 | } 564 | 565 | void OnDisable() 566 | { 567 | if (BoxesMinMaxAoSoANative.IsCreated) 568 | BoxesMinMaxAoSoANative.Dispose(); 569 | 570 | VisibilityMask.Release(); 571 | BoxesMinMaxAoSoA.Release(); 572 | FrustumData.Release(); 573 | } 574 | 575 | // Update is called once per frame 576 | void Update() 577 | { 578 | var ProjMatrix = Camera.main.projectionMatrix; 579 | var ViewMatrix = Camera.main.worldToCameraMatrix; 580 | 581 | var ViewProjMatrix = ProjMatrix * ViewMatrix; 582 | 583 | var Row3 = ViewProjMatrix.GetRow(3); 584 | var Row0 = ViewProjMatrix.GetRow(0); 585 | var Row1 = ViewProjMatrix.GetRow(1); 586 | var Row2 = ViewProjMatrix.GetRow(2); 587 | 588 | var PlaneL = NormalizePlane(Row3 + Row0); 589 | var PlaneR = NormalizePlane(Row3 - Row0); 590 | var PlaneT = NormalizePlane(Row3 - Row1); 591 | var PlaneB = NormalizePlane(Row3 + Row1); 592 | 593 | var PlaneN = NormalizePlane(Row3 + Row2); 594 | var PlaneF = NormalizePlane(Row3 - Row2); 595 | 596 | var PointNTL = PointFrom3Planes(PlaneN, PlaneT, PlaneL); 597 | var PointNTR = PointFrom3Planes(PlaneN, PlaneT, PlaneR); 598 | var PointNBL = PointFrom3Planes(PlaneN, PlaneB, PlaneL); 599 | var PointNBR = PointFrom3Planes(PlaneN, PlaneB, PlaneR); 600 | 601 | DebugDrawCross(PointNTL, RayDirFromPlanes(PlaneT, PlaneL), 0.3f); 602 | DebugDrawCross(PointNTR, RayDirFromPlanes(PlaneR, PlaneT), 0.3f); 603 | DebugDrawCross(PointNBL, RayDirFromPlanes(PlaneL, PlaneB), 0.3f); 604 | DebugDrawCross(PointNBR, RayDirFromPlanes(PlaneB, PlaneR), 0.3f); 605 | 606 | var PointFTL = PointFrom3Planes(PlaneF, PlaneT, PlaneL); 607 | var PointFTR = PointFrom3Planes(PlaneF, PlaneT, PlaneR); 608 | var PointFBL = PointFrom3Planes(PlaneF, PlaneB, PlaneL); 609 | var PointFBR = PointFrom3Planes(PlaneF, PlaneB, PlaneR); 610 | 611 | var FrustumAABBMin = 612 | Vector3.Min(Vector3.Min(Vector3.Min(PointFTL, PointFTR), Vector3.Min(PointFBL, PointFBR)), 613 | Vector3.Min(Vector3.Min(PointNTL, PointNTR), Vector3.Min(PointNBL, PointNBR))); 614 | 615 | var FrustumAABBMax = 616 | Vector3.Max(Vector3.Max(Vector3.Max(PointFTL, PointFTR), Vector3.Max(PointFBL, PointFBR)), 617 | Vector3.Max(Vector3.Max(PointNTL, PointNTR), Vector3.Max(PointNBL, PointNBR))); 618 | 619 | 620 | DebugDrawCross(PointFTL, -RayDirFromPlanes(PlaneT, PlaneL), 1.3f); 621 | DebugDrawCross(PointFTR, -RayDirFromPlanes(PlaneR, PlaneT), 1.3f); 622 | DebugDrawCross(PointFBL, -RayDirFromPlanes(PlaneL, PlaneB), 1.3f); 623 | DebugDrawCross(PointFBR, -RayDirFromPlanes(PlaneB, PlaneR), 1.3f); 624 | 625 | FrustumData.Array[0] = PlaneL; 626 | FrustumData.Array[1] = PlaneR; 627 | FrustumData.Array[2] = PlaneT; 628 | FrustumData.Array[3] = PlaneB; 629 | FrustumData.Array[4] = PlaneN; 630 | FrustumData.Array[5] = PlaneF; 631 | FrustumData.Array[6] = new Vector4(FrustumAABBMin.x, FrustumAABBMax.x, FrustumAABBMin.y, FrustumAABBMax.y); 632 | FrustumData.Array[7] = new Vector4(FrustumAABBMin.z, FrustumAABBMax.z, FrustumAABBMin.z, FrustumAABBMax.z); 633 | 634 | int NumSoAPackets = (NumMeshTranforms + 3) >> 2; 635 | NumMeshTranforms = NumSoAPackets << 2; 636 | 637 | if (PrevNumMeshTransforms != NumMeshTranforms) 638 | { 639 | Debug.Log("Re-allocate AABB data because of size change."); 640 | 641 | VisibilityMask.Reserve((NumSoAPackets + 1) >> 1); 642 | 643 | VisibilityMaskForParallel = new byte[NumSoAPackets]; 644 | 645 | BoxesMinMaxAoSoA.Reserve(NumSoAPackets * 6); 646 | 647 | Debug.Log("Native" + BoxesMinMaxAoSoANative); 648 | if (BoxesMinMaxAoSoANative.IsCreated) 649 | BoxesMinMaxAoSoANative.Dispose(); 650 | 651 | BoxesMinMaxAoSoANative = new NativeArray(BoxesMinMaxAoSoA.Array, Allocator.Persistent); 652 | 653 | MeshTransforms = new Matrix4x4[NumMeshTranforms]; 654 | RandomizeBoxes = true; 655 | } 656 | PrevNumMeshTransforms = NumMeshTranforms; 657 | 658 | if (RandomizeBoxes) 659 | { 660 | UnityEngine.Random.InitState((int)(Time.time * 1000.0f)); 661 | for (int i = 0; i < NumSoAPackets; ++i) 662 | { 663 | var c0 = RandomCenter(); 664 | var e0 = RandomExtent(); 665 | 666 | var c1 = RandomCenter(); 667 | var e1 = RandomExtent(); 668 | 669 | var c2 = RandomCenter(); 670 | var e2 = RandomExtent(); 671 | 672 | var c3 = RandomCenter(); 673 | var e3 = RandomExtent(); 674 | 675 | BoxesMinMaxAoSoA.Array[i * 6 + 0] = new Vector4(c0[0] - e0[0], c1[0] - e1[0], c2[0] - e2[0], c3[0] - e3[0]); 676 | BoxesMinMaxAoSoA.Array[i * 6 + 1] = new Vector4(c0[0] + e0[0], c1[0] + e1[0], c2[0] + e2[0], c3[0] + e3[0]); 677 | BoxesMinMaxAoSoA.Array[i * 6 + 2] = new Vector4(c0[1] - e0[1], c1[1] - e1[1], c2[1] - e2[1], c3[1] - e3[1]); 678 | BoxesMinMaxAoSoA.Array[i * 6 + 3] = new Vector4(c0[1] + e0[1], c1[1] + e1[1], c2[1] + e2[1], c3[1] + e3[1]); 679 | BoxesMinMaxAoSoA.Array[i * 6 + 4] = new Vector4(c0[2] - e0[2], c1[2] - e1[2], c2[2] - e2[2], c3[2] - e3[2]); 680 | BoxesMinMaxAoSoA.Array[i * 6 + 5] = new Vector4(c0[2] + e0[2], c1[2] + e1[2], c2[2] + e2[2], c3[2] + e3[2]); 681 | } 682 | BoxesMinMaxAoSoANative.CopyFrom(BoxesMinMaxAoSoA.Array); 683 | RandomizeBoxes = false; 684 | } 685 | 686 | if (CullingJobType == JobType.ReferenceSerialOnly || CullingJobType == JobType.NativePluginSerialOnly) 687 | { 688 | CullingJobParallel = false; 689 | } 690 | 691 | if (CullingJobType == JobType.ReferenceSerialOnly) 692 | { 693 | UnityEngine.Profiling.Profiler.BeginSample("CullBoxesAoSoA_Default"); 694 | CullBoxesAoSoA_Default( 695 | ref VisibilityMask.Array, 696 | ref BoxesMinMaxAoSoA.Array, 697 | ref FrustumData.Array, 698 | FrustumAABBMax, 699 | FrustumAABBMin, 700 | TestFrustumCorners); 701 | UnityEngine.Profiling.Profiler.EndSample(); 702 | } 703 | else if (CullingJobType == JobType.NativePluginSerialOnly) 704 | { 705 | UnityEngine.Profiling.Profiler.BeginSample("UnityNativePlugin_CountCulledBoxesAoSoA"); 706 | UnityNativePlugin_CountCulledBoxesAoSoA( 707 | VisibilityMask.Address(), 708 | BoxesMinMaxAoSoA.Address(), 709 | FrustumData.Address(), 710 | NumSoAPackets); 711 | UnityEngine.Profiling.Profiler.EndSample(); 712 | } 713 | else 714 | { 715 | if (CullingJobParallel) 716 | { 717 | UnityEngine.Profiling.Profiler.BeginSample("BeginCullBoxesAoSoA_Parallel"); 718 | if (CullingJobType == JobType.MathematicsBurstOptimized) 719 | { 720 | ParallelCullingJob = new CullingJob_Parallel(); 721 | CullingJobHandle = ParallelCullingJob.Run(ref BoxesMinMaxAoSoANative, ref FrustumData.Array, TestFrustumCorners); 722 | } 723 | else 724 | { 725 | ParallelCullingJobNoBurst = new CullingJob_ParallelNoBurst(); 726 | CullingJobHandle = ParallelCullingJobNoBurst.Run(ref BoxesMinMaxAoSoANative, ref FrustumData.Array, TestFrustumCorners); 727 | } 728 | UnityEngine.Profiling.Profiler.EndSample(); 729 | } 730 | else 731 | { 732 | UnityEngine.Profiling.Profiler.BeginSample("BeginCullBoxesAoSoA_Serial"); 733 | if (CullingJobType == JobType.MathematicsBurstOptimized) 734 | { 735 | SerialCullingJob = new CullingJob_Serial(); 736 | CullingJobHandle = SerialCullingJob.Run(ref BoxesMinMaxAoSoANative, ref FrustumData.Array, TestFrustumCorners); 737 | } 738 | else 739 | { 740 | SerialCullingJobNoBurst = new CullingJob_SerialNoBurst(); 741 | CullingJobHandle = SerialCullingJobNoBurst.Run(ref BoxesMinMaxAoSoANative, ref FrustumData.Array, TestFrustumCorners); 742 | } 743 | UnityEngine.Profiling.Profiler.EndSample(); 744 | } 745 | } 746 | } 747 | 748 | public void LateUpdate() 749 | { 750 | if (CullingJobType != JobType.ReferenceSerialOnly && CullingJobType != JobType.NativePluginSerialOnly) 751 | { 752 | 753 | if (CullingJobParallel) 754 | { 755 | UnityEngine.Profiling.Profiler.BeginSample("EndCullBoxesAoSoA_Parallel"); 756 | if (CullingJobType == JobType.MathematicsBurstOptimized) 757 | ParallelCullingJob.Complete(CullingJobHandle, ref VisibilityMaskForParallel); 758 | else 759 | ParallelCullingJobNoBurst.Complete(CullingJobHandle, ref VisibilityMaskForParallel); 760 | UnityEngine.Profiling.Profiler.EndSample(); 761 | } 762 | else 763 | { 764 | UnityEngine.Profiling.Profiler.BeginSample("EndCullBoxesAoSoA_Parallel"); 765 | if (CullingJobType == JobType.MathematicsBurstOptimized) 766 | SerialCullingJob.Complete(CullingJobHandle, ref VisibilityMaskForParallel); 767 | else 768 | SerialCullingJobNoBurst.Complete(CullingJobHandle, ref VisibilityMaskForParallel); 769 | UnityEngine.Profiling.Profiler.EndSample(); 770 | } 771 | } 772 | 773 | UnityEngine.Profiling.Profiler.BeginSample("FilterOutCulledBoxes"); 774 | if (CullingJobType != JobType.ReferenceSerialOnly && CullingJobType != JobType.NativePluginSerialOnly) 775 | NumMeshesToDraw = FilterOutCulledBoxes(ref BoxesMinMaxAoSoA.Array, ref VisibilityMaskForParallel, ref MeshTransforms, true); 776 | else 777 | NumMeshesToDraw = FilterOutCulledBoxes(ref BoxesMinMaxAoSoA.Array, ref VisibilityMask.Array, ref MeshTransforms, false); 778 | UnityEngine.Profiling.Profiler.EndSample(); 779 | 780 | if (UnitCubeMesh && DefaultMaterial) 781 | { 782 | DefaultMaterial.enableInstancing = true; 783 | Graphics.DrawMeshInstanced( 784 | UnitCubeMesh, 785 | 0, 786 | DefaultMaterial, 787 | MeshTransforms, 788 | Mathf.Min(NumMeshesToDraw, 1023), 789 | null, 790 | ShadowCastingMode.Off, 791 | false, 792 | 0, 793 | null, 794 | LightProbeUsage.Off, 795 | null); 796 | } 797 | } 798 | } 799 | -------------------------------------------------------------------------------- /Assets/FrustumCullSetupScript.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c7b7c12ab96263943a100fd4a61a30ca 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/FrustumCullSetupScript.preset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!181963792 &2655988077585873504 4 | Preset: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_Name: FrustumCullSetupScript 10 | m_TargetType: 11 | m_NativeTypeID: 114 12 | m_ManagedTypePPtr: {fileID: 11500000, guid: c7b7c12ab96263943a100fd4a61a30ca, 13 | type: 3} 14 | m_ManagedTypeFallback: 15 | m_Properties: 16 | - target: {fileID: 0} 17 | propertyPath: m_Enabled 18 | value: 1 19 | objectReference: {fileID: 0} 20 | - target: {fileID: 0} 21 | propertyPath: m_EditorHideFlags 22 | value: 0 23 | objectReference: {fileID: 0} 24 | - target: {fileID: 0} 25 | propertyPath: m_EditorClassIdentifier 26 | value: 27 | objectReference: {fileID: 0} 28 | - target: {fileID: 0} 29 | propertyPath: UnitCubeMesh 30 | value: 31 | objectReference: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} 32 | - target: {fileID: 0} 33 | propertyPath: DefaultMaterial 34 | value: 35 | objectReference: {fileID: 10302, guid: 0000000000000000f000000000000000, type: 0} 36 | - target: {fileID: 0} 37 | propertyPath: RandomDistanceH 38 | value: 350 39 | objectReference: {fileID: 0} 40 | - target: {fileID: 0} 41 | propertyPath: RandomDistanceV 42 | value: 50 43 | objectReference: {fileID: 0} 44 | - target: {fileID: 0} 45 | propertyPath: RandomExtentLimitX 46 | value: 28 47 | objectReference: {fileID: 0} 48 | - target: {fileID: 0} 49 | propertyPath: RandomExtentLimitY 50 | value: 22 51 | objectReference: {fileID: 0} 52 | - target: {fileID: 0} 53 | propertyPath: RandomExtentLimitZ 54 | value: 35 55 | objectReference: {fileID: 0} 56 | - target: {fileID: 0} 57 | propertyPath: RandomizeBoxes 58 | value: 0 59 | objectReference: {fileID: 0} 60 | - target: {fileID: 0} 61 | propertyPath: NumMeshTranforms 62 | value: 1023 63 | objectReference: {fileID: 0} 64 | - target: {fileID: 0} 65 | propertyPath: TestFrustumCorners 66 | value: 1 67 | objectReference: {fileID: 0} 68 | - target: {fileID: 0} 69 | propertyPath: NumMeshesToDraw 70 | value: 54 71 | objectReference: {fileID: 0} 72 | - target: {fileID: 0} 73 | propertyPath: CullingJobType 74 | value: 0 75 | objectReference: {fileID: 0} 76 | - target: {fileID: 0} 77 | propertyPath: CullingJobParallel 78 | value: 0 79 | objectReference: {fileID: 0} 80 | -------------------------------------------------------------------------------- /Assets/UnityNativePlugin.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinsteam/Unity-FrustumCulling/c160f5341626a45e8ed7fc0333454866ddcecd82/Assets/UnityNativePlugin.dll -------------------------------------------------------------------------------- /Assets/UnityNativePlugin.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8bf48b28eaa83ca41aa9792ac1e6e727 3 | PluginImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | iconMap: {} 7 | executionOrder: {} 8 | defineConstraints: [] 9 | isPreloaded: 0 10 | isOverridable: 0 11 | isExplicitlyReferenced: 0 12 | platformData: 13 | - first: 14 | '': Any 15 | second: 16 | enabled: 0 17 | settings: 18 | Exclude Editor: 0 19 | Exclude Linux: 0 20 | Exclude Linux64: 0 21 | Exclude LinuxUniversal: 0 22 | Exclude OSXUniversal: 0 23 | Exclude Win: 1 24 | Exclude Win64: 0 25 | - first: 26 | Any: 27 | second: 28 | enabled: 0 29 | settings: {} 30 | - first: 31 | Editor: Editor 32 | second: 33 | enabled: 1 34 | settings: 35 | CPU: x86_64 36 | DefaultValueInitialized: true 37 | OS: Windows 38 | - first: 39 | Facebook: Win 40 | second: 41 | enabled: 0 42 | settings: 43 | CPU: None 44 | - first: 45 | Facebook: Win64 46 | second: 47 | enabled: 0 48 | settings: 49 | CPU: AnyCPU 50 | - first: 51 | Standalone: Linux 52 | second: 53 | enabled: 1 54 | settings: 55 | CPU: x86 56 | - first: 57 | Standalone: Linux64 58 | second: 59 | enabled: 1 60 | settings: 61 | CPU: x86_64 62 | - first: 63 | Standalone: LinuxUniversal 64 | second: 65 | enabled: 1 66 | settings: 67 | CPU: AnyCPU 68 | - first: 69 | Standalone: OSXUniversal 70 | second: 71 | enabled: 1 72 | settings: 73 | CPU: AnyCPU 74 | - first: 75 | Standalone: Win 76 | second: 77 | enabled: 0 78 | settings: 79 | CPU: None 80 | - first: 81 | Standalone: Win64 82 | second: 83 | enabled: 1 84 | settings: 85 | CPU: AnyCPU 86 | userData: 87 | assetBundleName: 88 | assetBundleVariant: 89 | -------------------------------------------------------------------------------- /Assets/main.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 0} 41 | m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} 42 | m_UseRadianceAmbientProbe: 0 43 | --- !u!157 &3 44 | LightmapSettings: 45 | m_ObjectHideFlags: 0 46 | serializedVersion: 11 47 | m_GIWorkflowMode: 1 48 | m_GISettings: 49 | serializedVersion: 2 50 | m_BounceScale: 1 51 | m_IndirectOutputScale: 1 52 | m_AlbedoBoost: 1 53 | m_EnvironmentLightingMode: 0 54 | m_EnableBakedLightmaps: 1 55 | m_EnableRealtimeLightmaps: 1 56 | m_LightmapEditorSettings: 57 | serializedVersion: 12 58 | m_Resolution: 2 59 | m_BakeResolution: 40 60 | m_AtlasSize: 1024 61 | m_AO: 0 62 | m_AOMaxDistance: 1 63 | m_CompAOExponent: 1 64 | m_CompAOExponentDirect: 0 65 | m_ExtractAmbientOcclusion: 0 66 | m_Padding: 2 67 | m_LightmapParameters: {fileID: 0} 68 | m_LightmapsBakeMode: 1 69 | m_TextureCompression: 1 70 | m_FinalGather: 0 71 | m_FinalGatherFiltering: 1 72 | m_FinalGatherRayCount: 256 73 | m_ReflectionCompression: 2 74 | m_MixedBakeMode: 2 75 | m_BakeBackend: 1 76 | m_PVRSampling: 1 77 | m_PVRDirectSampleCount: 32 78 | m_PVRSampleCount: 512 79 | m_PVRBounces: 2 80 | m_PVREnvironmentSampleCount: 256 81 | m_PVREnvironmentReferencePointCount: 2048 82 | m_PVRFilteringMode: 1 83 | m_PVRDenoiserTypeDirect: 1 84 | m_PVRDenoiserTypeIndirect: 1 85 | m_PVRDenoiserTypeAO: 1 86 | m_PVRFilterTypeDirect: 0 87 | m_PVRFilterTypeIndirect: 0 88 | m_PVRFilterTypeAO: 0 89 | m_PVREnvironmentMIS: 1 90 | m_PVRCulling: 1 91 | m_PVRFilteringGaussRadiusDirect: 1 92 | m_PVRFilteringGaussRadiusIndirect: 5 93 | m_PVRFilteringGaussRadiusAO: 2 94 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 95 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 96 | m_PVRFilteringAtrousPositionSigmaAO: 1 97 | m_ShowResolutionOverlay: 1 98 | m_ExportTrainingData: 0 99 | m_LightingDataAsset: {fileID: 0} 100 | m_UseShadowmask: 1 101 | --- !u!196 &4 102 | NavMeshSettings: 103 | serializedVersion: 2 104 | m_ObjectHideFlags: 0 105 | m_BuildSettings: 106 | serializedVersion: 2 107 | agentTypeID: 0 108 | agentRadius: 0.5 109 | agentHeight: 2 110 | agentSlope: 45 111 | agentClimb: 0.4 112 | ledgeDropHeight: 0 113 | maxJumpAcrossDistance: 0 114 | minRegionArea: 2 115 | manualCellSize: 0 116 | cellSize: 0.16666667 117 | manualTileSize: 0 118 | tileSize: 256 119 | accuratePlacement: 0 120 | debug: 121 | m_Flags: 0 122 | m_NavMeshData: {fileID: 0} 123 | --- !u!1 &708399407 124 | GameObject: 125 | m_ObjectHideFlags: 0 126 | m_CorrespondingSourceObject: {fileID: 0} 127 | m_PrefabInstance: {fileID: 0} 128 | m_PrefabAsset: {fileID: 0} 129 | serializedVersion: 6 130 | m_Component: 131 | - component: {fileID: 708399411} 132 | - component: {fileID: 708399410} 133 | - component: {fileID: 708399409} 134 | - component: {fileID: 708399408} 135 | m_Layer: 0 136 | m_Name: Main Camera 137 | m_TagString: MainCamera 138 | m_Icon: {fileID: 0} 139 | m_NavMeshLayer: 0 140 | m_StaticEditorFlags: 0 141 | m_IsActive: 1 142 | --- !u!114 &708399408 143 | MonoBehaviour: 144 | m_ObjectHideFlags: 0 145 | m_CorrespondingSourceObject: {fileID: 0} 146 | m_PrefabInstance: {fileID: 0} 147 | m_PrefabAsset: {fileID: 0} 148 | m_GameObject: {fileID: 708399407} 149 | m_Enabled: 1 150 | m_EditorHideFlags: 0 151 | m_Script: {fileID: 11500000, guid: c7b7c12ab96263943a100fd4a61a30ca, type: 3} 152 | m_Name: 153 | m_EditorClassIdentifier: 154 | UnitCubeMesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} 155 | FrustumMesh: {fileID: 0} 156 | DefaultMaterial: {fileID: 10302, guid: 0000000000000000f000000000000000, type: 0} 157 | RandomDistanceH: 350 158 | RandomDistanceV: 50 159 | RandomExtentLimitX: 28 160 | RandomExtentLimitY: 22 161 | RandomExtentLimitZ: 35 162 | RandomizeBoxes: 0 163 | NumMeshTranforms: 16384 164 | TestFrustumCorners: 1 165 | TestFrustumNative: 0 166 | TestFrustumParallel: 0 167 | TestFrustumParallelBurst: 1 168 | NumMeshesToDraw: 54 169 | --- !u!81 &708399409 170 | AudioListener: 171 | m_ObjectHideFlags: 0 172 | m_CorrespondingSourceObject: {fileID: 0} 173 | m_PrefabInstance: {fileID: 0} 174 | m_PrefabAsset: {fileID: 0} 175 | m_GameObject: {fileID: 708399407} 176 | m_Enabled: 1 177 | --- !u!20 &708399410 178 | Camera: 179 | m_ObjectHideFlags: 0 180 | m_CorrespondingSourceObject: {fileID: 0} 181 | m_PrefabInstance: {fileID: 0} 182 | m_PrefabAsset: {fileID: 0} 183 | m_GameObject: {fileID: 708399407} 184 | m_Enabled: 1 185 | serializedVersion: 2 186 | m_ClearFlags: 1 187 | m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} 188 | m_projectionMatrixMode: 1 189 | m_GateFitMode: 2 190 | m_FOVAxisMode: 0 191 | m_SensorSize: {x: 36, y: 24} 192 | m_LensShift: {x: 0, y: 0} 193 | m_FocalLength: 50 194 | m_NormalizedViewPortRect: 195 | serializedVersion: 2 196 | x: 0 197 | y: 0 198 | width: 1 199 | height: 1 200 | near clip plane: 0.3 201 | far clip plane: 220 202 | field of view: 60 203 | orthographic: 0 204 | orthographic size: 5 205 | m_Depth: -1 206 | m_CullingMask: 207 | serializedVersion: 2 208 | m_Bits: 4294967295 209 | m_RenderingPath: -1 210 | m_TargetTexture: {fileID: 0} 211 | m_TargetDisplay: 0 212 | m_TargetEye: 3 213 | m_HDR: 1 214 | m_AllowMSAA: 1 215 | m_AllowDynamicResolution: 0 216 | m_ForceIntoRT: 0 217 | m_OcclusionCulling: 1 218 | m_StereoConvergence: 10 219 | m_StereoSeparation: 0.022 220 | --- !u!4 &708399411 221 | Transform: 222 | m_ObjectHideFlags: 0 223 | m_CorrespondingSourceObject: {fileID: 0} 224 | m_PrefabInstance: {fileID: 0} 225 | m_PrefabAsset: {fileID: 0} 226 | m_GameObject: {fileID: 708399407} 227 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 228 | m_LocalPosition: {x: 0, y: 1, z: -10} 229 | m_LocalScale: {x: 1, y: 1, z: 1} 230 | m_Children: [] 231 | m_Father: {fileID: 0} 232 | m_RootOrder: 0 233 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 234 | --- !u!1 &1431068669 235 | GameObject: 236 | m_ObjectHideFlags: 0 237 | m_CorrespondingSourceObject: {fileID: 0} 238 | m_PrefabInstance: {fileID: 0} 239 | m_PrefabAsset: {fileID: 0} 240 | serializedVersion: 6 241 | m_Component: 242 | - component: {fileID: 1431068671} 243 | - component: {fileID: 1431068670} 244 | m_Layer: 0 245 | m_Name: Directional Light 246 | m_TagString: Untagged 247 | m_Icon: {fileID: 0} 248 | m_NavMeshLayer: 0 249 | m_StaticEditorFlags: 0 250 | m_IsActive: 1 251 | --- !u!108 &1431068670 252 | Light: 253 | m_ObjectHideFlags: 0 254 | m_CorrespondingSourceObject: {fileID: 0} 255 | m_PrefabInstance: {fileID: 0} 256 | m_PrefabAsset: {fileID: 0} 257 | m_GameObject: {fileID: 1431068669} 258 | m_Enabled: 1 259 | serializedVersion: 9 260 | m_Type: 1 261 | m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} 262 | m_Intensity: 1 263 | m_Range: 10 264 | m_SpotAngle: 30 265 | m_InnerSpotAngle: 21.80208 266 | m_CookieSize: 10 267 | m_Shadows: 268 | m_Type: 2 269 | m_Resolution: -1 270 | m_CustomResolution: -1 271 | m_Strength: 1 272 | m_Bias: 0.05 273 | m_NormalBias: 0.4 274 | m_NearPlane: 0.2 275 | m_CullingMatrixOverride: 276 | e00: 1 277 | e01: 0 278 | e02: 0 279 | e03: 0 280 | e10: 0 281 | e11: 1 282 | e12: 0 283 | e13: 0 284 | e20: 0 285 | e21: 0 286 | e22: 1 287 | e23: 0 288 | e30: 0 289 | e31: 0 290 | e32: 0 291 | e33: 1 292 | m_UseCullingMatrixOverride: 0 293 | m_Cookie: {fileID: 0} 294 | m_DrawHalo: 0 295 | m_Flare: {fileID: 0} 296 | m_RenderMode: 0 297 | m_CullingMask: 298 | serializedVersion: 2 299 | m_Bits: 4294967295 300 | m_RenderingLayerMask: 1 301 | m_Lightmapping: 4 302 | m_LightShadowCasterMode: 0 303 | m_AreaSize: {x: 1, y: 1} 304 | m_BounceIntensity: 1 305 | m_ColorTemperature: 6570 306 | m_UseColorTemperature: 0 307 | m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} 308 | m_UseBoundingSphereOverride: 0 309 | m_ShadowRadius: 0 310 | m_ShadowAngle: 0 311 | --- !u!4 &1431068671 312 | Transform: 313 | m_ObjectHideFlags: 0 314 | m_CorrespondingSourceObject: {fileID: 0} 315 | m_PrefabInstance: {fileID: 0} 316 | m_PrefabAsset: {fileID: 0} 317 | m_GameObject: {fileID: 1431068669} 318 | m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} 319 | m_LocalPosition: {x: 0, y: 3, z: 0} 320 | m_LocalScale: {x: 1, y: 1, z: 1} 321 | m_Children: [] 322 | m_Father: {fileID: 0} 323 | m_RootOrder: 1 324 | m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} 325 | -------------------------------------------------------------------------------- /Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.burst": "1.1.0-preview.3", 4 | "com.unity.collections": "0.0.9-preview.17", 5 | "com.unity.jobs": "0.0.7-preview.10", 6 | "com.unity.mathematics": "1.1.0-preview.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Pictures/Showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinsteam/Unity-FrustumCulling/c160f5341626a45e8ed7fc0333454866ddcecd82/Pictures/Showcase.png -------------------------------------------------------------------------------- /ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2019.1.0b5 2 | m_EditorVersionWithRevision: 2019.1.0b5 (9899a5bd7e43) 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity-FrustumCulling 2 | 3 | A short example to compare performance of simple frustum culling implemented using the following available components in __Unity__ 4 | 5 | | Method | Multithreading 6 | |-----------------------------------------------:|:---------------: 7 | | Standard Unity Math | - 8 | | __Unity.Mathematics__ | optional, via __Unity.Jobs__ 9 | | __Unity.Mathematics__ compiled with __Burst__ | optional, via __Unity.Jobs__ 10 | | Native code via __Plugins__ | - 11 | 12 | ![](https://raw.githubusercontent.com/reinsteam/Unity-FrustumCulling/master/Pictures/Showcase.png) 13 | 14 | ## Package Dependencies 15 | 16 | - Burst 17 | - Collections 18 | - Jobs 19 | - Mathematics --------------------------------------------------------------------------------