作者:omar-a-rodrigue
下载
借助 OpenGL* ES 2.0 实现动态分辨率渲染 [PDF 677KB]
代码样本: dynamic-resolution.zip [ZIP 4MB]
像素处理成本昂贵
当在游戏和显卡工作负载上执行性能分析时,似乎处理片段或(像素)着色器是主要的性能瓶颈。 当然这也在情理之中,因为照明计算、纹理采样和后期处理效果等计算均在片段着色器中执行。 计算屏幕上每个像素的最终颜色需要大量处理能力和时间,且可能会非常昂贵。 此外,每次新发布的移动平台均采用更高的分辨率,从而提高了总体成本。 支持高分辨率需要增加片段着色器的调用次数。 但是,高分辨率并非移动开发人员的唯一问题。 如何确定分辨率不同的设备也是一个问题。 在写这篇文章时,我对市场上的一些设备做的快速调查显示分辨率的差异非常大 — 即使是运行相同操作系统的设备。
- Apple iPhone* 5: 1136 x 640,PowerVR* SGX543MP3
- Apple iPhone 4S: 960 x 640,PowerVR SGX543MP2
- Nexus* 4,1280 x 768, Adreno* 320
- Galaxy Nexus, 1280 x 720, PowerVR SGX540
- Motorola RAZR* i, 960 x 540, PowerVR SGX540
- Samsung Galaxy SIII, 1280 x 720, Adreno 225
不仅要清楚地找到不断提高的分辨率还要找到不同的分辨率是游戏开发人员正在面临或即将面临的问题。 另一个问题是,即使显卡硬件不断改进,它也会不可避免地因为处理更多像素而被消耗掉。
渲染游戏画面的几种方法
有许多合理且业经验证的方式来处理游戏中的各种分辨率。 最简单的方式是将场景绘制为原始分辨率。 在某些类型的游戏中,您可能会一直需要用到这种方法。 或者片段着色器可能能力不够强大而成为性能的瓶颈。 如果您遇到这种情况,多半会受到局限,但是仍然需要确保游戏开发资源(art asset)能够在各种重要分辨率中使用。
第二种方法是确定固定分辨率而非原始分辨率。 这种方法支持您将游戏开发资源(art asset)和着色器调整为固定分辨率。 但是,它可能无法为用户提供最佳的游戏体验。
另一种常见的方法是让用户在开始时设置所需的分辨率。 这种方法可以使用玩家选择的分辨率生成后台缓冲,并能有效解决分辨率各异的问题。 它支持玩家选择其设备上最适合的分辨率。 此外,您还需要确认游戏开发资源(art asset)是否能够在用户可选的分辨率中使用。
本文中介绍的第三种方法名为动态分辨率渲染。 这是单机游戏和高级电脑游戏上的一种常用技术。 本文中介绍的实施情况是从 [Binks 2011] 中的 DirectX* 版本中获取,并用于配合 OpenGL* ES 2.0 使用。 借助动态分辨率渲染,后台缓冲是原始分辨率的尺寸,但是场景使用了固定分辨率绘制为屏幕外纹理。 如图 1 所示,画面被绘制为屏幕外纹理的一部分,然后被采样填充后台缓冲。 UI 元素在原始分辨率下绘制。
图 1. 动态分辨率渲染
绘制为屏幕外纹理
第一步是创建屏幕外纹理。 在 OpenGL ES 2.0 中,使用所需的纹理尺寸创建 GL_FRAMEBUFFER。 下列是完成此操作的代码:
01 |
glGenFramebuffers(1, &(_render_target->frame_buffer)); |
02 |
glGenTextures(1, &(_render_target->texture)); |
03 |
glGenRenderbuffers(1, &(_render_target->depth_buffer)); |
05 |
_render_target->width = width; |
06 |
_render_target->height = height; |
08 |
glBindTexture(GL_TEXTURE_2D, _render_target->texture); |
09 |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
10 |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
11 |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
12 |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
13 |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _render_target->width, _render_target->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); |
15 |
glBindRenderbuffer(GL_RENDERBUFFER, _render_target->depth_buffer); |
16 |
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _render_target->width, _render_target->height); |