Вычисление ограничивающей сферы для 3D-сетки в Python

В рамках написания библиотеки 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 до 1 кадра в секунду! Это неудивительно, поскольку я предполагаю, что временная сложность для этого попарного кода равна O (n ^ 2).

Мой вопрос: какой алгоритм является быстрым и достаточно простым для реализации, который вычисляет (по крайней мере) приблизительную ограничивающую сферу с учетом набора трехмерных точек из сетки? Я просмотрел эту страницу Википедии и увидел там алгоритм под названием «ограничивающая сфера Риттера». Однако для этого мне нужно выбрать какую-то случайную точку 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)

person CodeSurgeon    schedule 08.01.2017    source источник
comment
@ user3386109 Основная причина, по которой я рассматривал сферу, заключалась в том, что тогда действительно просто увидеть, сталкивается ли сфера со сторонами пирамиды просмотра. Все, что вам нужно сделать, это испытать на плоскости. В жестком случае, когда центр сферы находится за пределами усеченной пирамиды, я бы просто нашел кратчайшее расстояние от центра сферы до плоскости. Если это расстояние меньше радиуса, то эту сетку необходимо отрендерить. Есть ли такой же быстрый способ проверить столкновение коробки и пирамиды? В любом случае, мне обязательно нужно реализовать ограничивающие рамки!   -  person CodeSurgeon    schedule 08.01.2017
comment
Лучшим подходом может быть загрузка предварительно вычисленных ограничивающих объектов и их поворот в соответствии с ориентацией сетки. Также стоит отметить, что python не очень быстрый язык для обработки чисел, связанных с рендерингом. Использование numpy - хорошая оптимизация, но я скептически отношусь к ее масштабированию. Возможно, будет лучше создать движок на C и использовать python в качестве языка сценариев поверх.   -  person Aaron3468    schedule 08.01.2017
comment
@ Aaron3468 Это, вероятно, разумный подход для повторяющейся статической геометрии. Поскольку в моем тестовом сценарии все тестовые модели используют одни и те же данные сетки, я могу перенести функцию compute_bounding_volume в свой класс Mesh из моего класса Model. Все классы, которые я написал для pyorama.math3d, завершают numpy массивы. Весь этот код можно найти на моей странице репозитория github для pyorama. Я по-прежнему склонен полагать, что есть что-то, что я могу сделать для улучшения этого кода, помимо переноса нагрузки с времени выполнения на инициализацию сетки.   -  person CodeSurgeon    schedule 08.01.2017
comment
Пройдите по вершинам один раз и соберите самое высокое и самое низкое значение для каждого измерения - ограничивающую рамку. Используйте среднее значение наивысшего и наименьшего значения для каждого измерения - в центре поля. Затем найдите евклидово расстояние между центром и каждым из 8 углов коробки. Соблюдайте максимальное расстояние и центр. Теперь у вас есть ограничивающий круг только с 1 итерацией по точкам.   -  person Aaron3468    schedule 08.01.2017
comment
@ Aaron3468 Думаю, вы правы. Вероятно, не может быть ничего проще или быстрее, тем более что он проходит через вершины только один раз! Я пошел дальше и реализовал это, и он увеличился до 25 кадров в секунду, когда я вычисляю сферу каждый кадр для каждой модели, а не один раз в начале. Я добавил этот новый код к вопросу. Мне, вероятно, придется профилировать мою библиотеку math3d и посмотреть, насколько сильно там узкое место.   -  person CodeSurgeon    schedule 08.01.2017
comment
На компьютере в 800 раз быстрее, чем gameboy, мой эмулятор python работал со скоростью около 50% ... без звука и видео. Вы сражаетесь против зерна, но, возможно, вы многому научитесь, оптимизировав это для python :)   -  person Aaron3468    schedule 08.01.2017


Ответы (1)


Обойдите вершины один раз и соберите наибольшее и наименьшее значение для каждого измерения. Это создает ограничивающую рамку из Vec3 (самый низкий.x, самый низкий.y, самый низкий.z) и Vec3 (самый высокий.x, самый высокий.y, самый высокий.z).

Используйте среднее значение наивысшего и наименьшего значения для каждого измерения. Это создает центр поля как Vec3 ((самый низкий.x + самый высокий.x) / 2, ...).

Затем найдите евклидово расстояние между центром и каждым из 8 углов коробки. Используйте наибольшее расстояние и центр, который вы нашли, чтобы образовать ограничивающий круг.

Вы только один раз повторили набор данных и получили хорошее приближение к ограничивающему кругу!


Вычисленная таким образом ограничивающая окружность почти наверняка будет больше, чем сетка. Чтобы уменьшить его, вы можете установить радиус равным расстоянию по самому широкому измерению от центра. При таком подходе есть риск обрубить грани в углах.

Вы можете итеративно уменьшить радиус и проверить, что все точки находятся в ограничивающем круге, но тогда производительность будет хуже, чем у исходного алгоритма.

person Aaron3468    schedule 08.01.2017
comment
Спасибо за вашу помощь! Я обновил вопрос, добавив в него реализацию вашего решения с использованием ограничивающей рамки. - person CodeSurgeon; 08.01.2017