2013-08-09 141 views
10

Tôi mới ở số OpenGL ES 2 và tôi đã đọc nhiều chủ đề về cách vẽ vòng kết nối trong OpenGL ES 2 trên Android. Dựa trên Drawing Shapesthis code found on gamedev.net, tôi có thể vẽ hình tam giác và hình tam giác, nhưng tôi vẫn không biết cách vẽ hình tròn. Bây giờ tôi có ba cách để vẽ một vòng tròn:Cách vẽ vòng tròn cơ bản trong OpenGL ES 2.0 Android

  1. Tạo các đỉnh theo hình tròn và sử dụng glDrawArray (GL_LINES, ...). Tùy thuộc vào bao nhiêu đỉnh bạn tạo ra này sẽ mang lại một kết quả tốt đẹp và sắc nét.
  2. Sử dụng kết cấu được tạo trước của một vòng tròn (có độ trong suốt alpha) và ánh xạ nó trên hình tứ giác. Điều này sẽ dẫn đến đồ họa rất trơn tru và cho phép một vòng tròn của ´thick, nhưng nó sẽ không được linh hoạt: Ngay cả với mipmapping, bạn sẽ muốn kết cấu của bạn được về kích thước tương tự bạn đang vẽ quad.
  3. Sử dụng trình đổ bóng phân đoạn.

Nhưng làm cách nào để triển khai chúng?

Trả lời

11

Nếu bạn muốn tạo hình cho vòng tròn, làm một cái gì đó như thế này:

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

Sau đó, bạn có thể rút ra sử dụng glDrawArrays() hoặc là:

  • GL_LINE_LOOP (đường viền chỉ) hoặc
  • GL_TRIANGLE_FAN (hình đầy)

.

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

Thank cho mã của bạn nhưng tôi sử dụng OpenGL ES 2, và tôi không thể thực hiện của bạn mã. Làm thế nào để nó trong OPENGL ES 2? –

+3

Tất nhiên điều này làm việc với es es 2. Nếu bạn hiểu mã và bạn không biết làm thế nào để áp dụng nó cho opengl es 2.0, bạn nên tìm một số hướng dẫn cơ bản về 2.0 làm thế nào để render một vbo đơn giản. – Dirk

+0

Đây là mã của tôi để có được các vòng tròn của một vòng tròn: –

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

Không nên là 'gl_Position = uMVPMatrix * vPosition;'? – Flo

+0

ý kiến ​​trong mã là khó hiểu: Đề cập đến "tam giác", nhưng dường như để vẽ một fan hâm mộ tam giác; một chuỗi các tam giác gần đúng một vòng tròn. – ToolmakerSteve

+0

Nó không hoạt động –

0

Một lỗ hổng lớn tôi nhận thấy trong 'post mục tiêu: Bạn không thể thay đổi vị trí của các vòng tròn.

Đây là bản sửa lỗi. Lưu ý kết thúc của hai dòng đầu tiên trong vòng lặp '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

Đây là phiên bản sửa đổi của câu trả lời ở trên. Nó cũng bao gồm mã để tô màu vòng tròn. Hầu hết các chức năng được sử dụng như OpenGL ES1 mặc dù. Hãy nhớ các quy ước đặt tên của nhà vệ sinh lớp học, LOL. Nếu bạn cần mã của các lớp khác, nơi tôi cũng hiển thị OpenGL, hãy cho tôi biết.

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

"trên câu trả lời" có lẽ đề cập đến câu trả lời của dirk, vì đây là xây dựng một danh sách các đỉnh. (không đề cập đến một trong các câu trả lời dựa trên shader, hiện đang "ở trên") – ToolmakerSteve

+0

Làm thế nào để bạn gọi phương thức onDraw cho điều này? Nó nói nó cần GL10. – L1ghtk3ira

+0

Trong OnDrawFrame() của bạn trong Render sử dụng: gl.glClear (GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); và vượt qua gl – L1ghtk3ira

14

Tôi chắc chắn không khuyên bạn nên vẽ vòng tròn thông qua hình học. Nó có hai nhược điểm chính:

  1. Nó chậm. Nếu bạn muốn có độ chính xác chấp nhận được, bạn cần rất nhiều đỉnh và bất kỳ đỉnh nào trong số các đỉnh này cần được xử lý trong trình đổ bóng. Đối với một vòng tròn thực, bạn cần nhiều đỉnh vì vòng tròn của bạn có pixel.
  2. Nó không thực sự linh hoạt. Có các vòng tròn khác nhau, tạo kiểu và làm cho chúng trở nên khó nắm vững.

Có một phương pháp khác mà cá nhân tôi sử dụng trong mọi API đồ họa. Hiển thị ít nhất một hình tam giác hoặc sqare/quad và sử dụng trình đổ bóng phân đoạn để chỉ hiển thị pixel bị bỏ qua (dựa trên phương trình). Nó rất dễ hiểu. Nó rất linh hoạt và nhanh chóng.Nó cần pha trộn, nhưng điều này không thực sự khó để làm việc.

Các bước:

Khởi tạo bộ đệm của bạn bằng dữ liệu. Bạn cần một bộ đệm đỉnh cho các đỉnh, một bộ đệm chỉ mục cho các chỉ mục nếu bạn đang sử dụng một hình vuông và một bộ đệm textureCoord cho các tọa độ kết cấu của bạn. Đối với một hình vuông, tôi khuyên bạn nên sử dụng -1.0 là thấp nhất và 1,0 là tọa độ kết cấu cao nhất, vì sau đó bạn có thể sử dụng phương trình vòng tròn đơn vị.

Trong fragment-shader của bạn, sử dụng một cái gì đó như thế này:

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

Trong khi (textureCoord.x * textureCoord.x) + (textureCoord.y * textureCoord.y) < = 1.0 là sự bất bình đẳng, bởi vì bạn cần một vòng tròn, bạn phải render mọi pixel trong phạm vi đó, không chỉ là đường viền. Bạn có thể thay đổi điều này để nó cung cấp cho bạn đầu ra mong muốn.

Và đó là nó. Không phải rất phức tạp để triển khai, vì vậy tôi không cung cấp bất kỳ mã hiển thị cơ bản nào ở đây. Tất cả những gì bạn cần xảy ra trong phần đổ bóng.

+0

Bạn làm điều đó như thế nào với một hình vuông? Tôi có một quad đầy như một GL_TRIANGLE_FAN. Làm thế nào để có được vị trí x, y trong FragmentShaderCode để áp dụng đúng màu với công thức vòng tròn? – gregoiregentil