Reputation: 10152
I want to draw a stroked circle like this:
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.
How can I draw a stroked circle like the example above?
Upvotes: 3
Views: 949
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.
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
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