如何优化圆画?

时间:2018-06-24 14:15:38

标签: c++ geometry

我想知道如何优化我的圆形绘制方法

it('can change email', () => {
    const user = {
        name: 'jane',
        email: 'jane@jane.com',
        password: '12345'
    }

    // register and login w/o UI
    cy.register(user)
    cy.login(user)

    cy.visit('/settings')
    cy.get('input[name="email"]').type('UpdatedEmail@jane.com')
    cy.get('input[type="submit"]').click()

    cy.getUser(user.name)
    .then((dbUser) => expect(dbUser.email).to.eql('UpdatedEmail@jane.com'))
})

FillRect函数仅绘制一个四边形,因此DrawCircle函数绘制100个由cos,sin和半径移动的四边形。

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);
    }
}

如何绘制不同的圆圈?

2 个答案:

答案 0 :(得分:2)

由于您明确要求优化生成顶点坐标的方法的方法,因此我将为此提供解决方案。但是,看看一些基准测试(请参阅下面的演示链接),我不认为这确实是导致任何性能问题的原因...

您可以使用rotation matrices递归计算以(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);

这仅用一些浮点数乘法,加法和减法来代替循环内的cossin计算,这些运算通常更快。

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;
    }
}

Live demo & simplistic comparison benchmark

仅使用递归的缺点是您在每次迭代中都会累积微小的计算错误。如该演示所示,该错误对于100次迭代而言微不足道。

答案 1 :(得分:1)

您在评论中提到您正在使用 OpenGL 进行渲染(假设使用旧的API),因此使用单个GL_QUADS是您的问题。在执行 OpenGL 时,需要调用圆的每个单独的“像素”。这通常比渲染本身要慢得多。有什么解决方案?

  1. VBO

    这是您最好的选择,只需创建{strong> VBO 并按住cos(a),sin(a)单位圆点并呈现为单个glDrawArray调用(应用变换矩阵将单位圆变换到所需位置并半径)。这应该几乎与单个GL_QUADS调用一样快...(除非您每个圆有太多的点),但是您将失去厚度(除非将2个圆和模板组合在一起...)。在这里:

    您可以找到 OpenGL 中如何使用 VAO / VBO 。另请参阅有关如何打孔(针对厚度)的信息:

  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 bbox来使用它。然后在片段着色器内部丢弃圆圆周以外的所有片段。像这样:

实施#2 是最容易的。使用#1 进行一些工作,但如果您知道如何使用 VBO ,则不需要太多工作。 #3 也不是太复杂,但是如果您没有任何可能会带来问题的着色器经验……