web-dev-qa-db-ja.com

OpenGL ES 2.0で基本的な円を描く方法Android

私は OpenGL ES 2の初心者であり、AndroidのOpenGL ES 2で円を描画する方法に関する多くのトピックを読みました。 Drawing Shapesgamedev.netにあるこのコード に基づいて、三角形と四角形を描くことができますが、円を描く方法はまだわかりません。円を描く方法は3つあります。

  1. 円の頂点を生成し、glDrawArray(GL_LINES、...)を使用します。生成する頂点の数に応じて、これは見栄えのよい結果になります。
  2. 円の事前生成されたテクスチャ(アルファ透明度付き)を使用して、四角形にマッピングします。これにより、非常に滑らかなグラフィックスが得られ、「太い」円が可能になりますが、それほど柔軟ではありません。ミップマッピングを使用しても、テクスチャは、クワッドをレンダリングしているのとほぼ同じサイズにする必要があります。
  3. フラグメントシェーダーを使用します。

しかし、どうすれば実装できますか?

13
Sơn Nguyễn

ジオメトリを介して円をレンダリングすることはお勧めしません。これには2つの大きな欠点があります。

  1. 遅いです。許容可能な精度を得るには、多数の頂点が必要であり、これらの頂点のいずれかをシェーダーで処理する必要があります。実際の円では、円のピクセル数と同じ数の頂点が必要です。
  2. それは本当に柔軟ではありません。さまざまなサークルがあり、それらをスタイリングしてまとめることは習得が困難です。

すべてのグラフィックAPIで個人的に使用する別の方法があります。少なくとも三角形または正方形/四角形をレンダリングし、フラグメントシェーダーを使用して、(方程式に基づいて)不要なピクセルのみを表示します。とてもわかりやすいです。柔軟で高速です。ブレンドが必要ですが、これは実際に動作するように難しくありません。

手順:

データでバッファを初期化します。頂点の頂点バッファー、正方形のジオメトリを使用している場合はインデックスのインデックスバッファー、テクスチャ座標のtextureCoordバッファーが必要です。正方形の場合、最小の-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は不等式ですが、円が必要なため、境界だけでなく、その範囲内のすべてのピクセルをレンダリングする必要があります。これを変更して、目的の出力が得られるようにすることができます。

そして、それはそれです。実装はそれほど複雑ではないので、ここでは基本的なレンダリングコードは提供していません。必要なのはフラグメントシェーダー内だけです。

18

円のジオメトリを作成する場合は、次のようにします。

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

}

}
9
user2541581

これは上記の回答の修正版です。また、円に色を付けるためのコードも含まれています。ただし、ほとんどの関数は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);
    }
}
5
user3148594

https://Gist.github.com/beetsolutions/9c343f86ec44987de4550cada118e56

あなたはここで完全な実用的な例を見つけることができます: https://github.com/beetsolutions/opengl_circle

完璧に機能し、最適化されています

2

「目標の投稿」で気付いた大きな欠陥の1つは、円の位置を変更できないことです。

これが修正です。 「for」ループの最初の2行の終わりに注意してください。

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