2015-05-04 117 views
1

我想在Android上使用OpenGL ES 2.0实现动态环境反射。OpenGL ES 2.0:如何以低分辨率渲染frameBuffer以进行环境映射?

为此,我将相机放置在反射对象的位置,并在6个不同的方向(每个轴两个)渲染到屏幕外渲染缓冲区以构建立方体贴图,但速度很慢,所以我的想法是使立方体地图具有较低的分辨率,以加快速度。我认为这应该很简单,但我不明白我的意见。

我想看看这6个渲染的结果来检查结果是否如预期的那样,因此我在将它们导出到磁盘之前将它们导出为png文件,然后再渲染下一个。我使用了1024×256的framebuffer渲染一次。但是,当我查看导出的文件时,我可以看到256.png仅包含较大文件的一小部分内容。我期待他们拥有相同的内容(如果你喜欢的话),但不同的分辨率(“更大的像素”),但这不是发生了什么。

我有静态常量REFLECTION_TEX_WIDTH和REFLECTION_TEX_HEIGHT来设置所创建的纹理和渲染缓冲区的宽度和高度,我用这些常量来创建和导出。但是导出的文件从来没有像我期望的那样覆盖太多的区域。当我将这些尺寸设置得非常大时,例如每个尺寸为2000,渲染区域似乎覆盖大约1080x1550像素,文件的其余部分保持黑色。有人能告诉我这里发生了什么事吗?

我不确定问题出在我的理解framebuffer如何工作或如果渲染是正确的,但问题是在我的文件导出引入...这些文件导出方法是从互联网上复制我不太了解他们。

我想呈现相同的面积/ FOV,但在较粗糙的分辨率。那是过分的要求?

然后一些代码。 初始化:

// create 6 textures for the dynamic environment reflection 
final int skyboxFaces=6; 
final int[] textureId=new int[1]; 
GLES20.glGenTextures(1, textureId, 0); 
skyboxTexture=textureId[0]; 
ShaderFactory.checkGLError("initRendering::createSkyboxTextures"); 

GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, skyboxTexture); 
GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); 
GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); 
GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); 
GLES20.glTexParameteri(GLES20.GL_TEXTURE_CUBE_MAP, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); 
for(int i=0; i < skyboxFaces; i++) 
{ 
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GLES20.GL_RGBA, 
    REFLECTION_TEX_WIDTH, REFLECTION_TEX_HEIGHT, 0, GLES20.GL_RGBA, 
    GLES20.GL_UNSIGNED_BYTE, null); 
    ShaderFactory.checkGLError("initRendering::setSkyboxTexture(" + i + ")"); 
} 
// create renderbuffer and bind 16-bit depth buffer 
renderBuffers=new int[1]; 
GLES20.glGenRenderbuffers(1, renderBuffers, 0); 
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBuffers[0]); 
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, 
    REFLECTION_TEX_WIDTH, REFLECTION_TEX_HEIGHT); 
ShaderFactory.checkGLError("initRendering::createRenderbuffer"); 

frameBuffers=new int[1]; 
GLES20.glGenFramebuffers(1, frameBuffers, 0); 
// GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, 
// GLES20.GL_RENDERBUFFER, frameBuffers[0]); 
ShaderFactory.checkGLError("initRendering::createFrameBuffer"); 

然后在renderloop我做了以下内容:

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffers[0]); 
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBuffers[0]); 
// assign the cubemap texture to the framebuffer 
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, 
    GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + bufferId, skyboxTexture, 0); 
// assign the depth renderbuffer to the framebuffer 
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, 
    GLES20.GL_RENDERBUFFER, frameBuffers[0]); 
// clear the current framebuffer (color and depth) 
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 

并导出结果的文件,我这样做:

public void savePNG(final int x, final int y, final int w, final int h, final String name) 
{ 
    final Bitmap bmp=savePixels(x, y, w, h); 
    try 
    { 
    final File file=new File(Environment.getExternalStoragePublicDirectory(
     Environment.DIRECTORY_PICTURES), name); 
    final File parent=file.getParentFile(); 
    // create parent directories if necessary 
    if(null != parent && !parent.isDirectory()) 
     parent.mkdirs(); 
    // delete existing file to avoid mixing old data with new 
    if(file.exists()) 
     file.delete(); 

    final FileOutputStream fos=new FileOutputStream(file); 
    bmp.compress(CompressFormat.PNG, 100, fos); 
    fos.flush(); 
    fos.close(); 
    context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file))); 
    } 
    catch(final FileNotFoundException e) 
    { 
    // TODO Auto-generated catch block 
    LOG.error("problem " + e); 
    } 
    catch(final IOException e) 
    { 
    // TODO Auto-generated catch block 
    LOG.error("problem " + e); 
    } 
} 

// TODO: magic imported code 
public Bitmap savePixels(final int x, final int y, final int w, final int h) 
{ 
    final int b[]=new int[w * (y + h)]; 
    final int bt[]=new int[w * h]; 
    final IntBuffer ib=IntBuffer.wrap(b); 
    ib.position(0); 
    GLES20.glReadPixels(x, 0, w, y + h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib); 

    for(int i=0, k=0; i < h; i++, k++) 
    { 
    // OpenGL bitmap is incompatible with Android bitmap and needs some correction. 
    for(int j=0; j < w; j++) 
    { 
     final int pix=b[i * w + j]; 
     final int pb=(pix >> 16) & 0xff; 
     final int pr=(pix << 16) & 0x00ff0000; 
     final int pix1=(pix & 0xff00ff00) | pr | pb; 
     bt[(h - k - 1) * w + j]=pix1; 
    } 
    } 

    final Bitmap sb=Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888); 
    return sb; 
} 
+0

想到它,观察到的尺寸可能等于我手机的屏幕尺寸。起初由于比例不同,我并没有意识到这一点,但高度通常会因为动作条等而降低,宽度似乎与我的Nexus 5与1920x1080屏幕相匹配。 – peedee

回答

2

它看起来像你在提交给FBO之前缺少设置视口。在FBO渲染设置,添加以下调用:

glViewport(0, 0, REFLECTION_TEX_WIDTH, REFLECTION_TEX_HEIGHT); 

可以围绕其放置在那里你有glClear()。在完成FBO渲染并且渲染到默认帧缓冲区之前,不要忘记将其设置回默认帧缓冲区的大小。

视口大小是全局状态,默认为默认帧缓冲区的初始大小。因此,无论何时您使用与默认绘制表面大小不同的FBO,都需要相应地设置视口。

+0

完全正确,谢谢。 – peedee