无法在 OpenGL ES 上写入 GL_RGBA32UI FBO 纹理

Can't write to GL_RGBA32UI FBO texture on OpenGL ES

我有两个 GL_RGBA32UI FBO 纹理,用于存储每个纹素的粒子 positions/velocities 当前状态。

第一次我只填了一次这样的数据:

Gdx.gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL30.GL_RGBA32UI, width, height, 0, GL30.GL_RGBA_INTEGER, GL20.GL_UNSIGNED_INT, buffer);

在每个渲染循环中,第二个是通过着色器写入的,而第一个用作纹理,第二个用作目标。我通过绘制一个从 [-1, -1][1, 1] 的四边形来做到这一点,同时将视口设置在 [0, 0][textureSize, textureSize] 之间。这样,在片段着色器中,每个纹素都有一个着色器 运行。在每个 运行 中,我读取第一个纹理作为输入,更新它并将其写出到第二个纹理。

然后我使用不同的着色器和网格将第二个 FBO 的纹理渲染到屏幕上,其中每个纹素将由网格中的一个顶点表示。这样我就可以从纹理中提取粒子位置并在顶点着色器中相应地设置 gl_Position

之后我切换第一个和第二个 FBO 并继续下一个渲染循环。这意味着这两个 FBO 被用作基于 GPU 的渲染数据存储。

这在桌面应用程序甚至 Android 模拟器中都可以正常工作。但是它在真实 Android 设备上失败了:特定循环的第二个 FBO 纹理仅在真实 Android 设备上更新后始终具有值 [0, 0, 0, 0]。不过,仅从第一个缓冲区渲染数据时,它完全可以正常工作。

有什么想法吗?

我的更新着色器(获取第一个 FBO 的纹理并将其渲染到第二个)如下。

顶点着色器:

#version 300 es

precision mediump float;

in vec2 a_vertex;
out vec2 v_texCoords;

void main()
{
    v_texCoords = a_vertex / 2.0 + 0.5;
    gl_Position = vec4(a_vertex, 0, 1);
}

片段着色器:

#version 300 es

precision mediump float;
precision mediump usampler2D;

uniform usampler2D u_positionTexture;
uniform float u_delta;

in vec2 v_texCoords;
out uvec4 fragColor;

void main()
{
    uvec4 position_raw = texture(u_positionTexture, v_texCoords);

    vec2 position = vec2(
        uintBitsToFloat(position_raw.x),
        uintBitsToFloat(position_raw.y)
    );
    vec2 velocity = vec2(
        uintBitsToFloat(position_raw.z),
        uintBitsToFloat(position_raw.w)
    );

    // Usually I would alter position and velocity vector here and write it back
    // like this:
    // position += (velocity * u_delta);
    //
    // fragColor = uvec4(
    //  floatBitsToUint(position.x),
    //  floatBitsToUint(position.y),
    //  floatBitsToUint(velocity.x),
    //  floatBitsToUint(velocity.y));

    // Even with this the output is 0 on all channels:
    fragColor = uvec4(
        floatBitsToUint(50.0),
        floatBitsToUint(50.0),
        floatBitsToUint(0.0),
        floatBitsToUint(0.0));

    // Even writing the input directly would not make the correct values appear in the texture pixels:
    // fragColor = position_raw;
}

我如何更新纹理(从 fbo1 到 fbo2):

private void updatePositions(float delta) {
  fbo2.begin();
  updateShader.bind();
  Gdx.gl20.glViewport(0, 0, textureSize, textureSize);
  fbo1.getColorBufferTexture().bind(0);
  updateShader.setUniformf("u_delta", delta);
  updateShader.setUniformi("u_positionTexture", 0);
  Gdx.gl20.glDisable(GL20.GL_BLEND);
  Gdx.gl20.glBlendFunc(GL20.GL_ONE, GL20.GL_ZERO);
  updateMesh.render(updateShader, GL20.GL_TRIANGLE_STRIP);
  fbo2.end();
}

如果您正在读取每个组件 32 位的纹理,您需要一个 highp 采样器并且需要将结果存储在一个 highp 变量中。

目前您正在为 usample2D 指定 mediump,默认的 int 精度也是 mediump。对于整数,mediump 被指定为“至少”16 位,因此其中任何一个都可能导致您的 32 位值被截断。

请注意“至少”——一个实现以更高的精度存储它是合法的——所以你可能会发现在某些实现(比如模拟器)上“它恰好可以工作”,因为该实现选择使用更宽的类型。