Transparency glitch seeing through objects?

Hi,

I’ve been running into an issue with transparent objects having their transparent zones not taking the color of the object behind them, but rather the viewport’s background color.

Here is a test case.

[java]package temp;

import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.material.RenderState.BlendMode;

import com.jme3.math.ColorRGBA;

import com.jme3.renderer.queue.RenderQueue.Bucket;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Quad;

public class TestTemp extends SimpleApplication

{

public static void main(final String[] args)

{

new TestTemp().start();

}

@Override

public void simpleInitApp()

{

flyCam.setMoveSpeed(10);

viewPort.setBackgroundColor(ColorRGBA.Blue);

final Material mat1 = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

mat1.setColor(“Color”, ColorRGBA.Red);

final Geometry obj1 = new Geometry(“Background”, new Quad(2, 2));

obj1.setLocalTranslation(-1, -1, 0f);

obj1.setMaterial(mat1);

obj1.setQueueBucket(Bucket.Transparent);

rootNode.attachChild(obj1);

final Material mat2 = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);

mat2.setTransparent(true);

mat2.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);

mat2.setColor(“Color”, new ColorRGBA(0, 1, 0, 0.5f));

final Geometry obj2 = new Geometry(“Foreground”, new Quad(1, 1));

obj2.setLocalTranslation(-0.5f, -0.5f, 0.1f);

obj2.setMaterial(mat2);

obj2.setQueueBucket(Bucket.Transparent);

rootNode.attachChild(obj2);

}

}[/java]

As you can see, this simply creates one big red quad, and one smaller half-transparent green quad in front of it.

If you run it, you’ll see what should be expected: the half-transparent green quad merges color with the red quad behind it, giving it a brown tint:



But if you press the W key (or any key to move the camera really), you get this:



The half-transparent green quad is now merging with the blue background color!

It seems to happen whenever the camera is not directly looking at the green quad. Either looking at the green quad directly or moving close to it fixes the problem. But is it possible to make it do the right thing no matter the camera angle?

Thanks.

I’m experiencing a very similar problem. While trying to apply transparent textures to boxes. The bug seems to occur at specific camera distances and angles.

Bump :frowning:

Transparency often has these sorting issues. They have to be drawn back to front or the Z buffer causes the one in the back not to get drawn. This is an extremely common rite of passage for 3D programmers.



My guess as to why it changes as you move around is because JME currently sorts the transparent bucket by distance to the bounding volume. I can’t be sure what the bounding volume for a quad is but if it’s a sphere then you might see the behavior in question… the two objects are very near each other in Z so as it moves off to the side the back one may sort as nearer.

Well, I tried manually putting bounding bounding boxes:



[java]package temp;



import com.jme3.app.SimpleApplication;

import com.jme3.bounding.BoundingBox;

import com.jme3.material.Material;

import com.jme3.material.RenderState.BlendMode;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.renderer.queue.RenderQueue.Bucket;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Quad;



public class Transparency extends SimpleApplication

{

public static void main(final String[] args)

{

new Transparency().start();

}



@Override

public void simpleInitApp()

{

flyCam.setMoveSpeed(10);

viewPort.setBackgroundColor(ColorRGBA.Blue);

final Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

mat1.setColor("Color", ColorRGBA.Red);

final Geometry obj1 = new Geometry("Background", new Quad(2, 2));

obj1.setLocalTranslation(-1, -1, 0f);

obj1.setMaterial(mat1);

obj1.setQueueBucket(Bucket.Transparent);

obj1.setModelBound(new BoundingBox(new Vector3f(-1, -1, -Float.MIN_VALUE), new Vector3f(1, 1, Float.MIN_VALUE)));

rootNode.attachChild(obj1);

final Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

mat2.setTransparent(true);

mat2.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);

mat2.setColor("Color", new ColorRGBA(0, 1, 0, 0.5f));

final Geometry obj2 = new Geometry("Foreground", new Quad(1, 1));

obj2.setLocalTranslation(-0.5f, -0.5f, 0.1f);

obj2.setMaterial(mat2);

obj2.setQueueBucket(Bucket.Transparent);

obj2.setModelBound(new BoundingBox(new Vector3f(-0.5f, -0.5f, -Float.MIN_VALUE), new Vector3f(0.5f, 0.5f, Float.MIN_VALUE)));

rootNode.attachChild(obj2);

}

}[/java]



But the behavior is the same.

It’s still not going to matter. When it moves off to the side, the bigger box in the back is “nearer” to the camera because its corner is closer to the eye.



There is no good way to sort transparent objects that will always work. That’s why I modified RenderQueue to support customized sorting. In my case, my app (Mythruna) knew better how to sort transparent materials.



Actually, if you want yours to sort correctly in this case, then you need to give the smaller quad a bigger bounding box than the big one that it’s in front of. At least on the side that the smaller one is on.

Since my use case is simply for a 2D map (on the x/y plane) with objects on top of it, Z-based comparison is enough. So this is what I ended up using:

[java] viewPort.getQueue().setGeometryComparator(Bucket.Transparent, new TransparentComparator()

{

@Override

public int compare(final Geometry obj1, final Geometry obj2)

{

final float z1 = obj1.getWorldTranslation().z;

final float z2 = obj2.getWorldTranslation().z;

if (z1 == z2) {

return 0;

}

return z1 < z2 ? -1 : 1;

}

});[/java]

And then it worked. Thanks~

1 Like

To anyone who stumbles on this page from a Google search: Doing mat.getAdditionalRenderState().setAlphaTest(true) will help if you are dealing with something more complex than a plain 2D map.

1 Like

In this case, it really shouldn’t. My understanding of alpha test (which also requires some value for alpha falloff (not sure what the default is) is that it sets the threshold for what is considered 0 alpha and discards pixels that are less than the threshold. So, for decals with alpha of 0 or near 0, it will help the background show through since the z-buffer won’t be written for those pixels.



…I wouldn’t have expected it to do anything in your case.

Here is a comparison of the effect of setAlphaTest. (Please ignore the fact that the character is upside-down, lol)

setAlphaTest(true):



And if I comment out “setAlphaTest(true)”:



Basically, setAlphaTest(true) reduces the “buggy rendering area” from being the whole Quad on which the texture is drawn, to a one-pixel-thin line around the sprite. Not perfect, but much better~

Yeah, so it’s an entirely different test than the original plain box. :slight_smile:



In this case, alpha test is pretty critical. You can use the alpha threshold to control what it considers invisible enough to throw away… which would let it get closer to the hard edge of your sprite.



Also, this implies that your brick walls are also in the alpha bucket… since the alpha issues only happen between objects in the alpha bucket.

Indeed, walls are in the transparent bucket as well so that it can be faded out whenever a wall gets between the player and the camera.



Is the alpha test threshold set by “setAlphaFallOff”? I’ll play with it until I get a perfect edge. Thanks!

Yeah… that’s the one.



You might consider leaving your walls in the opaque bucket until you need to fade them. Though I guess if you’ve replaced the transparent comparator with your own then there’s no strong performance reason to do so.

pspeed said:
Yeah... that's the one.

You might consider leaving your walls in the opaque bucket until you need to fade them. Though I guess if you've replaced the transparent comparator with your own then there's no strong performance reason to do so.


I had vast problems with transparency due to this at one point. I haven't worked on my program in a while, but it looks quite a bit similar to yours actually. I ended up going with having everything opaque until the player decided to go behind something that needed to be faded out. It was at that point I smacked it into the transparent bucket and did the fade out. And of course you just do the opposite to reverse the effect when the player is no longer behind it.

There is still the issue of how it looks when it's fading out (you'll probably get some weird sorting issues there), but I hadn't looked at mitigating that issue yet. For the time being I figured that was a bit too much work to look into versus the real impact of the effect.

Also setAlphaFallOff() is awesome for helping with the sorting issues. Again, I'm a bit rusty, but it fixed some issues I was having like 'magic' (tm). Keep in mind it is sorta terrible for feathered/gradient edges. If you do have these, consider hard edges for your sprites if you can. Otherwise.... I dunno. Though I'm sure there's some trickery that can be employed though to get it to work just fine.

Cheers!
~FlaH

Narf, this is freaking me out. -.-



I have a cylinder that should look like the following:







but the problem is when I turn around I get these graphical bugs:







As I readed this has something to do with the drawing-order of the viewPort and that it would be fixed if I reorder the geometry. But now I have this issue in one geometry object.



This is my sample code:



[java]

@Override

public void simpleInitApp()

{

viewPort.setBackgroundColor(ColorRGBA.DarkGray);



cam.setLocation(new Vector3f(-20,20,-20));

cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);

flyCam.setUpVector(Vector3f.UNIT_Y);

flyCam.setMoveSpeed(40);

flyCam.setRotationSpeed(8);



Texture texture = assetManager.loadTexture(“tests/alphaTest/fade.png”);



Material m = new Material(assetManager, “Common/MatDefs/Misc/ColoredTextured.j3md”);

m.setTexture(“m_ColorMap”, texture);

m.getAdditionalRenderState().setBlendMode(BlendMode.AlphaAdditive);

m.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);



Geometry cyl = new Geometry(“cln”);

Cylinder c1 = new Cylinder(2,5,5,10);

cyl.getLocalRotation().fromAngles(-FastMath.PI/2,0,0);

cyl.setMesh(c1);

cyl.setMaterial(m);



rootNode.attachChild(cyl);

}

[/java]



and this is the texture:

If you want two sides transparent geometry to render properly then you need to split them into two separate objects and make sure the inside gets rendered before the outside. If they have the same bounding volume then this “should” just be a matter of making sure the inside is added to the parent before the outside. I’m 90% sure that JME’s sort preserves existing ordering when rendering if the objects are otherwise the same distance from camera.

@pspeed: So in this case you mean I have to build two cylinders?

You can use the same mesh but you will need two geometries.



…that’s just the fact of life with 3D transparency unfortunately.

@pspeed: ah, now this is finally working. : D thanks. Two sided smooth transparency is really a tricky one.



I wonder how other engines like ogre deal with that…