5

我很想问一个简洁的问题,可以得到一个明确的答案,但我担心关于 FBO 初始化有太多我不完全理解的小事情需要澄清。我正在编写一个针对 OpenGL 4.3 和 OpenGL ES 3.0 的延迟着色器,前者的行为完全符合我的预期,但后者给了我无法识别来源的问题。

首先,我将描述我对为 GL 4.2 和 ES 3.0 设置 MRT FBO 的理解/困惑,并希望有人能够纠正任何误解。

  1. OpenGL ES 3.0 规范说它支持“四个或更多渲染目标”,但没有提及(我能找到)这些渲染目标的规范。对这些渲染目标的大小有什么安全的假设?我可以简单地假设它可以具有RGBA32F(四个 32 位浮点通道)的内部格式吗?在我看来,这是着色器写入 RT 的关键假设/知识。是否通用程序:尝试创建具有特定规范的 FBO,然后测试 FBO 的完整性?如果失败:减少要求并使用替代着色器来补偿减少的位深度?

  2. 精度限定符据说“有助于 OpenGL ES 的代码可移植性,并且对常规 OpenGL 没有影响”,但我发现很难理解这些highp, mediump, lowp, 到底是做什么用的,以及它们如何与渲染目标。首先,我假设渲染目标的位深度是在 FBO 中确定和配置的,并且精度限定符会自动匹配这个,这让我认为highmedium并且与深度位有low某种关系。我已经看过了,这并不是很清楚。32168OpenGL ES 3.0 specs

  3. FBO 的纹理附件是使用glTexStorage2D(with target=GL_TEXTURE_2D, levels=1) 配置的,我认为在这里使用它比 更正确glTexImage2D,因为只有internalformat应该重要。

  4. COLOR_ATTACHMENT然后使用(3.) 将配置的纹理附加到 FBO glFramebufferTexture2D


它变得奇怪的地方(packHalf2x16/ unpackHalf2x16):

假设我为 FBO 设置了两个颜色附件,第一个 ( RT1) 带有 internalformat GL_RGBA32UI,第二个 ( RT2) 带有GL_RGBA32F. 对象分两次渲染。第一个是 FBO 的 RT,然后是两个由默认帧缓冲区处理的全屏四边形。

为简化起见,我将只关注在两个阶段之间传递 RGB 颜色数据。我试图以三种不同的方式这样做:

  1. [适用于 GL 和 ES]使用RT2, 将颜色数据定期存储为浮点数,将其读取为浮点纹理并将其输出到默认帧缓冲区。

  2. [适用于 GL 和 ES]使用RT1,存储转换为uint(在[0,..,255]每个通道中)的颜色数据,将其作为uint纹理读取,将其转换为浮点数[0,1]并将其输出到默认帧缓冲区。

  3. [仅适用于 GL]使用RT1,将颜色数据打包到一个半通道中,使用packHalf2x16. 将其作为uint纹理读取,然后使用unpackHalf2x16.


不确定代码细节的相关性/重要性(我会迅速跟进任何请求)。我highp同时使用floatint。第一遍的渲染目标定义为:

layout (location = 0) out uvec4 fs_rt1;
layout (location = 1) out vec4 fs_rt2;

在第二遍中,作为纹理访问:

uniform highp usampler2D RT1;
uniform highp sampler2D RT2;
...

// in main():
uvec4 rt1 = texelFetch(RT1, ivec2(gl_FragCoord.xy), 0);
vec4 rt2 = texelFetch(RT2, ivec2(gl_FragCoord.xy), 0);

方法1.

// in first pass:
fs_rt2.rgb = decal.rgb;

// in second pass:
color = vec4(rt2.rgb, 1.0);

方法2.

// in first pass:
fs_rt1.rgb = uvec3(decal.xyz * 256.0f);

// in second pass:
color = vec4(vec3(rt1.xyz)/256.0f, 1);

方法3.

// in first pass:
fs_rt1.x = packHalf2x16(decal.xy);
fs_rt1.y = packHalf2x16(vec2(decal.z, 0.0f));

// in second pass:
vec2 tmp = unpackHalf2x16(rt1.y);
color = vec4(vec3(unpackHalf2x16(rt1.x), tmp.x), 1);

在方法123中,桌面 GL 输出如下所示: 桌面 OpenGL 4.3 输出

在 Nexus 5 上,方法12OpenGL ES 3.0 输出如下所示:

Nexus 5 OpenGL ES 3.0 输出

然而,nexus 5 上的方法3如下所示:

Nexus 5 OpenGL ES 3.0 输出错误

我无法弄清楚为什么第三种方法在 OpenGL ES 3.0 上失败。任何帮助或建议将不胜感激。我并不反对阅读文档,所以如果您只想为我指明正确的方向,那也会有所帮助。

4

1 回答 1

1

对于前几个问题:

  1. 您可以查询GL_MAX_COLOR_ATTACHMENTS以获取可以附加到 FBO 的颜色附件的数量。对于 ES 3.0,这保证 > 4。这与颜色附件的格式无关(无论是渲染缓冲区还是纹理)。但是,您可以渲染的格式存在限制。查看glTexStorage2D的表格,特别是“Color renderable”列。这让您知道可以附加到 FBO 的格式。代码确实需要测试 FBO 的完整性,但不是因为有多个颜色附件。glCheckFramebufferStatus检查所有附件是否具有相同数量的样本和其他供应商特定的内容,例如深度/模板附件。

  2. GLES 中有精确限定符来帮助优化。当您知道您正在处理一定范围内的数字时,一些算术运算在低精度下执行更快或更有效,请参阅GLSL ES 规范的第 4.5.1 节。请注意,这些是最小精度值,即使您请求 lowp,某些供应商也会为您提供 highp。这些精度限定符仅在 GLSL 着色语言中有效。它影响渲染目标的格式。您可能想要做的一些优化,例如对 lowp 中的颜色值进行操作:值从 0.0f 到 1.0f。这允许 gpu 使用更少的能量,因为它使用的是为低精度操作而设计的 ALU,例如PowerVR. 我想说的是,当您的应用程序运行缓慢或作为优化过程时,应该保留使用除 highp 之外的任何东西。你不需要从一开始就担心它。

  3. 使用 glTexStorage2D 和 glTexImage2D 是有区别的。当您使用 glTexStorage2D 时,它会提示驱动程序此纹理格式不会改变。它变得不可变。这意味着驱动程序可以在您使用此纹理时执行优化。总是喜欢 glTexStorage2D。:)

  4. 是的。再次,检查GL_MAX_COLOR_ATTACHMENTS您可以附加到 FBO 的数量。您可以GL_COLOR_ATTACHMENT0使用GL_COLOR_ATTACHMENT0 + maxColourAttachments.

至于您的问题,我在您的代码中看不到任何错误,但是我担心您正在使用的 GPU 驱动程序可能存在错误。uint在某些 GPU 上使用变量之前,我遇到过类似的问题。我建议您在具有不同供应商 GPU 的手机上尝试该应用程序,看看您是否遇到相同的错误。

您也可以尝试通过使用不同的包装方法来避免该问题,请参阅此答案。这不使用uints 来存储信息,而是使用浮动渲染目标,因此可能会绕过错误。

我希望这回答了你的问题。:)

于 2014-07-26T10:39:52.287 回答