获取多面体(3D 对象)的表面积

Posted

技术标签:

【中文标题】获取多面体(3D 对象)的表面积【英文标题】:Get the surface area of a polyhedron (3D object) 【发布时间】:2011-01-21 23:28:08 【问题描述】:

我有一个 3D 表面,(想想 xy 平面)。飞机可以倾斜。 (想想坡路)。

给定定义表面的 3D 坐标列表(Point3D1XPoint3D1YPoint3D1ZPoint3D12XPoint3D2YPoint3D2ZPoint3D3XPoint3D3YPoint3D3Z、等等),如何计算表面的面积?

请注意,我的问题类似于在 2D 平面中查找区域。在二维平面中,我们有一个定义多边形的点列表,使用这个点列表我们可以找到多边形的面积。现在假设所有这些点都具有z 值,它们在 3D 中被提升以形成表面。我的问题是如何找到那个 3D 表面的面积?

【问题讨论】:

平面的面积是无限的。也许你说的不是飞机? 平面的面积通常是无限的。您需要更好地定义您的形状才能确定面积。 @John,我的意思是一个至少有 3 个点限制其区域的有界平面,“表面”是正确的术语吗? 听起来你可能需要找到平面上多个点的凸包面积。 您的意思是您有一个包含在 3D 空间中的平面中的曲面,还是您对其进行了参数化的 3D 曲面? 【参考方案1】:

您可以根据二维解导出解。

考虑由一堆较小的三角形组成的多边形。

将每个三角形投影回 XY 平面。您可以证明原始三角形的面积是投影三角形面积的 1/(n.k) 倍。 (这里n是包含多边形的平面的法线单位,k是z方向的单位向量)

所以原件的总面积是投影到 XY 平面上的多边形面积的 1/(n.k) 倍。您可以使用现有的 2D 公式来计算。

您可以通过 (e1 x e2 ) / || 计算 n e1 x e2 ||其中 e1 和 e2 是多边形的任意 2 条非平行边。

当然,通过投影到 XZ 或 YZ 平面,您可以获得更好(更准确)的结果。您应该选择法线最接近您的平面的那个。

【讨论】:

你打算如何将公式推广到一个点列表,只有 3 个点?【参考方案2】:

我不知道如何优化这种方法(我之前没有在代码中做过),但是从数学上接近它的方法是将你的形状分成三角形,然后可以很容易地计算出三角形的面积并求和。 (记住:三角形的面积是宽 * 高 * 0.5 - 你需要计算非直角三角形的高度。)

在 3D 中执行这些操作通常意味着每个阶段都需要进行一次计算。例如,在 2D 中,2 点之间的距离(形状的边长)是这样计算的(伪代码,因为我这台机器上没有 VS):

double DistanceBetween(Point a, Point b)

   double dx = a.x - b.x;
   double dy = a.y - b.y;
   return SquareRoot(dx*dx + dy*dy);

在三个维度上变成:

double DistanceBetween(Point3d a, Point3d b)

   double dx = a.x - b.x;
   double dy = a.y - b.y;
   double dz = a.z - b.z;
   return SquareRoot(dx*dx + dy*dy + dz*dz);

将一个形状分割成任意三角形只需要一次选择任意三个相邻的顶点,直到你只剩下最后三个。

【讨论】:

【参考方案3】:

既然你说它是一个多面体,那么stacker的链接(http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm)是适用的。

这是我根据您的情况对 C 代码的大致 C# 翻译:

// NOTE: The original code contained the following notice:
// ---------------------------------------
// Copyright 2000 softSurfer, 2012 Dan Sunday
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// iSurfer.org makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.
// ---------------------------------------
// area3D_Polygon(): computes the area of a 3D planar polygon
//    Input:  int n = the number of vertices in the polygon
//            Point[] V = an array of n+2 vertices in a plane
//                       with V[n]=V[0] and V[n+1]=V[1]
//            Point N = unit normal vector of the polygon's plane
//    Return: the (float) area of the polygon
static float
area3D_Polygon( int n, Point3D[] V, Point3D N )

    float area = 0;
    float an, ax, ay, az;  // abs value of normal and its coords
    int   coord;           // coord to ignore: 1=x, 2=y, 3=z
    int   i, j, k;         // loop indices

    // select largest abs coordinate to ignore for projection
    ax = (N.x>0 ? N.x : -N.x);     // abs x-coord
    ay = (N.y>0 ? N.y : -N.y);     // abs y-coord
    az = (N.z>0 ? N.z : -N.z);     // abs z-coord

    coord = 3;                     // ignore z-coord
    if (ax > ay) 
        if (ax > az) coord = 1;    // ignore x-coord
    
    else if (ay > az) coord = 2;   // ignore y-coord

    // compute area of the 2D projection
    for (i=1, j=2, k=0; i<=n; i++, j++, k++)
        switch (coord) 
        case 1:
            area += (V[i].y * (V[j].z - V[k].z));
            continue;
        case 2:
            area += (V[i].x * (V[j].z - V[k].z));
            continue;
        case 3:
            area += (V[i].x * (V[j].y - V[k].y));
            continue;
        

    // scale to get area before projection
    an = Math.Sqrt( ax*ax + ay*ay + az*az);  // length of normal vector
    switch (coord) 
    case 1:
        area *= (an / (2*ax));
        break;
    case 2:
        area *= (an / (2*ay));
        break;
    case 3:
        area *= (an / (2*az));
        break;
    
    return area;

【讨论】:

多面体*(多边形是二维的) 不需要计算an,长度为1。你也得到负数,所以返回abs(area)是正确的。【参考方案4】:

您是指 3D 平面多边形的面积吗?

    http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm http://local.wasp.uwa.edu.au/~pbourke/geometry/area3d/ http://thebuildingcoder.typepad.com/blog/2008/12/3d-polygon-areas.html

【讨论】:

【参考方案5】:

我赞成a fewanswers,我认为这是正确的。但我认为最简单的方法——无论是 2D 还是 3D,都是使用以下公式:

area = sum(V(i+1) × V(i))/2;

其中× 是vector cross。

执行此操作的代码是:

    public double Area(List<Point3D> PtList)
    

        int nPts = PtList.Count;
        Point3D a;
        int j = 0;

        for (int i = 0; i < nPts; ++i)
        
            j = (i + 1) % nPts;
            a += Point3D.Cross(PtList[i], PtList[j]);
        
        a /= 2;
        return Point3D.Distance(a,default(Point3D));
    

    public static Point3D Cross(Point3D v0, Point3D v1)
    
        return new Point3D(v0.Y * v1.Z - v0.Z * v1.Y,
            v0.Z * v1.X - v0.X * v1.Z,
            v0.X * v1.Y - v0.Y * v1.X);
    

请注意,该解决方案不依赖于 x 平面的投影,我认为这很笨重。

你怎么看?

【讨论】:

其他解决方案(如我发布的)每点执行更少的数学运算(1 个乘法,1 个减法)。你的每点做 6 次乘法和 3 次减法。如果你没有太多的积分或者不需要经常做,就用看起来更简单的方法。否则,如果一个速度足以注意到差异,请使用更快的那个。 @gabe:您提出的解决方案需要投影,我不喜欢。否则它是一个很好的。 @Graviton:你确定这是正确的吗?我认为这不适用于一般情况。您正在沿点列表对每个三角形的面积求和。您是否假设这个点列表实际上构成了一个凸多边形?无论哪种方式,我都看不出这会如何产生正确的表面区域,因为 (1) 你会有重叠的三角形,(2) 你会错过区域。 如果您假设一个有序的点列表会产生一个凸多边形,我假设您希望正确进行三角测量 - 在这种情况下,不应使用 PtList[i]、PtList[j]、但保持一个(例如第一个)点不变。含义:您不需要使用 i 并且计算局部区域如下所示:Point3D.Cross(PtList[0], PtList[j]); @Chris,这是二维面积公式的直接扩展。而在 2D 中,等效公式并不真正关心您是凸面还是凹面。【参考方案6】:

另一个不需要您创建多边形网格的解决方案是围绕周边进行轮廓积分。您使用Green's theorem 将面积积分转换为轮廓积分,然后使用Gauss quadrature 之类的简单方法对每个贡献进行积分和求和。你必须有一个周长的定义。

此过程也适用于有孔的 2D 形状。您只需要定义一个从外周边延伸到孔的切口,围绕孔整合,然后返回周边。

【讨论】:

【参考方案7】:

@Graviton 我无法对上述答案发表评论,因此我将提交一个新答案。

这可能是我对 c# 语法的不熟悉,但我相信您的答案是缺少单位法向量的点积。公式应该是:

area = n.sum( V(i+1) x V(i) )/2;

其中n 是指平面的单位法向量,. 是点积,x 是叉积。

可以使用多边形的任意 3 个向量计算法线:

n = (V1-V0)x(V2-V0)/magnitude((V1-V0)x(V2-V0))

这是一个使用 Vector.js 库的 javascript 实现:

  function getArea (vecs) 
    var area = 0;
    var vecs = [];
    var j = 0;
    var a = new Vector(0,0,0);

    for (var i = 0; i < vecs.length; i++) 
      j = (i + 1) % vecs.length;
      a = a.add( vecs[i].cross(vecs[j]) );
    
    a = a.divide(2);
    var v1 = vecs[1].subtract(vecs[0]);
    var v2 = vecs[2].subtract(vecs[0]);
    var normal = v1.cross(v2);
    normal = normal.unit();
    // area = a.length()/10000; // convert to m2
    area = (normal.dot(a))/10000;
    return area;
  ;

【讨论】:

以上是关于获取多面体(3D 对象)的表面积的主要内容,如果未能解决你的问题,请参考以下文章

详细的光度立体法以及实现

3d一些术语

已知多面体(全是三角形)的顶点坐标,如何判断空间一点是不是在多面体内?

Unity3D绘制物体表面三角形网格

当我更改 gluLookAt 时,我只能看到一个点

Open3d:如何创建一个冰球多面体球体?