在 Python 中计算 3D 网格的包围球

Posted

技术标签:

【中文标题】在 Python 中计算 3D 网格的包围球【英文标题】:Computing the Bounding Sphere for a 3D Mesh in Python 【发布时间】:2017-01-08 05:19:35 【问题描述】:

作为编写 3D 游戏库的一部分,我正在尝试实现截锥体剔除,以避免渲染相机透视截锥体之外的对象。为此,我首先需要为每个网格计算一个边界球,并查看它是否与视锥体的六个侧面中的任何一个发生碰撞。这是我目前(非常)幼稚的计算每个模型的边界球的实现,如我的代码中的model.py 所写:

from pyorama.entity import Entity
from pyorama.math3d.vec3 import Vec3
from pyorama.math3d.mat4 import Mat4
from pyorama.physics.sphere import Sphere
import math
import numpy as np
import itertools as its

class Model(Entity):

    def __init__(self, mesh, texture, transform=Mat4.identity()):
        super(Model, self).__init__()
        self.mesh = mesh
        self.texture = texture
        self.transform = transform

    def compute_bounding_sphere(self):
        vertex_data = self.mesh.vertex_buffer.get_data()
        vertices = []
        for i in range(0, len(vertex_data), 3):
            vertex = Vec3(vertex_data[i: i+3])
            vertices.append(vertex)
        max_pair = None
        max_dist = 0
        for a, b in its.combinations(vertices, 2):
            dist = Vec3.square_distance(a, b)
            if dist > max_dist:
                max_pair = (a, b)
                max_dist = dist
        radius = math.sqrt(max_dist)/2.0
        center = Vec3.lerp(max_pair[0], max_pair[1], 0.5)
        return Sphere(center, radius)

我只是从我的网格中获取成对的点,并使用我找到的最大距离作为我的直径。每帧在 100 个简单的立方体测试模型上调用它非常慢,将我的帧速率从 120 fps 提高到 1 fps!这并不奇怪,因为我假设这个成对代码的时间复杂度是 O(n^2)。

我的问题是,在给定网格中的一组 3D 点的情况下,哪种算法可以快速且相当简单地计算(至少)一个近似边界球?我查看了this Wikipedia 页面,发现有一种算法称为“Ritter 的边界球”。但是,这需要我在网格中选择一些随机点 x 并希望它是近似中心,以便我得到一个相当紧凑的边界球。有没有一种快速的方法来选择一个好的起点 x?任何帮助或建议将不胜感激!

更新:

根据@Aaron3468 的回答,这是我的库中的代码,用于计算边界框和相应的边界球:

from pyorama.entity import Entity
from pyorama.math3d.vec3 import Vec3
from pyorama.math3d.mat4 import Mat4
from pyorama.physics.sphere import Sphere
from pyorama.physics.box import Box
import math
import numpy as np
import itertools as its


class Model(Entity):

    def __init__(self, mesh, texture, transform=Mat4.identity()):
        super(Model, self).__init__()
        self.mesh = mesh
        self.texture = texture
        self.transform = transform

    def compute_bounding_sphere(self):
        box = self.compute_bounding_box()
        a, b = box.min_corner, box.max_corner
        radius = Vec3.distance(a, b)/2.0
        center = Vec3.lerp(a, b, 0.5)
        return Sphere(center, radius)

    def compute_bounding_box(self):
        vertex_data = self.mesh.vertex_buffer.get_data()
        max_corner = Vec3(vertex_data[0:3])
        min_corner = Vec3(vertex_data[0:3])
        for i in range(0, len(vertex_data), 3):
            vertex = Vec3(vertex_data[i: i+3])
            min_corner = Vec3.min_components(vertex, min_corner)
            max_corner = Vec3.max_components(vertex, max_corner)
        return Box(min_corner, max_corner)

【问题讨论】:

@user3386109 我考虑球体的主要原因是因为这样很容易查看球体是否与视锥体的侧面发生碰撞。您所要做的就是进行点平面测试。在球体中心在截锥体之外的困难情况下,我会找到从球体中心到平面的最短距离。如果此距离小于半径,则需要渲染该网格。是否有类似的快速方法来测试截锥体碰撞?不管怎样,我肯定也需要实现边界框! 最好的方法可能是加载预先计算的边界对象并旋转它们以匹配网格的方向。还值得注意的是,对于与渲染相关的数字运算,python 并不是一种非常快速的语言。使用numpy 是一个很好的优化,但我怀疑它可以扩展多少。最好用 C 创建引擎,并在上面使用 python 作为脚本语言。 遍历顶点一次并收集每个维度的最高和最低值 - 一个边界框。使用每个维度的最高和最低值的中值 - 框的中心。然后得到中心和盒子的8个角中的每一个之间的欧几里得距离。保持最大距离和中心。您现在有一个边界圆,在这些点上只进行了 1 次迭代。 @Aaron3468 我认为你是对的。它可能不会比这更简单或更快,尤其是因为它只通过顶点一次!我已经继续并实现了它,当我为每个模型计算球体时,它达到了 25 fps,而不是在开始时计算一次。我已将此新代码添加到问题中。我可能不得不分析一下我的 math3d 库,看看瓶颈有多严重。 在比游戏机快 800 倍的计算机上,我的 python 模拟器只运行了大约 50% 的速度……没有音频或视频。您正在与谷物作斗争,但也许通过为 python 优化它您会学到很多东西:) 【参考方案1】:

遍历顶点一次并收集每个维度的最高和最低值。这将创建一个由 Vec3(lowest.x,lowest.y,lowest.z) 和 Vec3(highest.x,highest.y,highest.z) 组成的边界框。

使用每个维度的最高和最低值的中值。这会将框的中心创建为 Vec3((lowest.x +highest.x)/2, ...)。

然后得到中心和盒子的8个角中的每一个之间的欧几里得距离。使用最大的距离和你找到的中心来做一个边界圆。

您只对数据集进行了一次迭代,并且对边界圆有很好的近似!


以这种方式计算的边界圆几乎肯定会比网格更大。要缩小它,您可以将半径设置为距中心最宽尺寸的距离。这种方法确实有砍掉角落里的人脸的风险。

您可以迭代地缩小半径并检查所有点是否都在边界圆内,但这样您的性能就会比原来的算法差。

【讨论】:

感谢您的帮助!我使用边界框通过您的解决方案实现更新了问题。

以上是关于在 Python 中计算 3D 网格的包围球的主要内容,如果未能解决你的问题,请参考以下文章

如何从3D不规则数据中切割2D网格?

关于Unity中Mesh网格的详解

200. 岛屿数量(Python)

使用 python 将 3D 网格渲染到图像中

点云转3D网格Python

在网格 xcode 中移动球