无法在真实设备上使用OpenGL ES映射纹理

时间:2011-12-21 09:34:29

标签: android opengl-es android-ndk textures

我尝试了一个简单的程序,动态生成纹理并使用NDK将其映射到四边形。在模拟器上一切都很好但在真实设备上失败了。 这是我的代码:

private static class Renderer implements GLSurfaceView.Renderer
{
    @Override
    public void onDrawFrame(GL10 gl)
    {
        nativeDrawFrame();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        nativeInit(width, height);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        // do nothing...
    }
}

和本机代码:

const static GLfloat vertices_f[4][2] = 
{
    { 0.0f,    0.0f },
    { 100.0f,    0.0f },
    { 100.0f,    100.0f },
    { 0.0f,    100.0f }
};

const static GLfloat texCoords_f[4][2] = 
{
    { 0.0f,    0.0f },
    { 1.0f,    0.0f },
    { 1.0f,    1.0f },
    { 0.0f,    1.0f }
};

JNIEXPORT void JNICALL Java_com_sangfor_gltest_GLView_nativeInit(JNIEnv * env, jobject obj, jint width, jint height)
{
    if (!bitmap)
    {
        // allocate dynamic texture memory
        bitmap = memalign(16, 1024*1024);
        if (!bitmap)
        {
            __android_log_print(ANDROID_LOG_ERROR, "native-render", "failed allocation.");
            return;
        }
    }

    glViewport(0, 0, width, height);
    //glMatrixMode(GL_PROJECTION);
    //glLoadIdentity();
    //glOrthox(0, 0x10000, 0, 0x10000, 0x10000, -0x10000);
    //glClearColorx(0, 0, 0, 0);
    glGenTextures(1, &texture);
    __android_log_print(ANDROID_LOG_INFO, "native-render", "texture = %d", texture);
    // glVertexPointer(2, GL_FIXED, 0, vertices);
    // glTexCoordPointer(2, GL_FIXED, 0, texCoords);
    //glEnableClientState(GL_COLOR_ARRAY);
    glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
    glEnable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);
    glDisable(GL_ALPHA_TEST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

JNIEXPORT void JNICALL Java_com_sangfor_gltest_GLView_nativeDrawFrame(JNIEnv * env, jobject obj)
{
    struct timeval tv;
    unsigned char color_value;

    glClear(GL_COLOR_BUFFER_BIT);

    // fill texture according to current timestamp
    gettimeofday(&tv, NULL);
    color_value = (unsigned char)(tv.tv_usec * 0.000255f);
    memset(bitmap, color_value, 1024*1024);
    __android_log_print(ANDROID_LOG_INFO, "native-render", "color_value = %d", color_value);

    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, bitmap);

    glBindTexture(GL_TEXTURE_2D, texture);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);

    glTexCoordPointer(2, GL_FLOAT, 0, texCoords_f);
    glVertexPointer(2, GL_FLOAT, 0, vertices_f);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    glFlush();
}

在模拟器上,它按照我的预期工作:屏幕上的某个区域不断改变其颜色。 但是,当我在真实设备(三星Galaxy S 2.3.3和华硕Transformer TF101 3.2.1)上运行时,它只显示一个白色块,纹理映射似乎无法正常工作。

我尝试添加并注释掉投影变换,通过调用glEnable(GL_TEXTURE_2D)启用纹理映射,通过调用glDisable(...)禁用Alpha混合和测试,将glBindTextureglTexImage2D移至init功能,将纹理大小更改为32 x 32,但这些都不起作用。

任何人都可以帮我弄清楚为什么纹理映射在真实设备上失败了吗?有GPU限制吗?

编辑:我已经尝试过Spoon的建议并找到了真正的问题。无论我绑定哪个纹理,设备都使用名为 0 的纹理来渲染四边形,因此glBindTexture(GL_TEXTURE_2D, 0)工作正常,但glBindTexture(GL_TEXTURE_2D, 1)并且glGenTextures(...)返回的任何内容都会失败。这意味着我只能保存一个纹理,但我必须使用2个或更多。

2 个答案:

答案 0 :(得分:0)

白色块意味着纹理正在工作,但纹理未正确加载。

如果实际的映射是个问题,你会看到纹理,但所有颜色都会变形或混乱。

检查glGenTextures()给出的实际值。我发现在更高版本的android(2.2+)上使用openGL-es 1.x时,glGenTextures()抛弃了一些非常随机的数字,而不是给出0,1,2,3等的id。可能是你必须手动提供您自己的ID,而不是使用glGenTextures()

或者,检查像素数据是否正在从文件中正确加载。如果您的文件名错误或文件丢失,但可能会出现一些允许应用程序继续运行的错误处理,则可能会出现空白的白色纹理。和/或不要忘记刷新res文件夹,甚至在添加新纹理时清理项目

更新:

我就这样做了:

public class Texture{

private int textureId = -1;
// initialise to -1

public int getTextureId(){
    return textureId;
}

public void loadTexture(GL10 gl, Context context){

    String[] filenamesplit = filename.split("\\.");

    name = filenamesplit[filenamesplit.length-2];

    int[] textures = new int[1];
    //Generate one texture pointer...
    //GLES20.glGenTextures(1, textures, 0);             
    textures[0] = ((IridianGraphicsEngine)context).texturecount;
    ((IridianGraphicsEngine)context).texturecount++;

    //...and bind it to our array
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);

    //Create Nearest Filtered Texture
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

    //Different possible texture parameters, e.g. GLES20.GL_CLAMP_TO_EDGE
    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);

    Bitmap bitmap = FileUtil.openBitmap(name, context);

    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

    bitmap.recycle();   

    textureId = textures[0];

}
}

然后在绘图时:

public class Mesh{

private Texture texture;

public void draw(GL10 gl){

    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

    // Pass the vertex buffer in
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0,
                             vertices);

    int textureId = texture.getTextureId();

    if(textureID>=0){

        // Enable Textures
        gl.glEnable(GL10.GL_TEXTURE_2D);

        // Get specific texture.
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);

        // Use UV coordinates.
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        // Pass in texture coordinates          
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureCoordinates);

    } 

    // Pass in vertex normals
    gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);

    gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);

    gl.glDrawElements(GL10.GL_TRIANGLES, numindices,GL10.GL_UNSIGNED_SHORT, indices);

    gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);

    if(textureID>=0){
        // Disable buffers          
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    }

    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

}

然而,这一切都在java中,并且使用了一个奇怪的opengl-es 2.0混合纹理加载和opengl-es 1.1用于绘制

答案 1 :(得分:0)

我将我的JNI本机方法移动到一个单独的包装类,类似于Android NDK中的gl2-jni示例,然后问题解决了。我不知道为什么在包装类中实现静态本机代码会产生差异,但无论如何,它都有效。

<强> GLJNILib.java

// Wrapper for native library
public class GLJNILib {

     static {
         System.loadLibrary("gljni");
     }

    /**
     * @param width the current view width
     * @param height the current view height
     */
     public static native void init(int width, int height);
     public static native void step();
}

<强> GLJNIView.java

// some other stuff

private class Renderer implements GLSurfaceView.Renderer {
    private static final String TAG = "Renderer";
    public void onDrawFrame(GL10 gl) {
        GLJNILib.step();
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLJNILib.init(width, height);
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Do nothing.
    }
}

// some other stuff