【发布时间】:2011-08-28 10:01:57
【问题描述】:
我有一个开源 iOS 应用程序,它使用自定义 OpenGL ES 2.0 着色器来显示分子结构的 3-D 表示。它通过使用在矩形上绘制的程序生成的球体和圆柱体冒名顶替来实现这一点,而不是使用大量顶点构建的相同形状。这种方法的缺点是这些冒名顶替对象的每个片段的深度值需要在片段着色器中计算,以便在对象重叠时使用。
不幸的是,OpenGL ES 2.0 does not let you write to gl_FragDepth,所以我需要将这些值输出到自定义深度纹理。我使用帧缓冲区对象 (FBO) 对场景进行传递,仅渲染出与深度值相对应的颜色,并将结果存储到纹理中。然后将此纹理加载到我的渲染过程的后半部分,生成实际的屏幕图像。如果该阶段的片段处于屏幕上该点的深度纹理中存储的深度级别,则会显示该片段。如果没有,它就被扔掉了。有关该过程的更多信息,包括图表,可以在我的帖子here 中找到。
这种深度纹理的生成是我渲染过程中的一个瓶颈,我正在寻找一种方法让它更快。它似乎比它应该的要慢,但我不知道为什么。为了正确生成此深度纹理,GL_DEPTH_TEST 被禁用,GL_BLEND 使用glBlendFunc(GL_ONE, GL_ONE) 启用,glBlendEquation() 设置为GL_MIN_EXT。我知道以这种方式输出的场景在 iOS 设备中的 PowerVR 系列等基于图块的延迟渲染器上并不是最快的,但我想不出更好的方法来做到这一点。
我的球体深度片段着色器(最常见的显示元素)看起来是这个瓶颈的核心(仪器中的渲染器利用率固定为 99%,这表明我受到片段处理的限制)。它目前如下所示:
precision mediump float;
varying mediump vec2 impostorSpaceCoordinate;
varying mediump float normalizedDepth;
varying mediump float adjustedSphereRadius;
const vec3 stepValues = vec3(2.0, 1.0, 0.0);
const float scaleDownFactor = 1.0 / 255.0;
void main()
{
float distanceFromCenter = length(impostorSpaceCoordinate);
if (distanceFromCenter > 1.0)
{
gl_FragColor = vec4(1.0);
}
else
{
float calculatedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter);
mediump float currentDepthValue = normalizedDepth - adjustedSphereRadius * calculatedDepth;
// Inlined color encoding for the depth values
float ceiledValue = ceil(currentDepthValue * 765.0);
vec3 intDepthValue = (vec3(ceiledValue) * scaleDownFactor) - stepValues;
gl_FragColor = vec4(intDepthValue, 1.0);
}
}
在 iPad 1 上,使用直通着色器渲染 DNA 空间填充模型的帧以进行显示需要 35 到 68 毫秒(在 iPhone 4 上为 18 到 35 毫秒)。根据 PowerVR PVRUniSCo 编译器(their SDK 的一部分),此着色器最多使用 11 个 GPU 周期,最差使用 16 个周期。我知道建议您不要在着色器中使用分支,但在这种情况下,这会带来更好的性能。
当我把它简化为
precision mediump float;
varying mediump vec2 impostorSpaceCoordinate;
varying mediump float normalizedDepth;
varying mediump float adjustedSphereRadius;
void main()
{
gl_FragColor = vec4(adjustedSphereRadius * normalizedDepth * (impostorSpaceCoordinate + 1.0) / 2.0, normalizedDepth, 1.0);
}
在 iPad 1 上需要 18 - 35 毫秒,但在 iPhone 4 上只需要 1.7 - 2.4 毫秒。此着色器的估计 GPU 周期数为 8 个周期。基于循环计数的渲染时间变化似乎不是线性的。
最后,如果我只是输出一个恒定的颜色:
precision mediump float;
void main()
{
gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0);
}
iPad 1 上的渲染时间降至 1.1 - 2.3 毫秒(iPhone 4 上为 1.3 毫秒)。
渲染时间的非线性缩放以及 iPad 和 iPhone 4 之间第二个着色器的突然变化让我觉得我在这里缺少一些东西。如果您想自己尝试一下,可以从here 下载包含这三个着色器变体的完整源项目(查看 SphereDepth.fsh 文件并注释掉相应的部分)和测试模型。
如果您已经阅读到这里,我的问题是:基于此分析信息,我如何提高我的自定义深度着色器在 iOS 设备上的渲染性能?
【问题讨论】:
-
有关于着色器条件的帖子。您必须避免着色器中的条件
-
@Joe - 整体上很难进行基准测试,因为它比上述设备快得多,所以无论如何它都会以 60 FPS 的速度渲染测试模型。我扔给它的任何模型都很少遇到问题,因此我将精力集中在速度较慢的设备上。
-
我在阅读这个问答时学到了很多东西。感谢您跟进并彻底了解代码和图表。 (赞成)
标签: iphone ios ipad opengl-es glsl