Android – OpenGL ES 2.0: эмулятор (работает) – устройство (не)

Добрый день!

Я делаю несколько простых тестов с OpenGL ES 2.0 для Android. Я использую модельный загрузчик, который хорошо работает в эмуляторе. Однако, когда я пытаюсь использовать его на ASUS ZenFone 2E (Android 5.0.1) (Prepaid Phone), он просто показывает чистый цвет фона без вращающейся модели. Я надеюсь, что тот, кто хорошо знаком с OpenGL ES 2.0 и Android, поможет мне. Извините за многословие, я действительно не знаю, почему он не работает по телефону. Вот источник (я крайний новичок):

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> 

Vertex Shader

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

Фрагментный шейдер

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

Введите описание изображения здесь

Solutions Collecting From Web of "Android – OpenGL ES 2.0: эмулятор (работает) – устройство (не)"

Решение этого конкретного вопроса:

Итак, я добавил эту строку в свой шейдер фрагмента:

precision mediump float;

Дать нам:

Фрагментный шейдер

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

Почему это сработало, мне стыдно сказать, что я не знаю. Если кто-то хочет подробно остановиться, пожалуйста, мне любопытно. Я все еще изучаю OpenGL ES 2.0. В моем чтении OpenGL ES 2.0 Programming Guide ( Good Book ) я столкнулся с этой линией. И они отметили: «В OpenGL ES 2.0 ничего нельзя сделать, если не загружены действительные вершинные и флеш-шейдеры». В учебнике Android также была включена эта строка, поэтому я знаю, что это очень важно.