Как нарисовать основной круг в OpenGL ES 2.0 Android

Я новичок в OpenGL ES 2, и я прочитал много тем о том, как нарисовать круг в OpenGL ES 2 на Android. На основе Рисование фигур и этот код найден на gamedev.net, Я могу рисовать треугольники и кварты, но я до сих пор не знаю, как нарисовать круг. Теперь у меня есть три способа рисовать круг:

  • Создайте вершины в круге и используйте glDrawArray (GL_LINES,...). В зависимости от того, сколько вершин вы создадите, это даст хороший и четкий результат.
  • Используйте предварительно сформированную текстуру круга (с альфа-прозрачностью) и нарисуйте его на квадрате. Это приведет к очень плавной графике и позволит использовать "крутой" круг, но он не будет таким гибким: даже при использовании mipmapping вы хотите, чтобы ваша текстура была примерно того же размера, что и рендеринг квадранта.
  • Используйте шейдер фрагмента.

Но как их реализовать?

Ответ 1

Я определенно не рекомендую показывать круг через геометрию. Он имеет два основных недостатка:

  • Это медленно. Если вы хотите получить приемлемую точность, вам нужно много вершин, и любая из этих вершин должна обрабатываться в шейдере. Для реального круга вам нужно столько вершин, сколько ваш круг имеет пиксели.
  • Это не очень гибко. Наличие разных кругов, стилизация и совмещение их трудно освоить.

Существует еще один метод, который я лично использую в каждом графическом API. Предоставляя по крайней мере треугольник или квадрат/квадрат, и используйте фрагмент-шейдер, чтобы отображать только видимый (на основе уравнения) пиксель. Это очень легко понять. Он гибкий и быстрый. Он нуждается в смешении, но на самом деле это не так сложно.

Шаги:

Инициализируйте ваши буферы данными. Для вершин нужен вершинный буфер, индексный буфер для индексов, если вы используете квадратную геометрию, и буфера текстуры для координат текстуры. Для квадрата я рекомендую использовать -1.0 как самую низкую и 1.0 как самую высокую координату текстуры, потому что тогда вы можете использовать уравнение окружности единицы.

В вашем фрейш-шейдере используйте что-то вроде этого:

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

Пока (textureCoord.x * textureCoord.x) + (textureCoord.y * textureCoord.y) <= 1.0 - это неравенство, потому что вам нужен круг, вы должны отображать каждый пиксель в этом диапазоне, а не только границы. Вы можете изменить это так, чтобы он выдавал желаемый результат.

И это все. Не очень сложно реализовать, поэтому здесь я не предлагаю базовый код рендеринга. Все, что вам нужно, происходит в фрагмент-шейдере.

Ответ 2

Если вы хотите создать геометрию для круга, сделайте следующее:

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

Затем вы можете рисовать с помощью glDrawArrays() либо как:

  • GL_LINE_LOOP (только контур) или
  • GL_TRIANGLE_FAN (заполненная форма)

.

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

Ответ 3

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

}

}

Ответ 4

Это модифицированная версия вышеупомянутого ответа. Он также включает код для окраски круга. Однако большинство функций используются как OpenGL ES1. Обратите внимание на соглашения об именах туалет класса, LOL. Если вам нужен код других классов, где я также показываю OpenGL, дайте мне знать.

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

Ответ 6

Один главный недостаток, который я заметил в "постах цели": вы не можете изменить положение круга.

Вот исправление. Обратите внимание на конец первых двух строк в цикле "for".

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