与packHalf2x16/unpackHalf2x16的OpenGL 4和ES 3.0差异

swa*_*log 5 c++ opengl opengl-es glsl glsles

我非常希望提出一个简洁的问题,允许一个明确的答案,但我担心有太多的小问题,我不完全理解我需要清理的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配置,而且精度预选赛自动匹配这一点,这让我觉得high,mediumlow具有某种关系的32,16,8深度位.我已经看过了OpenGL ES 3.0 specs,对此并不是很清楚.

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

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


它变得奇怪(packHalf2x16/ unpackHalf2x16):

假设我使用两个颜色附件设置FBO,第一个(RT1)使用内部格式,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;
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

方法1.:

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

// in second pass:
color = vec4(rt2.rgb, 1.0);
Run Code Online (Sandbox Code Playgroud)

方法2.:

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

// in second pass:
color = vec4(vec3(rt1.xyz)/256.0f, 1);
Run Code Online (Sandbox Code Playgroud)

方法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);
Run Code Online (Sandbox Code Playgroud)

在方法12,和3,桌面GL输出如下所示: 桌面OpenGL 4.3输出

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

Nexus 5 OpenGL ES 3.0输出

3关于nexus 5的方法看起来像这样:

Nexus 5 OpenGL ES 3.0输出不良

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

ash*_*gpu 1

对于前几个问题:

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

  2. GLES 中的精度限定符可帮助优化。当您知道正在处理特定范围内的数字时,某些算术运算在低精度下会更快或更有效,请参阅GLSL ES 规范的第 4.5.1 节。请注意,这些是最小精度值,即使您要求低精度,某些供应商也会为您提供高精度。这些精度限定符仅在 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 来存储信息,而是使用浮动渲染目标,因此可以绕过错误。

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