2013-08-09 21 views
10

Jestem nowy w OpenGL ES 2 i przeczytałem wiele tematów na temat rysowania koła w OpenGL ES 2 na Androida. Na podstawie Drawing Shapes i this code found on gamedev.net, mogę rysować trójkąty i kwarty, ale nadal nie wiem, jak narysować okrąg. Mam teraz trzy sposoby narysowania okręgu:Jak narysować podstawowe koło w OpenGL ES 2.0 Android

  1. Wygeneruj wierzchołki w kole i użyj glDrawArray (GL_LINES, ...). W zależności od tego, ile wygenerowanych wierzchołków da dobry i wyraźny wynik.
  2. Użyj wstępnie wygenerowanej tekstury koła (z przezroczystością alfa) i zamapuj ją na kwadracie. Spowoduje to bardzo płynną grafikę i pozwoli na "krąg", ale nie będzie tak elastyczny: nawet przy mipmapach, będziesz chciał, aby twoja tekstura miała mniej więcej ten sam rozmiar, co renderowanie kwadratu.
  3. Użyj modułu cieniującego fragmentu.

Ale jak mogę je wdrożyć?

Odpowiedz

11

Jeśli chcesz utworzyć geometrię koła, zrób coś takiego:

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

Następnie można wyciągnąć za pomocą glDrawArrays() albo jako:

  • GL_LINE_LOOP (kontur tylko) lub
  • GL_TRIANGLE_FAN (wypełniony kształt)

.

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

podziękować za kodzie ale używam OpenGL ES 2, i nie mogę realizować swoje kod. Jak to działa w OPENGL ES 2? –

+3

Oczywiście działa to z OpenGL es 2. Jeśli znasz kod i nie wiesz, jak zastosować go do OpenGL es 2.0, powinieneś poszukać podstawowych samouczków OpenGL es 2.0, jak renderować prosty vbo. – Dirk

+0

To jest mój kod, aby uzyskać znaki koła: –

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

} 

} 
+0

Czy nie powinno to być 'gl_Position = uMVPMatrix * vPosition;'? – Flo

+0

Komentarze w kodzie są mylące: Odnosi się do "trójkąta", ale wydaje się rysować trójkątny wentylator; sekwencja trójkątów, które przybliżają okrąg. – ToolmakerSteve

+0

Nie działa –

0

Główną wadą jaką zauważyłem w „celem poście: Nie można zmienić położenie koła.

Oto poprawka. Zwróć uwagę na koniec pierwszych dwóch linii w pętli "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; 
} 
4

To jest zmodyfikowana wersja powyższej odpowiedzi. Zawiera również kod do kolorowania okręgu. Większość funkcji jest jednak wykorzystywana jako OpenGL ES1. Pamiętaj konwencji nazewnictwa klasy WC, LOL. Jeśli potrzebujesz kodu innych klas, w których również renderuję OpenGL, daj mi znać.

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

"powyższa odpowiedź" prawdopodobnie odnosi się do odpowiedzi dirka, ponieważ buduje ona listę wierzchołków. (nie odnosząc się do jednej z odpowiedzi opartych na cieniu, które są obecnie "powyżej"). – ToolmakerSteve

+0

Jak wywołujesz metodę onDraw? Mówi, że potrzebuje GL10. – L1ghtk3ira

+0

W swojej OnDrawFrame() w używanym renderingu: gl.glClear (GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); i przekazać gl – L1ghtk3ira

14

Zdecydowanie nie polecam renderowania okręgu przez geometrię. Ma dwie główne wady:

  1. Jest powolny. Jeśli chcesz uzyskać akceptowalną dokładność, potrzebujesz wielu wierzchołków i każdy z tych wierzchołków musi zostać przetworzony w module cieniującym. Dla prawdziwego koła potrzebujesz tyle wierzchołków, ile twój okrąg ma piksele.
  2. To naprawdę nie jest elastyczne. Różne kręgi, stylizacja i kolokacja są trudne do opanowania.

Istnieje inna metoda, którą osobiście używam w każdym interfejsie graficznym API. Renderowanie co najmniej trójkąta lub kwadratu/kwadratu i użycie modułu cieniującego powoduje, że widoczny staje się tylko pik zaciemniający (oparty na równaniu). Jest bardzo łatwy do zrozumienia. Jest elastyczny i szybki.To wymaga mieszania, ale nie jest tak trudno dostać się do pracy.

Kroki:

Inicjalizuj swoje bufory za pomocą danych. Dla wierzchołków potrzebujesz bufora wierzchołków, bufora indeksu dla indeksów, jeśli używasz kwadratowej geometrii i bufora textCoord dla twoich współrzędnych tekstury. Dla kwadratu zalecam użycie -1.0 jako najniższego i 1,0 jako najwyższego współrzędnego tekstury, ponieważ wtedy możesz użyć równania koła jednostkowego.

W swojej fragment-shader, użyć czegoś takiego:

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

Chociaż (textureCoord.x * textureCoord.x) + (textureCoord.y * textureCoord.y) < = 1,0 jest nierówność, ponieważ potrzebujesz okręgu, musisz renderować każdy piksel w tym zakresie, a nie tylko obramowanie. Możesz to zmienić, aby uzyskać pożądane wyniki.

I to jest to. Niezbyt skomplikowane do wdrożenia, więc nie oferuję tutaj żadnego podstawowego kodu renderującego. Wszystko, czego potrzebujesz, dzieje się w module cieniowania fragmentów.

+0

Jak to zrobić z kwadratową geometrią? Mam wypełniony quad jako GL_TRIANGLE_FAN. Jak uzyskać pozycję x, y w kodzie FragmentShaderCode, aby zastosować odpowiedni kolor za pomocą formuły okręgu? – gregoiregentil