OpenGLES,为什么 glReadPixels() 无法从 FBO 的 Renderbuffer 的颜色缓冲区中读取数据?
OpenGLES , why glReadPixels() can't read data from FBO's Renderbuffer's color buffer?
这是完整的代码:
public class FBORenderer2 implements GLSurfaceView.Renderer {
private static final String TAG = "FBORenderer2";
protected FloatBuffer mVerBuffer = ShaderUtils.floatBuffer(-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f);
protected FloatBuffer mTexBuffer = ShaderUtils.floatBuffer(0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f);
private Bitmap mBitmap;
private ByteBuffer mBuffer;
private final int[] fFrame = new int[1];
private final int[] fRender = new int[1];
private final int[] fTexture = new int[3];
protected int mHPosition;
protected int mHCoord;
protected int mHTexture;
protected int mHMatrix;
protected int program;
private Context context;
private float[] mMatrix = {
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
};
private Callback mCallback;
public FBORenderer2(Context context){
this.context = context;
mMatrix = flip(mMatrix,false,true);
}
public static float[] flip(float[] m,boolean x,boolean y){
if(x||y){
Matrix.scaleM(m,0,x?-1:1,y?-1:1,1);
}
return m;
}
public void setBitmap(Bitmap bitmap){
this.mBitmap = bitmap;
}
public void setmCallback(Callback callback){
this.mCallback = callback;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
program = ShaderUtils.createProgram(ShaderUtils.loadFromAssets("base_vertex.vert",context.getResources()),
ShaderUtils.loadFromAssets("gray_fragment.frag",context.getResources()));
GLES30.glEnable(GL_DEPTH_TEST);
mHPosition= GLES30.glGetAttribLocation(program, "vPosition");
mHCoord=GLES30.glGetAttribLocation(program,"vCoord");
mHMatrix=GLES30.glGetUniformLocation(program,"vMatrix");
mHTexture=GLES30.glGetUniformLocation(program,"vTexture");
// Texture source Texture
GLES30.glGenTextures(1, fTexture, 0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fTexture[0]);
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, mBitmap, 0);
// RenderBuffer
GLES30.glGenRenderbuffers(1, fRender, 0);
GLES30.glBindRenderbuffer(GLES30.GL_RENDERBUFFER, fRender[0]);
GLES30.glRenderbufferStorage(GLES30.GL_RENDERBUFFER, GLES30.GL_RGBA8, mBitmap.getWidth(), mBitmap.getHeight());
LogUtils.checkError(TAG,"glRenderbufferStorage");
// FrameBuffer
GLES30.glGenFramebuffers(1, fFrame, 0);
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fFrame[0]);
GLES30.glFramebufferRenderbuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_RENDERBUFFER, fRender[0]);
LogUtils.checkFrameBuffer(TAG,GLES30.glCheckFramebufferStatus(GL_FRAMEBUFFER));
mBuffer = ByteBuffer.allocate(mBitmap.getWidth() * mBitmap.getHeight() * 4);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES30.glViewport(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
}
@Override
public void onDrawFrame(GL10 gl) {
if(mBitmap != null && !mBitmap.isRecycled()){
clear();
GLES30.glBindFramebuffer(GL_FRAMEBUFFER,fFrame[0]);
GLES30.glUseProgram(program);
// set GLSL's variable value
GLES30.glUniformMatrix4fv(mHMatrix,1,false, mMatrix,0);
GLES30.glUniform1i(mHTexture,0);
GLES30.glEnableVertexAttribArray(mHPosition);
GLES30.glVertexAttribPointer(mHPosition,2, GLES30.GL_FLOAT, false, 0,mVerBuffer);
GLES30.glEnableVertexAttribArray(mHCoord);
GLES30.glVertexAttribPointer(mHCoord, 2, GLES30.GL_FLOAT, false, 0, mTexBuffer);
// bind source Texture
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,fTexture[0]);
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP,0,4);
GLES30.glDisableVertexAttribArray(mHPosition);
GLES30.glDisableVertexAttribArray(mHCoord);
GLES30.glReadPixels(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), GLES30.GL_RGBA,
GLES30.GL_UNSIGNED_BYTE, mBuffer);
LogUtils.checkError(TAG,"glReadPixels");
GLES30.glDeleteTextures(1, fTexture, 0);
GLES30.glDeleteRenderbuffers(1, fRender, 0);
GLES30.glDeleteFramebuffers(1, fFrame, 0);
if(mCallback!=null){
mCallback.onCall(mBuffer);
}
mBitmap.recycle();
mBuffer.clear();
}
}
private void clear(){
GLES30.glClearColor(1.0f,1.0f,1.0f,1.0f);
GLES30.glClear(GL_COLOR_BUFFER_BIT|GLES30.GL_DEPTH_BUFFER_BIT);
}
interface Callback{
void onCall(ByteBuffer data);
}
}
我调试得到的mBuffer数据是:
[0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0,
-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0,
0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0,
0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +586,656 more]
如果我在 glReadPixels() 之后使用附加到 FBO 的纹理替换 Renderbuffer,我可以获得这样的数据:
[-10, -10, -10, -1, -10, -10, -10, -1, -10, -10, -10, -1, -10, -10, -10, -1, -10, -10,
-10, -1, -10, -10, -10, -1, -10, -10, -10, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11,
-11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11,
-1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -12, -12, -12, -1, -11, -11,
-11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11,
-11, -11, -1, -11, -11, -11, -1, +586,656 more]
并且可以在ImageView上显示完整的渲染结果。使用 RenderBuffer 只能得到黑色视图。
我想这可能是因为 GLES30.glRenderbufferStorage(GLES30.GL_RENDERBUFFER, GLES30.GL_RGBA8, mBitmap.getWidth(), mBitmap.getHeight())
的 GL_RGBA8 没有被 glReadPixels()
接受,但我不知道如何解决这个问题。
我不确定为什么这不起作用 - 它可能只是您正在使用的设备上的驱动程序错误。
解决方案就是使用纹理。对于所有实际用途,它们与渲染缓冲区相同。
这是完整的代码:
public class FBORenderer2 implements GLSurfaceView.Renderer {
private static final String TAG = "FBORenderer2";
protected FloatBuffer mVerBuffer = ShaderUtils.floatBuffer(-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f);
protected FloatBuffer mTexBuffer = ShaderUtils.floatBuffer(0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f);
private Bitmap mBitmap;
private ByteBuffer mBuffer;
private final int[] fFrame = new int[1];
private final int[] fRender = new int[1];
private final int[] fTexture = new int[3];
protected int mHPosition;
protected int mHCoord;
protected int mHTexture;
protected int mHMatrix;
protected int program;
private Context context;
private float[] mMatrix = {
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
};
private Callback mCallback;
public FBORenderer2(Context context){
this.context = context;
mMatrix = flip(mMatrix,false,true);
}
public static float[] flip(float[] m,boolean x,boolean y){
if(x||y){
Matrix.scaleM(m,0,x?-1:1,y?-1:1,1);
}
return m;
}
public void setBitmap(Bitmap bitmap){
this.mBitmap = bitmap;
}
public void setmCallback(Callback callback){
this.mCallback = callback;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
program = ShaderUtils.createProgram(ShaderUtils.loadFromAssets("base_vertex.vert",context.getResources()),
ShaderUtils.loadFromAssets("gray_fragment.frag",context.getResources()));
GLES30.glEnable(GL_DEPTH_TEST);
mHPosition= GLES30.glGetAttribLocation(program, "vPosition");
mHCoord=GLES30.glGetAttribLocation(program,"vCoord");
mHMatrix=GLES30.glGetUniformLocation(program,"vMatrix");
mHTexture=GLES30.glGetUniformLocation(program,"vTexture");
// Texture source Texture
GLES30.glGenTextures(1, fTexture, 0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fTexture[0]);
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, mBitmap, 0);
// RenderBuffer
GLES30.glGenRenderbuffers(1, fRender, 0);
GLES30.glBindRenderbuffer(GLES30.GL_RENDERBUFFER, fRender[0]);
GLES30.glRenderbufferStorage(GLES30.GL_RENDERBUFFER, GLES30.GL_RGBA8, mBitmap.getWidth(), mBitmap.getHeight());
LogUtils.checkError(TAG,"glRenderbufferStorage");
// FrameBuffer
GLES30.glGenFramebuffers(1, fFrame, 0);
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fFrame[0]);
GLES30.glFramebufferRenderbuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_RENDERBUFFER, fRender[0]);
LogUtils.checkFrameBuffer(TAG,GLES30.glCheckFramebufferStatus(GL_FRAMEBUFFER));
mBuffer = ByteBuffer.allocate(mBitmap.getWidth() * mBitmap.getHeight() * 4);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES30.glViewport(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
}
@Override
public void onDrawFrame(GL10 gl) {
if(mBitmap != null && !mBitmap.isRecycled()){
clear();
GLES30.glBindFramebuffer(GL_FRAMEBUFFER,fFrame[0]);
GLES30.glUseProgram(program);
// set GLSL's variable value
GLES30.glUniformMatrix4fv(mHMatrix,1,false, mMatrix,0);
GLES30.glUniform1i(mHTexture,0);
GLES30.glEnableVertexAttribArray(mHPosition);
GLES30.glVertexAttribPointer(mHPosition,2, GLES30.GL_FLOAT, false, 0,mVerBuffer);
GLES30.glEnableVertexAttribArray(mHCoord);
GLES30.glVertexAttribPointer(mHCoord, 2, GLES30.GL_FLOAT, false, 0, mTexBuffer);
// bind source Texture
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,fTexture[0]);
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP,0,4);
GLES30.glDisableVertexAttribArray(mHPosition);
GLES30.glDisableVertexAttribArray(mHCoord);
GLES30.glReadPixels(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), GLES30.GL_RGBA,
GLES30.GL_UNSIGNED_BYTE, mBuffer);
LogUtils.checkError(TAG,"glReadPixels");
GLES30.glDeleteTextures(1, fTexture, 0);
GLES30.glDeleteRenderbuffers(1, fRender, 0);
GLES30.glDeleteFramebuffers(1, fFrame, 0);
if(mCallback!=null){
mCallback.onCall(mBuffer);
}
mBitmap.recycle();
mBuffer.clear();
}
}
private void clear(){
GLES30.glClearColor(1.0f,1.0f,1.0f,1.0f);
GLES30.glClear(GL_COLOR_BUFFER_BIT|GLES30.GL_DEPTH_BUFFER_BIT);
}
interface Callback{
void onCall(ByteBuffer data);
}
}
我调试得到的mBuffer数据是:
[0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0,
-1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0,
0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0,
0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +586,656 more]
如果我在 glReadPixels() 之后使用附加到 FBO 的纹理替换 Renderbuffer,我可以获得这样的数据:
[-10, -10, -10, -1, -10, -10, -10, -1, -10, -10, -10, -1, -10, -10, -10, -1, -10, -10,
-10, -1, -10, -10, -10, -1, -10, -10, -10, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11,
-11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11,
-1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -12, -12, -12, -1, -11, -11,
-11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11, -11, -11, -1, -11,
-11, -11, -1, -11, -11, -11, -1, +586,656 more]
并且可以在ImageView上显示完整的渲染结果。使用 RenderBuffer 只能得到黑色视图。
我想这可能是因为 GLES30.glRenderbufferStorage(GLES30.GL_RENDERBUFFER, GLES30.GL_RGBA8, mBitmap.getWidth(), mBitmap.getHeight())
的 GL_RGBA8 没有被 glReadPixels()
接受,但我不知道如何解决这个问题。
我不确定为什么这不起作用 - 它可能只是您正在使用的设备上的驱动程序错误。
解决方案就是使用纹理。对于所有实际用途,它们与渲染缓冲区相同。