OpenGL-ES 1.0 2d rounded rectangle

How to make a rounded rectangle in OpenGL or any rounded polygon?

+7
source share
6 answers

Using polygons

If the use of polygons is absolutely necessary, for example, if objects with rounding need to be scaled or scaled a lot, or if rounding is required to round, you can split the rectangle into several sub-objects.

Rounded corners

There are at least three rectangular parts and four corners. Calculation of angular coordinates is easy. Just find the point from the circle and build the triangles as in the 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 the corners more round. Small objects require less points than large objects.

A simple route is to use GL_TRIANGLE_FAN for corners. However, using OpenGL ES it may be wise to minimize the number of OpenGL calls and just use more vertices, since you can construct the whole object as GL_TRIANGLE_STRIP.

This approach can be used with any form. With a rectangle, the angle is always 90 degrees, but with other corners of the shape, you need to calculate at the edges.

Using textures

Another approach is called 9-slice scaling . The rectangle and texture are divided into 9 fragments. Actual rounding is in the corner of the texture. The idea is that the corners do not scale, but retain their original size. This approach is widely used in UI design, allowing the use of variable size UI elements, such as buttons. Its advantage is that one rectangle only needs these 9 quads for rendering. But it will look bad if you also need to scale the corners and especially if the texture has a low resolution.

+16
source

A bit of luck, but today I came across the same problem, this is what I output, created using Desktop GL, but it should be very easy to convert to GLES, it's all a strip. This is probably not as optimized as it should be, but if someone wants to strike, 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 a rounded rectangle, just call something like a spelling 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 */ ); 
+5
source

I needed to draw a similar rectangle, but transparent - and the code above draws some triangles overlap. Fixed this, also removed malloc, just to simplify the 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 
+2
source
 #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
source

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

Although I think the @vime answer is concise and complete, I saw many similar examples, none of which gave me confidence and that I thought it was not obvious, so here for the record ... the calling function implements 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 () is already called and displays both the beginning and the end of the arc - so you do not need to explicitly add 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); } } 

Using another glBegin, for example with GL_LINE_LOOP, I think you will get an unfilled rounded rectangle. For large angular radii, you may need to use various smoothing tips or the like to make them look prettier, but there are other posts regarding this.

Hope this helps someone.

+1
source

The following code handles my own project, I added some comments to explain in the code. If you draw a rectangle with a gradient of a rounded rectangle without a 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 to draw a 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(); } 
+1
source

All Articles