Android OpenGL 2 滚动纹理随时间推移质量下降

Android OpenGL 2 Scrolling Texture Loses Quality Over Time

我正在尝试使用 OpenGL 2 ES 为 Android 创建一个简单的游戏,该游戏使用背景的重复滚动纹理。我设法绘制纹理,滚动它,然后重复。它在最初几秒钟内效果非常好,然后图像质量开始下降 - 变得非常像素化。大约 10 秒后,滚动图像几乎无法识别。

纹理使用传递给 u_Scroll 的浮点值滚动 automatically/infinitely。

片段着色器:

precision mediump float;

uniform float u_Scroll;
uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;

void main()
{
    gl_FragColor = texture2D(u_TextureUnit, vec2(v_TextureCoordinates.x, v_TextureCoordinates.y + u_Scroll));
}

加载纹理方法

public static int loadBackgroundTexture(Context context, int resourceId) {
    final int [] textureObjectIds = new int[1];
    glGenTextures(1, textureObjectIds, 0);

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inScaled = false;

    final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);

    glBindTexture(GL_TEXTURE_2D, textureObjectIds[0]);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
    bitmap.recycle();

    glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);

    return textureObjectIds[0];
}

来自渲染器的 onDraw:

public void onDrawFrame(GL10 gl) {

    glClear(GL_COLOR_BUFFER_BIT);

    multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
    invertM(invertedViewProjectionMatrix, 0, viewProjectionMatrix, 0);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    positionBackgroundInScene();
    backgroundProgram.useProgram();
    backgroundProgram.setUniforms(modelViewProjectionMatrix, texture[1]);
    background.bindData(backgroundProgram);
    background.draw();

    positionAngelInScene(angelPosition.x, angelPosition.y, angelPosition.z);
    textureProgram.useProgram();
    textureProgram.setUniforms(modelViewProjectionMatrix, texture[0]);
    angel.bindData(textureProgram);
    angel.draw();

}

我对此很陌生,所以我不确定 post 还有哪些其他代码。为什么我的背景纹理一开始很好,但随着时间的推移会退化?不幸的是,我没有足够高的代表 post 截图。

看起来您在滚动时只是 incrementing/decrementing u_Scroll 值,并依靠纹理 GL_REPEAT 行为让您无限滚动。

这里的问题很可能是浮点精度。 medium 浮点值,这是您用于纹理坐标数学的值,在典型实现中只有大约 0.1%(10 位)的相对精度。

为了说明这一点的含义,假设您已经滚动了 10 次,并且您的纹理坐标范围现在介于 10.0 和 11.0 之间。在 0.1% 的相对精度下,您可以在此范围内表示的浮点值之间的增量约为 0.01。或者换句话说,您可以表示 10.0 到 11.0 之间的大约 100 个值。如果你的质地是512x512,你只能用你的纹理坐标定位每 5 个像素。

使用它的一个选项是将浮点精度更改为 highp。但不能保证所有 ES 2.0 实现都支持它。

除非我误解了你在做什么,否则应该有一个非常简单的解决方案:在你的应用程序代码中将你用来设置 u_Scroll 的值范围保持在 0.0 和 1.0 之间。

您的应用程序代码中可能有逻辑上看起来像这样的代码,用于将用户输入转换为滚动:

if (scrollRight) {
    scrollValue += scrollIncrement;
} else if (scrollLeft) {
    scrollValue -= scrollIncrement;
}
...
glUniform1f(scrollLoc, scrollValue);

将此更改为:

if (scrollRight) {
    scrollValue += scrollIncrement;
    if (scrollValue > 1.0f) {
        scrollValue -= 1.0f;
    }
} else if (scrollLeft) {
    scrollValue -= scrollIncrement;
    if (scrollValue < 0.0f) {
        scrollValue += 1.0f;
    }
}
...
glUniform1f(scrollLoc, scrollValue);

这将使 scrollValue 保持在 0.0 和 1.0 之间,这允许您在片段着色器中使用 medium 纹理坐标的完整相对精度。