英文:
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<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();
In my case, that was enough.
答案1
得分: 2
你不能同时使用混合和深度测试的优势。你需要通过两次绘制场景来实现。首先,使用深度测试开启和混合关闭绘制不透明对象,然后使用深度测试开启但不写入深度缓冲,混合开启,按照从背后到前面的顺序绘制透明对象。请参阅透明度排序。
- 开启深度测试,关闭混合
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_TRUE);
GLES20.glDisable(GLES20.GL_BLEND);
-
绘制不透明对象
-
开启深度测试,关闭深度缓冲写入,开启混合
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_FALSE);
GLES20.glEnable(GLES20.GL_BLEND);
- 按照从后到前的顺序绘制排序后的透明对象。
如果透明对象未经排序,则透明对象仍会与不透明对象相比正确排列,但透明对象本身可能未被正确排列。当使用混合时,顺序很重要。混合函数不是可交换的,如果覆盖透明对象的顺序更改,则结果会不同。
英文:
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.
- enable depth test and disable blending
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(GLES20.GL_TRUE);
GLES20.glDisable(GLES20.GL_BLEND);
-
draw opaque objects
-
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);
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论