在Python中计算三维网格的边界球

时间:2017-01-08 05:19:36

标签: python algorithm 3d frustum bounding-volume

作为编写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维基百科页面,看到有一个名为" 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)

1 个答案:

答案 0 :(得分:1)

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

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

然后获得中心与盒子8个角落之间的欧氏距离。使用最大距离,找到的中心形成一个边界圆。

您只需在数据集中迭代一次,并且可以很好地逼近边界圆圈!

以这种方式计算的边界圆几乎肯定会比网格大。要缩小它,可以将半径设置为距离中心最宽的维度。这种方法有可能切断角落里的面孔。

您可以迭代缩小半径并检查所有点是否在边界圆中,但是您的性能会比原始算法差。