How to properly organize zOrder for transparent quads?

I’m applying a sprite from a spritesheet to a quad and I can see that it can be transparent to a background comprised and some other quads created in a similar manner, but sometimes alpha channel seem to stop working when two quads overlap. Here is an example - the sprite I have is a dude and you can see that its bounding quad box interfering with some other “dude” sprites, but not all of them:

1

In the example that I have I’m adding a bunch of quads on top of each other and increment z-component of a new quad’s location to make sure the new quad I’m adding will end up on top of the previously created ones. I do this by creating a float variable I call zOrder and after adding another quad I increment it by some number. I use ortho projection so I tried various deltas for zOrder value that in theory should all work but they don’t - ranging from 0.01f to simply 1f.

A sprite is represented by a quad created in the loop as follows:

Quad b = new Quad(1, 2);
Geometry geom = new Geometry("Quad", b);
Material mat = new Material(assetManager, "MatDefs/Spritesheet/Spritesheet.j3md");
mat.setTexture("ColorMap", assetManager.loadTexture("Textures/spritesheet-4x4.png"));
// specify the number of columns and rows
mat.setFloat("SizeX", 4f); mat.setFloat("SizeY", 4f); mat.setFloat("Position", 2f);
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
geom.setMaterial(mat);

// add z order value to make sprites appear on top of each other
zOrder += 0.01f;
geom.setLocalTranslation(4 - 8 * random.nextFloat(), 4 - 8 * random.nextFloat(), zOrder);

geom.setQueueBucket(RenderQueue.Bucket.Transparent);
rootNode.attachChild(geom);

Edit: I tried some fixed values for zOrder thinking I’m using too many values so I tried “layers”, e.g. an array of fixed zOrder values I’m choosing from for each quad but it too didn’t work and I saw artifacts of wrong alpha channel handling for some quads.

The geometry in the transparent bucket are sorted back to front by distance from camera… there is no perfect solution because “distance from camera” can mean many different things even for quads.

Probably what you really want in this case is to set the alpha discard threshold so that transparent pixels aren’t even rendered.

If you’re sprites are truly orthogonal then you can also put them in the guiNode instead which sorts strictly based on z value.

2 Likes

Thanks for the quick reply! I came across some comment that I couldn’t find anymore saying that guiNode may not be very performant. Is there any performance-specific concern related to using gui node or it should work fine for rendering sprites? Oh and I don’t care about custom lighting I guess it if gui node should be OK to use from the performance standpoint, I guess it adds one more pros for using it…

The only real difference between the rootNode and the guiNode is that the guiNode is in the Gui bucket… which means it sorts things in a different order and forces a strict orthogonal mode.

I don’t know anything about any performance implications. There shouldn’t be any.

1 Like

Thanks again!

After more playing I found that the behavior I described in my original post seem to depend on the value of zOrder-increment I’m choosing. I can see artifacts if z increments are in the order of 0.1f in my app, i.e. I can clearly see one quad disappearing when the other is overlapping it however these artifacts seem to disappear completely if I increase increments to be strictly 1f.

Strange, the big values of z-increment have the same strange effect as small ones and I start seeing artifacts when z-increments are 10f. I wonder if camera settings could have something to do with this? Or could it be a graphics card issue? I’ll try my example on another computer when I have a chance.

In the guiNode? Surely not. guiNode is sorted by ‘z’… strictly.

Regular root node is sorted by (roughly) point nearest the camera… which will vary quite a bit depending on where they are on screen, etc…

Set alphaDiscardThreshold to 0.1 on your materials. Or put things in the guiNode.

…or both.

1 Like

No, I haven’t tried guiNode yet as I need to rework my app to be able to use it properly.
I’ll also look more into setting alphaDiscardThreshold.