How to draw basic circle in OpenGL ES 2.0 Android

2019-01-17 08:37发布

I'm new in OpenGL ES 2, and I have read many topics about how to draw a circle in OpenGL ES 2 on Android. Based on Drawing Shapes and this code found on gamedev.net, I can draw triangles and quares, but I still don't know how to draw a circle. I now have three ways to draw a circle:

  1. Generate vertices in a circle and use glDrawArray( GL_LINES, ... ). Depending on how many vertices you generate this will yield a nice and crisp result.
  2. Use a pregenerated texture of a circle (with alpha transparency) and map it on a quad. This will result in very smooth graphics and allow for a ´thick´ circle, but it will not be as flexible: Even with mipmapping, you'll want your texture to be about the same size you are rendering the quad.
  3. Use a fragment shader.

But how do I implement them?

6条回答
Summer. ? 凉城
2楼-- · 2019-01-17 08:41

I definitely do not recommend rendering a circle through geometry. It has two major disadvantages:

  1. It is slow. If you want to get acceptable accuracy you need a lot of vertices and any of these vertices need to be processed in the shader. For a real circle you need as many vertices as your circle have pixels.
  2. It is not really flexible. Having different circles, styling and colring them is hard to master.

There is another method, which I personally use in every graphics API. Rendering at least a triangle or a sqare/quad and use the fragment-shader to only make the disired (based on a equation) pixel visible. It is very easy to understand. It is flexible and fast. It needs blending, but this is not really hard to get to work.

Steps:

Initialize your buffers with data. You need a vertex-buffer for the vertices, an index-buffer for the indices if you're a using a square geometry, and a textureCoord-buffer for your texture coordinates. For a square I recommend using -1.0 as the lowest and 1.0 as the highest texture coordinate, because then you are able to use the unit circle equation.

In your fragment-shader, use something like this:

if ((textureCoord.x * textureCoord.x) + (textureCoord.y * textureCoord.y) <= 1.0)
{
    // Render colored and desired transparency
}
else
{
    // Render with 0.0 in alpha channel
}

While (textureCoord.x * textureCoord.x) + (textureCoord.y * textureCoord.y) <= 1.0 is the inequality, because you need a circle, you have to render every pixel within that range, not just the border. You can change this so that it gives you the desired output.

And that is it. Not very complex to implement, so I don't offer any basic rendering code here. All you need happens within the fragment-shader.

查看更多
放我归山
3楼-- · 2019-01-17 08:44

This is a modified version of the above answer. It also includes the code to color the circle as well. Most of the functions are used as OpenGL ES1 though. Mind the naming conventions of class toilet, LOL. If you need the code of other classes where I render OpenGL as well, let me know.

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;

public class Toilet {

    // Circle variables
    int circlePoints = 30;
    float radius = 1.0f;
    float center_x = 0.0f;
    float center_y = 0.0f;

    // Outer vertices of the circle i.e. excluding the center_x, center_y
    int circumferencePoints = circlePoints-1;

    // Circle vertices and buffer variables
    int vertices = 0;
    float circleVertices[] = new float[circlePoints*2];
    private FloatBuffer toiletBuff; // 4 bytes per float

    // Color values
    private float rgbaValues[] = {
               1,     1,  0,     .5f,
               .25f,  0,  .85f,  1,
               0,     1,  1,     1
    };

    private FloatBuffer colorBuff;

    public Toilet()
    {
        // The initial buffer values
        circleVertices[vertices++] = center_x;
        circleVertices[vertices++] = center_y;

        // Set circle vertices values
        for (int i = 0; i < circumferencePoints; i++)
        {
            float percent = (i / (float) (circumferencePoints - 1));
            float radians = (float) (percent * 2 * Math.PI);

            // Vertex position
            float outer_x = (float) (center_x + radius * Math.cos(radians));
            float outer_y = (float) (center_y + radius * Math.sin(radians));

            circleVertices[vertices++] = outer_x;
            circleVertices[vertices++] = outer_y;
        }

        // Float buffer short has four bytes
        ByteBuffer toiletByteBuff = ByteBuffer
                .allocateDirect(circleVertices.length * 4);

        // Garbage collector won't throw this away
        toiletByteBuff.order(ByteOrder.nativeOrder());
        toiletBuff = toiletByteBuff.asFloatBuffer();
        toiletBuff.put(circleVertices);
        toiletBuff.position(0);

        // Float buffer short has four bytes
        ByteBuffer clrBuff = ByteBuffer.allocateDirect(rgbaValues.length * 4);
        // garbage collector wont throw this away
        clrBuff.order(ByteOrder.nativeOrder());
        colorBuff = clrBuff.asFloatBuffer();
        colorBuff.put(rgbaValues);
        colorBuff.position(0);
    }

    // Draw methods
    public void draw(GL10 gl) {

        // Get the front face
        gl.glFrontFace(GL10.GL_CW); // Front facing is clockwise
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

        // Enable color array
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

        // Pointer to the buffer
        gl.glVertexPointer(2, GL10.GL_FLOAT, 0, toiletBuff);

        // Pointer to color
        gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuff);

        // Draw hollow circle
        //gl.glDrawArrays(GL10.GL_LINE_LOOP, 1, circumferencePoints);

        // Draw circle as filled shape
        gl.glDrawArrays(GL10.GL_TRIANGLE_FAN, 0, circlePoints);

        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    }
}
查看更多
Deceive 欺骗
4楼-- · 2019-01-17 08:55

https://gist.github.com/beetsolutions/9c343f86ec44987de4550cada118e560

You can find a complete working example here: https://github.com/beetsolutions/opengl_circle

Works perfectly and well optimised

查看更多
孤傲高冷的网名
5楼-- · 2019-01-17 08:55

One major flaw I noticed in 'goal's post: You can't change the position of the circle.

Here is the fix. Notice the end of the first two lines in the 'for' loop.

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[0]);
    vertices[(i * 3)+ 1] = (float) (0.5 * Math.sin((3.14/180) * (float)i ) + vertices[1]);
    vertices[(i * 3)+ 2] = 0;
}
查看更多
Juvenile、少年°
6楼-- · 2019-01-17 08:56
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import android.opengl.GLES20;
import android.util.Log;

public class Circle {

private  int mProgram, mPositionHandle, mColorHandle, mMVPMatrixHandle ;
private FloatBuffer mVertexBuffer;
private float vertices[] = new float[364 * 3];
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

private final String vertexShaderCode =
        "uniform mat4 uMVPMatrix;" +
        "attribute vec4 vPosition;" +
        "void main() {" +
        "  gl_Position = uMVPMatrix * vPosition;" +
        "}";

    private final String fragmentShaderCode =
        "precision mediump float;" +
        "uniform vec4 vColor;" +
        "void main() {" +
        "  gl_FragColor = vColor;" +
        "}";

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


    Log.v("Thread",""+vertices[0]+","+vertices[1]+","+vertices[2]);
    ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
    vertexByteBuffer.order(ByteOrder.nativeOrder());
    mVertexBuffer = vertexByteBuffer.asFloatBuffer();
    mVertexBuffer.put(vertices);
    mVertexBuffer.position(0);
    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    mProgram = GLES20.glCreateProgram();             // create empty OpenGL ES Program
    GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
    GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
    GLES20.glLinkProgram(mProgram);  

 }

public static int loadShader(int type, String shaderCode){

    int shader = GLES20.glCreateShader(type);
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);
    return shader;
}


public void draw (float[] mvpMatrix){

    GLES20.glUseProgram(mProgram);

    // get handle to vertex shader's vPosition member
     mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // Enable a handle to the triangle vertices
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // Prepare the triangle coordinate data
    GLES20.glVertexAttribPointer(mPositionHandle, 3,
                                 GLES20.GL_FLOAT, false,12
                                 ,mVertexBuffer);

    // get handle to fragment shader's vColor member
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");



    // Set color for drawing the triangle
    GLES20.glUniform4fv(mColorHandle, 1, color, 0);

    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

    // Apply the projection and view transformation
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);



    // Draw the triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 364);

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(mPositionHandle);

}

}
查看更多
劫难
7楼-- · 2019-01-17 09:05

If you want to create geometry for the circle, do something like this:

int vertexCount = 30;
float radius = 1.0f;
float center_x = 0.0f;
float center_y = 0.0f;

// Create a buffer for vertex data
float buffer[] = new float[vertexCount*2]; // (x,y) for each vertex
int idx = 0;

// Center vertex for triangle fan
buffer[idx++] = center_x;
buffer[idx++] = center_y;

// Outer vertices of the circle
int outerVertexCount = vertexCount-1;

for (int i = 0; i < outerVertexCount; ++i){
    float percent = (i / (float) (outerVertexCount-1));
    float rad = percent * 2*Math.PI;

    //Vertex position
    float outer_x = center_x + radius * cos(rad);
    float outer_y = center_y + radius * sin(rad);

    buffer[idx++] = outer_x;
    buffer[idx++] = outer_y;
}

//Create VBO from buffer with glBufferData()

Then you can draw using glDrawArrays() either as:

  • GL_LINE_LOOP(contour only) or
  • GL_TRIANGLE_FAN(filled shape)

.

// Draw circle contours (skip center vertex at start of the buffer)
glDrawArrays(GL_LINE_LOOP, 2, outerVertexCount);

// Draw circle as a filled shape
glDrawArrays(GL_TRIANGLE_FAN, 0, vertexCount);
查看更多
登录 后发表回答