Алгоритм покрытия круга с переменными радиусами
Для игры я рисую плотные кластеры из нескольких тысяч случайно распределенных кругов с различными радиусами, определяемыми последовательностью (x, y, r) троек. Здесь пример изображения, состоящего из 14 000 кругов:
![example image consisting of 14,000 circles]()
У меня есть некоторые динамические эффекты, такие как слияние кластеров, но чтобы это было возможно, мне нужно будет перерисовать все круги в каждом кадре.
Многие (возможно, 80-90%) кругов, которые нарисованы, покрываются последующими тиражами. Поэтому я подозреваю, что при предварительной обработке я могу значительно ускорить цикл рисования, исключив покрытые круги. Есть ли алгоритм, который может идентифицировать их с разумной эффективностью?
Я могу терпеть довольно большое количество ложных негативов (т.е. нарисовать некоторые круги, которые на самом деле покрыты), если это не так много, что страдает эффективность рисования. Я также могу терпеть ложные срабатывания, если они почти позитивны (например, удалите некоторые круги, накрытые только 99%). Я также поддаюсь изменениям в распределении кругов, если он все еще выглядит нормально.
Ответы
Ответ 1
На основе представленного вами примера изображения кажется, что ваши круги имеют почти постоянный радиус. Если их радиус не может быть ниже значительного количества пикселей, вы можете воспользоваться простой геометрией окружностей, чтобы попробовать подход к пространству изображения.
Представьте, что вы разделите поверхность рендеринга в сетке квадратов так, чтобы наименьший отображаемый круг мог вписаться в сетку следующим образом:
![grid 1]()
![grid 2]()
радиус окружности представляет собой квадратные единицы sqrt (10) и покрывает не менее 21 квадрата, поэтому, если вы пометьте квадраты, полностью перекрытые любым кругом, как уже нарисованы, вы удалите приблизительно 21/10pi долю поверхности круга, что составляет около 2/3.
Вы можете получить некоторые идеи об оптимальном охвате круга квадратами здесь
Процесс отбора будет немного похож на алгоритм обратного рисования:
For each circle from closest to farthest
if all squares overlapped (even partially) by the circle are painted
eliminate the circle
else
paint the squares totally overlapped by the circle
Вы также можете "обманывать", рисуя квадраты сетки, не полностью покрытые заданным кругом (или устраняя круги, которые слегка перетекают с уже окрашенной поверхности), увеличивая количество исключенных кругов за счет некоторых ложных срабатываний.
Затем вы можете отобразить оставшиеся круги с помощью алгоритма Z-буфера (т.е. позволить графическому процессору выполнять остальную часть работы).
Основанный на ЦП подход
Это предполагает, что вы реализуете сетку в виде растрового изображения памяти без помощи GPU.
Чтобы определить квадраты, которые нужно нарисовать, вы можете использовать предварительно вычисленные шаблоны, основанные на расстоянии центра окружности относительно сетки (красные крестики в примерах) и фактический радиус круга.
Если относительные изменения диаметра достаточно малы, вы можете определить двумерную таблицу рисунков, индексированных радиусом окружности и расстоянием центра от ближайшей точки сетки.
После того как вы получили правильный шаблон, вы можете применить его в соответствующем месте, используя простые симметрии.
Тот же принцип может использоваться для проверки того, подходит ли круг к уже окрашенной поверхности.
подход на основе GPU
Прошло много времени с тех пор, как я работал с компьютерной графикой, но если это позволяет современное состояние, вы можете позволить графическому процессору сделать чертеж для вас.
Покраска сетки будет достигнута путем рендеринга каждого круга, масштабированного, чтобы соответствовать сетке
Для проверки исключения потребуется прочитать значение всех пикселей, содержащих круг (масштабируется до размеров сетки).
Эффективность
Должно быть какое-то сладкое пятно для измерения сетки. Более плотная сетка будет покрывать более высокий процент поверхности кругов и, таким образом, исключить больше кругов (меньше ложных негативов), но стоимость вычислений будет возрастать в o (1/grid_step²).
Конечно, если обработанные круги могут сокращаться до диаметра в 1 пиксель, вы также можете сбросить весь алгоритм и позволить графическому процессору выполнять работу. Но эффективность по сравнению с графическим подходом GPU растет как квадрат шага сетки.
Используя сетку в моем примере, вы, вероятно, можете ожидать около 1/3 ложных негативов для абсолютно случайного набора кругов.
Для вашей фотографии, которая, как представляется, определяет тома, следует исключить 2/3 кругов переднего плана и (почти) всех обратных. Отбирать более 80% кругов может стоить усилий.
Все это говорит о том, что нелегко обыграть GPU в соревновании по вычислению грубой силы, так что у меня есть только смутное представление о фактическом выигрыше в производительности, которого вы могли бы ожидать. Однако может быть интересно попробовать.
Ответ 2
Этот вид отбраковки - это, по сути, то, что скрывают скрытые алгоритмы поверхности - особенно разнообразие, называемое "пространство объектов". В вашем случае отсортированный порядок кругов дает им эффективную постоянную координату глубины. Тот факт, что он постоянно упрощает задачу.
Классическая ссылка на HSA здесь. Я бы дал ему прочитать идеи.
Идея, вдохновленная этим мышлением, состоит в том, чтобы рассматривать каждый круг с помощью алгоритма "развертки", например горизонтальную линию, движущуюся сверху вниз. Строка развертки содержит набор кругов, которые он касается. Инициализируйте, отсортировав входной список окружностей по верхней координате.
Сдвиг продвигается в "событиях", которые являются верхней и нижней координатами каждого круга. Когда вершина будет достигнута, добавьте круг в развертку. Когда его дно произойдет, удалите его (если он уже не прошел, как описано ниже). Когда новый круг войдет в развертку, рассмотрите его против уже существовавших кругов. Вы можете сохранять события в куче макс (y-координаты), добавляя их лениво по мере необходимости: следующую верхнюю координату входного круга плюс все нижние координаты кругов линии сканирования.
Новый круг, входящий в развертку, может делать все или все из трех вещей.
-
Неясные круги в развертке с большей глубиной. (Поскольку мы отождествляем круги, чтобы не рисовать, консервативная сторона этого решения заключается в том, чтобы использовать самый большой включенный по оси прямоугольник (BIALB) нового круга для записи скрытой области для каждого существующего более глубокого круга.)
-
Затушевать другие круги с меньшей глубиной. (Здесь консервативным способом является использование BIALB каждого другого соответствующего круга для записи скрытой области нового круга.)
-
Есть области, которые не затенены.
Затененная область каждого круга должна поддерживаться (обычно она будет расти по мере обработки большего количества кругов), пока линия сканирования не достигнет своего дна. Если в любой момент скрытая область покрывает весь круг, ее можно удалить и никогда не рисовать.
Чем детальнее будет записываться скрытая область, тем лучше будет работать алгоритм. Объединение прямоугольных областей - одна из возможностей (например, код региона региона Android). Один прямоугольник - другой, хотя это может вызвать много ложных срабатываний.
Аналогичным образом необходима быстрая структура данных для поиска, возможно, скрывающих и затененных кругов в линии сканирования. Дерево интервалов, содержащее BIALB, вероятно, будет хорошим.
Обратите внимание, что на практике алгоритмы, подобные этому, производят только выигрыш от числа примитивов, потому что быстрое графическое оборудование так... быстро.
Ответ 3
Вот простой алгоритм с головы:
- Вставьте круги
N
в квадрат (сначала нижний круг)
- Для каждого пикселя используйте квадрант, чтобы определить самый верхний круг (если он существует)
- Заполните пиксель цветом круга
Добавив круг, я имею в виду добавить центр круга в квадрант. Это создает 4 детей к листу node. Храните круг в этом листе node (который теперь уже не является листом). Таким образом, каждый не-лист node соответствует кругу.
Чтобы определить самый верхний круг, пройдите по квадранту, протестировав каждый node по пути, если пиксель пересекает окружность на node. Самый верхний кружок - самый глубокий по дереву, который пересекает пиксель.
Это должно занимать время O(M log N)
(если круги распределены красиво), где M
- количество пикселей, а N
- количество кругов. Хуже того, сценарий по-прежнему O(MN)
, если дерево вырождено.
псевдокод:
quadtree T
for each circle c
add(T,c)
for each pixel p
draw color of top_circle(T,p)
def add(quadtree T, circle c)
if leaf(T)
append four children to T, split along center(c)
T.circle = c
else
quadtree U = child of T containing center(c)
add(U,c)
def top_circle(quadtree T, pixel p)
if not leaf(T)
if intersects(T.circle, p)
c = T.circle
quadtree U = child of T containing p
c = top_circle(U,p) if not null
return c
Ответ 4
Если круг полностью находится внутри другого круга, то он должен следить за тем, чтобы расстояние между их центрами плюс радиус меньшего круга не превышало радиуса большего круга (сделайте это для себя, чтобы увидеть!). Поэтому вы можете проверить:
float distanceBetweenCentres = sqrt((topCircle.centre.x - bottomCircle.centre.x) * (topCircle.centre.x - bottomCircle.centre.x) + (topCircle.centre.y - bottomCircle.centre.y) * (topCircle.centre.y - bottomCircle.centre.y));
if((bottomCircle.radius + distanceBetweenCentres) <= topCircle.radius){
// The bottom circle is covered by the top circle.
}
Чтобы повысить скорость вычислений, вы можете сначала проверить, имеет ли верхний круг больший радиус нижний круг, как будто он этого не делает, он не может закрывать нижний круг. Надеюсь, это поможет!
Ответ 5
Вы не упоминаете компонент Z, поэтому я предполагаю, что они находятся в порядке Z в вашем списке и обращены назад (например, алгоритм живописца).
Как отмечалось в предыдущих плакатах, это упражнение для отвлечения окклюзии.
В дополнение к упомянутым алгоритмам пространственного пространства я также изучу алгоритмы экранного пространства, такие как Иерархический Z-буфер. Вам даже не нужны значения z, только битфлаги, указывающие, есть ли что-то или нет.
Смотрите: http://www.gamasutra.com/view/feature/131801/occlusion_culling_algorithms.php?print=1