【问题标题】:Draw a curved line from an arc edge从圆弧边缘绘制曲线
【发布时间】:2013-12-11 00:30:06
【问题描述】:

这是我正在执行的操作的屏幕截图。目前,我无法在这个矩形中绘制曲线边框。

我的第一个解决方案是:在矩形后面画一个四等分圆,但如果我调整形状的不透明度,如您所见,四等分圆会显示出来。

我知道这对你们来说很基础,但我不是很擅长数学。

我确实尝试重用弧的计算边缘并添加边框的大小,但结果是这样。

我也认为 bezier 曲线 可以替代,但我认为重用计算的顶点并添加所有缺失的顶点会更有效。另外,我不知道如何计算 bezier 曲线 的曲线点,并且找到正确数量的 t 计算量非常大,所以我没有实现它。

这是我如何绘制四等分圆的代码,我想我可以重复使用它。

void drawArc(int x, int y,
             int startAngle, int endAngle,
             uint32_t radiusX, uint32_t radiusY,
             int border_x, int border_y,
             const rgb color,
             const rgb bcX, const rgb bcY,
             uint8_t opacity)
{
    if (radiusX <= 0 || radiusY <= 0) return;

    static constexpr float DTR = 3.14159 / 180;

    float cx, cy;
    int step;

    static std::vector<float> verts;
    static std::vector<uint8_t> colors;

    if (startAngle < endAngle)
    {
        step = +1;
        ++ endAngle;
    } else
    {
        step = -1;
        -- endAngle;
    }

    verts.clear();
    colors.clear();

    verts.push_back(x);
    verts.push_back(y);

    colors.push_back(color[R]);
    colors.push_back(color[G]);
    colors.push_back(color[B]);
    colors.push_back(opacity);

    while (startAngle != endAngle)
    {
        cx = cos(DTR * startAngle) * radiusX;
        cy = sin(DTR * startAngle) * radiusY;

        verts.push_back(x + cx);
        verts.push_back(y - cy);

        colors.push_back(color[R]);
        colors.push_back(color[G]);
        colors.push_back(color[B]);
        colors.push_back(opacity);

        startAngle += step;
    }

    drawElements(GL_POLYGON, sizeof(arcIndices) / sizeof(arcIndices[0]), GL_FLOAT,
                 &verts[0], &colors[0], &arcIndices[0]);

    if (border_x != 0 || border_y != 0)
    {
        //remove (x, y)
        verts.erase(verts.begin(), verts.begin() + 2);

//        float px, py;
//
//        px = *(verts.begin() + 0);
//        py = *(verts.begin() + 1);
//
//        glPointSize(5);
//
//        glBegin(GL_POINTS);
//
//        glColor3ub(0,0,255);
//        glVertex2i(px, py);
//
//        px = *(verts.end() - 2);
//        py = *(verts.end() - 1);
//
//        glColor3ub(255,0,0);
//        glVertex2i(px , py);
//        glEnd();

        //attempting to reuse the edges
        //I think the last vertices are opposed
        //that's why I got a crossed out lines??
        for (int i = 0;i <= 90; ++i)
        {
            verts.push_back(verts[i + 0] + border_x);
            verts.push_back(verts[i + 1] + border_y);

            colors.push_back(bcX[R]);
            colors.push_back(bcX[G]);
            colors.push_back(bcX[B]);
            colors.push_back(opacity);
        }

        //91 = steps from 0-90 degree revolution
        //182 = 91 * 2
        unsigned int index[182 + 91 * 2];
        for (int i = 0;i < 182 + 91 * 2; ++i)
            index[i] = i;

        drawElements(GL_LINE_LOOP, verts.size() / 2, GL_FLOAT,
                    &verts[0], &colors[0], &index[0]);
    }

}

编辑:

我不能重复使用之前预先计算的 (x,y) 吗?

抱歉用了太多图片

红点是我所指的预先计算好的 (x, y),并在此基础上附加下一个弧。

我要渲染很多这样的东西,所以我需要尽可能高效(不要过多地使用三角函数)。

更新:

这是我使用stencil buffer 得到的结果,正如Andon M. Coleman 所建议的那样:

顺便说一句,如您所见,我正在尝试使用 OpenGL 模拟我自己的 UI:D

【问题讨论】:

  • 您是否考虑过使用模板缓冲区从字面上切出灰色形状覆盖的区域?您将不得不稍微改变绘制的顺序,但您不必使用讨厌的线图元。
  • @AndonM.Coleman 我可能会在应用该矩形的纹理时使用它,但这样做不是很贵吗?
  • @AndonM.Coleman 你能给个链接吗?我该怎么做?我认为这很有趣,并且当我开始在其上应用纹理时会满足我未来的需求。
  • 实际上并不比深度测试贵多少。这两件事非常相关。到目前为止,这还不是一个完美的解决方案,但您不必担心圆角正方形外部的顶点与轮廓形状的内部顶点是否匹配。我不知道任何可以描述如何做到这一点的链接,我必须编写一些伪代码来演示......如果你有兴趣,我明天可以做到。
  • @AndonM.Coleman 出于某种原因,我想像 html 的矩形一样绘制它,例如带有锐边和圆边的边框,而内部矩形与外部矩形不同(边框)

标签: c++ opengl curves


【解决方案1】:

您昨天表示有兴趣了解如何使用模板缓冲区解决此问题,因此我正在跟进一些基本的伪代码。

glClearStencil (0x0);
glClear        (GL_STENCIL_BUFFER_BIT);

glEnable       (GL_STENCIL_TEST);
glStencilFunc  (GL_ALWAYS, 0x0, 0x0);

// Add 1 to stencil buffer at every location the object to be bordered is visible
glStencilOp    (GL_KEEP, GL_KEEP, GL_INCR);

// Draw your grey object

// Only draw the red border where the grey object was never drawn (stencil = 0x0)
glStencilFunc  (GL_EQUAL, 0x0, 0xff);

// Draw your red quarter circles

glDisable     (GL_STENCIL_TEST);

每次绘制轮廓对象时都清除模板缓冲区可能是矫枉过正。如果您选择每帧清除一次模板缓冲区,您可以做一些非常有趣的事情。例如,如果您在绘制所有非轮廓形状后将轮廓作为单独的通道绘制,您可以使用此模板缓冲区设置来勾勒 union(而不是包括任何重叠对象的交集作为绘制轮廓的一部分)。这将允许您从简单的圆角矩形构造更复杂的形状。

当然,要实现这一点,您的像素格式必须有模板缓冲区。我将不得不把这部分留给你,因为设置的过程是特定于实现的。

【讨论】:

  • 如果我移到顶部 glEnable(GL_STENCIL_TEST) 它可以工作,但如果我按照您发布的内容进行操作,则不会。无论如何,它现在真的很棒!但是如果我尝试在上面添加纹理,它就不会出现。也许我只需要预先计算我的可绘制对象的顺序。也许我会暂时离开您所说的setup for pixel format,只要我看不到有什么可担心的。非常感谢你:D stencil buffer 是我在 OpenGL 中发现的最好的东西之一
  • @mr5:啊,好点子。这完全让我忘记了,我做了一些应该更好的改变。
  • 即使没有glStencilFunc (GL_ALWAYS, 0x0, 0x0);,它也可以工作,为什么?
  • 因为你清除了模板缓冲区。它重新使用了最后一个模板函数集,它是 EQUAL 0x0 ......这只有在模板缓冲区已经被清除时才会通过。当您想绘制无条件写入模板缓冲区的内容时,通常使用GL_ALWAYS 是个好主意,这样即使在绘制之前没有立即清除模板缓冲区,它也可以工作。
【解决方案2】:

GL_POLYGON 仅适用于 多边形。

将内半径和外半径上的顶点连接在一起形成四边形/三角形:


#include <GL/glut.h>
#include <cmath>

void Torus2d
    ( 
    float angle,            // starting angle in radians
    float length,           // length of arc in radians, >0
    float radius,           // inner radius, >0
    float width,            // width of torus, >0
    unsigned int samples    // number of circle samples, >=3
    )
{
    if( samples < 3 ) samples = 3;
    const float outer = radius + width;
    glBegin( GL_QUAD_STRIP );
    for( unsigned int i = 0; i <= samples; ++i )
    {
        float a = angle + ( i / (float)samples ) * length;
        glVertex2f( radius * cos( a ), radius * sin( a ) );
        glVertex2f( outer * cos( a ), outer * sin( a ) );
    }
    glEnd();
}

void display()
{
    glClear( GL_COLOR_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    double w = glutGet( GLUT_WINDOW_WIDTH );
    double h = glutGet( GLUT_WINDOW_HEIGHT );
    double ar = w / h;
    glOrtho( -4 * ar, 4 * ar, -4, 4, -1, 1);

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    glColor3ub( 255, 0, 0 );
    Torus2d( 0, 1.57079633, 2, 1, 20 );

    glutSwapBuffers();
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
    glutInitWindowSize( 640, 480 );
    glutCreateWindow( "GLUT" );
    glutDisplayFunc( display );
    glutMainLoop();
    return 0;
}

【讨论】:

  • 我其实对Torus2D中将传递的参数感到困惑。能稍微解释一下吗?
  • @mr5:已编辑。 angle 是逆时针方向启动环面的距离。 length 是圆环段应该有多长。 radius 是内环面半径。 width 是环面的宽度。 samples 越大,圆环看起来就越好,但会牺牲更多的几何形状。
  • 我对那些三角函数有点困惑。在我的例子中,圆弧半径是分开的,即 rX、rY。我怎样才能修改它来实现这一点?我不能再摆脱那些三角吗?我不能重复使用之前预先计算的 x,y 吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多