OpenGL ES 2.0:如何以较低的分辨率渲染到frameBuffer以进行环境映射?

时间:2022-09-10 23:05:41

I'm trying to implement dynamic environment reflections on Android using OpenGL ES 2.0.

我正在尝试使用OpenGL ES 2.0在Android上实现动态环境反射。

For this I set my camera in the place of my reflective object and render to an off-screen renderbuffer in 6 different directions (two per axis) to build a cube map, but that's very slow, so my idea is to make the cube map have a lower resolution in order to speed things up. I thought this should be simple but I don't understand my observations.

为此,我将相机设置在我的反射物体的位置,并在6个不同的方向(每个轴两个)渲染到离屏渲染缓冲区以构建立方体贴图,但这非常慢,所以我的想法是制作立方体贴图分辨率较低,以加快速度。我认为这应该很简单,但我不明白我的观察。

I want to see the results of those 6 renders to check if the results are as expected, so I export them to disk as png files before rendering the next one. I rendered once with framebuffer of 1024x1024 and once with 256x256. However, when I look at the exported files, I can see that the 256.png only contains a fraction of the content of the bigger one. I was expecting them to have the same content (field of view if you like) but different resolutions ("bigger pixels"), but that's not what happens.

我想看到这6个渲染的结果来检查结果是否符合预期,所以我在渲染下一个之前将它们作为png文件导出到磁盘。我使用1024x1024的帧缓冲区渲染一次,使用256x256渲染一次。但是,当我查看导出的文件时,我可以看到256.png只包含较大内容的一小部分内容。我期待他们拥有相同的内容(如果你喜欢的话,视野),但不同的分辨率(“更大的像素”),但这不是发生的事情。

I have static constants REFLECTION_TEX_WIDTH and REFLECTION_TEX_HEIGHT to set the width and height of the created textures and renderbuffers, and I use those constants for both creation and exporting. But the exported files never cover as much area as I expect. When I set those dimensions really large, like 2000 each, the rendered area seems to cover about 1080x1550 pixel, the rest of the file remains black. Can someone tell me what's going on here?

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

I'm not sure if the problem is in my understanding of how the framebuffer works or if the rendering is correct but the problem is introduced in my file export... those file export methods are copied from the internet, I don't really understand them.

我不确定问题是我的理解帧缓冲如何工作或者渲染是否正确但问题是在我的文件导出中引入的......那些文件导出方法是从互联网上复制的,我不是真的了解他们。

I want to render the same area/FOV but in a coarser resolution. Is that too much to ask?

我想呈现相同的区域/ FOV但是以更粗糙的分辨率。那是过分的要求?

Some code then. Initializations:

然后是一些代码。初始化:

// 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");

Then in the renderloop I do the following:

然后在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);

And to export the results as files I do this:

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

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

1 个解决方案

#1


It looks like you're missing to set the viewport before rendering to the FBO. During the setup of FBO rendering, add this call:

在渲染到FBO之前,您似乎缺少设置视口。在设置FBO渲染期间,添加此调用:

glViewport(0, 0, REFLECTION_TEX_WIDTH, REFLECTION_TEX_HEIGHT);

You can place it around where you have the glClear(). Don't forget to set it back to the size of the default framebuffer after you're done with the FBO rendering, and before rendering to the default framebuffer.

你可以把它放在你有glClear()的地方。在完成FBO渲染之后,在渲染到默认帧缓冲区之前,不要忘记将其设置回默认帧缓冲区的大小。

The viewport size is global state, and defaults to the initial size of the default framebuffer. So anytime you use an FBO with a size different from the default draw surface, you need to set the viewport accordingly.

视口大小是全局状态,默认为默认帧缓冲区的初始大小。因此,只要您使用尺寸与默认绘图表面不同的FBO,就需要相应地设置视口。

#1


It looks like you're missing to set the viewport before rendering to the FBO. During the setup of FBO rendering, add this call:

在渲染到FBO之前,您似乎缺少设置视口。在设置FBO渲染期间,添加此调用:

glViewport(0, 0, REFLECTION_TEX_WIDTH, REFLECTION_TEX_HEIGHT);

You can place it around where you have the glClear(). Don't forget to set it back to the size of the default framebuffer after you're done with the FBO rendering, and before rendering to the default framebuffer.

你可以把它放在你有glClear()的地方。在完成FBO渲染之后,在渲染到默认帧缓冲区之前,不要忘记将其设置回默认帧缓冲区的大小。

The viewport size is global state, and defaults to the initial size of the default framebuffer. So anytime you use an FBO with a size different from the default draw surface, you need to set the viewport accordingly.

视口大小是全局状态,默认为默认帧缓冲区的初始大小。因此,只要您使用尺寸与默认绘图表面不同的FBO,就需要相应地设置视口。