遊戲引擎數學庫 Plane

Dba_sys發表於2024-04-04

0 前言

平面的表達方式有很多,常用的就兩種。向量形式的點法式,標量形式的平面方程。兩者可以互相轉化。

\[(\mathbf{p}-\mathbf{p_0})\cdot\mathbf{n}=0 \]

\[Ax + By + Cz + D = 0 \]

\[A = \mathbf{n}.x,\; B = \mathbf{n}.y,\; C = \mathbf{n}.z,\; D = \mathbf{n} \cdot -\mathbf{p_0} \]

平面方程就是任意一點到平面的垂直投影距離。距離以法線為座標基。

以點法式來看,平面方程有兩種意義:

  1. 任意一點p投影到n上的距離,減去p0投影到n上的距離。

  2. 向量p-p0投影到n上的距離。

可以把n想象為一根一維數軸,投影距離區分正負的。

image
image

1 平面的應用 物理碰撞

加abs()是因為要切換包圍盒的頂點。

// Test if AABB b intersects plane p
int TestAABBPlane(AABB b, Plane p) {
    // Convert AABB to center-extents representation
    Point c = (b.max + b.min) * 0.5f; // Compute AABB center
    Point e = b.max - c; // Compute positive extents

    // 此處只有聰明人才能懂為什麼加絕對值,這個和unreal的判斷異曲同工之妙
    float r = e[0]*Abs(p.n[0]) + e[1]*Abs(p.n[1]) + e[2]*Abs(p.n[2]);

    // Compute distance of box center from plane
    float s = Dot(p.n, c) - p.d;

    // Intersection occurs when distance s falls within [-r,+r] interval
    return Abs(s) <= r;
}

2 Unreal Math 平面位置關係

判斷正負也是因為要切換包圍盒的頂點。

// Copyright Epic Games, Inc. All Rights Reserved.

/*=============================================================================
	UnMath.cpp: Unreal math routines
=============================================================================*/

int32 FMath::PlaneAABBRelativePosition(const FPlane& P, const FBox& AABB)
{
	// find diagonal most closely aligned with normal of plane
	FVector Vmin, Vmax;

	// Bypass the slow FVector[] operator. Not RESTRICT because it won't update Vmin, Vmax
	FVector::FReal* VminPtr = (FVector::FReal*)&Vmin;
	FVector::FReal* VmaxPtr = (FVector::FReal*)&Vmax;

	// Use restrict to get better instruction scheduling and to bypass the slow FVector[] operator
	const FVector::FReal* RESTRICT AABBMinPtr = (const FVector::FReal*)&AABB.Min;
	const FVector::FReal* RESTRICT AABBMaxPtr = (const FVector::FReal*)&AABB.Max;
	const FPlane::FReal* RESTRICT PlanePtr = (const FPlane::FReal*)&P;

	for(int32 Idx=0;Idx<3;++Idx)
	{
		if(PlanePtr[Idx] >= 0.f)
		{
			VminPtr[Idx] = AABBMinPtr[Idx];
			VmaxPtr[Idx] = AABBMaxPtr[Idx];
		}
		else
		{
			VminPtr[Idx] = AABBMaxPtr[Idx];
			VmaxPtr[Idx] = AABBMinPtr[Idx]; 
		}
	}

	// if either diagonal is right on the plane, or one is on either side we have an interesection
	FPlane::FReal dMax = P.PlaneDot(Vmax);
	FPlane::FReal dMin = P.PlaneDot(Vmin);

	// if Max is below plane, or Min is above we know there is no intersection.. otherwise there must be one
	if (dMax < 0.f)
	{
		return -1;
	}
	else if (dMin > 0.f)
	{
		return 1;
	}
	return 0;
}

bool FMath::PlaneAABBIntersection(const FPlane& P, const FBox& AABB)
{
	return PlaneAABBRelativePosition(P, AABB) == 0;
}

X. Reff

  1. https://en.wikipedia.org/wiki/Line–plane_intersection
  2. https://gdbooks.gitbooks.io/3dcollisions/
  3. 《Games Physicks CookBook》

相關文章