游戏引擎数学库 Plane

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》
posted @ 2024-04-04 20:13  Dba_sys  阅读(30)  评论(0编辑  收藏  举报