OpenGL—ES 1.0 2d 圆角矩形

发布于 2024-10-23 23:45:10 字数 35 浏览 2 评论 0原文

如何在OpenGL中制作圆角矩形,或任何带圆角的多边形?

How to make rounded rectangle in OpenGL, or any polygon with rounded corners?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

梦屿孤独相伴 2024-10-30 23:45:10

使用多边形

如果绝对需要使用多边形,例如,如果需要大量缩放或缩放具有舍入的对象,或者需要控制舍入量,则可以将矩形分解为多个子对象。

圆角

至少有三个矩形部分和四个角。计算角坐标很容易。只需从圆中找到一点并构建三角形,如上图所示。

 float anglerad = PI * angle / 180.0f;
 float x = sinf(anglerad) * radius; 
 float y = cosf(anglerad) * radius;

它仍然具有锋利的边缘,但更多的点使角更圆。小物体比大物体需要更少的点。

简单的路线是使用 GL_TRIANGLE_FAN 进行拐角。然而,对于 OpenGL ES,明智的做法是最大程度地减少 OpenGL 调用量并仅使用更多顶点,因为可以将整个对象构建为 GL_TRIANGLE_STRIP。

这种方法可以用于任何形状。对于矩形,角的角度始终为 90 度,但对于其他形状,需要从边缘计算角度。

使用纹理

另一种方法称为9 切片缩放。矩形和纹理被分成 9 个切片。实际的圆角位于纹理的角落。想法是角不缩放,但保持其原始大小。这种方法是 UI 设计中广泛使用的模式,允许可变大小的 UI 元素(如按钮)。它的优点是一个矩形只需要这9个四边形即可渲染。但如果角落也需要缩放,尤其是纹理分辨率较低时,看起来会很糟糕。

Using polygons

If using polygons is absolutely required, for example if objects with rounding need to be scaled or zoomed a lot or if amount of rounding needs to be controlled, it is possible to break rectangle into several sub-objects.

Rounded corners

There are at least three rectangular parts and four corners. Calculating corner coordinates is easy. Just find a point from circle and build triangles like in picture above.

 float anglerad = PI * angle / 180.0f;
 float x = sinf(anglerad) * radius; 
 float y = cosf(anglerad) * radius;

It will still have sharp edges, but more points make corners more round. Small objects need less points than big objects.

Easy route is to use GL_TRIANGLE_FAN for corners. However with OpenGL ES it might be wise to minimize amount of OpenGL calls and just use more vertices as it is possible to build whole object as GL_TRIANGLE_STRIP.

This approached can be used with any shape. With a rectangle, angle of corner is always 90 degrees, but with other shapes angle needs to be calculated from the edges.

Using textures

Another approach is called 9-slice scaling. Rectangle and texture are broken into 9 slices. Actual rounding is in corner of a texture. Idea is that corners are not scaled, but maintain their original size. This approach is widely used pattern in UI-design allowing variable size UI-elements like buttons. Its advantage is that one rectangle needs only these 9 quads to render. But it will look bad if also corners need to be scaled and especially if texture is low resolution.

御弟哥哥 2024-10-30 23:45:10

有点颠簸,但今天我遇到了同样的问题,这就是我输出的内容,它是用 Desktop GL 创建的,但应该很容易转换为 GLES,一切都是条带。它可能没有达到应有的优化程度,但如果有人想尝试一下,请成为我的客人;)

typedef struct
{
    float x;
    float y;

} Vector2f;


void RoundRect( int x,
            int y,
            int width,
            int height,
            int radius,
            int resolution )
{
float step = ( 2.0f * M_PI ) / resolution,
      angle = 0.0f,
      x_offset,
      y_offset;

int i = 0;

unsigned int index = 0,
             segment_count = ( int )( resolution / 4 );

Vector2f *top_left             = ( Vector2f * ) malloc( segment_count * sizeof( Vector2f ) ), 
         *bottom_left         = ( Vector2f * ) malloc( segment_count * sizeof( Vector2f ) ),
         *top_right             = ( Vector2f * ) malloc( segment_count * sizeof( Vector2f ) ),
         *bottom_right         = ( Vector2f * ) malloc( segment_count * sizeof( Vector2f ) ),
          bottom_left_corner = { x + radius,
                                 y - height + radius }; 

while( i != segment_count )
{
    x_offset = cosf( angle );
    y_offset = sinf( angle );


    top_left[ index ].x = bottom_left_corner.x - 
                          ( x_offset * radius );
    top_left[ index ].y = ( height - ( radius * 2.0f ) ) + 
                            bottom_left_corner.y - 
                          ( y_offset * radius );


    top_right[ index ].x = ( width - ( radius * 2.0f ) ) + 
                             bottom_left_corner.x + 
                           ( x_offset * radius );
    top_right[ index ].y = ( height - ( radius * 2.0f ) ) + 
                             bottom_left_corner.y -
                           ( y_offset * radius );


    bottom_right[ index ].x = ( width - ( radius * 2.0f ) ) +
                                bottom_left_corner.x + 
                              ( x_offset * radius );
    bottom_right[ index ].y = bottom_left_corner.y + 
                              ( y_offset * radius );


    bottom_left[ index ].x = bottom_left_corner.x - 
                             ( x_offset * radius );
    bottom_left[ index ].y = bottom_left_corner.y +
                             ( y_offset * radius );


    top_left[ index ].x = roundf( top_left[ index ].x );
    top_left[ index ].y = roundf( top_left[ index ].y );


    top_right[ index ].x = roundf( top_right[ index ].x );
    top_right[ index ].y = roundf( top_right[ index ].y );


    bottom_right[ index ].x = roundf( bottom_right[ index ].x );
    bottom_right[ index ].y = roundf( bottom_right[ index ].y );


    bottom_left[ index ].x = roundf( bottom_left[ index ].x );
    bottom_left[ index ].y = roundf( bottom_left[ index ].y );

    angle -= step;

    ++index;

    ++i;
}


glBegin( GL_TRIANGLE_STRIP );
{
    // Top
    {
        i = 0;
        while( i != segment_count )
        {
            //glColor4f( 1.0f, 0.0f, 0.0f, 1.0f );
            glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
            glVertex2i( top_left[ i ].x,
                        top_left[ i ].y );

            //glColor4f( 0.0f, 1.0f, 0.0f, 1.0f );
            glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
            glVertex2i( top_right[ i ].x,
                        top_right[ i ].y );

            ++i;
        }
    }


    // In order to stop and restart the strip.
    glColor4f( 0.0f, 1.0f, 0.0f,  1.5f );
    glVertex2i( top_right[ 0 ].x,
                top_right[ 0 ].y );

    glColor4f( 0.0f, 1.0f, 0.0f,  1.5f );
    glVertex2i( top_right[ 0 ].x,
                top_right[ 0 ].y );


    // Center
    {
        //glColor4f( 0.0f, 1.0f, 0.0f,  1.0f );
        glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
        glVertex2i( top_right[ 0 ].x,
                    top_right[ 0 ].y );


        //glColor4f( 1.0f, 0.0f, 0.0f,  1.0f );
        glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
        glVertex2i( top_left[ 0 ].x,
                    top_left[ 0 ].y );


        //glColor4f( 0.0f, 0.0f, 1.0f,  1.0f );
        glColor4f( 0.5f, 0.5f, 0.5f,  1.0f );
        glVertex2i( bottom_right[ 0 ].x,
                    bottom_right[ 0 ].y );


        //glColor4f( 1.0f, 1.0f, 0.0f,  1.0f );
        glColor4f( 0.5f, 0.5f, 0.5f,  1.0f );
        glVertex2i( bottom_left[ 0 ].x,
                    bottom_left[ 0 ].y );
    }


    // Bottom
    i = 0;
    while( i != segment_count )
    {
        //glColor4f( 0.0f, 0.0f, 1.0f,  1.0f );
        glColor4f( 0.5f, 0.5f, 0.5f,  1.0f );
        glVertex2i( bottom_right[ i ].x,
                    bottom_right[ i ].y );    

        //glColor4f( 1.0f, 1.0f, 0.0f,  1.0f );
        glColor4f( 0.5f, 0.5f, 0.5f,  1.0f );
        glVertex2i( bottom_left[ i ].x,
                    bottom_left[ i ].y );                                    

        ++i;
    }    
}
glEnd();



glBegin( GL_LINE_STRIP );

//glColor4f( 0.0f, 1.0f, 1.0f, 1.0f );
glColor4f( 1.0f, 0.5f, 0.0f, 1.0f );

// Border
{
    i = ( segment_count - 1 );
    while( i > -1 )
    {    
        glVertex2i( top_left[ i ].x,
                    top_left[ i ].y );

        --i;
    }


    i = 0;
    while( i != segment_count )
    {    
        glVertex2i( bottom_left[ i ].x,
                    bottom_left[ i ].y );

        ++i;
    }


    i = ( segment_count - 1 );
    while( i > -1 )
    {    
        glVertex2i( bottom_right[ i ].x,
                    bottom_right[ i ].y );

        --i;
    }


    i = 0;
    while( i != segment_count )
    {    
        glVertex2i( top_right[ i ].x,
                    top_right[ i ].y );

        ++i;
    }


    // Close the border.
    glVertex2i( top_left[ ( segment_count - 1 ) ].x,
                top_left[ ( segment_count - 1 ) ].y );
}
glEnd();




glBegin( GL_LINES );

//glColor4f( 0.0f, 1.0f, 1.0f, 1.0f );
glColor4f( 0.0f, 0.5f, 1.0f, 1.0f );

// Separator
{
    // Top bar
    glVertex2i( top_right[ 0 ].x,
                top_right[ 0 ].y );

    glVertex2i( top_left[ 0 ].x,
                top_left[ 0 ].y );    


    // Bottom bar
    glVertex2i( bottom_left[ 0 ].x,
                bottom_left[ 0 ].y );    

    glVertex2i( bottom_right[ 0 ].x,
                bottom_right[ 0 ].y );    
}
glEnd();



free( top_left );
free( bottom_left );
free( top_right );
free( bottom_right );
}

要绘制圆角矩形,只需在正交视图内调用类似的命令:

RoundRect( 200, /* x */
           400, /* y */
           400, /* width */
           300, /* height */
           25,  /* Corner radius, at least less than 140? */
           64  /* need to be "dividable" by 4 */ );

Bit of a bump but I was stuck on the same problem today, this is what I output, its created with Desktop GL, but should be very easy to convert to GLES, everything is a strip. It is probably not as optimized as it should be but if someone want to have a stab at it please be my guest ;)

typedef struct
{
    float x;
    float y;

} Vector2f;


void RoundRect( int x,
            int y,
            int width,
            int height,
            int radius,
            int resolution )
{
float step = ( 2.0f * M_PI ) / resolution,
      angle = 0.0f,
      x_offset,
      y_offset;

int i = 0;

unsigned int index = 0,
             segment_count = ( int )( resolution / 4 );

Vector2f *top_left             = ( Vector2f * ) malloc( segment_count * sizeof( Vector2f ) ), 
         *bottom_left         = ( Vector2f * ) malloc( segment_count * sizeof( Vector2f ) ),
         *top_right             = ( Vector2f * ) malloc( segment_count * sizeof( Vector2f ) ),
         *bottom_right         = ( Vector2f * ) malloc( segment_count * sizeof( Vector2f ) ),
          bottom_left_corner = { x + radius,
                                 y - height + radius }; 

while( i != segment_count )
{
    x_offset = cosf( angle );
    y_offset = sinf( angle );


    top_left[ index ].x = bottom_left_corner.x - 
                          ( x_offset * radius );
    top_left[ index ].y = ( height - ( radius * 2.0f ) ) + 
                            bottom_left_corner.y - 
                          ( y_offset * radius );


    top_right[ index ].x = ( width - ( radius * 2.0f ) ) + 
                             bottom_left_corner.x + 
                           ( x_offset * radius );
    top_right[ index ].y = ( height - ( radius * 2.0f ) ) + 
                             bottom_left_corner.y -
                           ( y_offset * radius );


    bottom_right[ index ].x = ( width - ( radius * 2.0f ) ) +
                                bottom_left_corner.x + 
                              ( x_offset * radius );
    bottom_right[ index ].y = bottom_left_corner.y + 
                              ( y_offset * radius );


    bottom_left[ index ].x = bottom_left_corner.x - 
                             ( x_offset * radius );
    bottom_left[ index ].y = bottom_left_corner.y +
                             ( y_offset * radius );


    top_left[ index ].x = roundf( top_left[ index ].x );
    top_left[ index ].y = roundf( top_left[ index ].y );


    top_right[ index ].x = roundf( top_right[ index ].x );
    top_right[ index ].y = roundf( top_right[ index ].y );


    bottom_right[ index ].x = roundf( bottom_right[ index ].x );
    bottom_right[ index ].y = roundf( bottom_right[ index ].y );


    bottom_left[ index ].x = roundf( bottom_left[ index ].x );
    bottom_left[ index ].y = roundf( bottom_left[ index ].y );

    angle -= step;

    ++index;

    ++i;
}


glBegin( GL_TRIANGLE_STRIP );
{
    // Top
    {
        i = 0;
        while( i != segment_count )
        {
            //glColor4f( 1.0f, 0.0f, 0.0f, 1.0f );
            glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
            glVertex2i( top_left[ i ].x,
                        top_left[ i ].y );

            //glColor4f( 0.0f, 1.0f, 0.0f, 1.0f );
            glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
            glVertex2i( top_right[ i ].x,
                        top_right[ i ].y );

            ++i;
        }
    }


    // In order to stop and restart the strip.
    glColor4f( 0.0f, 1.0f, 0.0f,  1.5f );
    glVertex2i( top_right[ 0 ].x,
                top_right[ 0 ].y );

    glColor4f( 0.0f, 1.0f, 0.0f,  1.5f );
    glVertex2i( top_right[ 0 ].x,
                top_right[ 0 ].y );


    // Center
    {
        //glColor4f( 0.0f, 1.0f, 0.0f,  1.0f );
        glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
        glVertex2i( top_right[ 0 ].x,
                    top_right[ 0 ].y );


        //glColor4f( 1.0f, 0.0f, 0.0f,  1.0f );
        glColor4f( 0.0f, 0.0f, 0.0f, 1.0f );
        glVertex2i( top_left[ 0 ].x,
                    top_left[ 0 ].y );


        //glColor4f( 0.0f, 0.0f, 1.0f,  1.0f );
        glColor4f( 0.5f, 0.5f, 0.5f,  1.0f );
        glVertex2i( bottom_right[ 0 ].x,
                    bottom_right[ 0 ].y );


        //glColor4f( 1.0f, 1.0f, 0.0f,  1.0f );
        glColor4f( 0.5f, 0.5f, 0.5f,  1.0f );
        glVertex2i( bottom_left[ 0 ].x,
                    bottom_left[ 0 ].y );
    }


    // Bottom
    i = 0;
    while( i != segment_count )
    {
        //glColor4f( 0.0f, 0.0f, 1.0f,  1.0f );
        glColor4f( 0.5f, 0.5f, 0.5f,  1.0f );
        glVertex2i( bottom_right[ i ].x,
                    bottom_right[ i ].y );    

        //glColor4f( 1.0f, 1.0f, 0.0f,  1.0f );
        glColor4f( 0.5f, 0.5f, 0.5f,  1.0f );
        glVertex2i( bottom_left[ i ].x,
                    bottom_left[ i ].y );                                    

        ++i;
    }    
}
glEnd();



glBegin( GL_LINE_STRIP );

//glColor4f( 0.0f, 1.0f, 1.0f, 1.0f );
glColor4f( 1.0f, 0.5f, 0.0f, 1.0f );

// Border
{
    i = ( segment_count - 1 );
    while( i > -1 )
    {    
        glVertex2i( top_left[ i ].x,
                    top_left[ i ].y );

        --i;
    }


    i = 0;
    while( i != segment_count )
    {    
        glVertex2i( bottom_left[ i ].x,
                    bottom_left[ i ].y );

        ++i;
    }


    i = ( segment_count - 1 );
    while( i > -1 )
    {    
        glVertex2i( bottom_right[ i ].x,
                    bottom_right[ i ].y );

        --i;
    }


    i = 0;
    while( i != segment_count )
    {    
        glVertex2i( top_right[ i ].x,
                    top_right[ i ].y );

        ++i;
    }


    // Close the border.
    glVertex2i( top_left[ ( segment_count - 1 ) ].x,
                top_left[ ( segment_count - 1 ) ].y );
}
glEnd();




glBegin( GL_LINES );

//glColor4f( 0.0f, 1.0f, 1.0f, 1.0f );
glColor4f( 0.0f, 0.5f, 1.0f, 1.0f );

// Separator
{
    // Top bar
    glVertex2i( top_right[ 0 ].x,
                top_right[ 0 ].y );

    glVertex2i( top_left[ 0 ].x,
                top_left[ 0 ].y );    


    // Bottom bar
    glVertex2i( bottom_left[ 0 ].x,
                bottom_left[ 0 ].y );    

    glVertex2i( bottom_right[ 0 ].x,
                bottom_right[ 0 ].y );    
}
glEnd();



free( top_left );
free( bottom_left );
free( top_right );
free( bottom_right );
}

To draw the rounded rectangle simply call something like inside an orthographic view:

RoundRect( 200, /* x */
           400, /* y */
           400, /* width */
           300, /* height */
           25,  /* Corner radius, at least less than 140? */
           64  /* need to be "dividable" by 4 */ );
匿名的好友 2024-10-30 23:45:10

我需要绘制类似的矩形,但透明 - 上面的代码绘制了一些三角形重叠。修复了这个问题,还删除了 malloc,只是为了简化解决方案。这是我的版本:

typedef struct
{
    float x;
    float y;
} Vector2f;

//
//  Draws rounded rectangle.
//
//  Slightly tuned version of http://stackoverflow.com/questions/5369507/opengles-1-0-2d-rounded-rectangle
//
#define ROUNDING_POINT_COUNT 8      // Larger values makes circle smoother.
void DrawRoundRect( float x, float y, float width, float height, float* color = 0, float radius = 0.0 )
{
    Vector2f top_left[ROUNDING_POINT_COUNT];
    Vector2f bottom_left[ROUNDING_POINT_COUNT];
    Vector2f top_right[ROUNDING_POINT_COUNT];
    Vector2f bottom_right[ROUNDING_POINT_COUNT];

    if( radius == 0.0 )
    {
        radius = min(width, height);
        radius *= 0.10; // 10%
    }

    int i = 0;
    float x_offset, y_offset;
    float step = ( 2.0f * pi ) / (ROUNDING_POINT_COUNT * 4),
          angle = 0.0f;

    unsigned int index = 0, segment_count = ROUNDING_POINT_COUNT;
    Vector2f bottom_left_corner = { x + radius, y - height + radius }; 


    while( i != segment_count )
    {
        x_offset = cosf( angle );
        y_offset = sinf( angle );


        top_left[ index ].x = bottom_left_corner.x - 
                              ( x_offset * radius );
        top_left[ index ].y = ( height - ( radius * 2.0f ) ) + 
                                bottom_left_corner.y - 
                              ( y_offset * radius );


        top_right[ index ].x = ( width - ( radius * 2.0f ) ) + 
                                 bottom_left_corner.x + 
                               ( x_offset * radius );
        top_right[ index ].y = ( height - ( radius * 2.0f ) ) + 
                                 bottom_left_corner.y -
                               ( y_offset * radius );


        bottom_right[ index ].x = ( width - ( radius * 2.0f ) ) +
                                    bottom_left_corner.x + 
                                  ( x_offset * radius );
        bottom_right[ index ].y = bottom_left_corner.y + 
                                  ( y_offset * radius );


        bottom_left[ index ].x = bottom_left_corner.x - 
                                 ( x_offset * radius );
        bottom_left[ index ].y = bottom_left_corner.y +
                                 ( y_offset * radius );


        top_left[ index ].x = top_left[ index ].x;
        top_left[ index ].y = top_left[ index ].y;


        top_right[ index ].x = top_right[ index ].x;
        top_right[ index ].y = top_right[ index ].y;


        bottom_right[ index ].x = bottom_right[ index ].x ;
        bottom_right[ index ].y = bottom_right[ index ].y;


        bottom_left[ index ].x =  bottom_left[ index ].x ;
        bottom_left[ index ].y =  bottom_left[ index ].y ;

        angle -= step;

        ++index;

        ++i;
    }

    static GLubyte clr[] = { 156, 207, 255, 128 };   // Light blue, 50% transparent.

    if( color )
        glColor4fv(color);
    else
        glColor4ubv(clr);

    glBegin( GL_TRIANGLE_STRIP );
    {
        // Top
        for( i = segment_count - 1 ; i >= 0 ; i--)
        {
            glVertex2f( top_left[ i ].x, top_left[ i ].y );
            glVertex2f( top_right[ i ].x, top_right[ i ].y );
        }

        // In order to stop and restart the strip.
        glVertex2f( top_right[ 0 ].x, top_right[ 0 ].y );
        glVertex2f( top_right[ 0 ].x, top_right[ 0 ].y );

        // Center
        glVertex2f( top_right[ 0 ].x, top_right[ 0 ].y );
        glVertex2f( top_left[ 0 ].x, top_left[ 0 ].y );
        glVertex2f( bottom_right[ 0 ].x, bottom_right[ 0 ].y );
        glVertex2f( bottom_left[ 0 ].x, bottom_left[ 0 ].y );

        // Bottom
        for( i = 0; i != segment_count ; i++ )
        {
            glVertex2f( bottom_right[ i ].x, bottom_right[ i ].y );    
            glVertex2f( bottom_left[ i ].x, bottom_left[ i ].y );                                    
        }    
    }
    glEnd();
} //DrawRoundRect

I needed to draw similar rectangle, but transparent - and code above draws some of triangles overlap. Fixed that, also removed malloc, just to simplify solution. Here is my version:

typedef struct
{
    float x;
    float y;
} Vector2f;

//
//  Draws rounded rectangle.
//
//  Slightly tuned version of http://stackoverflow.com/questions/5369507/opengles-1-0-2d-rounded-rectangle
//
#define ROUNDING_POINT_COUNT 8      // Larger values makes circle smoother.
void DrawRoundRect( float x, float y, float width, float height, float* color = 0, float radius = 0.0 )
{
    Vector2f top_left[ROUNDING_POINT_COUNT];
    Vector2f bottom_left[ROUNDING_POINT_COUNT];
    Vector2f top_right[ROUNDING_POINT_COUNT];
    Vector2f bottom_right[ROUNDING_POINT_COUNT];

    if( radius == 0.0 )
    {
        radius = min(width, height);
        radius *= 0.10; // 10%
    }

    int i = 0;
    float x_offset, y_offset;
    float step = ( 2.0f * pi ) / (ROUNDING_POINT_COUNT * 4),
          angle = 0.0f;

    unsigned int index = 0, segment_count = ROUNDING_POINT_COUNT;
    Vector2f bottom_left_corner = { x + radius, y - height + radius }; 


    while( i != segment_count )
    {
        x_offset = cosf( angle );
        y_offset = sinf( angle );


        top_left[ index ].x = bottom_left_corner.x - 
                              ( x_offset * radius );
        top_left[ index ].y = ( height - ( radius * 2.0f ) ) + 
                                bottom_left_corner.y - 
                              ( y_offset * radius );


        top_right[ index ].x = ( width - ( radius * 2.0f ) ) + 
                                 bottom_left_corner.x + 
                               ( x_offset * radius );
        top_right[ index ].y = ( height - ( radius * 2.0f ) ) + 
                                 bottom_left_corner.y -
                               ( y_offset * radius );


        bottom_right[ index ].x = ( width - ( radius * 2.0f ) ) +
                                    bottom_left_corner.x + 
                                  ( x_offset * radius );
        bottom_right[ index ].y = bottom_left_corner.y + 
                                  ( y_offset * radius );


        bottom_left[ index ].x = bottom_left_corner.x - 
                                 ( x_offset * radius );
        bottom_left[ index ].y = bottom_left_corner.y +
                                 ( y_offset * radius );


        top_left[ index ].x = top_left[ index ].x;
        top_left[ index ].y = top_left[ index ].y;


        top_right[ index ].x = top_right[ index ].x;
        top_right[ index ].y = top_right[ index ].y;


        bottom_right[ index ].x = bottom_right[ index ].x ;
        bottom_right[ index ].y = bottom_right[ index ].y;


        bottom_left[ index ].x =  bottom_left[ index ].x ;
        bottom_left[ index ].y =  bottom_left[ index ].y ;

        angle -= step;

        ++index;

        ++i;
    }

    static GLubyte clr[] = { 156, 207, 255, 128 };   // Light blue, 50% transparent.

    if( color )
        glColor4fv(color);
    else
        glColor4ubv(clr);

    glBegin( GL_TRIANGLE_STRIP );
    {
        // Top
        for( i = segment_count - 1 ; i >= 0 ; i--)
        {
            glVertex2f( top_left[ i ].x, top_left[ i ].y );
            glVertex2f( top_right[ i ].x, top_right[ i ].y );
        }

        // In order to stop and restart the strip.
        glVertex2f( top_right[ 0 ].x, top_right[ 0 ].y );
        glVertex2f( top_right[ 0 ].x, top_right[ 0 ].y );

        // Center
        glVertex2f( top_right[ 0 ].x, top_right[ 0 ].y );
        glVertex2f( top_left[ 0 ].x, top_left[ 0 ].y );
        glVertex2f( bottom_right[ 0 ].x, bottom_right[ 0 ].y );
        glVertex2f( bottom_left[ 0 ].x, bottom_left[ 0 ].y );

        // Bottom
        for( i = 0; i != segment_count ; i++ )
        {
            glVertex2f( bottom_right[ i ].x, bottom_right[ i ].y );    
            glVertex2f( bottom_left[ i ].x, bottom_left[ i ].y );                                    
        }    
    }
    glEnd();
} //DrawRoundRect
×纯※雪 2024-10-30 23:45:10

以下代码来自我自己的项目,我在代码中添加了一些注释来解释。
如果会画一个没有边框的渐变圆角矩形。

#define GLW_SMALL_ROUNDED_CORNER_SLICES 5  // How many vertexes you want of each corner

#define glwR(rgb) ((float)(((rgb) >> 16) & 0xff) / 255)
#define glwG(rgb) ((float)(((rgb) >> 8) & 0xff) / 255)
#define glwB(rgb) ((float)(((rgb)) & 0xff) / 255)


typedef struct glwVec2 {
  float x;
  float y;
} glwVec2;

static glwVec2 glwRoundedCorners[GLW_SMALL_ROUNDED_CORNER_SLICES] = {{0}}; // This array keep the generated vertexes of one corner

static void createRoundedCorners(glwVec2 *arr, int num) {
  // Generate the corner vertexes
  float slice = M_PI / 2 / num;
  int i;
  float a = 0;
  for (i = 0; i < num; a += slice, ++i) {
    arr[i].x = cosf(a);
    arr[i].y = sinf(a);
  }
}

createRoundedCorners(glwRoundedCorners, GLW_SMALL_ROUNDED_CORNER_SLICES);

void glwDrawRoundedRectGradientFill(float x, float y, float width, float height,
    float radius, unsigned int topColor, unsigned int bottomColor) {
  float left = x;
  float top = y;
  float bottom = y + height - 1;
  float right = x + width - 1;
  int i;
  glDisable(GL_TEXTURE_2D);
  glBegin(GL_QUAD_STRIP);
    // Draw left rounded side.
    for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) {
      glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor));
      glVertex2f(left + radius - radius * glwRoundedCorners[i].x,
        bottom - radius + radius * glwRoundedCorners[i].y);
      glColor3f(glwR(topColor), glwG(topColor), glwB(topColor));
      glVertex2f(left + radius - radius * glwRoundedCorners[i].x,
        top + radius - radius * glwRoundedCorners[i].y);
    }
    // Draw right rounded side.
    for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) {
      glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor));
      glVertex2f(right - radius + radius * glwRoundedCorners[i].x,
        bottom - radius + radius * glwRoundedCorners[i].y);
      glColor3f(glwR(topColor), glwG(topColor), glwB(topColor));
      glVertex2f(right - radius + radius * glwRoundedCorners[i].x,
        top + radius - radius * glwRoundedCorners[i].y);
    }
  glEnd();
}

如果你想绘制边框,这里是代码。

static void glwDrawRightTopVertexs(float left, float top, float right,
    float bottom, float radius) {
  int i;
  for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) {
    glVertex2f(right - radius + radius * glwRoundedCorners[i].x,
      top + radius - radius * glwRoundedCorners[i].y);
  }
}

static void glwDrawRightBottomVertexs(float left, float top, float right,
    float bottom, float radius) {
  int i;
  for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) {
    glVertex2f(right - radius + radius * glwRoundedCorners[i].x,
      bottom - radius + radius * glwRoundedCorners[i].y);
  }
}

static void glwDrawLeftBottomVertexs(float left, float top, float right,
    float bottom, float radius) {
  int i;
  for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) {
    glVertex2f(left + radius - radius * glwRoundedCorners[i].x,
      bottom - radius + radius * glwRoundedCorners[i].y);
  }
}

static void glwDrawLeftTopVertexs(float left, float top, float right,
    float bottom, float radius) {
  int i;
  for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) {
    glVertex2f(left + radius - radius * glwRoundedCorners[i].x,
      top + radius - radius * glwRoundedCorners[i].y);
  }
}

void glwDrawRoundedRectBorder(float x, float y, float width, float height,
    float radius, unsigned int color) {
  float left = x;
  float top = y;
  float bottom = y + height - 1;
  float right = x + width - 1;
  glDisable(GL_TEXTURE_2D);
  glColor3f(glwR(color), glwG(color), glwB(color));
  glBegin(GL_LINE_LOOP);
    glVertex2f(left, top + radius);
    glwDrawLeftTopVertexs(left, top, right, bottom, radius);
    glVertex2f(left + radius, top);

    glVertex2f(right - radius, top);
    glwDrawRightTopVertexs(left, top, right, bottom, radius);
    glVertex2f(right, top + radius);

    glVertex2f(right, bottom - radius);
    glwDrawRightBottomVertexs(left, top, right, bottom, radius);
    glVertex2f(right - radius, bottom);

    glVertex2f(left + radius, bottom);
    glwDrawLeftBottomVertexs(left, top, right, bottom, radius);
    glVertex2f(left, bottom - radius);
  glEnd();
}

The following code is coping from my own project, I have added some comments to explain in the code.
If will draw a gradient rounded rectangle without border.

#define GLW_SMALL_ROUNDED_CORNER_SLICES 5  // How many vertexes you want of each corner

#define glwR(rgb) ((float)(((rgb) >> 16) & 0xff) / 255)
#define glwG(rgb) ((float)(((rgb) >> 8) & 0xff) / 255)
#define glwB(rgb) ((float)(((rgb)) & 0xff) / 255)


typedef struct glwVec2 {
  float x;
  float y;
} glwVec2;

static glwVec2 glwRoundedCorners[GLW_SMALL_ROUNDED_CORNER_SLICES] = {{0}}; // This array keep the generated vertexes of one corner

static void createRoundedCorners(glwVec2 *arr, int num) {
  // Generate the corner vertexes
  float slice = M_PI / 2 / num;
  int i;
  float a = 0;
  for (i = 0; i < num; a += slice, ++i) {
    arr[i].x = cosf(a);
    arr[i].y = sinf(a);
  }
}

createRoundedCorners(glwRoundedCorners, GLW_SMALL_ROUNDED_CORNER_SLICES);

void glwDrawRoundedRectGradientFill(float x, float y, float width, float height,
    float radius, unsigned int topColor, unsigned int bottomColor) {
  float left = x;
  float top = y;
  float bottom = y + height - 1;
  float right = x + width - 1;
  int i;
  glDisable(GL_TEXTURE_2D);
  glBegin(GL_QUAD_STRIP);
    // Draw left rounded side.
    for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) {
      glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor));
      glVertex2f(left + radius - radius * glwRoundedCorners[i].x,
        bottom - radius + radius * glwRoundedCorners[i].y);
      glColor3f(glwR(topColor), glwG(topColor), glwB(topColor));
      glVertex2f(left + radius - radius * glwRoundedCorners[i].x,
        top + radius - radius * glwRoundedCorners[i].y);
    }
    // Draw right rounded side.
    for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) {
      glColor3f(glwR(bottomColor), glwG(bottomColor), glwB(bottomColor));
      glVertex2f(right - radius + radius * glwRoundedCorners[i].x,
        bottom - radius + radius * glwRoundedCorners[i].y);
      glColor3f(glwR(topColor), glwG(topColor), glwB(topColor));
      glVertex2f(right - radius + radius * glwRoundedCorners[i].x,
        top + radius - radius * glwRoundedCorners[i].y);
    }
  glEnd();
}

If you want draw the border, here is the code.

static void glwDrawRightTopVertexs(float left, float top, float right,
    float bottom, float radius) {
  int i;
  for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) {
    glVertex2f(right - radius + radius * glwRoundedCorners[i].x,
      top + radius - radius * glwRoundedCorners[i].y);
  }
}

static void glwDrawRightBottomVertexs(float left, float top, float right,
    float bottom, float radius) {
  int i;
  for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) {
    glVertex2f(right - radius + radius * glwRoundedCorners[i].x,
      bottom - radius + radius * glwRoundedCorners[i].y);
  }
}

static void glwDrawLeftBottomVertexs(float left, float top, float right,
    float bottom, float radius) {
  int i;
  for (i = GLW_SMALL_ROUNDED_CORNER_SLICES - 1; i >= 0; --i) {
    glVertex2f(left + radius - radius * glwRoundedCorners[i].x,
      bottom - radius + radius * glwRoundedCorners[i].y);
  }
}

static void glwDrawLeftTopVertexs(float left, float top, float right,
    float bottom, float radius) {
  int i;
  for (i = 0; i < GLW_SMALL_ROUNDED_CORNER_SLICES; ++i) {
    glVertex2f(left + radius - radius * glwRoundedCorners[i].x,
      top + radius - radius * glwRoundedCorners[i].y);
  }
}

void glwDrawRoundedRectBorder(float x, float y, float width, float height,
    float radius, unsigned int color) {
  float left = x;
  float top = y;
  float bottom = y + height - 1;
  float right = x + width - 1;
  glDisable(GL_TEXTURE_2D);
  glColor3f(glwR(color), glwG(color), glwB(color));
  glBegin(GL_LINE_LOOP);
    glVertex2f(left, top + radius);
    glwDrawLeftTopVertexs(left, top, right, bottom, radius);
    glVertex2f(left + radius, top);

    glVertex2f(right - radius, top);
    glwDrawRightTopVertexs(left, top, right, bottom, radius);
    glVertex2f(right, top + radius);

    glVertex2f(right, bottom - radius);
    glwDrawRightBottomVertexs(left, top, right, bottom, radius);
    glVertex2f(right - radius, bottom);

    glVertex2f(left + radius, bottom);
    glwDrawLeftBottomVertexs(left, top, right, bottom, radius);
    glVertex2f(left, bottom - radius);
  glEnd();
}
冰魂雪魄 2024-10-30 23:45:10
#define PI_2   1.57079632679490f

#define SIN(x) SDL_sinf (x)
#define COS(x) SDL_cosf (x)

typedef struct _g2d_vertex_t g2d_vertex_t;

struct _g2d_vertex_t {

    float x, y;
};

// pVertices - destination buffer
// nVertices - buffer size
// dx - width
// dy - height
// r - radius
// returnes the number of used vertices
int
__cdecl buildRoundedRect (g2d_vertex_t * pVertices, int nVertices, float dx, float dy, float r) {

    float a, da;
    int i1, i2, i3, i4, n;

    if (nVertices < 4) { return 0; }

    if (nVertices == 4) {

        pVertices [0].x = 0.f; pVertices [0].y = 0.f;
        pVertices [1].x = dx;  pVertices [1].y = 0.f;
        pVertices [2].x = dx;  pVertices [2].y = dy;
        pVertices [3].x = 0.f; pVertices [3].y = dy;

        return nVertices;
    }

    n = nVertices >> 2;

    if (r > dx / 2.f) { r = dx / 2.f; }
    if (r > dy / 2.f) { r = dy / 2.f; }

    a = 0.f;
    da = PI_2 / (float) (n - 1);

    for (i1 = 0, i2 = (n << 1) - 1, i3 = n << 1, i4 = (n << 2) - 1; i1 < n; i1++, i2--, i3++, i4--, a += da) {

        float cosA = COS (a), sinA = SIN (a);

        pVertices [i1].x = (dx - r) + r * cosA; pVertices [i1].y = (dy - r) + r * sinA;
        pVertices [i2].x =     r    - r * cosA; pVertices [i2].y = (dy - r) + r * sinA;
        pVertices [i3].x =     r    - r * cosA; pVertices [i3].y =     r    - r * sinA;
        pVertices [i4].x = (dx - r) + r * cosA; pVertices [i4].y =     r    - r * sinA;
    }

    return n << 2;
}

void drawRoundedRect () {

    g2d_vertex_t vertices [50];

    glColor3f (0.3f, 0.5f, 0.2f);

    glVertexPointer (2, GL_FLOAT, 0, vertices);
    glEnableClientState (GL_VERTEX_ARRAY);

    glDrawArrays (GL_LINE_LOOP, 0, buildRoundedRect (vertices, 50 /* max count of vertices to use: 4 - 50 */, 150.f, 80.f, 20.f));
}
#define PI_2   1.57079632679490f

#define SIN(x) SDL_sinf (x)
#define COS(x) SDL_cosf (x)

typedef struct _g2d_vertex_t g2d_vertex_t;

struct _g2d_vertex_t {

    float x, y;
};

// pVertices - destination buffer
// nVertices - buffer size
// dx - width
// dy - height
// r - radius
// returnes the number of used vertices
int
__cdecl buildRoundedRect (g2d_vertex_t * pVertices, int nVertices, float dx, float dy, float r) {

    float a, da;
    int i1, i2, i3, i4, n;

    if (nVertices < 4) { return 0; }

    if (nVertices == 4) {

        pVertices [0].x = 0.f; pVertices [0].y = 0.f;
        pVertices [1].x = dx;  pVertices [1].y = 0.f;
        pVertices [2].x = dx;  pVertices [2].y = dy;
        pVertices [3].x = 0.f; pVertices [3].y = dy;

        return nVertices;
    }

    n = nVertices >> 2;

    if (r > dx / 2.f) { r = dx / 2.f; }
    if (r > dy / 2.f) { r = dy / 2.f; }

    a = 0.f;
    da = PI_2 / (float) (n - 1);

    for (i1 = 0, i2 = (n << 1) - 1, i3 = n << 1, i4 = (n << 2) - 1; i1 < n; i1++, i2--, i3++, i4--, a += da) {

        float cosA = COS (a), sinA = SIN (a);

        pVertices [i1].x = (dx - r) + r * cosA; pVertices [i1].y = (dy - r) + r * sinA;
        pVertices [i2].x =     r    - r * cosA; pVertices [i2].y = (dy - r) + r * sinA;
        pVertices [i3].x =     r    - r * cosA; pVertices [i3].y =     r    - r * sinA;
        pVertices [i4].x = (dx - r) + r * cosA; pVertices [i4].y =     r    - r * sinA;
    }

    return n << 2;
}

void drawRoundedRect () {

    g2d_vertex_t vertices [50];

    glColor3f (0.3f, 0.5f, 0.2f);

    glVertexPointer (2, GL_FLOAT, 0, vertices);
    glEnableClientState (GL_VERTEX_ARRAY);

    glDrawArrays (GL_LINE_LOOP, 0, buildRoundedRect (vertices, 50 /* max count of vertices to use: 4 - 50 */, 150.f, 80.f, 20.f));
}
浮世清欢 2024-10-30 23:45:10

我在修复某些开源软件中的崩溃时遇到了这个问题 - 非 GL 版本工作正常,但基本上目的是实现圆角矩形,但开发人员太懒了,决定强制崩溃:-(

虽然我认为@vime的答案是简洁和完整的,我见过很多类似的例子,但没有一个给我任何信心,而且我认为这些例子并不明显,所以这是我的记录......调用函数
实现 4 角(代码片段)……

glBegin(GL_POLYGON);

// top-left corner
DrawGLRoundedCorner(x, y + radius, 3 * PI / 2, PI / 2, radius);

// top-right
DrawGLRoundedCorner(x + size_x - radius, y, 0.0, PI / 2, radius);

// bottom-right
DrawGLRoundedCorner(x + size_x, y + size_y - radius, PI / 2, PI / 2, radius);

// bottom-left
DrawGLRoundedCorner(x + radius, y + size_y, PI, PI / 2, radius);

glEnd();

和圆弧截面函数 DrawGLRoundedCorner()。请注意,这假设 glBegin() 已被调用并绘制弧的起点和终点 - 这就是为什么您不需要在边的末尾显式添加顶点。

void DrawGLRoundedCorner(int x, int y, double sa, double arc, float r) {
    // centre of the arc, for clockwise sense
    float cent_x = x + r * cos(sa + PI / 2);
    float cent_y = y + r * sin(sa + PI / 2);

    // build up piecemeal including end of the arc
    int n = ceil(N_ROUNDING_PIECES * arc / PI * 2);
    for (int i = 0; i <= n; i++) {
        double ang = sa + arc * (double)i  / (double)n;

        // compute the next point
        float next_x = cent_x + r * sin(ang);
        float next_y = cent_y - r * cos(ang);
        glVertex2f(next_x, next_y);
    }
}

通过使用不同的 glBegin 例如 GL_LINE_LOOP 我认为你最终会得到一个未填充的圆角矩形。对于较大的角半径,可能需要使用各种抗锯齿提示等以使其看起来更漂亮,但还有其他相关帖子。

希望对某人有帮助。

I came across this fixing a crash in some open-source software - the non-GL version worked fine but basically the intention was to implement a rounded rectangle but the developer was too lazy for that and decided to force a crash instead :-(

Although I think @vime's answer is succinct and complete, I have seen lots of similar examples, none of which gave me any confidence and that I thought were non-obvious, so here's mine for the record... the calling function
implements the 4-corners ( code snippet )...

glBegin(GL_POLYGON);

// top-left corner
DrawGLRoundedCorner(x, y + radius, 3 * PI / 2, PI / 2, radius);

// top-right
DrawGLRoundedCorner(x + size_x - radius, y, 0.0, PI / 2, radius);

// bottom-right
DrawGLRoundedCorner(x + size_x, y + size_y - radius, PI / 2, PI / 2, radius);

// bottom-left
DrawGLRoundedCorner(x + radius, y + size_y, PI, PI / 2, radius);

glEnd();

... and the arc-section function DrawGLRoundedCorner(). Note that this assumes that glBegin() has already been called and plots both the start and the end of the arc - which is why you don't need to explicitly add the vertices at the end of the sides.

void DrawGLRoundedCorner(int x, int y, double sa, double arc, float r) {
    // centre of the arc, for clockwise sense
    float cent_x = x + r * cos(sa + PI / 2);
    float cent_y = y + r * sin(sa + PI / 2);

    // build up piecemeal including end of the arc
    int n = ceil(N_ROUNDING_PIECES * arc / PI * 2);
    for (int i = 0; i <= n; i++) {
        double ang = sa + arc * (double)i  / (double)n;

        // compute the next point
        float next_x = cent_x + r * sin(ang);
        float next_y = cent_y - r * cos(ang);
        glVertex2f(next_x, next_y);
    }
}

By using a different glBegin such as with GL_LINE_LOOP I think you would end up with a non-filled rounded rectangle. For larger corner radii there might be a need to use various anti-aliasing hints or the like to make it look prettier, but there are other posts regarding that.

Hope that helps someone.

木有鱼丸 2024-10-30 23:45:10

您还可以制作三角形而不是矩形来使边缘倾斜。
输入图片此处描述

You can also make triangles instead of rectangles to bevel the edges.
enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文