11
votes

OpenGL-ES 1.0 2D Rectangle arrondi

Comment faire du rectangle arrondi dans OpenGL, ou tout polygone avec des coins arrondis?


1 commentaires

Pour quel but avez-vous besoin de cela? Coins arrondis pour certains éléments d'interface utilisateur? Avez-vous besoin d'une mise à l'échelle?


7 Réponses :


19
votes

à l'aide de polygones

Si l'utilisation de polygones est absolument nécessaire, par exemple si des objets dont l'arrondi doivent être mis à l'échelle ou zoomé ou si la quantité d'arrondissement doit être contrôlée, il est possible de briser le rectangle en plusieurs sous-objets. . p>

coins arrondis p>

Il y a au moins trois parties rectangulaires et quatre coins. Calculer les coordonnées de coin est facile. Trouvez simplement un point de cercle et construisez des triangles comme dans l'image ci-dessus. P>

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


5 commentaires

Vous pouvez utiliser efficacement GL_TRIANGLE_FAN et un appel de tirage en plaçant le premier sommet au centre du rectangle.


C'est absolument correct! Si certaines coordonnées de texture sont nécessaires, cela pourrait être une approche peu délicate.


Est gl_triangle_strip pas suffisant pour ce type de rendu?


N'est-ce pas très coûteux sur les ressources de la mémoire? J'essaie de mettre en œuvre cela et j'ai beaucoup de code sur celui-ci juste d'appeler une fois que le GLDRAWELES . Les bords ne sont pas si doux, même si je définis comptage de tranche à 90


Il est possible d'utiliser GL_TRIANGLE_STRIP pour tirer le tout. Mais le code pour cela devient facilement compliqué. Comme @schellsan suggère, un gl_triangle_fan est plus simple. Mais je ne pense pas que les ressources de mémoire sont un problème ici. La limitation des appels de tirage est plus importante. Il est vrai que sans bords anti-aliasing peut être rugueux, peu importe le nombre de polygones utilisés. Personnellement, je préfère la mise à l'échelle de 9 tranches de nos jours, car elle permet de lisser les bords de la texture.



5
votes

Un peu d'une bosse, mais j'étais coincé sur le même problème aujourd'hui, c'est ce que je sortit, c'est créé avec le bureau GL, mais doit être très facile à convertir en gle, tout est une bande. Ce n'est probablement pas aussi optimisé que possible, mais si quelqu'un veut avoir un coup de poignard, s'il vous plaît être mon invité;) XXX

Pour dessiner le rectangle arrondi, appelez simplement quelque chose comme à l'intérieur d'une vue orthographique : xxx


0 commentaires

3
votes

Je devais dessiner un rectangle similaire, mais transparent - et le code ci-dessus dessine certains des triangles se chevauchent. Correction de cela, également supprimé MALLOC, juste pour simplifier la solution. Voici ma 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


1 commentaires

top_left [index] .x = top_left [index] .x; - Un peu redondant ne pensez-vous pas?



1
votes
#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));
}

1 commentaires

une petite solution: lorsque vous avez lié le rayon d'utilisation: r = min (r, 0.499999 * min (dx, dy)); sinon des points en double sont produits lorsque r est exactement la moitié de la largeur ou la hauteur du rectangle.



1
votes

Je suis tombé sur cette fixation d'un crash dans certains logiciels open-source - la version non GL a fonctionné bien, mais l'intention était essentiellement de mettre en place un rectangle arrondi mais le développeur était trop paresseux pour cela et décidé de forcer un crash à la place: - (

Bien que je pense que la réponse de Vime est succincte et complète, j'ai vu beaucoup d'exemples similaires, dont aucun ne m'a donné de confiance et que je pensais être non évident, alors voici le mien pour le record ... Fonction d'appel Implémente les 4 coins (snippet de code) ... p> xxx pré>

... et la fonction Arc-Section Drawglroundcorner (). Notez que cela suppose que GLBEGIN () a déjà été appelé et parcelle à la fois le début et la fin de l'arc - c'est pourquoi vous n'avez pas besoin d'ajouter explicitement les sommets à la fin des côtés. P>

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


0 commentaires

2
votes

Le code suivant fait face à mon propre projet, j'ai ajouté quelques commentaires à expliquer dans le code. Si vous dessinerez un rectangle de gradient arrondi sans bordure. xxx pré>

Si vous voulez dessiner la bordure, voici le code. P>

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


1 commentaires

S'il vous plaît ajouter des explications



-1
votes

Vous pouvez également faire des triangles au lieu de rectangles pour biseler les bords. Entrez la description de l'image ici


0 commentaires