在 Android 平台上,OpenGL ES 2.0 中的纹理是什么?

7
我是一个新手OpenGL开发者,正在使用ES 2.0为Android制作2D游戏。我首先创建了一个“Sprite”类,它创建了一个平面并将纹理渲染到其上。为了练习,我创建了两个Sprite对象,交替地绘制在同一位置。在ES 1.0中,我已经成功地完成了这项工作,但现在我切换到ES 2.0后,出现了黑屏无错误的情况。我已经尝试过很多方法来找出问题所在,但我强烈感觉这与我的着色器有关。我将在此处放置所有相关代码,希望有人能给我一个答案或一些关于如何解决问题的建议。如果看不出我做错了什么,也许可以提供一些寻找问题的建议?非常感谢您事先浏览我即将发布的所有代码。
我要发布的三个类是:
GameRenderer - 我的GLSurfaceView渲染器
Shader - 创建一个着色器程序对象
Sprite - 创建一个正方形并在其上绘制纹理
同时,我还会发布我的顶点和片段着色器源代码。
我认为不需要发布的相关类包括:
GameActivity
GameView - 一个GLSurfaceView
GameLoopThread - 我的主要游戏循环
FPSCounter - 每100帧将平均FPS输出到logcat。
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;
}

Sprite类:

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

}

顶点着色器(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
当OpenGL给出错误的参数时,显示黑色(或白色)纹理/屏幕是相当常见的...我不确定Android API是否通过异常处理此问题,但编译和链接着色器时应始终打印信息日志。显然,GLES20.getShaderInfoLog()和GLES20.getProgramInfoLog()确实被包装了,因此尝试调用它们并查看是否在着色器中有任何问题。 - UncleZeiv
很好的建议。getProgramInfo 给了我这个信息:“无效的顶点着色器。无效的片段着色器。链接无法继续。”所以我猜我的着色器不对。我会重新编写它们。 - Amplify91
我尝试只删除那一行,但结果还是一样的 :/ - Amplify91
@UncleZeiv 是的,在Android上用ES2.0,我必须重写与1.0相同的方法。你只需要忽略GL10并使用GLES20静态方法。谢谢你继续尝试帮助我! - Amplify91
3
周末休息前还有两件事情:1)确保你正在正确阅读文件,在toString之后打印它们; 2)我不确定你是否正确处理了顶点属性;无论如何,我建议先尝试一个更简单的着色器,例如 gl_Position = ftransform();gl_FragColor = vec(1.0);,然后逐步添加功能,直到出现错误。祝你好运! - UncleZeiv
显示剩余2条评论
1个回答

4
一些观察/问题:
  1. I don't know how you changed the fragment shader, but the version that is currently posted needs a precision specifier. Just add:

    precision mediump float;
    

    to the top, and it should work. Now regarding the black screen here are some questions:

  2. When you change the glClearColor to something not black and comment out all the draw commands, does it still look black? If so, then you have a bigger problem than textures.

  3. Second, if you ignore the texture output and try drawing each sprite as just a flat colored rectangle with no texture data, what do you get? You should be able to see some colored rectangle on the screen.

  4. Finally, you need to bind the texture before you call glDrawElements. (Though this shouldn't matter in this example since you haven't changed the state yet.)


1
只需要片段着色器。顶点着色器带有默认的精度限定符。如果您正在从GL ES 1.0移植代码,我猜这可能是您的主要问题。另外两个测试只是为了帮助弄清楚发生了什么,最后一个项目在这种情况下可能不是问题,但以后可能会出现问题。 - Mikola
1
哦!在OpenGL ES 2中不需要那个。调用纹理绑定会自动设置纹理状态。请将该行注释掉。 - Mikola
1
而对于您的DrawElements中的类型,您需要将索引更改为UNSIGNED_BYTE或UNSIGNED_SHORT。请参见:http://www.khronos.org/opengles/sdk/docs/man/ - Mikola
1
只展示第二张图像是我所说的。在绘制精灵时需要重新绑定纹理,否则它将只绘制上次绑定到 GPU 上的纹理。 - Mikola
我所做的就是在我的draw()方法中,在glDrawElements之前添加了glBindTexture。太棒了 :) 再次感谢您的所有帮助!!! - Amplify91
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接