网资酷

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 71|回复: 0

如何构造三维平面

[复制链接]

2

主题

6

帖子

10

积分

新手上路

Rank: 1

积分
10
发表于 2022-12-11 07:43:16 | 显示全部楼层 |阅读模式
几何平面,是空间上基础的图元,也是游戏中经常遇到的元素,本篇文章侧重介绍几何平面的数学表示,以及如何初始化一个平面对象。文章分为两部分,第一部分介绍平面的数学表示,第二部分介绍如何根据指定条件构造一个平面对象,第三部分是C++源码实现。
文章目录:

  • 数学表示
  • 构造平面
  • 源码实现
  • 参考
数学表示

直观的讲,平面就是一个平坦的无穷大的纸,上面有无穷个点。在三维空间的任何一个平面Г,都可以用参数方程表示为:
\Gamma :a\cdot x+b\cdot y+c\cdot z+d=0 \tag{1}
其中,a,b,c,d是常量且不全为0,\left( x,y,z \right)表示点的坐标,即任意一个满足等式(1)的点都在平面Г上。\left( a,b,c \right)构成平面的法向量\vec{n},因此平面的参数方程的另一种形式为:
\Gamma :\vec{n}\cdot P+d=0 \tag{2}



图1. 平面的表示

其中,\vec{n}是平面的法向量,d是常量,P是变量,表示满足方程的点。 如图1所示,显然,只需要一个三维法向量\vec{n}和常量d,就可以表示三维空间的平面。向量\vec{n}是平面的法向量,即垂直于平面Г,法向量所指的方向是平面的正方向,与它相对的,则是平面的负方向。平面在正方向上的面,称为平面的正面,与它对应的是平面的背面。一个平面把三维空间分成两半,这样一个平面,也称为超平面,分成的两半三维空间称为半空间,在平面正方向上的空间称为正半空间,在平面负方向上的空间称为负半空间。在实际实现平面对象的时候,可以根据具体的情况,选择是否需要对法向量\vec{n}进行归一化,毕竟归一化需要3个乘法、2个加法和1个求根操作,而且可能加剧浮点数的精度问题。
此外,给定平面上的一个点,在它上面的两个向量,也可以表示平面。如图2所示,设P在平面\Gamma 上,\vec{u}和\vec{v}是平面上不平行的两个向量,可以把它们看成由点P引出的两个指向平面上不同方向的方向向量,那么任意一组实数s,t构成平面上的所有点M:
M=P+s\vec{v}+t\vec{u} \tag{3}



图2. 平面的另一种表示

两个不同的平面,要么平行,要么相交于一条直线。如图3所示,图(a)中两个平面{{\Gamma }_{1}}和{{\Gamma }_{2}}是相互平行的;如果它们不平行,那么它们一定相交于一条直线,如图(b)所示。



图3. 两个平面间的关系

但对于三个不同的平面,则有五种不同的关系。如图4所示,图(a)中,三个平面互相平行;图(b)中,平面{{\Gamma }_{1}}、{{\Gamma }_{2}}、{{\Gamma }_{3}}互相相交于直线{{L}_{1}};图(c)中,平面{{\Gamma }_{1}}分别与{{\Gamma }_{2}}和{{\Gamma }_{3}}相交于直线{{L}_{1}}、{{L}_{2}},平面{{\Gamma }_{2}}与{{\Gamma }_{3}}互相平行且相交的直线{{L}_{1}}与{{L}_{2}}也是互相平行的;图(d)中,平面{{\Gamma }_{1}}与{{\Gamma }_{2}}相交于直线{{L}_{3}},平面{{\Gamma }_{1}}与{{\Gamma }_{3}}相交于直线{{L}_{1}},平面{{\Gamma }_{2}}与{{\Gamma }_{3}}相交于直线{{L}_{2}},相交的三条直线互相平行;图(e)中,平面{{\Gamma }_{1}}与{{\Gamma }_{2}}相交于直线{{L}_{2}},平面{{\Gamma }_{1}}与{{\Gamma }_{3}}相交于直线{{L}_{1}},平面{{\Gamma }_{2}}与{{\Gamma }_{3}}相交于直线{{L}_{3}},三个平面经过同一个点。



图4. 三个平面间的关系

平面有很多重要的性质,例如: - 垂直于同一个平面的两条不同的直线,是互相平行的; - 垂直于同一条直线的两个不同的平面,是互相平行的; - 对于一条直线与平面的关系,要么在平面上,要么平行于平面,要么与平面相交于一点。
构造平面

给定下列三种条件,可以确定一个平面:

  • 给定三个不共线的点;
  • 两条相交的直线;
  • 一条直线和一个不在该直线上的点。
三个不共线的点
在三维空间上,有不共线的三个点{{V}_{i}},i\in \{0,1,2\},构造经过它们的平面。
三个点的顺序影响平面对象法向量的方向,可以使用如下等式,初始化一个经过它们的平面:
\vec{n}=({{V}_{1}}-{{V}_{0}})\times ({{V}_{2}}-{{V}_{0}}),d=-\vec{n}\times {{V}_{i}},i\in \{0,1,2\} \tag{4}
向量\vec{n}与边\overline{{{V}_{0}}{{V}_{1}}}、\overline{{{V}_{1}}{{V}_{2}}}、\overline{{{V}_{2}}{{V}_{0}}}都垂直,求常量d时,可以选择法向量与任意一个点的点积。
如图5(a)所示的三个点顺序,可以初始化得到一个平面,沿着平面法向量向下观察,三个点是沿逆时针方向的。另外一种很简单、实用的判断平面法向量方向的方法,如图(b)所示,张开右手掌,将四根手指与点的朝向一致,拇指就指向法向量的方向了。 如果三个点在同一条直线上,那么求出的法向量是零向量,零向量是无法归一化的。因此,如果计算出的法向量\vec{n}是零向量,就可以判断出三个点是共线的。



图5. 三个点确定一个平面

两条相交直线
在三维空间上,有两条相交于点Q的直线{{L}_{1}}(t)={{P}_{1}}+t{{\vec{d}}_{1}}和{{L}_{2}}(s)={{P}_{2}}+s{{\vec{d}}_{2}},构造经过它们的平面\Gamma ,其中,{{P}_{1}},{{P}_{2}}分别表示直线{{L}_{1}},{{L}_{2}}上的点,{{\vec{d}}_{1}},{{\vec{d}}_{2}}分别表示直线{{L}_{1}},{{L}_{2}}的方向,s,t\in \left( -\infty ,+\infty  \right)。
如图6所示,平面\Gamma 的法向量与平面上的任意一个方向向量垂直,则易计算出平面的法向量为:
\vec{n}={{\vec{d}}_{1}}\times {{\vec{d}}_{2}} \tag{5}
设P是直线{{L}_{1}}或者{{L}_{2}}上的任意一点,平面表示中的常量为:
d=-\vec{n}\cdot P \tag{6}



图6. 两条相交的直线确定一个平面

一条直线和一个点
在三维空间上,有一条直线L(t)=P+t\vec{d},t\in \left( -\infty ,+\infty  \right)和一个不在该直线上的点Q,构造经过它们的平面,其中,P表示直线L上的点,\vec{d}表示直线L的方向。
如图7所示,由于点Q不在直线L,所以由点P指向Q的向量\vec{u}=Q-P一定不是零向量,而且方向与向量\vec{d}不平行。则平面的法向量可以表示为:
\vec{n}=\vec{u}\times \vec{d} \tag{6}
设点Y是直线L上的任意一个点或者为点Q,平面表示中的常量为:
d=-\vec{n}\cdot Y \tag{7}



图7. 一条直线与不在该直线上的点确定一个平面

源码实现

基础库源码链接,参见这里,下面是前面所描述的几何平面对象的实现。
#include "SrGeometricTools.h"
#include "SrDataType.h"

/**
\brief 3D plane class.

This is a 3D plane class with public data members.
The line is parameterized as ^n*^X+d=0,in which ^n is the 'normal' data,d is the 'd' data.
The normal isn't normalized.
*/
class SrPlane3D
{
public:
        /**
        \brief Default constructor.
        */
        SrPlane3D()
        {
                mNormal.set(0, 0, 0);
                mD = 0;
        }
        /**
        \brief  Initialize a plane by giving the parameters, normal and d, of the plane.
        */
        SrPlane3D(const SrVector3D& normal, SrReal d)
        {
                if (EQUAL(normal.x, 0) && EQUAL(normal.y, 0) && EQUAL(normal.z, 0))
                        ASSERT(false);
                mNormal = normal;
                mD = d;
        }
        /**
        \brief  Initialize a plane by plane normal and a given point that the plane includes.
        */
        SrPlane3D(const SrVector3D& direction, const SrPoint3D& point)
        {
                if (EQUAL(direction.x, 0) && EQUAL(direction.y, 0) && EQUAL(direction.z, 0))
                        ASSERT(false);
                mNormal = direction;
                mD = -mNormal.dot(point);
        }
        /**
        \brief  Initialize a plane by three points.The order of the points determine the direction of the normal of plane.
        */
        SrPlane3D(const SrPoint3D& p0, const SrPoint3D& p1, const SrPoint3D&p2)
        {
                SrVector3D norm = (p1 - p0).cross(p2 - p0);
                if (EQUAL(norm.x, 0) && EQUAL(norm.y, 0) && EQUAL(norm.z, 0))
                        ASSERT(false);
                mNormal = norm;
                mD = -mNormal.dot(p0);
        }
        /**
        \brief Destructor. Do nothing.
        */
        ~SrPlane3D() {}
        /**
        \brief  The plane is valid if the normal of the plane is not zero.
        */
        bool        isValid()const
        {
                if (EQUAL(mNormal.x, 0) && EQUAL(mNormal.y, 0) && EQUAL(mNormal.z, 0))
                        return false;
                return true;
        }

public:
        SrVector3D        mNormal;
        SrReal                mD;
};
参考

[1] Hill, F., and S. Kelley. Computer Graphics Using OpenGL, 3/E, Pearson, 2007.
[2] Philip Schneider, and David H. Eberly. Geometric tools for computer graphics, Morgan Kaufmann, 2002.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|网资酷

GMT+8, 2025-3-15 05:56 , Processed in 0.078679 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表