我已经在解决这个问题一段时间了,现在需要向更广泛的社区寻求帮助。我阅读了许多关于此主题的StackOverflow问题,但尚未找到相关的解决方案。
我有一个成熟的Android OpenGL项目,它将渲染到纹理,然后将该纹理渲染到屏幕上。这种机制对我的应用程序非常重要,我对其有很多历史和信心。最近,我添加了新功能来内部截取渲染的屏幕截图;也就是说,我的应用程序能够将渲染的纹理保存为文件。这些图像传统上都是与显示器大小完全相同的。
现在,我想生成比屏幕尺寸更大的图像,以便生成的截图反映更大的图像大小,但在显示在屏幕上时也会缩放到屏幕尺寸。这应该是一个简单而容易的过程,然而我得到了意外的结果。生成的截图是正确的大小,但除了屏幕大小的区域之外,其他地方都是空的。例如,如果渲染的纹理和生成的截图打算是屏幕显示大小的4倍(每个维度X和Y的大小为屏幕大小的两倍),则截图图像文件将是预期的大小,但只有图像的左上象限被绘制了。在这个例子中,这是生成的截图结果。我的视口是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);
我需要生成大小为_frameBufferWidth
乘以_frameBufferHeight
的帧缓冲区。
我调用了两次glViewport()
。在第一次调用glBindframebuffer()
渲染到纹理而不是屏幕后,进行相关错误处理后,我调用glViewport(0, 0, _frameBufferWidth, _frameBufferHeight)
,没有出现错误。当我稍后想要将这个纹理绘制到屏幕上时,我进行了第二个glBindframebuffer()
调用,紧接着立即调用glViewport(0, 0, _viewportWidth, _viewportHeight)
。我的想法是,原始的渲染到纹理正在进入一个_frameBufferWidth
乘以_frameBufferHeight
大小的图像中,当我们在屏幕上呈现它时,我们想要一个_viewportWidth
乘以_viewportHeight
的大小。
有什么想法我可能会错过什么吗?提前感谢。
编辑(2016年3月10日):
我刚尝试了quality=0.5f
并且得到了不寻常的结果。我更愿意分享更多的图像来澄清这种情况,但我是一个新成员,只允许两个。当我们使用quality=0.5f
绘制到屏幕时,根据上面的GLSL代码,屏幕的颜色是正确的:显示与上面链接的截图的768x887左上象限(对应于quality=2f
)完全相同。然而,生成的quality=0.5f
截图,颜色与屏幕不同。这个截图的大小为384x443,但仍然被呈现为768x887,并且只裁剪出了384x443的一部分。
尽管代码表明不同,但似乎我们总是渲染到一个_viewportWidth
乘以_viewportHeight
的区域,而不是预期的_frameBufferWidth
乘以_frameBufferHeight
区域。
我基本上为两个渲染通道都有一个全屏四边形,并且习惯于它可以正常工作。当我渲染到屏幕时,我会对我刚刚渲染的纹理进行采样:
gl_FragColor = texture2D(u_sampler, uv); // during render to screen
访问我们渲染的纹理,在两个维度上都是[0,1]。因此,为了使屏幕显示任何内容,它必须进行纹理查找以获取其颜色信息。因此,屏幕上显示的明亮的红色和蓝色必须最初存在于帧缓冲区中,即使在正确大小的截图中缺失。