Android – OpenGL ES 2.0: Emulador (Works) – Dispositivo (no)

¡Buen día!

Estoy haciendo algunas pruebas sencillas con OpenGL ES 2.0 para Android. Estoy usando un cargador de modelos que funciona bien en el Emulador. Sin embargo, cuando trato de usarlo en un ASUS ZenFone 2E (Android 5.0.1) (teléfono prepago) simplemente muestra el color claro del fondo sin modelo giratorio. Espero que alguien que tenga experiencia en OpenGL ES 2.0 y Android me ayude. Lo siento por la verbosidad, realmente no tengo ni idea de por qué no está funcionando en el teléfono. Aquí está la fuente (soy un principiante extremo):

GameView.java:

package wise.child.dials; import android.content.Context; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import android.os.SystemClock; import java.io.IOException; import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import model.Model; import render.Program; import render.Shader; import util.OBJLoader; public class GameView extends GLSurfaceView implements GLSurfaceView.Renderer { // App Context private Context mContext; // Handles private int mPositionHandle; private int mColorHandle; private int mMVPMatrixHandle; // Program & Shaders private Program testProgram; private Shader testVertexShader; private Shader testFragmentShader; // Model private Model model; private FloatBuffer vertexFloatBuffer; private int vertexCount; // Matrices private final float[] mMVPMatrix = new float[16]; private final float[] mProjectionMatrix = new float[16]; private final float[] mViewMatrix = new float[16]; private float[] mRotationMatrix = new float[16]; // Constructor public GameView(Context context) { super(context); // App Context mContext = context; // OpenGL ES 2.0 Context setEGLContextClientVersion(2); // Renderer setRenderer(this); } /*-------------------*/ /* Rendering Methods */ /*-------------------*/ // One Time Initialization @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { GLES20.glClearColor(0.95f, 0.95f, 0.95f, 1f); // Initialize Shaders testVertexShader = new Shader(GLES20.GL_VERTEX_SHADER, mContext, R.raw.test_vertex_shader); testFragmentShader = new Shader(GLES20.GL_FRAGMENT_SHADER, mContext, R.raw.test_fragment_shader); // Create Program testProgram = new Program(testVertexShader, testFragmentShader); testProgram.use(); // Get Handles - Uniforms & Attributes mPositionHandle = testProgram.getAttribute("vPosition"); mColorHandle = testProgram.getUniform("vColor"); mMVPMatrixHandle = testProgram.getUniform("uMVPMatrix"); // Model try { model = OBJLoader.loadOBJ(mContext, R.raw.spider); vertexFloatBuffer = model.getVerticesFromIndices(); vertexCount = model.getVertexCount(); } catch (IOException e) { e.printStackTrace(); } } // Drawing Call @Override public void onDrawFrame(GL10 gl) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // Time and Rotation Animation float[] scratch = new float[16]; long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); Matrix.setRotateM(mRotationMatrix, 0, angle, angle, angle, angle); // Set and Bind Data GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexFloatBuffer); // Set Color float[] color = {.75f, 0f, 0f, 1f}; GLES20.glUniform4fv(mColorHandle, 1, color, 0); // Camera Position - View Matrix Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -15, 0f, 0f, 0f, 0f, 1.0f, 0.0f); // Projection x View Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); // Rotation x MVP Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); // Final Matrix GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, scratch, 0); // Draw GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable GLES20.glDisableVertexAttribArray(mPositionHandle); } // GLSurface Changed @Override public void onSurfaceChanged(GL10 gl, int width, int height) { // GL Viewport & Aspect Ratio GLES20.glViewport(0, 0, width, height); float aspectRatio = (float) width / height; // Calculate Projection Matrix.frustumM(mProjectionMatrix, 0, -aspectRatio, aspectRatio, -1, 1, 3, 50); } } 

Shader.java

 package render; import android.content.Context; import android.opengl.GLES20; import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class Shader { // Shader Source Code public String shaderSource; // Shader Handle public int shaderHandle; public int programHandle; public Shader(int type, Context context, int resID) { try { shaderSource = loadShader(context, resID); Log.d("Shader Load", "Success!"); System.out.println(shaderSource); } catch (IOException e) { Log.d("Shader Load", "Failed."); e.printStackTrace(); } shaderHandle = GLES20.glCreateShader(type); GLES20.glShaderSource(shaderHandle, shaderSource); GLES20.glCompileShader(shaderHandle); } // Get From Raw Folder private String loadShader(Context context, int resID) throws IOException { BufferedReader reader = new BufferedReader( new InputStreamReader(context.getResources().openRawResource(resID)) ); String line, shader; StringBuilder builder = new StringBuilder(); while ((line = reader.readLine()) != null) { builder.append(line).append('\n'); } reader.close(); shader = builder.toString(); return shader; } // Associated Program public void setProgram(int handle) { programHandle = handle; } } 

Program.java

 package render; import android.opengl.GLES20; import java.util.ArrayList; import java.util.List; public class Program { public int handle; private Shader vertexShader; private Shader fragmentShader; private List<String> attributes = new ArrayList<String>(); private List<String> uniforms = new ArrayList<String>(); public Program(Shader vertexShader, Shader fragmentShader) { this.vertexShader = vertexShader; this.fragmentShader = fragmentShader; this.vertexShader.setProgram(handle); this.fragmentShader.setProgram(handle); handle = GLES20.glCreateProgram(); GLES20.glAttachShader(handle, vertexShader.shaderHandle); GLES20.glAttachShader(handle, fragmentShader.shaderHandle); GLES20.glLinkProgram(handle); } public void use() { GLES20.glUseProgram(handle); } public int getAttribute(String name) { return GLES20.glGetAttribLocation(handle, name); } public void setAttribute(String name) { } public int getUniform(String name) { return GLES20.glGetUniformLocation(handle, name); } } 

Model.java

 package model; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.List; public class Model { private static final int NUM_OF_COORDS = 3; public List<Vertex> vertices = new ArrayList<Vertex>(); public List<Vertex> normals = new ArrayList<Vertex>(); public List<Face> faces = new ArrayList<Face>(); public Model() {} public int getVertexCount() { return faces.size() * NUM_OF_COORDS; } public FloatBuffer getVerticesFromIndices() { int numOfVertices = 3; int bytesPerFloat = 4; ByteBuffer bb = ByteBuffer.allocateDirect(faces.size() * numOfVertices * NUM_OF_COORDS * bytesPerFloat); bb.order(ByteOrder.nativeOrder()); FloatBuffer vertexFloatBuffer = bb.asFloatBuffer(); // Use indices to find proper vertex for (Face face : faces) { // VERTEX 1 vertexFloatBuffer.put(vertices.get((int) (face.vertex.x - 1)).x); vertexFloatBuffer.put(vertices.get((int) (face.vertex.x - 1)).y); vertexFloatBuffer.put(vertices.get((int)(face.vertex.x - 1)).z); // VERTEX 2 vertexFloatBuffer.put(vertices.get((int)(face.vertex.y - 1)).x); vertexFloatBuffer.put(vertices.get((int)(face.vertex.y - 1)).y); vertexFloatBuffer.put(vertices.get((int)(face.vertex.y - 1)).z); // VERTEX 3 vertexFloatBuffer.put(vertices.get((int)(face.vertex.z - 1)).x); vertexFloatBuffer.put(vertices.get((int)(face.vertex.z - 1)).y); vertexFloatBuffer.put(vertices.get((int)(face.vertex.z - 1)).z); } vertexFloatBuffer.position(0); return vertexFloatBuffer; } public FloatBuffer getNormalsFromIndices() { int numOfVertices = 3; int bytesPerFloat = 4; ByteBuffer bb = ByteBuffer.allocateDirect(faces.size() * numOfVertices * NUM_OF_COORDS * bytesPerFloat); bb.order(ByteOrder.nativeOrder()); FloatBuffer normalFloatBuffer = bb.asFloatBuffer(); // Use indices to find proper normal for (Face face : faces) { // VERTEX 1 normalFloatBuffer.put(normals.get((int) (face.normal.x - 1)).x); normalFloatBuffer.put(normals.get((int) (face.normal.x - 1)).y); normalFloatBuffer.put(normals.get((int)(face.normal.x - 1)).z); // VERTEX 2 normalFloatBuffer.put(normals.get((int)(face.normal.y - 1)).x); normalFloatBuffer.put(normals.get((int)(face.normal.y - 1)).y); normalFloatBuffer.put(normals.get((int)(face.normal.y - 1)).z); // VERTEX 3 normalFloatBuffer.put(normals.get((int)(face.normal.z - 1)).x); normalFloatBuffer.put(normals.get((int)(face.normal.z - 1)).y); normalFloatBuffer.put(normals.get((int)(face.normal.z - 1)).z); } normalFloatBuffer.position(0); return normalFloatBuffer; } } 

OBJLoader.java

 package util; import android.content.Context; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import model.Face; import model.Model; import model.Vertex; public class OBJLoader { /* loads .obj data from file in res/raw folder */ public static Model loadOBJ(Context context, int resID) throws IOException { Model model = new Model(); BufferedReader reader = new BufferedReader( new InputStreamReader(context.getResources().openRawResource(resID)) ); String line; while ((line = reader.readLine()) != null) { if (line.startsWith("v ")) { // Vertex float x = Float.valueOf(line.split(" ")[1]); float y = Float.valueOf(line.split(" ")[2]); float z = Float.valueOf(line.split(" ")[3]); model.vertices.add(new Vertex(x, y, z)); } else if (line.startsWith("vn ")) { // Normal float x = Float.valueOf(line.split(" ")[1]); float y = Float.valueOf(line.split(" ")[2]); float z = Float.valueOf(line.split(" ")[3]); model.normals.add(new Vertex(x, y, z)); } else if (line.startsWith("f ")) { // Face Vertex vertexIndices = new Vertex( Float.valueOf(line.split(" ")[1].split("/")[0]), Float.valueOf(line.split(" ")[2].split("/")[0]), Float.valueOf(line.split(" ")[3].split("/")[0]) ); Vertex normalIndices = new Vertex( Float.valueOf(line.split(" ")[1].split("/")[2]), Float.valueOf(line.split(" ")[2].split("/")[2]), Float.valueOf(line.split(" ")[3].split("/")[2]) ); model.faces.add(new Face(vertexIndices, normalIndices)); } } reader.close(); return model; } } 

MainActivity.java

 package wise.child.dials; import android.app.Activity; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Fullscreen & No Title Bar requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // Set OpenGL ES Drawing Surface (Game View) setContentView(new GameView(this)); } } 

AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="wise.child.dials"> <uses-feature android:glEsVersion="0x00020000" android:required="true" /> <supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" /> <supports-gl-texture android:name="GL_OES_compressed_paletted_texture" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MenuActivity" android:screenOrientation="landscape" android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"> </activity> <activity android:name=".MainActivity" android:screenOrientation="landscape" android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SplashScreen" /> </application> </manifest> 

Sombreado de vértice

 attribute vec4 vPosition; uniform mat4 uMVPMatrix; void main() { gl_Position = uMVPMatrix * vPosition; } 

Shader de fragmentos

 uniform vec4 vColor; void main() { gl_FragColor = vColor; } 

Introduzca aquí la descripción de la imagen

La solución a este problema particular:

Por lo tanto, he añadido esta línea a mi shader fragmento:

precision mediump float;

Para darnos:

Shader de fragmentos

 precision mediump float; uniform vec4 vColor; void main() { gl_FragColor = vColor; } 

¿Por qué esto funcionó, me da vergüenza decir que no lo sé. Si alguien está dispuesto a elaborar más, por favor, soy curioso. Todavía estoy aprendiendo OpenGL ES 2.0. En mi lectura de OpenGL ES 2.0 Guía de programación ( Good Book ), me encontré con esa línea. Y observaron: "En OpenGL ES 2.0, no se puede dibujar nada a menos que se haya cargado un sombreado de vértices y fragmentos válido". El tutorial de Android también incluyó esta línea, así que sé que es crítica.

  • referencia indefinida glBindVertexArrayOES en eclipse
  • GlDepthMask (GL_FALSE) trashes el búfer de cuadros en algunas GPUs
  • ¿Cuál es el parámetro "offset" en GLES20.glVertexAttribPointer / glDrawElements, y de dónde proviene ptr / indices?
  • Optimización de un dibujo VBO enorme en dispositivos Android / iOS
  • La forma correcta de dibujar texto en OpenGL ES 2
  • Mostrar un texto como textura con OpenGL ES 2.0
  • EGL trabajando en Linux pero no en Android
  • OpenGL ES 2.0 Error al asignar correctamente el atributo de color
  • Android.grapics.matrix to OpenGL 2.0 ES traducción de la textura
  • ¿Es posible deformar la imagen subyacente con OpenGL ES 2.0?
  • GLSurfaceView - cómo hacer el fondo translúcido
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.