Текстуры в OpenGL ES 2.0 для Android

Я новичок в OpenGL, и я учу себя, создав 2D-игру для Android с ES 2.0. Я начинаю с создания класса «Sprite», который создает плоскость и создает текстуру. Для практики у меня есть два объекта Sprite, которые нарисованы чередующимися в одном и том же месте. Я очень хорошо работал с ES 1.0, но теперь, когда я переключился на 2.0, я получаю черный экран без ошибок . Я измучен, пытаясь понять, что я делаю неправильно, но у меня есть сильное чувство, что это связано с моими шейдерами. Я собираюсь свалить весь соответствующий код здесь, и, надеюсь, кто-то может дать мне ответ или какой-то совет относительно того, что я делаю неправильно. И если не сразу видно, что я делаю неправильно, возможно, некоторые советы о том, как это понять? Заранее спасибо за просмотр кода, который я собираюсь опубликовать.

Три класса, которые я публикую:
GameRenderer – средство рендеринга для моего GLSurfaceView
Shader – создает программный объект шейдера
Sprite – создает квадрат и рисует на нем текстуру
Кроме того, я выложу свой источник вершин и фрагментарных шейдеров.

Связанные классы Я не думал, что они достаточно актуальны для публикации:
GameActivity
GameView – GLSurfaceView
GameLoopThread – моя основная игровая петля
FPSCounter – выводит среднее значение FPS для логарифма каждые 100 кадров.

Класс GameRender:

package com.detour.raw; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.opengl.GLES20; import android.opengl.GLU; import android.opengl.Matrix; import android.opengl.GLSurfaceView; public class GameRenderer implements GLSurfaceView.Renderer{ private static final String LOG_TAG = GameRenderer.class.getSimpleName(); Context mContext; Bitmap bitmap; private float red = 0.0f; private float green = 0.0f; private float blue = 0.0f; Shader shader; FPSCounter fps; Sprite sprite; Sprite sprite2; int x = 0; private float[] mProjMatrix = new float[16]; private float[] mVMatrix = new float[16]; //int[] vertexShader; //int[] fragmentShader; //int program; //String vShaderSource = ""; //String fShaderSource = ""; public GameRenderer(Context context){ mContext = context; //create objects/sprites sprite = new Sprite(mContext); sprite2 = new Sprite(mContext); fps = new FPSCounter(); } @Override public void onDrawFrame(GL10 gl) { GLES20.glClearColor(red, green, blue, 1.0f); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); if(x>3){ x=0; } if(x%2==0){ sprite.draw(gl); }else{ sprite2.draw(gl); } x++; fps.calculate(); //fps.draw(gl); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); float ratio = (float)(width/height); Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 0.5f, 10); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // TODO Auto-generated method stub GLES20.glEnable(GLES20.GL_TEXTURE_2D); GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glClearDepthf(1.0f); GLES20.glDepthFunc(GLES20.GL_LEQUAL); GLES20.glDepthMask(true); GLES20.glEnable(GLES20.GL_CULL_FACE); GLES20.glCullFace(GLES20.GL_BACK); GLES20.glClearColor(red, green, blue, 1.0f); //load sprite/object textures (preferably loop through an array of all sprites). sprite.loadGLTexture(gl, mContext, R.drawable.raw1); sprite2.loadGLTexture(gl, mContext, R.drawable.raw2); Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5.0f, 0.0f, 0f, 0f, 0f, 0.0f, 0.0f); System.gc(); } } 

Шейдерный класс:

 package com.detour.raw; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import android.content.Context; import android.opengl.GLES20; import android.util.Log; public class Shader { public static final String TAG = Shader.class.getSimpleName(); int program; int vertexShader; int fragmentShader; String vShaderSource; String fShaderSource; public Shader(){ //blank constructor //createProgram(); } public Shader(String vs_source, String fs_source){ this.vShaderSource = vs_source; this.fShaderSource = fs_source; createProgram(); } public Shader(int vs_source_id, int fs_source_id, Context context) { StringBuffer vs = new StringBuffer(); StringBuffer fs = new StringBuffer(); try{ InputStream inputStream = context.getResources().openRawResource(vs_source_id); BufferedReader in = new BufferedReader(new InputStreamReader(inputStream)); String read = in.readLine(); while (read != null) { vs.append(read + "\n"); read = in.readLine(); } vs.deleteCharAt(vs.length() - 1); inputStream = context.getResources().openRawResource(fs_source_id); in = new BufferedReader(new InputStreamReader(inputStream)); read = in.readLine(); while (read != null) { fs.append(read + "\n"); read = in.readLine(); } fs.deleteCharAt(fs.length() - 1); }catch (Exception e){ Log.d("ERROR-readingShader", "Could not read shader: " + e.getLocalizedMessage()); } this.vShaderSource = vs.toString(); this.fShaderSource = fs.toString(); createProgram(); } private void createProgram(){ program = GLES20.glCreateProgram(); if(program!=0){ vertexShader = createShader(GLES20.GL_VERTEX_SHADER, vShaderSource); fragmentShader = createShader(GLES20.GL_FRAGMENT_SHADER, fShaderSource); GLES20.glAttachShader(program, vertexShader); GLES20.glAttachShader(program, fragmentShader); GLES20.glLinkProgram(program); }else{ Log.e(TAG, "Couldn't create program."); } } private int createShader(int type, String source){ int shader = GLES20.glCreateShader(type); if(shader!=0){ GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader); } return shader; } public int getProgram(){ return program; } 

Класс спрайтов:

 package com.detour.raw; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLES20; import android.opengl.GLUtils; public class Sprite { //public static final int FRAME_WIDTH = 64; //public static final int FRAME_HEIGHT = 64; private static final String LOG_TAG = Sprite.class.getSimpleName(); Context mContext; Bitmap bitmap; private int textureLoc; private int vertexLoc; private int[] textures = new int[1]; //private int[] pixels; /*private float textureCoordinates[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f};*/ private float vertices[] = { -1.0f, 1.0f,// 0.0f, -1.0f, -1.0f,// 0.0f, 1.0f, -1.0f,// 0.0f, 1.0f, 1.0f// 0.0f }; private short[] indices = { 0, 1, 2, 0, 2, 3}; private FloatBuffer vertexBuffer; //private IntBuffer textureBuffer; private ShortBuffer indexBuffer; Shader shader; int program; String vShaderSource = ""; String fShaderSource = ""; public Sprite(Context context){ mContext = context; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); vertexBuffer = vbb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2); ibb.order(ByteOrder.nativeOrder()); indexBuffer = ibb.asShortBuffer(); indexBuffer.put(indices); indexBuffer.position(0); } public void draw(GL10 gl) { GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_FLOAT, indexBuffer); } public void loadGLTexture(GL10 gl, Context context, int id){ shader = new Shader(R.raw.sprite_vs, R.raw.sprite_fs, mContext); program = shader.getProgram(); GLES20.glUseProgram(program); vertexLoc = GLES20.glGetAttribLocation(program, "a_position"); textureLoc = GLES20.glGetUniformLocation(program, "u_texture"); //texture InputStream is = context.getResources().openRawResource(id); try { bitmap = BitmapFactory.decodeStream(is); } finally { try { is.close(); is = null; } catch (IOException e) { } } //pixels = new int[(bitmap.getWidth()*bitmap.getHeight())]; //bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); /*ByteBuffer byteBuf = ByteBuffer.allocateDirect(pixels.length * 4); byteBuf.order(ByteOrder.nativeOrder()); textureBuffer = byteBuf.asIntBuffer(); textureBuffer.put(pixels); textureBuffer.position(0);*/ GLES20.glDeleteTextures(1, textures, 0); GLES20.glGenTextures(1, textures, 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); GLES20.glUniform1i(textureLoc, 0); GLES20.glEnableVertexAttribArray(vertexLoc); GLES20.glVertexAttribPointer(vertexLoc, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT); //GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, FRAME_WIDTH, FRAME_HEIGHT, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuf);//(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); } } 

Vertex shader (sprite_vs.txt):

 #version 110 attribute vec2 a_position; varying vec2 v_texcoord; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_texcoord = a_position * vec2(0.5) + vec2(0.5); } 

Фрагмент (пиксельный) шейдер (sprite_fs.txt):

 #version 110 uniform sampler2D u_texture; varying vec2 v_texcoord; void main() { gl_FragColor = texture2D(u_texture, v_texcoord); } 

Большое вам спасибо, если вы на самом деле потратили время, чтобы просмотреть это! Надеюсь, кто-то еще может использовать это как ресурс для себя в будущем.

Несколько наблюдений / вопросов:

  1. Я не знаю, как вы изменили шейдер фрагмента, но версия, которая в настоящее время размещена, нуждается в спецификаторе точности. Просто добавь:

     precision mediump float; 

    Наверху, и он должен работать. Теперь о черном экране есть несколько вопросов:

  2. Когда вы меняете glClearColor на что-то не черное и комментируете все команды рисования, все равно выглядит черным? Если это так, то у вас есть большая проблема, чем текстуры.

  3. Во-вторых, если вы проигнорируете выход текстуры и пытаетесь рисовать каждый спрайт как ровный цветной прямоугольник без данных текстуры, что вы получаете? Вы должны иметь возможность увидеть на экране цветной прямоугольник.

  4. Наконец, вам нужно привязать текстуру до вызова glDrawElements. (Хотя это не имеет значения в этом примере, так как вы еще не изменили состояние.)