nhoxbypass
nhoxbypass

Reputation: 10152

Draw a circle stroke using OpenGLES

I want to draw a stroked circle like this:

enter image description here

I've tried use normal vertex shader & fragment shader like google samples, with 364 points for vertex coords:

vertices = new float[364 * 3];
vertices[0] = 0;
vertices[1] = 0;
vertices[2] = 0;

for (int i =1; i <364; i++){
    vertices[(i * 3)+ 0] = (float) (0.5 * Math.cos((3.14/180) * (float)i ));
    vertices[(i * 3)+ 1] = (float) (0.5 * Math.sin((3.14/180) * (float)i ));
    vertices[(i * 3)+ 2] = 0;
}

Then draw using:

int COORDS_PER_VERTEX = 3;
int vertexCount = 364 * 3 / COORDS_PER_VERTEX;
int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);

GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 1, vertexCount - 1);

But the result is not as expected, there are 4 missing part in my circle.

enter image description here

How can I draw a stroked circle like the example above?

Upvotes: 3

Views: 949

Answers (2)

slaviboy
slaviboy

Reputation: 1912

Most libraries use 'triangulated lines' which in my opinion is the best way to draw stroke lines since you can set gradient color, control the line width at different part of the line and draw smooth line.

But if you want to create stroke circle with single color, one trick you can do is to create multiple circles with strokeWidth of 1f and just variate the radius of the circles in such a way that each next circle has a radius of +(1f/2) bigger than the previous circle.

enter image description here

I have created a OpenGL library that can create multiple type of shapes like triangles, circles, triangles..and many more. Below is the code that generate multiple circles using the library and the result is shown on the image above.

     lateinit var circles: Circles // OpenGL circles object

     fun createCircles() {

        val coordinatesInfo = generateCoordinateInfo(400f, 400f, 100f, 20)

        // set object for the OpenGL circles
        circles = Circles(
            // in format: [cx1,cy1,radius1,angle1,  cx2,cy2,radius2,angle2,...]
            coordinatesInfo = coordinatesInfo,
            colors = intArrayOf(Color.RED),
            style = STYLE_STROKE,
            strokeWidth = 1f,
            preloadProgram = program,
            gestureDetector = mainGestureDetector,
            useSingleColor = true
        )
     }

    /**
     * Get the coordinates for the stroke circles
     * @param x x coordinate of the circle
     * @param y y coordinate of the circle
     * @param r radius of the circle
     * @param strokeWidth stroke width of the circle
     */
    fun generateCoordinateInfo(x: Float, y: Float, r: Float, strokeWidth: Int = 20): FloatArray {

        val coordinatesInfo = FloatArray((strokeWidth * 2) * 4)

        for (i in 0 until strokeWidth) {
            val j = i * 4
            coordinatesInfo[j] = x                // x
            coordinatesInfo[j + 1] = y            // y
            coordinatesInfo[j + 2] = r - i / 2f   // radius
            coordinatesInfo[j + 3] = 0f           // angle
        }

        for (i in 0 until strokeWidth) {
            val j = (strokeWidth + i) * 4
            coordinatesInfo[j] = x                // x
            coordinatesInfo[j + 1] = y            // y
            coordinatesInfo[j + 2] = r + i / 2f   // radius
            coordinatesInfo[j + 3] = 0f           // angle
        }

        return coordinatesInfo
    }

    fun draw(transformedMatrixOpenGL: FloatArray) {

        // draw circles
        if (::circles.isInitialized) {
            circles.draw(transformedMatrixOpenGL)
        } 
    }

Upvotes: 0

Columbo
Columbo

Reputation: 6766

The problem is the way that glLineWidth is implemented. According to the spec:

Non-antialiased line segments of width other than one are rasterized by off-setting them in the minor direction (for an x-major line, the minor direction is y, and for a y-major line, the minor direction is x) and replicating fragments in the minor direction (see figure 3.3).

Those 4 missing parts correspond to the locations where your circle lines change from x-major to y-major. If you look carefully you can see your circle also gets thinner as it approaches the switch between x-major and y-major.

Probably your best bet is to abandon line drawing and use a triangle strip to render a solid band instead. i.e. The 'triangulated lines' example from this webpage which also suggests some more advanced solutions.

Upvotes: 3

Related Questions