Наложение контура пересекающихся кругов

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

Они будут перекрываться, но я хочу нарисовать внешнюю часть сформированной формы. Я знаю, что это, вероятно, потребует от меня разработки там, где они пересекаются, и каким-то образом формирует общую форму для рисования. Проблема в том, что некоторые моменты могут даже не касаться других! Поэтому мне тоже нужно иметь возможность работать.

Я попытался проиллюстрировать, что я имею в виду просто:

enter image description here

Обратите внимание, что я хочу просто нарисовать черную линию, без заполнения. Это потому, что я хочу, чтобы фоновые изображения и другая геометрия отображались.

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

Если у кого-нибудь есть какие-либо советы или могут указать мне, как я могу это сделать, будем очень благодарны!

Это может быть скорее вопрос математики, я не ищу фрагменты кода, а на самом деле, как это сделать. Я просто не могу думать о том, как это сделать!

***** Edit: с решением, которое я придумал, надеюсь, может помочь кому-то еще!

Поэтому я использовал предложенные идеи и в основном решил, что лучший способ - рисовать с использованием буфера трафарета. Это теперь означает, что я прокручиваю свои очки 3 раза, но мне нужно сделать некоторую тщательную сортировку, чтобы найти только видимые.

Итак, код, который у меня есть, теперь имеет следующее:

private void stencilCircleAroundStars()
{
    //Lets try and draw something here using stencil
    glColorMask(false, false, false, false); //Disable colour mask
    glEnable(GL_STENCIL_TEST); // Enable Stencil Buffer For "marking" the outer circle
    glDisable(GL_DEPTH_TEST);// Disable Depth Testing

    for (Object value : stars.values())
    {
        Star star = (Star)value;

        glStencilFunc(GL_ALWAYS, 1, 1); // Always Passes, 1 Bit Plane, 1 As Mask
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // We Set The Stencil Buffer To 1 Where We Draw Any Polygon

        //Draw the large circle
        starOb.location.copy(star.location);
        starOb.setScale(2000);
        starOb.draw();
    }

    for (Object value : stars.values())
    {
        Star star = (Star)value;
        //Now we change the functions and remove a slightly smaller circle from buffer.
        glStencilFunc(GL_ALWAYS, 0, 0); // Always passes, 0 bit plane, 0 as mask;
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // We Set The Stencil Buffer To 0 Where We Draw Any Polygon
        starOb.location.copy(star.location);
        starOb.setScale(1900);
        starOb.draw();
    }

    //Now we enable the colour
    glColorMask(true, true, true, true);
    glStencilFunc(GL_EQUAL, 1, 1); // We Draw Only Where The Stencil Is 1
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Don't Change The Stencil Buffer

    glColor4f(0.5f, 1.0f, 0.5f, 0.5f);
    for (Object value : stars.values())
    {
        Star star = (Star)value;
        starOb.location.copy(star.location);
        starOb.setScale(2000);
        starOb.draw();
    }

    //Now we are done .. disable
    glDisable(GL_STENCIL_TEST);  
}

Мои точки суть сущности, называемые "звёзды" для моей программы, а StarOb - это набор квадов, загруженных из файла, который образует приятный гладкий круг.

Я отключаю цветную маску, и я прохожу один раз, рисуя самый большой круг, который я могу в буфере трафарета, и устанавливаю значение 1. Затем я снова петлю, рисуя меньший масштабированный круг в буфер трафарета, но на этот раз устанавливая значение из 0. Это должно оставить границу вокруг любой звезды, которая не касается других звезд и эффективно удалит, где они накладываются друг на друга.

Наконец, я снова включу цветную маску и нарисую цветные круги. буфер трафарета останавливает внутренности от визуализации, и я получаю то, что я хотел! Затем я отключу буфер трафарета.

Если вы действительно хотели его увидеть, вот видео сгенерировало несколько увеличивающихся количеств очков: Видео его запуска

Вот низкокачественная версия того, как это получилось (фон не был нарисован во время тестирования):

Overlapping circles with centre not drawn due to stencil

Ответ 1

Во-первых, представьте, что фона там не было. Я уверен, что вы знаете, как это сделать, нарисуйте каждый круг, затем нарисуйте их внутренности (как в заполненном круге), чтобы удалить дуги, которые находятся внутри.

Теперь, чтобы сделать то же самое над изображением, вы могли бы сделать одно из этих действий. Одна вещь, которую вы можете сделать, - отключить запись в цветовом буфере, выполнить эту процедуру и изменить буфер трафарета. Затем включите запись в цветовом буфере и нарисуйте весь прямоугольник экрана, который, следовательно, заполнит пиксели, которые вы отметили в буфере трафарета.

Буфер трафарета может не использоваться для вас по нескольким причинам, например, вы используете его для чего-то другого. В этом случае альтернативой будет делать то же самое, но вместо рендеринга в буфере трафарета вы визуализируете в текстуре. Затем привяжите эту текстуру и нарисуйте прямоугольник на экране.

Я вполне уверен, что вы тоже можете добиться этого с помощью накопительного буфера, но я никогда не использовал его, поэтому я не могу сказать (если кто-нибудь знает об этом, отредактируйте мой ответ и расскажите, как)

Ответ 2

Два прохода:

  • Нарисуйте все контуры круга с помощью GL_LINES или GL_LINE_LOOP. Убедитесь, что вы установили glLineWidth() в 3 или больше.
  • Нарисовать заполненные круги (GL_TRIANGLE_STRIP может быть полезно) с вашим цветом фона и тем же радиусом, что и круги с шага 1.

Заполненные круги на шаге 2 будут перезаписывать все пиксели контура с шага 1, но только там, где они перекрываются. Это оставляет вас неперекрывающимися контурами.

Если вам нужен более широкий диапазон ширины контура (т.е. больше, чем ~ 10, что большинство реализаций OpenGL позволяют glLineWidth()), вы должны повторно использовать рендеринг с заполненным кругом с шага 2 на шаге 1, за исключением большего радиуса.