3 namespace UnityEngine
.Experimental
.Rendering
7 public Plane
[] planes
; // Left, right, top, bottom, near, far
8 public Vector3
[] corners
; // Positions of the 8 corners
10 // The frustum will be camera-relative if given a camera-relative VP matrix.
11 public static void Create(Frustum frustum
, Matrix4x4 viewProjMatrix
, bool depth_0_1
, bool reverseZ
)
13 GeometryUtility
.CalculateFrustumPlanes(viewProjMatrix
, frustum
.planes
);
21 // See "Fast Extraction of Viewing Frustum Planes" by Gribb and Hartmann.
22 Vector3 f
= new Vector3(viewProjMatrix
.m20
, viewProjMatrix
.m21
, viewProjMatrix
.m22
);
23 float s
= (float)(1.0 / Math
.Sqrt(f
.sqrMagnitude
));
24 Plane np
= new Plane(s
* f
, s
* viewProjMatrix
.m23
);
26 frustum
.planes
[4] = np
;
31 Plane tmp
= frustum
.planes
[4];
32 frustum
.planes
[4] = frustum
.planes
[5];
33 frustum
.planes
[5] = tmp
;
36 Matrix4x4 invViewProjMatrix
= viewProjMatrix
.inverse
;
38 // Unproject 8 frustum points.
39 frustum
.corners
[0] = invViewProjMatrix
.MultiplyPoint(new Vector3(-1, -1, 1));
40 frustum
.corners
[1] = invViewProjMatrix
.MultiplyPoint(new Vector3(1, -1, 1));
41 frustum
.corners
[2] = invViewProjMatrix
.MultiplyPoint(new Vector3(-1, 1, 1));
42 frustum
.corners
[3] = invViewProjMatrix
.MultiplyPoint(new Vector3(1, 1, 1));
43 frustum
.corners
[4] = invViewProjMatrix
.MultiplyPoint(new Vector3(-1, -1, nd
));
44 frustum
.corners
[5] = invViewProjMatrix
.MultiplyPoint(new Vector3(1, -1, nd
));
45 frustum
.corners
[6] = invViewProjMatrix
.MultiplyPoint(new Vector3(-1, 1, nd
));
46 frustum
.corners
[7] = invViewProjMatrix
.MultiplyPoint(new Vector3(1, 1, nd
));
51 public struct OrientedBBox
53 // 3 x float4 = 48 bytes.
54 // TODO: pack the axes into 16-bit UNORM per channel, and consider a quaternionic representation.
59 public Vector3 center
;
62 public Vector3 forward { get { return Vector3.Cross(up, right); }
}
64 public OrientedBBox(Matrix4x4 trs
)
66 Vector3 vecX
= trs
.GetColumn(0);
67 Vector3 vecY
= trs
.GetColumn(1);
68 Vector3 vecZ
= trs
.GetColumn(2);
70 center
= trs
.GetColumn(3);
71 right
= vecX
* (1.0f
/ vecX
.magnitude
);
72 up
= vecY
* (1.0f
/ vecY
.magnitude
);
74 extentX
= 0.5f
* vecX
.magnitude
;
75 extentY
= 0.5f
* vecY
.magnitude
;
76 extentZ
= 0.5f
* vecZ
.magnitude
;
78 } // struct OrientedBBox
80 public static class GeometryUtils
82 // Returns 'true' if the OBB intersects (or is inside) the frustum, 'false' otherwise.
83 public static bool Overlap(OrientedBBox obb
, Frustum frustum
, int numPlanes
, int numCorners
)
87 // Test the OBB against frustum planes. Frustum planes are inward-facing.
88 // The OBB is outside if it's entirely behind one of the frustum planes.
89 // See "Real-Time Rendering", 3rd Edition, 16.10.2.
90 for (int i
= 0; overlap
&& i
< numPlanes
; i
++)
92 Vector3 n
= frustum
.planes
[i
].normal
;
93 float d
= frustum
.planes
[i
].distance
;
95 // Max projection of the half-diagonal onto the normal (always positive).
96 float maxHalfDiagProj
= obb
.extentX
* Mathf
.Abs(Vector3
.Dot(n
, obb
.right
))
97 + obb
.extentY
* Mathf
.Abs(Vector3
.Dot(n
, obb
.up
))
98 + obb
.extentZ
* Mathf
.Abs(Vector3
.Dot(n
, obb
.forward
));
100 // Positive distance -> center in front of the plane.
101 // Negative distance -> center behind the plane (outside).
102 float centerToPlaneDist
= Vector3
.Dot(n
, obb
.center
) + d
;
104 // outside = maxHalfDiagProj < -centerToPlaneDist
105 // outside = maxHalfDiagProj + centerToPlaneDist < 0
106 // overlap = overlap && !outside
107 overlap
= overlap
&& (maxHalfDiagProj
+ centerToPlaneDist
>= 0);
110 if (numCorners
== 0) return overlap
;
112 // Test the frustum corners against OBB planes. The OBB planes are outward-facing.
113 // The frustum is outside if all of its corners are entirely in front of one of the OBB planes.
114 // See "Correct Frustum Culling" by Inigo Quilez.
115 // We can exploit the symmetry of the box by only testing against 3 planes rather than 6.
116 Plane
[] planes
= new Plane
[3];
118 planes
[0].normal
= obb
.right
;
119 planes
[0].distance
= obb
.extentX
;
120 planes
[1].normal
= obb
.up
;
121 planes
[1].distance
= obb
.extentY
;
122 planes
[2].normal
= obb
.forward
;
123 planes
[2].distance
= obb
.extentZ
;
125 for (int i
= 0; overlap
&& i
< 3; i
++)
127 Plane plane
= planes
[i
];
129 // We need a separate counter for the "box fully inside frustum" case.
130 bool outsidePos
= true; // Positive normal
131 bool outsideNeg
= true; // Reversed normal
133 // Merge 2 loops. Continue as long as all points are outside either plane.
134 for (int j
= 0; j
< numCorners
; j
++)
136 float proj
= Vector3
.Dot(plane
.normal
, frustum
.corners
[j
] - obb
.center
);
137 outsidePos
= outsidePos
&& (proj
> plane
.distance
);
138 outsideNeg
= outsideNeg
&& (-proj
> plane
.distance
);
141 overlap
= overlap
&& !(outsidePos
|| outsideNeg
);
147 public static readonly Matrix4x4 FlipMatrixLHSRHS
= Matrix4x4
.Scale(new Vector3(1, 1, -1));
149 public static Vector4
Plane(Vector3 position
, Vector3 normal
)
152 var d
= -Vector3
.Dot(n
, position
);
153 var plane
= new Vector4(n
.x
, n
.y
, n
.z
, d
);
157 public static Vector4
CameraSpacePlane(Matrix4x4 worldToCamera
, Vector3 pos
, Vector3 normal
, float sideSign
= 1, float clipPlaneOffset
= 0)
159 var offsetPos
= pos
+ normal
* clipPlaneOffset
;
160 var cpos
= worldToCamera
.MultiplyPoint(offsetPos
);
161 var cnormal
= worldToCamera
.MultiplyVector(normal
).normalized
* sideSign
;
162 return new Vector4(cnormal
.x
, cnormal
.y
, cnormal
.z
, -Vector3
.Dot(cpos
, cnormal
));
165 public static Matrix4x4
CalculateWorldToCameraMatrixRHS(Vector3 position
, Quaternion rotation
)
167 return Matrix4x4
.Scale(new Vector3(1, 1, -1)) * Matrix4x4
.TRS(position
, rotation
, Vector3
.one
).inverse
;
170 public static Matrix4x4
CalculateWorldToCameraMatrixRHS(Transform transform
)
172 return Matrix4x4
.Scale(new Vector3(1, 1, -1)) * transform
.localToWorldMatrix
.inverse
;
175 public static Matrix4x4
CalculateObliqueMatrix(Matrix4x4 sourceProjection
, Vector4 clipPlane
)
177 var projection
= sourceProjection
;
178 var inversion
= sourceProjection
.inverse
;
180 var cps
= new Vector4(
181 Mathf
.Sign(clipPlane
.x
),
182 Mathf
.Sign(clipPlane
.y
),
185 var q
= inversion
* cps
;
186 Vector4 M4
= new Vector4(projection
[3], projection
[7], projection
[11], projection
[15]);
188 var c
= clipPlane
* ((2.0f
*Vector4
.Dot(M4
, q
)) / Vector4
.Dot(clipPlane
, q
));
190 projection
[2] = c
.x
- M4
.x
;
191 projection
[6] = c
.y
- M4
.y
;
192 projection
[10] = c
.z
- M4
.z
;
193 projection
[14] = c
.w
- M4
.w
;
198 public static Matrix4x4
CalculateReflectionMatrix(Vector3 position
, Vector3 normal
)
200 return CalculateReflectionMatrix(Plane(position
, normal
.normalized
));
203 public static Matrix4x4
CalculateReflectionMatrix(Vector4 plane
)
205 var reflectionMat
= new Matrix4x4();
207 reflectionMat
.m00
= (1F
- 2F
* plane
[0] * plane
[0]);
208 reflectionMat
.m01
= (-2F
* plane
[0] * plane
[1]);
209 reflectionMat
.m02
= (-2F
* plane
[0] * plane
[2]);
210 reflectionMat
.m03
= (-2F
* plane
[3] * plane
[0]);
212 reflectionMat
.m10
= (-2F
* plane
[1] * plane
[0]);
213 reflectionMat
.m11
= (1F
- 2F
* plane
[1] * plane
[1]);
214 reflectionMat
.m12
= (-2F
* plane
[1] * plane
[2]);
215 reflectionMat
.m13
= (-2F
* plane
[3] * plane
[1]);
217 reflectionMat
.m20
= (-2F
* plane
[2] * plane
[0]);
218 reflectionMat
.m21
= (-2F
* plane
[2] * plane
[1]);
219 reflectionMat
.m22
= (1F
- 2F
* plane
[2] * plane
[2]);
220 reflectionMat
.m23
= (-2F
* plane
[3] * plane
[2]);
222 reflectionMat
.m30
= 0F
;
223 reflectionMat
.m31
= 0F
;
224 reflectionMat
.m32
= 0F
;
225 reflectionMat
.m33
= 1F
;
227 return reflectionMat
;
230 public static Matrix4x4
GetWorldToCameraMatrixLHS(this Camera camera
)
232 return FlipMatrixLHSRHS
* camera
.worldToCameraMatrix
;
235 public static Matrix4x4
GetProjectionMatrixLHS(this Camera camera
)
237 return camera
.projectionMatrix
* FlipMatrixLHSRHS
;
240 public static bool IsProjectionMatrixOblique(Matrix4x4 projectionMatrix
)
242 return projectionMatrix
[2] != 0 || projectionMatrix
[6] != 0;
245 public static Matrix4x4
CalculateProjectionMatrix(Camera camera
)
247 if (camera
.orthographic
)
249 var h
= camera
.orthographicSize
;
250 var w
= camera
.orthographicSize
* camera
.aspect
;
251 return Matrix4x4
.Ortho(-w
, w
, -h
, h
, camera
.nearClipPlane
, camera
.farClipPlane
);
254 #if UNITY_2019_1_OR_NEWER
255 return Matrix4x4
.Perspective(camera
.GetGateFittedFieldOfView(), camera
.aspect
, camera
.nearClipPlane
, camera
.farClipPlane
);
257 return Matrix4x4
.Perspective(camera
.fieldOfView
, camera
.aspect
, camera
.nearClipPlane
, camera
.farClipPlane
);
260 } // class GeometryUtils