Using libgdx, I want to discard occluded sprites using a depth buffer. To do so I use the provided Decal and DecalBatch with an OrthographicCamera and I set the z position manually.
Depending my sprite position on the x and y axes, the depth buffer works or not as expected.
red square z = 98 green square z = 10
The square are 50% transparent so I can see if the depth test occurred as expected.
Here the test code:
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.decals.CameraGroupStrategy;
import com.badlogic.gdx.graphics.g3d.decals.Decal;
import com.badlogic.gdx.graphics.g3d.decals.DecalBatch;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import fr.t4c.ui.GdxTest;
public class DecalTest extends GdxTest {
DecalBatch batch;
Array<Decal> decals = new Array<Decal>();
OrthographicCamera camera;
OrthoCamController controller;
FPSLogger logger = new FPSLogger();
Decal redDecal;
Decal greenDecal;
public void create() {
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
//camera.near = 1;
camera.position.set(600, 600, 100);
camera.near = 1;
camera.far = 100;
controller = new OrthoCamController(camera);
Gdx.input.setInputProcessor(controller);
batch = new DecalBatch(new CameraGroupStrategy(camera));
TextureRegion[] textures = {
new TextureRegion(new Texture(Gdx.files.internal("src/test/resources/redsquare.png"))),
new TextureRegion(new Texture(Gdx.files.internal("src/test/resources/greensquare.png")
))};
redDecal = Decal.newDecal(textures[0], true);
redDecal.setPosition(600, 600, 98f);
decals.add(redDecal);
greenDecal = Decal.newDecal(textures[1], true);
greenDecal.setPosition(630, 632f, 10f);
decals.add(greenDecal);
Decal decal = Decal.newDecal(textures[0], true);
decal.setPosition(400, 500, 98f);
decals.add(decal);
decal = Decal.newDecal(textures[1], true);
decal.setPosition(430f, 532f, 10f);
decals.add(decal);
}
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);
Gdx.gl.glDepthFunc(GL20.GL_LEQUAL);
camera.update();
for (int i = 0; i < decals.size; i++) {
Decal decal = decals.get(i);
batch.add(decal);
}
batch.flush();
}
@Override
public void dispose() {
batch.dispose();
}
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.useGL30 = false;
cfg.width = 640;
cfg.height = 480;
cfg.resizable = false;
cfg.foregroundFPS = 0; // Setting to 0 disables foreground fps
// throttling
cfg.backgroundFPS = 0; // Setting to 0 disables background fps
new LwjglApplication(new DecalTest(), cfg);
}
}
It is a depth buffer precision problem, the orientation of the camera that messed up the calcul or something else?
Edit: I expect the sprites being occluded if they are behind another. So in my example the red square should occlude the green part he is in front of. The left bottom squares have the right behaviour but the upper right squares does not. The things is red squares have the same Z value and green squares have the same Z value too(different than the red squares Z of course). So the only things that made the square couples different is their x and y position, which should not impact the depth test.
So, what I want is a consistent depth test behaviour that occluded hided texture as we see whith the bottom left squares no matter their x and y position. According to the comment, I added information about what I expect.
Decal and DecalBatch rely on GroupStrategy for depth sorting, NOT the camera. Additionally, these strategies sort depth EITHER by the distance from the camera OR by only the Z axis, which would required for a perspective camera i.e. a decal that is closer and should occlude as measured by Z, might be further as measured by distance from camera.
i.e. (x,y,z) Camera 0,0,1.
Decal A 1,1,0 (Z distance 1, vector distance 1.73)
Decal B 0,0,-0.1 (Z distance 1.1, vector distance 1.1)
The depth strategy you chose for the above decals could either consider A or B first.
The most common recommended GroupStrategy is CameraGroupStrategy, but this does not sort by Z, but uses camera distance. If you intialise DecalBatch instead with SimpleOrthoGroupStrategy then depth will be sorted purely by Z, here is depth sort for it, you can look at the other group strategies and see its purely absolute distance.
class Comparator implements java.util.Comparator<Decal> {
@Override
public int compare (Decal a, Decal b) {
if (a.getZ() == b.getZ()) return 0;
return a.getZ() - b.getZ() < 0 ? -1 : 1;
}
}