Cómo dibujar círculo básico en OpenGL ES 2.0 Android
Soy nuevo en OpenGL ES 2, y he leído muchos temas sobre cómo dibujar un círculo en OpenGL ES 2 en Android. Basado en dibujo formas y este código se encuentra en gamedev.net , puedo dibujar triángulos y quares, pero todavía no sé cómo dibujar un círculo. Ahora tengo tres maneras de dibujar un círculo:
- Genere vértices en un círculo y use glDrawArray (GL_LINES, …). Dependiendo de cuántos vértices genere, obtendrá un resultado agradable y nítido.
- Usa una textura pregenerada de un círculo (con transparencia alfa) y haz un mapa en un cuadrángulo. Esto resultará en gráficos muy suaves y permitirá un círculo "grueso", pero no será tan flexible: Incluso con mipmapping, usted querrá que su textura sea aproximadamente del mismo tamaño que está representando el quad.
- Utilice un shader de fragmentos.
¿Pero cómo los implemento?
- OpenGL ES 2.0 texture2D sesgo / lod
- Android OpenGL ES 2,0 coordenadas de la pantalla a las coordenadas del mundo
- Solución para escribir el valor del búfer de profundidad / profundidad en OpenGL ES 2.0
- Procesamiento de texto Android OpenGL ES 2.0
- Intermitentes marcos lentos en Android OpenGL ES 2.0
- OpenGL ES profundidad buffer android, no puede llegar a trabajar
- Cómo escribir / prevenir la escritura en el buffer de profundidad OpenGL en GLSL
- Problemas de prueba OpenGL ES2 Alpha
- OpenGL ES 2.0 Error al asignar correctamente el atributo de color
- Android - OpenGL ES 2.0: Emulador (Works) - Dispositivo (no)
- ¿Cuál es el parámetro "offset" en GLES20.glVertexAttribPointer / glDrawElements, y de dónde proviene ptr / indices?
- OpenGL ES 2.0 múltiples programas o múltiples Shaders o qué? ¿Como funciona?
- ¿Por qué glReadPixels da resultados faltados en algunos dispositivos Android más antiguos?
Si desea crear geometría para el círculo, haga algo como esto:
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()
A continuación, puede dibujar utilizando glDrawArrays () como:
- GL_LINE_LOOP (solo contorno) o
- GL_TRIANGLE_FAN (forma llena)
.
// 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);
Definitivamente no recomiendo hacer un círculo a través de la geometría. Tiene dos desventajas principales:
- Es lento. Si desea obtener una precisión aceptable necesita muchos vértices y cualquiera de estos vértices debe procesarse en el sombreado. Para un círculo real necesita tantos vértices como su círculo tiene píxeles.
- No es muy flexible. Tener diferentes círculos, estilo y colring ellos es difícil de dominar.
Hay otro método, que personalmente uso en cada API de gráficos. Renderizar al menos un triángulo o un sqare / quad y utilizar el fragment-shader para hacer sólo visible el pixel (basado en una ecuación). Es muy fácil de entender. Es flexible y rápido. Necesita mezcla, pero esto no es realmente difícil de conseguir para trabajar.
Pasos:
Inicialice sus búferes con datos. Necesitas un buffer de vértices para los vértices, un buffer de índices para los índices si usas una geometría cuadrada y un buffer textureCoord para tus coordenadas de textura. Para un cuadrado recomiendo usar -1.0 como el más bajo y 1.0 como la mayor coordenada de textura, porque entonces usted es capaz de usar la ecuación de círculo unidad.
En tu fragment-shader, usa algo como esto:
if ((textureCoord.x * textureCoord.x) + (textureCoord.y * textureCoord.y) <= 1.0) { // Render colored and desired transparency } else { // Render with 0.0 in alpha channel }
Mientras que (textureCoord.x * textureCoord.x) + (textureCoord.y * textureCoord.y) <= 1.0 es la desigualdad, porque necesitas un círculo, tienes que renderizar cada píxel dentro de ese rango, no solo el borde. Puede cambiar esto para que le dé la salida deseada.
Y eso es todo. No es muy complejo de implementar, así que no ofrezco ningún código de renderización básico aquí. Todo lo que necesitas sucede dentro del fragment-shader.
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); } }
Esta es una versión modificada de la respuesta anterior. También incluye el código para colorear el círculo también. La mayoría de las funciones se utilizan como OpenGL ES1 sin embargo. Cuidado con las convenciones de nomenclatura del baño de clase, LOL. Si necesitas el código de otras clases donde también muestre OpenGL, hazme saber.
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 ie 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); } }
Un fallo importante que noté en el post de la portería: No puedes cambiar la posición del círculo.
Aquí está la solución. Observe el final de las dos primeras líneas en el bucle '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; }
- ¿Cómo funcionan la nueva Barra de herramientas de Android y la Barra de Acción Contextual?
- Agregando prefferencefragment a FragmentPagerAdapter