【问题标题】:OpenGL: Render-to-texture larger than screen size and glViewportOpenGL:渲染到纹理大于屏幕尺寸和 glViewport
【发布时间】:2016-06-24 17:00:04
【问题描述】:

我已经在这个问题上工作了一段时间,是时候向更大的社区寻求帮助了。我已经阅读了许多关于此主题的其他 StackOverflow 问题,但尚未找到相关的解决方案。

我有一个完善的 Android OpenGL 项目,它先渲染到纹理,然后再将该纹理渲染到屏幕上。这种机制是我的应用程序的基础,我对它有很多历史和信心。我最近添加了新功能,可以在内部截取渲染截图;也就是说,我的应用程序也能够将渲染的纹理保存到文件中。这些图像传统上与显示器的尺寸完全相同。

现在,我想生成大于屏幕尺寸的图像,以便生成的屏幕截图反映更大的图像尺寸,但在屏幕上显示时也会缩放到屏幕尺寸。这应该是一个简单明了的过程,但是,我得到了意想不到的结果。生成的屏幕截图大小正确,但除了屏幕大小的区域外是空的。例如,如果渲染纹理和生成的屏幕截图是屏幕显示大小的 4 倍(X 和 Y 维度的屏幕大小的两倍),则屏幕截图图像文件将是预期大小,但只有左上角图像的象限将被绘制。在此示例中,这里是 resulting generated screenshot。我的视口是 768x887,生成的屏幕截图是正确的 1536x1774,在屏幕截图中,唯一的彩色区域是 768x887。出于我们的目的,我的片段着色器用于渲染到纹理是对屏幕坐标映射的测试...

gl_FragColor = vec4(uv.x, 0.0, uv.y, 1.0);  // during render to texture

请注意,当我们在执行期间将相同的纹理绘制到屏幕上时,整个屏幕的颜色与该着色器一致。为什么只填充了屏幕截图的一个象限,而不是全部?为什么在屏幕上绘制这个纹理时,它只显示屏幕大小的部分,而不是三个空象限的整个东西?

我从GLSurfaceView.Renderer.onSurfaceChanged() 获取视口的原始大小并将其存储到_viewportWidth_viewportHeight。当我创建帧缓冲区纹理时,我通常直接从_viewportWidth_viewportHeight 创建它。现在,我举个例子……

float quality = 2f;
_frameBufferWidth = (int)((float)_viewportWidth * quality);
_frameBufferHeight = (int)((float)_viewportHeight * quality);

...并由_frameBufferHeight生成大小为_frameBufferWidth的帧缓冲区。

我也给glViewport()打了两次电话。在我第一次调用glBindframebuffer() 渲染到纹理而不是屏幕之后,并在进行了相关的错误处理之后,我调用了glViewport(0, 0, _frameBufferWidth, _frameBufferHeight),它没有错误地通过。当我稍后想将此纹理绘制到屏幕上时,我会进行第二次glBindframebuffer() 调用,然后立即调用glViewport(0, 0, _viewportWidth, _viewportHeight)。这个想法是,原始的纹理渲染将进入_frameBufferWidth by _frameBufferHeight 大小的图像,当我们在屏幕上呈现它时,我们想要_viewportWidth by _viewportHeight 大小。

有什么我可能遗漏的想法吗?提前致谢。

编辑(2016 年 3 月 10 日): 我刚刚尝试了quality=0.5f 并得到了不寻常的结果。我更愿意分享更多图片来澄清这种情况,但我是新成员,只允许两个。当我们使用quality=0.5f 绘制屏幕时,屏幕根据上面的 GLSL 代码正确着色:显示与上面链接的屏幕截图的左上象限 768x887 相同(对应于quality=2f)。但是,quality=0.5fscreenshot that is generated 的颜色与屏幕不同。此屏幕截图正确地具有预期的 384x443 大小,但仍被渲染为 768x887 并且仅裁剪出 384x443 部分。

尽管代码另有说明,但似乎我们总是渲染到 _viewportWidth by _viewportHeight 区域,而不是预期的 _frameBufferWidth by _frameBufferHeight 区域。

我基本上都有一个用于两个渲染通道的全屏四边形,并且习惯了这种工作正常。当我渲染到屏幕上时,我对刚刚渲染到的纹理进行采样:

gl_FragColor = texture2D(u_sampler, uv);  // during render to screen

u_sampler 访问我们渲染到的纹理,uv 在两个维度上都位于 [0,1] 中。因此,要让屏幕显示任何内容,它必须进行纹理查找以获取其颜色信息。因此,屏幕上显示的亮红色和蓝色必须最初存在于帧缓冲区中,即使正确大小的屏幕截图中缺少它。

【问题讨论】:

  • 这听起来很合理。你有其他可以测试的设备吗?最好来自不同的供应商,使用不同的 GPU?
  • 嗨,Reto。我刚刚在另一台设备上进行了测试并得到了相同的结果。我从三星 Galaxy Tab A 开始,然后尝试了 LG G2。根据我的 OpenGL 查询,两者都使用 Qualcomm Adreno 渲染器并允许最大纹理大小和视口尺寸为 4096。我相信我从您的另一个 answers 中得到了关于多个 glViewport 调用的想法。您可以提供的任何其他观点都会非常有益!
  • 听上去,你正在做一个 blit 操作(glBlitFramebuffer)。这些一直对我有用。您能否发布您正在使用的确切 GL 调用?
  • 嗨,斯威夫特。我正在使用 OpenGL ES 2.0,它不使用 glBlitFramebuffer。我正在使用 glBindFramebuffer、glFramebufferTexture2D 和 glTexImage2D 的大概简单/直接的组合。我会进一步考虑这一点,并可能会发布更具体的电话。感谢您的建议和帮助。
  • 您的 glViewport 调用似乎不起作用。之后你有没有通过调用 getViewport 来检查它?另请注意,glViewport 不设置帧缓冲区视口,而是设置 GL 状态的一部分。您的操作顺序需要是:绑定屏幕外帧缓冲区 -> 设置放大视口 -> 绘制屏幕外 -> 绑定屏幕帧缓冲区 -> 设置屏幕视口 -> 在屏幕上绘制。是这样吗?

标签: android opengl-es framebuffer fbo


【解决方案1】:

我以前遇到过同样的问题,就像在 iOS 上一样,在 openGL ES 上也是如此。我尝试渲染到 4096*4096 的帧缓冲区,它位于左上角。

我找到的解决方案是添加

glViewport(0, 0, 4096, 4096)

在任何其他功能之前,例如

glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

在主渲染函数中。

我可以稍后渲染它

glViewport(0, 0, view.bounds.width*3, view.bounds.height*3);

因为 glViewport 会将归一化坐标转换为像素坐标。

另外要提的是,在 iOS 上,视图的大小不是以像素为单位,而是以点为单位,所以我需要将它乘以 3。您可以查看在 Android 上是否获得了实际的像素坐标。

【讨论】:

    猜你喜欢
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 2014-08-10
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多