Multiple Overlapping Transparent Objects

Hi,

I’m trying to render several sprites. My current model involves hexagons, which means a square .png with a hexagon in it. If I place these where they overlap (as is necessary to make a tesselated game board), I get some nasty rendering glitches:

Here is the sprite:

sprite!

Aaand, here is the related code:


pic.setTexture(/*  Complicated function calls that get the texture from a manager I wrote */);
        pic.setQueueBucket(RenderQueue.Bucket.Transparent);

        picMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        picMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
        picMat.getAdditionalRenderState().setDepthWrite(false);

Any idea if this can be fixed? Thanks.

When you include images, you have to click through to the actual image and can’t just include the landing page.

For transparent objects, you probably want to set the alpha discard threshold on the material. Else you will run into the 20+ year old classic “z-buffer alpha transparency” issues that have been discussed a million times. (So I won’t repeat the long form here.)

pSpeed, Is there a good thread somewhere where I can learn to do this? I spent a good chunk of time searching, but was unable to find a thread that tells me how to do this. My guess is that it’s been discussed so many times that everyone but me knows exactly how to fix it, and can just use shorthand. xD

EDIT: I did try this:

picMat.getAdditionalRenderState().setAlphaFallOff(0.5f);
picMat.getAdditionalRenderState().setAlphaTest(true);

for various alpha fallof values, both high and low, to no effect whatsoever.

Well, I haven’t even seen what the effect is so I’m only guessing… but that should have worked.

Or you can look at the material docs and find alphaDiscardThreshold. But it basically is doing the same thing.

@ulfgur said: Hi,

I’m trying to render several sprites. My current model involves hexagons, which means a square .png with a hexagon in it. If I place these where they overlap (as is necessary to make a tesselated game board), I get some nasty rendering glitches:

Rendering problems! Whee!

Here is the sprite:

Sprite!

Aaand, here is the related code:


pic.setTexture(/*  Complicated function calls that get the texture from a manager I wrote */);
        pic.setQueueBucket(RenderQueue.Bucket.Transparent);

        picMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        picMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
        picMat.getAdditionalRenderState().setDepthWrite(false);

Any idea if this can be fixed? Thanks.

Lot’s of relevant things we can’t see in the above. We can’t see you setting the material to the picture (so maybe you don’t). We can’t see you setting the image to the picture material (so maybe you don’t).

We can’t assume these things because assumptions are what landed you where you are in the first place. Incorrect “assumption” is the cornerstone of all issues here.

@ulfgur said: Aaand, here is the related code:

related code, or code you think is related, but it is not?

what if your complicated manager somehow destroys png transparency in a first place? because it looks like there is “transparency” filled with a black color.
try to add skybox into the scene, i think you then will see there that your hexagons doesnt have alpha at all so they are squares, because even when you set transparency parameters correctly, there is no surviving transparency left in the loaded version of png after you loaded it somehow.

your narrow selection of code does not show anything.

edit: you should use j3md’s and j3o’s because you can then also visualize them in scene composer and “click and set” values you see available there. i for example do create ParticleEmitters there and never comes into transparency issues.
you can then save one complete hexagon as a j3o file and then load it through assetManager and immediately attach it into the scene, because it is fully setted up. try to use things you have available instead of doing complicated things yourself (i learned this after 5 yrs of programming).

Sorry for the wait, I’ve been trying to clean up some of the code so that you guys can see what’s going on, and I fixed the above images… I hope.

I gave the world a background, and the transparency is definitely there. It just seems to choose to render transparent pixels instead of non-transparent ones.

public class SpriteControl extends AbstractControl
{
    private SimpleApplication app;
    
    private Picture pic;
    
    private Material picMat;
    
    private HashMap<String, Animation> animations;
    private String currentAnim;
    
    private boolean finLast = true;
    private String nextAnim;
    
    
    /**
     * control for one of my sprites, given a set of animations.
     * @param app
     * @param ani 
     */
    public SpriteControl(SimpleApplication app, HashMap<String, Animation> ani)
    {
        this.app = app;
        animations = ani;
        
        currentAnim = "stand";
        nextAnim = currentAnim;
        
        //generate picture
        pic = new Picture("pic");
        
        //texture the picture
        pic.setTexture(app.getAssetManager(),animations.get(currentAnim).updateTex(app.getTimer().getTimeInSeconds()),true);
        pic.setQueueBucket(RenderQueue.Bucket.Transparent);
        
 
        //adjust picture
        //IGNORE THIS PART
        //TODO: find a good system for this.
        
        //float width = currentTex.getImage().getWidth() / 100;
        //float height = currentTex.getImage().getHeight() / 100;
        //ENDIGNORE
        float width = 4;
        float height = 4;
        pic.setWidth(width);
        pic.setHeight(height);
        pic.move(-width/2f,-height/2f,0);
 
        //define the material
        picMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        picMat.getAdditionalRenderState().setBlendMode(BlendMode.AlphaAdditive);
        picMat.getAdditionalRenderState().setAlphaFallOff(.5f);
        
    }
    
    @Override
    public void setSpatial(Spatial s)
    {
        Spatial oldSpatial = spatial;
        super.setSpatial(s);
        if(s instanceof Geometry)
        {
            System.out.println("SpriteControl IS MEANT TO BE USED WITH Nodes! DON'T APPLY IT TO A GEOMETRY!");
        }else if(s instanceof Node)
        {
            if(oldSpatial != null && oldSpatial instanceof Node)
            {
                ((Node) oldSpatial).detachChild(pic);
            }
            ((Node) s).setMaterial(picMat);
            ((Node) s).attachChild(pic);            
        }
    }
    
    public void animate(boolean interrupt, String next)
    {
        if(animations.containsKey(next))
        {
            finLast = !interrupt;
            nextAnim = currentAnim;
            if(interrupt)
            {
                currentAnim = nextAnim;
            }
        }
    }

    @Override
    protected void controlUpdate(float tpf)
    {
        if(!currentAnim.equals(nextAnim))
        {
            if(finLast)
            {
                if(animations.get(currentAnim).isLastFrame())
                {
                    currentAnim = nextAnim;
                }
            }else
            {
                currentAnim = nextAnim;
            }
        }
        Texture2D tex = animations.get(currentAnim).updateTex(app.getTimer().getTimeInSeconds());
        if(tex != null)
        {
            pic.setTexture(app.getAssetManager(),tex,true);
        }
    }
}

I also have an Animation class to store a series of images. I’m going to out it here, but I don’t think that this is the problem.

public class Animation
{
    private String chickenId;
    private String animName;
    private ArrayList<Texture2D> textures;
    private ArrayList<Float> durations;
    private float lastTexSwap = 0;
    private int currentAnim = 0;
    
    /**
     * defines an animation for one unit.
     * @param i
     * @param na
     * @param app 
     */
    public Animation(String i, String na, SimpleApplication app)
    {
        //textures and durations stored in seperate lists.
        textures = new ArrayList<Texture2D>();
        durations = new ArrayList<Float>();
        
        //stores the chicken this goes to for filepath reasons.
        chickenId = i;
        //stores the animation name for filepath reasons.
        animName = na;
        
        String directory = "assets//Textures//" + chickenId + "//" + animName;
        File file = new File(directory);
        
        if(file.exists())
        {
            FileReader readTiming = null;
            try 
            {
                //we get each animation from the timing file.
                readTiming = new FileReader(directory + "//timing.txt");
                Scanner scan = new Scanner(readTiming);
                while(scan.hasNext())
                {
                    //retreive each animation, and store it along with it's time.
                    Texture2D tex = (Texture2D) app.getAssetManager().loadTexture("Textures/" + chickenId + "/" + animName + "/" + scan.nextLine() + ".png");
                    textures.add(tex);
                    durations.add(Float.parseFloat(scan.nextLine()));
                }
            } catch (FileNotFoundException ex) { Logger.getLogger(Animation.class.getName()).log(Level.SEVERE, null, ex);
            } finally { try { readTiming.close(); } catch (IOException ex) {Logger.getLogger(Animation.class.getName()).log(Level.SEVERE, null, ex);}}
        }
    }
    
    /**
     * returns the updated texture.
     * @param time
     * @return 
     */
    public Texture2D updateTex(float time)
    {
        if(lastTexSwap + durations.get(currentAnim) < time)
        {
            lastTexSwap = time;
            currentAnim++;
            if(currentAnim >= durations.size())
            {
                currentAnim = 0;
            }
            return textures.get(currentAnim);
        }
        return null;
    }
    
    public boolean isLastFrame()
    {
        if(currentAnim == 0)
        {
            return true;
        }
        return false;
    }
}

Materials and textures have reliably given me a lot of trouble. I really appreciate the help.

This:
picMat.getAdditionalRenderState().setAlphaFallOff(.5f);
…won’t do anything unless you also turn on alpha test.

Or you could just set the alphaDiscardThreshold as already mentioned.

Fixed it so that

picMat.getAdditionalRenderState().setAlphaFallOff(.5f);
picMat.getAdditionalRenderState().setAlphaTest(true);

replaced only setting the alpha falloff to no affect whatsoever. In the same place, replaced it with:

picMat.setFloat(“AlphaDiscardThreshold”, .01f);

for the values 1, .5f, and .01f. None of them had any effect whatsoever.

Where do you actually set the picMat to the picture? You set it to the spatial but not the picture. Only Geometry can have materials. When you set the material on the node it’s a convenience function that goes out to all of the Geometry and sets their material but setting it on the Node before you attach something won’t have any affect on the thing you’ve attached… plus it’s odd to do that to the node you’ve added the control to since the material is meant for the picture and not whatever else the node might be holding.

As an aside: It seems like your logic here might be a little upside down. There might be some design fixing that needs to happen.

Thank you soo much! I have it working finally!

Pspeed, you’re right about my reasoning being upside down. For the life of me, I cannot seem to understand materials and textures properly. I think I need to take a class or something. :stuck_out_tongue:

For any of you who care, here is the (more or less) working code:

        //define the material
        picMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        picMat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
        picMat.getAdditionalRenderState().setAlphaFallOff(.1f);
        picMat.getAdditionalRenderState().setAlphaTest(true);
        picMat.setTexture("ColorMap", tex);
        pic.setMaterial(picMat);

Textures are an attribute of material. (One of many.) I’m not sure there is much more to understand than that, regarding textures and materials.

A Mesh is “what to draw”. A material is “how to draw it”. A Geometry pairs the two.