OpenGL ES 2.0/3.0 中有很多对象时出现 alpha 混合问题

Problem with alpha blending when there are many objects in OpenGL ES 2.0/3.0

游戏 3D 场景有很多对象(背景、小行星、火箭):

private Background background;
private Asteroid[] asteroids = new Asteroid[NUMBER_ASTEROIDS];
private Rocket[] rockets = new Rocket[NUMBER_ROCKETS];
...
public void onDrawFrame(GL10 glUnused) {
    GLES20.glEnable(GLES20.GL_DEPTH_TEST);
    ...
    background.draw();
    for (Asteroid asteroid: asteroids) asteroid.draw(); // draw all asteroids
    for (Rocket rocket: rockets) rocket.draw(); // draw all rockets
    ...
}

小行星和火箭的物体使用 alpha 混合:

public class IceAsteroid extends Object3D implements Asteroid {
    ...
    GLES20.glEnable(GLES20.GL_BLEND);
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
    ... // draw an object with a texture
    GLES20.glDisable(GLES20.GL_BLEND);
    ...
}

public class Rocket extends Object3D {
    ...
    GLES20.glEnable(GLES20.GL_BLEND);
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
    ... // draw an object without a texture
    GLES20.glDisable(GLES20.GL_BLEND);
    ...
}

一般来说,半透明对于 3D 场景效果很好,除了当火箭在小行星后面时它们(火箭)是不可见的。似乎此时小行星的透明度不起作用,虽然小行星后面的背景是可见的。谁能建议为什么在小行星后面看不到火箭?提前致谢!

注意:我试过这样做:

background.draw();
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
for (Asteroid asteroid: asteroids) asteroid.draw(); // draw all asteroids
for (Rocket rocket: rockets) rocket.draw(); // draw all rockets
GLES20.glDisable(GLES20.GL_BLEND);

但这并没有解决问题。

解决方法:根据Rabbid76的建议,我把所有的半透明物体按照从后到前的顺序排列:

Comparator<Object3D> comparatorByZ = (objectA, objectB) -> {
    Float z1 = objectA.getZ();
    Float z2 = objectB.getZ();
    return z1.compareTo(z2);
};
...
background.draw();
Collections.sort(transparentObjects, comparatorByZ);
for (Object3D object3D: transparentObjects) object3D.draw();

对我来说,这就足够了。

你不能两者兼得,Blending and the benefits of the Depth Test. You have to do draw the scene in 2 passes. First draw the opaque objects with depth test on and blending off, then draw the transparent objects with depth test on but no writing to the depth buffer, and blending on, in sorted order from the back to the front. See Transparency Sorting

  1. 启用深度测试并禁用混合
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_TRUE);
GLES20.glDisable(GLES20.GL_BLEND);
  1. 绘制不透明对象

  2. 启用深度测试,禁用写入深度缓冲区并启用混合

GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_FALSE);
GLES20.glEnable(GLES20.GL_BLEND);
  1. 按从后到前的排序顺序绘制透明对象。

如果不对透明物体进行排序,那么与不透明物体相比,透明物体仍然会正确排列,但透明物体本身可能排列不正确。当您使用混合时,顺序很重要。混合功能不是commutative,如果改变覆盖透明对象的顺序,结果是不同的。