问题:在OpenGL ES 2.0/3.0中存在许多对象时的Alpha混合问题。

huangapple go评论74阅读模式
英文:

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(); // 绘制所有小行星
    for (Rocket rocket: rockets) rocket.draw(); // 绘制所有火箭
    ...
}

小行星和火箭的物体使用了透明混合:

public class IceAsteroid extends Object3D implements Asteroid {
    ...
    GLES20.glEnable(GLES20.GL_BLEND);
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
    ... // 绘制带有纹理的物体
    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);
    ... // 绘制没有纹理的物体
    GLES20.glDisable(GLES20.GL_BLEND);
    ...
}
总的来说半透明效果在3D场景中运作良好但是当火箭位于小行星后面时火箭是不可见的似乎在这一刻小行星的透明度不起作用尽管小行星后面的背景是可见的请问有人可以建议为什么火箭在小行星后面不可见吗提前感谢

我尝试过这样做

```java
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(); // 绘制所有小行星
for (Rocket rocket: rockets) rocket.draw(); // 绘制所有火箭
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();

在我的情况下,这就足够了。

英文:

The game 3D-scene has many objects (background, asteroids, rockets):

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
    ...
}

Objects of asteroids and rockets use alpha-blending:

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);
    ...
}

In general, translucency works well for the 3D-scene except that when the rockets are behind the asteroids they (rockets) are not visible. It seems that at this moment the transparency of the asteroids does not work, although the background behind the asteroids is visible. Can anyone please suggest why the rockets are not visible behind the asteroids? Thanks in advance!

Note: I tried to do this:

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);

But this did not solve the problem.

Solution: On the Rabbid76 advice, I sorted all the translucent objects in order from the back to the front:

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

In my case, that was enough.

答案1

得分: 2

你不能同时使用混合深度测试的优势。你需要通过两次绘制场景来实现。首先,使用深度测试开启和混合关闭绘制不透明对象,然后使用深度测试开启但不写入深度缓冲,混合开启,按照从背后到前面的顺序绘制透明对象。请参阅透明度排序

  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. 按照从后到前的顺序绘制排序后的透明对象。

如果透明对象未经排序,则透明对象仍会与不透明对象相比正确排列,但透明对象本身可能未被正确排列。当使用混合时,顺序很重要。混合函数不是可交换的,如果覆盖透明对象的顺序更改,则结果会不同。

英文:

You cannot have both, 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. enable depth test and disable blending
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_TRUE);
GLES20.glDisable(GLES20.GL_BLEND);
  1. draw opaque objects

  2. enable depth test, disable writing to the depth buffer and enable blending

GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_FALSE);
GLES20.glEnable(GLES20.GL_BLEND);
  1. draw transparent objects in sorted order from the back to the front.

If the transparent objects are not sorted, then the transparent objects will still be arranged correctly, in compare to the opaque objects, but the transparent object itself may not be arranged correctly. When you use blending, then the order matters. The blending function is not commutative, the result is different if the order of covering transparent objects is changed.

huangapple
  • 本文由 发表于 2020年5月19日 19:50:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/61890341.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定