SOLVED - Transparent Texture Edge Render Issue - Code and Images


#1

I’m having trouble understanding why the following rendering issue is occurring. Am I setting the texture incorrectly?

When the player sprite is near to the sapling sprite (either in front or behind) then the texture of the ‘ground’ shows through - highlighted by red ovals. When I move the player a tiny fraction of a WU, then the rendering issue goes away. The ground is a quad with an opaque texture.

The code I’ve written to create the flat sprite.

private void addFlatSprite(float width, float height, String name, Vector3f position, Texture texture) {
    
    Quad quad = new Quad(width, height);
    Geometry geometry = new Geometry(name,quad);
    Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    material.setTexture("ColorMap", texture);
    material.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
    material.getAdditionalRenderState().setAlphaTest(true);
    geometry.setQueueBucket(RenderQueue.Bucket.Transparent);
    geometry.setMaterial(material);
    Node node = new Node(name+" Node");
    node.attachChild(geometry);
    geometry.setLocalTranslation(position);
    Quaternion quaternion = new Quaternion();
    quaternion.fromAngleAxis(-FastMath.QUARTER_PI, Vector3f.UNIT_X);
    node.rotate(quaternion);
    
    rootNode.attachChild(node);

    attachCoordinateAxes(geometry.getModelBound().getCenter());    

}

The quaternion rotates the sprites backwards 45 degrees. I’ve tested the sprites without being tilted and the same issue occurs. Any help would be greatly appreciated.


#2

.getAdditionalRenderState().setDepthWrite(false);


#3

@Ogli

Thanks for the suggestion. However it doesn’t solve the issue. When setDepthWrite(false) the rendering system seems unsure which sprite is in front of which.


#4

Okay, I will explain the situation to you and 4 different solutions.
Be prepared for my next post…


#5

So let’s start … + if anything is unclear to you then just ask again …

The situation:
You have two transparent objects which are very close to each other. You did not say which kind of camera you use (ortho or perspective) but I assume it’s a perspective camera.
Now you have a problem: the Z value of both objects (in camera space) is very near. Both the computation of the Z value and the Z buffer resolution are limited in their precision. For the Z buffer on today’s graphics cards it’s 32 bits (or 4 billion possible Z value ranges) which the computer can distinguish. These 4 billion Z ranges are distributed from the camera’s near plane to the camera’s far plane. They are not distributed linearly: Typically the closer you are to the near plane, the finer the resolution of the Z buffer.
Now you have two objects and they start what it’s called a “Z fight” - like you said, the rendering system seems unsure which sprite is in front of which - a flickering is the result.

4 different solutions (the order is not related to which is the “best” solution):


1st idea: use “alpha discard threshold”:
This will only render texels of a texture with an alpha values > threshold. What you want in your case is that only texels with alpha == 1.0 will be rendered. For this you can use the following:

setFloat("AlphaDiscardThreshold",0.9961f);

The value 0.9961f is just a guess - make it smaller or bigger to achieve the right look. Problem with this idea is, that you will lose all half-tranparent pixels (which may look bad).


2nd idea: use an “orthographic (parallel) camera”:
There are two types of camera: orthographic and perspective. The perspective camera renders distant objects smaller than nearby objects. The perspective camera is used to render photorealistic scenes (which have a “3D perspective” in them). The orthographic (or “parallel”) camera renders all objects in the same size (no matter how far) and layers them by Z value. It’s used for UI, blueprints, top-down-view-games, 2D sidescrolling games, etc. and can be used for 2D games in general. To activate the ortho (parallel) camera:

getCamera().setParallelProjection(true);

Note: Will need some playing with the frustum dimensions and view direction until it works as expected.


3rd idea: implement a custom render queue bucket and Z sorting
You could tell the computer exactly which object is closer and command the order in which the objects are being rendered (from most distant object to least distant object). You know, in 2D games we most certainly know which is the most distant object.
Alternatively you could implement a custom comparison. Here is a link to that topic: http://javadoc.jmonkeyengine.org/com/jme3/renderer/queue/RenderQueue.html#setGeometryComparator(com.jme3.renderer.queue.RenderQueue.Bucket,%20com.jme3.renderer.queue.GeometryComparator)
(the GuiComparator is what you need)
Or just make a second guiNode (behind the real guiNode) and just use that.
Also includes 2nd idea (parallel camera).


4th idea “don’t let two objects come too close”:
You could manage the distance between the objects and make sure that they don’t come to close to each other. This is most certainly the worst solution.


5th idea “???”
I had another one when I was outside for a couple of minutes, but I forgot that one. Sorry.


Now, that's it. Hope you can find the right solution from this. Have fun, :chimpanzee_cool:

#6

This might be relevant:

The issue is how things are sorted from back to front. Your best bet is to implement your own comparator since it seems like you might be able to sort on just Z or whatever.


#7

@Ogli and @pspeed

Thank you both for taking the time to reply.

I think I will try to get the Orthographic camera suggestion working. I have tried to use this camera before but I got utterly confused with the ‘playing with the frustum dimensions and view directions’ bit and gave up. I’ll give it another go.


#8

Hm … now that I think about it. I’m not sure if that ortho cam uses the Z buffer too.
@pspeed what do you know about it?

So, the GuiComparator (or a CustomComparator) might be one of 3 solutions, not one of 4. :chimpanzee_nogood:


#9

Ortho may have the same problem.

If you need pure sorting on Y… just use the GuiComparator on that bucket’s GeometryList or whatever.


#10

I’ve been trying to recreate the look of Don’t Starve which I believe is 3D using animated textures with alpha transparency on Quads viewed through a perspective camera.

I can’t replicate the rendering issue (see image) in Don’t Starve so I guess my question is how did the Don’t Starve developers solve this issue?


#11

Ah, I’ve seen this before. The game looks very creepy (e.g. the cartoon-like sea waves).

Could be one of the 3 solutions. Maybe even the 1st solution together with 4x MSAA.
It’s not too hard to use the 3rd solution (GuiComparator).
Did you try the first solution? It’s not that hard to do.

Ehm, but … @pspeed I think I heard that overdraw with same Z-value is not possible in jME 3.0 because LESS_OR_EQUAL as depth compare is missing … don’t remember exactly, but this could be needed too? Or is the logic of the GuiComparator able to deal with that.


#12

@Ogli

Yeah it is a bit creepy. My son got me into it. We play the multiplayer version together. I really like the game.

I’m trying the first solution now… just adding keylisteners so I can play with the AlphaDiscardThreshold property in realtime.


#13

Yes, but there is only one value which will have no artifact - the one that let’s only alpha == 1.0 through and removes all the others. I calculated the 0.9961 by using this formula: 1.0 - (1.0 / 256.0)
Explanation: There are 256 values for the alpha (8 bit) and they are mapped to 1.0 == 255 and 0.0 == 0 and the other values are in between.

Good Luck,


#14

I’ve found that

material.setFloat("AlphaDiscardThreshold", 0.25f);

and AntiAliasing set to x2 gives the desired effect.

Many thanks to both of you again :slight_smile:


#15

Since don’t starve never turns the camera, it would be possible to create a geometry comparator that sorts properly and then wouldn’t require the discard threshold. (Which can work fine but may still have artifacts for any partial transparency.)


#16

@pspeed

I’m sure you’re correct about the geometry comparator being a better solution than the discard threshold and anti-aliasing however, I have no idea where to even begin when it comes to making a geometry comparator.


#17

If your camera is oriented such that sorting on Z is enough (ie: the camera is facing directly down the Z axis) then you can just reuse the existing GuiComparator. (You can see an example of setting a new comparator in the Lemur source code which overrides the comparators.)

Lemur is wrapping the existing ones but hopefully it’s obvious how you’d set your own. The comparator implementations themselves just return -1, 0, or 1 to sort the first item before, equal, or after the second item. (Or vice-versa if I have it backwards.)

You can see from the source of the GuiComparator that it’s pretty straight forward in the axis-aligned case:

…note: never be afraid to look at the source code and poke around.