Opengl - 将纹理转换为数组

时间:2016-01-27 19:28:56

标签: android opengl-es

在我的Android应用程序中,我有一个帧缓冲对象,它将渲染的场景作为纹理。 该应用程序是一个折纸游戏,用户可以自由折叠纸张:

enter image description here

enter image description here

在每个折叠中,当前渲染的场景使用fbo保存到纹理,然后我用新坐标重新绘制纸张,并附加新纹理,看起来像折叠纸。这样,用户可以根据需要多次折叠纸张。

我想在每个帧中检查渲染的场景,确定用户是否富有最终形状(假设我在2d阵列中的最终形状为0和1填充,0表示透明度,1表示彩色像素)

我想要的是一些如何,将此纹理转换为填充0和1的2d数组, 0表示透明像素,1表示纹理的彩色像素。 我需要这个然后将这个结果与之前已知的2d-Array进行比较,以确定纹理是否是我想要的形状。

是否可以将纹理数据保存到数组中?

我不能使用glreadPixels,因为它太重了,不可能每帧调用它。

这里是FBO类(我希望将renderTex [0]作为数组):

public class FBO {
int [] fb, renderTex;
int texW; 
int texH;    
public FBO(int width,int height){
    texW = width; 
    texH = height;
    fb = new int[1];
    renderTex= new int[1];
}
public void setup(GL10 gl){
    // generate
    ((GL11ExtensionPack)gl).glGenFramebuffersOES(1, fb, 0); 
    gl.glEnable(GL10.GL_TEXTURE_2D);
    gl.glGenTextures(1, renderTex, 0);// generate texture
    gl.glBindTexture(GL10.GL_TEXTURE_2D, renderTex[0]);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D,
            GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D,
            GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
            GL10.GL_CLAMP_TO_EDGE); 
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
            GL10.GL_CLAMP_TO_EDGE);
    //texBuffer = ByteBuffer.allocateDirect(buf.length*4).order(ByteOrder.nativeOrder()).asIntBuffer();
    //gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,GL10.GL_MODULATE);
    gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, texW, texH, 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, null);
    gl.glDisable(GL10.GL_TEXTURE_2D); 
}  


public boolean RenderStart(GL10 gl){
    Log.d("TextureAndFBO", ""+renderTex[0] + " And " +fb[0]);
    // Bind the framebuffer
    ((GL11ExtensionPack)gl).glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, fb[0]);

    // specify texture as color attachment
    ((GL11ExtensionPack)gl).glFramebufferTexture2DOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, GL10.GL_TEXTURE_2D, renderTex[0], 0);

    int error = gl.glGetError();
    if (error != GL10.GL_NO_ERROR) {
        Log.d("err", "FIRST Background Load GLError: " + error+"      ");
    }
    int status = ((GL11ExtensionPack)gl).glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES);
    if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES)
    {
        Log.d("err", "SECOND Background Load GLError: " + status+"      ");;
        return true;
    }
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
    return true;
} 

public void RenderEnd(GL10 gl){
    ((GL11ExtensionPack)gl).glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);

    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

    gl.glEnable(GL10.GL_TEXTURE_2D);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
    gl.glColor4f(1.0f,1.0f,1.0f,1.0f);
    gl.glDisable(GL10.GL_TEXTURE_2D);
}

public int getTexture(){
    return renderTex[0];
}
public int getFBO(){
    return fb[0];
}


}

2 个答案:

答案 0 :(得分:3)

如果您使用的是openGL ES 3.0及更高版本,那么pbo将是一个很好的解决方案。但我认为你可以使用EGLImage。因为这只需要OpenGL ES 1.1或2.0。

创建EGLImageKHR的功能是:

EGLImageKHR eglCreateImageKHR(EGLDisplay dpy,
                          EGLContext ctx,
                          EGLenum target,
                          EGLClientBuffer buffer,
                          const EGLint *attrib_list)

要分配一个ANativeWindowBuffer,Android有一个名为GraphicBuffer的简单包装器:

    GraphicBuffer *window = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_HW_TEXTURE);
struct ANativeWindowBuffer *buffer = window->getNativeBuffer();
EGLImageKHR *image = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, *attribs);

从FBO读取像素使用以下两种方法之一:

void EGLImageTargetTexture2DOES(enum target, eglImageOES image)

void EGLImageTargetRenderbufferStorageOES(enum target, eglImageOES image)

这两种方法将建立目标GL_TEXTURE_2D或GL_RENDERBUFFER的所有属性

uint8_t *ptr;
glBindTexture(GL_TEXTURE_2D, texture_id);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);

window->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &ptr);
memcpy(pixels, ptr, width * height * 4);
window->unlock();

答案 1 :(得分:2)

要完成您想要的任务,您需要使用PBO (Pixel Buffer Object):如果它是常规数组,您可以将其映射到数组以进行读取。

  

OpenGL ARB_pixel_buffer_object扩展非常接近   ARB_vertex_buffer_object。它只是扩展ARB_vertex_buffer_object   扩展,以便不仅存储顶点数据,还存储像素数据   进入缓冲对象。该缓冲对象存储像素数据   称为像素缓冲对象(PBO)。 ARB_pixel_buffer_object扩展名   借用所有VBO框架和API,另外还增加了2个" target"   令牌。这些令牌可以帮助PBO内存管理器(OpenGL驱动程序)   确定缓冲对象的最佳位置;系统记忆   共享内存或视频内存。此外,目标令牌明确指定   绑定的PBO将用于两种不同的操作之一;   GL_PIXEL_PACK_BUFFER_ARB将像素数据传输到PBO,或   GL_PIXEL_UNPACK_BUFFER_ARB用于从PBO传输像素数据。

可以像其他缓冲区对象一样创建它:

glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, size, 0, GL_DYNAMIC_READ);

然后您可以轻松地从FBO(或纹理)中读取:

glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
GLubyte *array = (GLubyte*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, size, GL_MAP_READ_BIT);
// TODO: Do your checking of the shape inside of this 'array' pointer or copy it somewhere using memcpy()
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

此处GL_COLOR_ATTACHMENT0用作输入 - 请参阅glReadBuffer的规范,以获取有关如何指定要使用的前置缓冲区或后置缓冲区的更多详细信息。