Unity中无GC Alloc的CalculateFrustumPlanes

      如果你需要在逻辑层做一些预先的剔除操作,可能需要从MainCamera构建视锥体,然后进行简易相交测试,这时候在unity里面用到的函数接口是CalculateFrustumPlanes:

 1 namespace UnityEngine
 2 {
 3     // 摘要: 
 4     //     Utility class for common geometric functions.
 5     public sealed class GeometryUtility
 6     {
 7         public GeometryUtility();
 8 
 9         // 摘要: 
10         //     Calculates frustum planes.
11         public static Plane[] CalculateFrustumPlanes(Camera camera);
12         //
13         // 摘要: 
14         //     Calculates frustum planes.
15         public static Plane[] CalculateFrustumPlanes(Matrix4x4 worldToProjectionMatrix);
16         //
17         // 摘要: 
18         //     Returns true if bounds are inside the plane array.
19         public static bool TestPlanesAABB(Plane[] planes, Bounds bounds);
20     }
21 }

      然而它的主要问题是有gc alloc,每次调用都会自己new一个Plane数组,这很明显是不科学的,然而unity迟迟未修复此问题。

     下面提供的函数在C#层中重新实现了这个接口,同时没有gcalloc,然而由于在C#中实现的原因,其效率比引擎提供的C++版本慢一倍。C#版本实测一次调用在0.01毫秒左右。所以使用哪一个版本,根据实际需求来是最好的。

  C#版本的实现:

 1 public static class GeometryUtilityUser
 2 {
 3     /**
 4      * @warning OutPlanes must be new Plane[6]
 5      *    Plane Position :
 6      *       Left
 7      *       Right
 8      *       Bottom
 9      *       Top
10      *       Near
11      *       Far
12     */
13     enum EPlaneSide
14     {
15         Left,
16         Right,
17         Bottom,
18         Top,
19         Near,
20         Far
21     }
22 
23     static float[] RootVector = new float[4];
24     static float[] ComVector = new float[4];
25 
26     public static void CalculateFrustumPlanes(Camera InCamera, ref Plane[] OutPlanes)
27     {
28         Matrix4x4 projectionMatrix = InCamera.projectionMatrix;
29         Matrix4x4 worldToCameraMatrix = InCamera.worldToCameraMatrix;
30         Matrix4x4 worldToProjectionMatrix = projectionMatrix * worldToCameraMatrix;
31 
32         RootVector[0] = worldToProjectionMatrix[3, 0];
33         RootVector[1] = worldToProjectionMatrix[3, 1];
34         RootVector[2] = worldToProjectionMatrix[3, 2];
35         RootVector[3] = worldToProjectionMatrix[3, 3];
36 
37         ComVector[0] = worldToProjectionMatrix[0, 0];
38         ComVector[1] = worldToProjectionMatrix[0, 1];
39         ComVector[2] = worldToProjectionMatrix[0, 2];
40         ComVector[3] = worldToProjectionMatrix[0, 3];
41 
42         CalcPlane(ref OutPlanes[(int)EPlaneSide.Left], ComVector[0] + RootVector[0], ComVector[1] + RootVector[1], ComVector[2] + RootVector[2], ComVector[3] + RootVector[3]);
43         CalcPlane(ref OutPlanes[(int)EPlaneSide.Right], -ComVector[0] + RootVector[0], -ComVector[1] + RootVector[1], -ComVector[2] + RootVector[2], -ComVector[3] + RootVector[3]);
44 
45         ComVector[0] = worldToProjectionMatrix[1, 0];
46         ComVector[1] = worldToProjectionMatrix[1, 1];
47         ComVector[2] = worldToProjectionMatrix[1, 2];
48         ComVector[3] = worldToProjectionMatrix[1, 3];
49 
50         CalcPlane(ref OutPlanes[(int)EPlaneSide.Bottom], ComVector[0] + RootVector[0], ComVector[1] + RootVector[1], ComVector[2] + RootVector[2], ComVector[3] + RootVector[3]);
51         CalcPlane(ref OutPlanes[(int)EPlaneSide.Top], -ComVector[0] + RootVector[0], -ComVector[1] + RootVector[1], -ComVector[2] + RootVector[2], -ComVector[3] + RootVector[3]);
52 
53         ComVector[0] = worldToProjectionMatrix[2, 0];
54         ComVector[1] = worldToProjectionMatrix[2, 1];
55         ComVector[2] = worldToProjectionMatrix[2, 2];
56         ComVector[3] = worldToProjectionMatrix[2, 3];
57 
58         CalcPlane(ref OutPlanes[(int)EPlaneSide.Near], ComVector[0] + RootVector[0], ComVector[1] + RootVector[1], ComVector[2] + RootVector[2], ComVector[3] + RootVector[3]);
59         CalcPlane(ref OutPlanes[(int)EPlaneSide.Far], -ComVector[0] + RootVector[0], -ComVector[1] + RootVector[1], -ComVector[2] + RootVector[2], -ComVector[3] + RootVector[3]);
60 
61     }
62 
63     static void CalcPlane(ref Plane InPlane, float InA, float InB, float InC, float InDistance)
64     {
65         Vector3 Normal = new Vector3(InA, InB, InC);
66 
67         float InverseMagnitude = 1.0f / (float)System.Math.Sqrt(Normal.x * Normal.x + Normal.y * Normal.y + Normal.z * Normal.z);
68 
69         InPlane.normal = new Vector3(Normal.x * InverseMagnitude, Normal.y * InverseMagnitude, Normal.z * InverseMagnitude);
70 
71         InPlane.distance = InDistance * InverseMagnitude;
72     }
73 }

      下面的代码可用于验证其正确性:

 1 private Plane[] CalcFrustum(Camera InCamera)
 2     {
 3         GeometryUtilityUser.CalculateFrustumPlanes(InCamera, ref CachedPlanes);
 4 #if UNITY_EDITOR && false
 5         Plane[] SysPlanes = GeometryUtility.CalculateFrustumPlanes(InCamera);
 6         for (int i = 0; i < SysPlanes.Length; ++i )
 7         {
 8             if( !IsEqual(SysPlanes[i], CachedPlanes[i]) )
 9             {
10                 DebugHelper.Assert(false, "Internal error in CalcFrustum");
11             }
12         }
13 #endif
14             return CachedPlanes;
15     }
16     private static bool IsEqual(Plane InFirst, Plane InSecond)
17     {
18         return IsEqual(InFirst.normal, InSecond.normal) &&
19             IsEqual(InFirst.distance, InSecond.distance);
20     }
21     private static bool IsEqual(Vector3 InFirst, Vector3 InSecond)
22     {
23         return IsEqual(InFirst.x, InSecond.x) &&
24             IsEqual(InFirst.y, InSecond.y) &&
25             IsEqual(InFirst.y, InSecond.y);
26     }
27     private static bool IsEqual(float InFirst, float InSecond)
28     {
29         return System.Math.Abs(InFirst - InSecond) < 0.001f;
30     }
31     private Plane[] CachedPlanes = new Plane[6];

 

posted @ 2015-09-11 09:43  bodong  阅读(2080)  评论(0编辑  收藏