转载

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];
正文到此结束
Loading...