【问题标题】:Textures in OpenGL ES 2.0 for AndroidOpenGL ES 2.0 for Android 中的纹理
【发布时间】:2011-09-06 00:38:41
【问题描述】:

我是 OpenGL 的新手,我正在通过使用 ES 2.0 为 Android 制作 2D 游戏来自学。我首先创建一个“Sprite”类,该类创建一个平面并为其渲染纹理。为了练习,我有两个在同一个地方交替绘制的 Sprite 对象。我在 ES 1.0 上工作得很好,但现在我已经切换到 2.0,我得到了一个没有错误的黑屏。我很想弄清楚我做错了什么,但我有一种强烈的感觉,这与我的着色器有关。我将在这里转储所有相关代码,希望有人能给我一个答案或一些关于我做错了什么的建议。如果我做错了什么不是很明显,也许有一些关于如何解决的建议?提前感谢您浏览我即将发布的所有代码。

我要发布的三个课程是:
GameRenderer - 我的 GLSurfaceView 的渲染器
着色器 - 创建着色器程序对象
Sprite - 创建一个正方形并在其上绘制纹理
另外,我将发布我的顶点和片段着色器源代码。

我认为相关课程不够相关,无法发布:
游戏活动
GameView - GLSurfaceView
GameLoopThread - 我的主游戏循环
FPSCounter - 每 100 帧输出平均 FPS 到 logcat。

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

}

顶点着色器(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);
}

非常感谢您能抽出时间仔细阅读此内容!希望其他人将来也可以将其用作自己的资源。

【问题讨论】:

  • 当给定错误参数时,OpenGL 显示黑色(或白色)纹理/屏幕是相当常见的...我不确定 Android API 是否通过异常处理此问题,但您应该 总是 在编译和链接着色器时打印信息日志。显然 GLES20.getShaderInfoLog() 和 GLES20.getProgramInfoLog() 确实被包装了,所以尝试调用它们,看看它是否在抱怨着色器中的某些东西。
  • 很好的建议。 getProgramInfo 给了我消息:“无效的顶点着色器。无效的片段着色器。链接无法继续。”所以我假设我的着色器不正确。我会重写它们。
  • 它们看起来还不错。我想知道是否不支持“版本 110”? OpenGL ES 2.0 在这方面可能不向后兼容。
  • 我尝试删除该行,得到相同的结果:/
  • 不客气 :) 周末休息前还有两件事:1) 确保您正确阅读文件,在toString 之后打印出来; 2)我不确定您是否正确处理了顶点属性;无论如何,我建议先尝试一个更简单的着色器,例如gl_Position = ftransform();gl_FragColor = vec(1.0);,然后继续从那里添加功能,直到它中断。祝你好运!

标签: java android opengl-es sprite glsl


【解决方案1】:

一些观察/问题:

  1. 我不知道您是如何更改片段着色器的,但当前发布的版本需要一个精度说明符。只需添加:

    precision mediump float;
    

    到顶部,它应该可以工作。现在关于黑屏这里有一些问题:

  2. 当您将 glClearColor 更改为非黑色并注释掉所有绘图命令时,它仍然看起来是黑色的吗?如果是这样,那么您遇到的问题比纹理更大。

  3. 其次,如果您忽略纹理输出并尝试将每个精灵绘制为没有纹理数据的纯色矩形,您会得到什么?您应该能够在屏幕上看到一些彩色矩形。

  4. 最后,您需要在调用 glDrawElements 之前绑定纹理。 (虽然这在这个例子中应该无关紧要,因为你还没有改变状态。)

【讨论】:

  • 我现在正在尝试这些解决方案,但是我需要两个着色器的精度说明符还是只需要片段着色器?
  • 只是片段着色器。顶点着色器带有默认的精度说明符。如果您从 GL ES 1.0 移植代码,我猜这是您的主要问题。其他两件事只是你应该做的测试,以帮助弄清楚发生了什么,最后一项在这种情况下可能不是问题,尽管它可能会在以后搞砸。
  • 哦!在 OpenGL ES 2 中你不需要它。在纹理上调用绑定会自动设置纹理状态。注释掉那一行。
  • 对于 DrawElements 中的类型,您需要将索引更改为 UNSIGNED_BYTE 或 UNSIGNED_SHORT。见:khronos.org/opengles/sdk/docs/man
  • 唯一显示第二张图片的是我在说什么。绘制精灵时需要重新绑定纹理,否则只会绘制最后一个绑定到 GPU 的纹理。
猜你喜欢
  • 2012-04-08
  • 1970-01-01
  • 2011-05-19
  • 2016-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多