OpenGL ES 2.0/3.0多对象场景Alpha混合异常:火箭被遮挡问题
解决3D场景半透明对象遮挡显示异常问题
最近我在开发一款包含3D场景的游戏,场景里有背景、小行星和火箭三类对象,其中小行星和火箭都用了Alpha混合来实现半透明效果。
初始实现代码
先给大家看看最初的对象定义和绘制逻辑:
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(); // 绘制所有火箭 ... }
小行星和火箭的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); ... // 绘制带纹理的对象 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); ... }
遇到的异常问题
场景整体的半透明效果看起来没毛病,但出现了一个奇怪的现象:当火箭处于小行星后方时,火箭完全看不到,可小行星后方的背景却能正常显示。我试过全局开启混合,问题还是没解决。
最终解决方案
后来按照Rabbid76的建议,把所有半透明对象按Z轴从后到前排序后再绘制,问题直接就解决了!具体实现是把小行星和火箭统一放到半透明对象集合里,用Z轴坐标做排序依据:
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();
核心原理其实很直白:半透明对象的绘制顺序会直接影响混合结果,深度测试会挡住后方对象,但半透明需要让后方颜色透过来,所以必须先绘制最远的半透明对象,再依次绘制近的,这样混合计算才会正确。
内容的提问来源于stack exchange,提问作者alexrnov
相关产品推荐
相关产品推荐

