OpenGL:渲染到纹理大于屏幕大小和glViewport

时间:2016-03-10 02:28:34

标签: android opengl-es framebuffer fbo

我已经在这个问题上工作了一段时间,现在是时候向更大的社区寻求帮助了。我已经阅读了关于这个主题的许多其他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);

...并按_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 screenshot that is generated的颜色与屏幕不同。这个截图正确地具有预期的384x443大小,但仍然被渲染为好像它的768x887并且刚刚裁剪出384x443部分。

即使代码另有说明,似乎我们总是在_viewportWidth区域内呈现_viewportHeight,而不是_frameBufferWidth_frameBufferHeight区域。

对于两个渲染过程,我基本上都有一个全屏四边形,并且习惯了那个正常工作。当我渲染到屏幕时,我将刚渲染的纹理采样到:

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

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

1 个答案:

答案 0 :(得分: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上获得了实际的像素坐标。