Как оптимизировать рисование круга? - PullRequest
0 голосов
/ 24 июня 2018

Мне интересно, как я могу оптимизировать мой метод рисования кругов

void DrawCircle(float x, float y, float radius, Color color, float thickness)
{
    int numOfPoints = 100;
    for (float i = 0; i < numOfPoints; ++i) {
        float pi2 = 6.28318530718;
        float angle = i / numOfPoints * pi2;

        FillRect(
        cos(angle) * radius + x,
        sin(angle) * radius + y, 
        thickness, 
        thickness, 
        color);
    }
}

Функция FillRect просто рисует квад, поэтому функция DrawCircle рисует 100 квадов, которые перемещаются по cos, sin и радиусу.

FillRect(float x, float y, float w, float h, Color color)

Как я могу нарисовать круг по-другому?

Ответы [ 2 ]

0 голосов
/ 25 июня 2018

В ваших комментариях вы упоминаете, что вы используете OpenGL для рендеринга (при условии старого API), поэтому использование отдельных GL_QUADS является вашей проблемой. Как вы делаете OpenGL вызовов для каждого отдельного "пикселя" вашего круга. Это обычно намного медленнее, чем сам рендеринг. Какие варианты есть, чтобы решить эту проблему?

  1. ВБО

    это ваша лучшая ставка, просто создайте VBO , удерживая cos(a),sin(a) единичные точки окружности, и визуализируйте как одиночный вызов glDrawArray (применяя матрицы преобразования для преобразования единичного круга в желаемое положение и радиус). Это должно быть почти так же быстро, как один вызов GL_QUADS ... (если вы не набрали слишком много очков за круг), но вы потеряете толщину (если не объедините 2 круга и трафарет ...). Здесь:

    вы можете найти, как VAO / VBO используются в OpenGL . Также посмотрите, как сделать отверстия (для толщины):

  2. GL_LINE_LOOP

    Вы можете использовать толстые линии вместо прямоугольников, чтобы уменьшить glVertex вызовов с 4 до 1 на «пиксель». Это будет выглядеть примерно так:

    void DrawCircle(float x, float y, float radius, Color color, float thickness)
        {
        const int numOfPoints = 100;
        const float pi2=6.28318530718; // = 2.0*M_PI
        const float da=pi2/numOfPoints;
        float a;
        glColor3f(color.r,color.g,color.b);
        glLineWidth(thickness/2.0);
        glBegin(GL_LINE_LOOP);
        for (a=0;a<pi2;a+=da) glVertex2f(cos(a)*radius + x, sin(a) * radius + y); 
        glEnd();
        glLineWidth(1.0);
        }
    

    грубо, поскольку я не знаю, как устроен color, настройки цвета могут измениться. Также glLineWidth не гарантируется работа при произвольной толщине ...

    Если вы все еще хотите использовать GL_QUADS, по крайней мере, установите его на GL_QUAD_STRIP, что потребует половину вызовов glVertex ...

    void DrawCircle(float x, float y, float radius, Color color, float thickness)
        {
        const int numOfPoints = 100;
        const float pi2=6.28318530718; // = 2.0*M_PI
        const float da=pi2/numOfPoints;
        float a,r0=radius-0.5*thickness,r1=radius+0.5*thickness,c,s;
        int e;
        glColor3f(color.r,color.g,color.b);
        glBegin(GL_QUAD_STRIP);
        for (e=1,a=0.0;e;a+=da) 
          {
          if (a>=pi2) { e=0; a=pi2; }
          c=cos(a); s=sin(a);
          glVertex2f(c*r0 + x, s * r0 + y); 
          glVertex2f(c*r1 + x, s * r1 + y); 
          }
        glEnd();
        }
    
  3. шейдеры

    Вы даже можете создать шейдер, который будет принимать в качестве входного центра, толщину и радиус вашего круга (в форме) и использовать его, визуализируя один квадратный квадрат QUAD вокруг круга. затем внутри фрагмента шейдера отбросьте все фрагменты за пределами окружности вашего круга. Примерно так:

Реализация # 2 является самой простой. с # 1 требуется некоторая работа, но не слишком много, если вы знаете, как использовать VBO . # 3 тоже не так уж сложно, но если у вас нет опыта работы с шейдерами, которые могут создать проблему ...

0 голосов
/ 24 июня 2018

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

Вы можете вычислить вершины на окружности с центром в (0,0) рекурсивно, используя матрицы вращения :

// Init
X(0) = radius;
Y(0) = 0;
// Loop body
X(n+1) = cos(a) * X(n) - sin(a) * Y(n);
Y(n+1) = sin(a) * X(n) + cos(a) * Y(n);

Это заменяет вычисления cos и sin внутри цикла только несколькими умножениями, сложениями и вычитаниями с плавающей запятой, которые обычно бывают быстрее.

void DrawCircle(float x, float y, float radius, Color color, float thickness) {
    int numOfPoints = 100;
    float pi2 = 6.28318530718;
    float fSize = numOfPoints;
    float alpha = 1 / fSize * pi2;
    // matrix coefficients
    const float cosA = cos(alpha);
    const float sinA = sin(alpha);
    // initial values
    float curX = radius;
    float curY = 0;
    for (size_t i = 0; i < numOfPoints; ++i) {
        FillRect(curX + x, curY + y, thickness, thickness, color);
        // recurrence formula
        float ncurX = cosA * curX - sinA * curY;
        curY =        sinA * curX + cosA * curY;
        curX = ncurX;
    }
}

Демоверсия и упрощенный сравнительный тест

Недостатком использования исключительно рекурсии является то, что вы накапливаете крошечные ошибки вычислений за каждую итерацию. Как показывает демонстрация, ошибка незначительна для 100 итераций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...