BlendState for transparent png's

I have png's with transparent parts, but all the transparent bits are coming up black.  The code is mostly copied and modifier form the HUD for total n00bs tutorial.  Any help is appreciated, whether it be code corrections or just what you think the problem is.



Thanks!


 private void showImage(String str_imgName, float f_screenX, float f_screenY, float f_width, float f_height) {
        Quad hudQuad = new Quad("hud_" + str_imgName, f_width, f_height);
        hudQuad.setRenderQueueMode(Renderer.QUEUE_ORTHO);
        hudQuad.setLocalTranslation(new Vector3f(f_screenX, f_screenY, 0));

        /* does not work to disable light under v0.10 */
        //LightState ls = display.getRenderer().createLightState();
        //ls.setEnabled(false);
        //hudQuad.setRenderState(ls);

        hudQuad.setLightCombineMode(Spatial.LightCombineMode.Off);
        hudQuad.updateRenderState();

        hudNode.attachChild(hudQuad);
        //rootNode.attachChild(hudNode);

        // create the texture state to handle the texture
        TextureState ts = gsMain.getResourceManager().getTexture(str_imgName);
        System.out.println(str_imgName);
        // initialize texture width
        textureWidth = ts.getTexture().getImage().getWidth();
        // initialize texture height
        textureHeight = ts.getTexture().getImage().getHeight();

        // activate the texture state
        ts.setEnabled(true);

        final FloatBuffer texCoords = BufferUtils.createVector2Buffer(4);
        // coordinate (0,0) for vertex 0
        texCoords.put(getUForPixel(0)).put(getVForPixel(0));
        // coordinate (0,40) for vertex 1
        texCoords.put(getUForPixel(0)).put(getVForPixel(textureHeight));
        // coordinate (40,40) for vertex 2
        texCoords.put(getUForPixel(textureWidth)).put(getVForPixel(textureHeight));
        // coordinate (40,0) for vertex 3
        texCoords.put(getUForPixel(textureWidth)).put(getVForPixel(0));
        // assign texture coordinates to the quad
        hudQuad.setTextureCoords(new TexCoords(texCoords));
        // apply the texture state to the quad
        hudQuad.setRenderState(ts);

        // to handle texture transparency:
        // create a blend state
        BlendState bs = display.getRenderer().createBlendState();
        // activate blending
        bs.setBlendEnabled(true);
        // set the source function
        bs.setSourceFunctionAlpha(BlendState.SourceFunction.SourceAlpha);
        // set the destination function
        bs.setDestinationFunctionAlpha(BlendState.DestinationFunction.OneMinusSourceAlpha);
        // set the blend equation between source and destination
        bs.setBlendEquation(BlendState.BlendEquation.Subtract);
        bs.setTestEnabled(false);
        // activate the blend state
        bs.setEnabled(true);
        // assign the blender state to the quad
        hudQuad.setRenderState(bs);
        hudQuad.updateRenderState();
    }


Yeah, I also am having trouble with this.  Per the guy's instructions, I followed his note:



"For transparency to work with a texture you will have to add a Light- and MaterialState to your Quad and use the Diffuse alpha as the transparency value."



I still only see the black in the middle of the square.  



   //Note that ALWAYS_SHOW_PROPS_DIALOG forces the application to always ask the user to provide information about the display mode he/she wants to use.
   //
   //One major thing we need to do when using SimpleGame is to provide our own initialization code. This is what we look at now.
    protected void simpleInitGame()
    {
        display.setTitle("HUD Tutorial 1");

        /* create a rotating cylinder so we have something in the background */
        cylinder = new Cylinder("Cylinder", 6, 18, 5, 10);
        cylinder.setModelBound(new BoundingBox());
        cylinder.updateModelBound();

        MaterialState ms = display.getRenderer().createMaterialState();
        ms.setAmbient(new ColorRGBA(1f, 0f, 0f, 1f));
        ms.setDiffuse(new ColorRGBA(1f, 0f, 0f, 1f));

        /* has been depricated */
        //ms.setAlpha(1f);

        ms.setEnabled(true);
        cylinder.setRenderState(ms);
        cylinder.updateRenderState();

        rootNode.attachChild(cylinder);
      
        // So far this is all concerned setting up the cylinder and applying a red material to it.
      // Next we set up our HUD.
        hudNode = new Node("hudNode");
        Quad hudQuad = new Quad("hud", 40f, 40f);
        hudQuad.setRenderQueueMode(Renderer.QUEUE_ORTHO);       

        hudQuad.setLocalTranslation(new Vector3f(display.getWidth()/2,display.getHeight()/2,0));

        /* Does not work to disable light under v0.10 */
        LightState ls = display.getRenderer().createLightState();
        ls.setEnabled(true);
        hudQuad.setRenderState(ls);

        hudQuad.setLightCombineMode(Spatial.LightCombineMode.Off);
        hudQuad.updateRenderState();

        hudNode.attachChild(hudQuad);
        rootNode.attachChild(hudNode);

        //Now we need to add code to the simpleInitGame method. We add it right after we have created the quad.

        // create the texture state to handle the texture
        final TextureState ts = display.getRenderer().createTextureState();
       
        String filename = "/hudtutorial2.png";
       
        // load the image bs a texture (the image should be placed in the same directory bs this class)
        final Texture texture = TextureManager.loadTexture(
                getClass().getResource(filename),
                Texture.MinificationFilter.Trilinear, // of no use for the quad
                Texture.MagnificationFilter.Bilinear, // of no use for the quad
                1.0f,
                true);
       
        // set the texture for this texture state
        ts.setTexture(texture);
       
        // initialize texture width
        textureWidth = ts.getTexture().getImage().getWidth();
       
        // initialize texture height
        textureHeight = ts.getTexture().getImage().getHeight();
       
        // activate the texture state
        ts.setEnabled(true);
      
        //Here, we first create a texture render state, load our texture file, find out the dimensions of the image and enable this render state. Note that the image file is located and loaded via the class loader which means that it must be accessible by the classpath. The two constants MM_LINEAR and FM_LINEAR describe the way the texture gets filtered if it is far away, something that may not happen to our HUD anyway, so we can safely set them to the easiest setting.
       //Now we need to concentrate on applying the texture correctly to our quad by assigning texture coordinates to the vertices.
       //Look at the source:

        // correct texture application:
        final FloatBuffer texCoords = BufferUtils.createVector2Buffer(4);

        // coordinate (0,0) for vertex 0
        texCoords.put(getUForPixel(0)).put(getVForPixel(0));
         
        // coordinate (0,40) for vertex 1
        texCoords.put(getUForPixel(0)).put(getVForPixel(40));
         
        // coordinate (40,40) for vertex 2
        texCoords.put(getUForPixel(40)).put(getVForPixel(40));
         
        // coordinate (40,0) for vertex 3
        texCoords.put(getUForPixel(40)).put(getVForPixel(0));
        
        // assign texture coordinates to the quad
        hudQuad.setTextureCoords(new TexCoords(texCoords));
        
        // apply the texture state to the quad
        hudQuad.setRenderState(ts);
         
        //For each vertex there is a Vector2f which contains the appropriate texture coordinate. By using our recalculation methods and our knowledge of the internal setup of the quad, we can now easily assign the correct coordinates. We assign (0,0) to vertex 0, (0,39) to vertex 1 and so on. Finally we pass these coordinates to our quad and add the texture render state to the quad.
        //One thing is still missing: we wanted to have transparency but that won't work until we switch it on. Transparency is managed by the alpha render state. Have a look at how we set it up in our case.

        // to handle texture transparency:
        // create a blend state
        final BlendState bs = display.getRenderer().createBlendState();
       
        // activate blending
        bs.setBlendEnabled(true);
       
        // set the source function
        bs.setSourceFunctionAlpha(BlendState.SourceFunction.SourceAlpha);
       
        // set the destination function
        bs.setDestinationFunctionAlpha(BlendState.DestinationFunction.OneMinusSourceAlpha);
       
        // set the blend equation between source and destination
        bs.setBlendEquation(BlendState.BlendEquation.Subtract);
        bs.setTestEnabled(false);
       
        // activate the blend state
        bs.setEnabled(true);
       
        // assign the blender state to the quad
        hudQuad.setRenderState(bs);
       
        MaterialState msForHUDQuad = display.getRenderer().createMaterialState();
        msForHUDQuad.setDiffuse(new ColorRGBA(0.1f, 0f, 0f, 0.01f));
       
        hudQuad.setRenderState(msForHUDQuad);
       
        hudQuad.updateRenderState();
    }

Don't know of you guys are still trying to get transparency working, if you are this code may help



BlendState blendState = DisplaySystem.getDisplaySystem().getRenderer().createBlendState();
blendState.setBlendEnabled( true );
blendState.setSourceFunction( BlendState.SourceFunction.SourceAlpha );
blendState.setDestinationFunction( BlendState.DestinationFunction.OneMinusSourceAlpha );
blendState.setTestEnabled( true );
blendState.setTestFunction( BlendState.TestFunction.GreaterThanOrEqualTo );
blendState.setEnabled( true );



This codeblock is the default creation code I use for the BlendState that gets transparency working for me.

Hope it helps!

monkey_scratches_head said:

Don't know of you guys are still trying to get transparency working, if you are this code may help


BlendState blendState = DisplaySystem.getDisplaySystem().getRenderer().createBlendState();
blendState.setBlendEnabled( true );
blendState.setSourceFunction( BlendState.SourceFunction.SourceAlpha );
blendState.setDestinationFunction( BlendState.DestinationFunction.OneMinusSourceAlpha );
blendState.setTestEnabled( true );
blendState.setTestFunction( BlendState.TestFunction.GreaterThanOrEqualTo );
blendState.setEnabled( true );



This codeblock is the default creation code I use for the BlendState that gets transparency working for me.

Hope it helps!




Thanks alot! You saved me hours.
- Mikkel

Hi. I was about to start a new thread, but I saw this at the top of the list, so Ill add to it.



I'm working with some billboards with transparent .pngs on them, and I'm looking at ways to improve the quality. In particular, to remove the blending artifacts that you get around the edges of the opaque areas. Here are some articles that describe the problem really well:



This one is especially clear…

Rendering Plants with Smooth Edges - Wolfire Games Blog



These basically describe the same thing…

http://www.gamedev.net/community/forums/topic.asp?topic_id=455616

http://www.vterrain.org/Plants/Alpha/index.html



The technique they describe for getting the optimal results (ie smooth edges and no artifacts) involves drawing the transparent objects twice, which might not always be acceptable, but in my case it's fine. I was hoping someone might have some tips for me about what would be the best way to go about drawing something twice. Would I use instancing, or some kind of custom renderpass? Sorry if this seems like an ignorant question. I've jumped in the deep end with jMonkeyEngine, and my deadline is getting very close! I'm getting reasonably familiar with jME, but there is still a lot I've had to gloss over. Hopefully I'll have more time in future to explore at my leisure.



BTW, we are doing a commercial augmented reality project using NYARtoolkit, that makes use of transparent billboards to display props and characters in a 3d scene, and I'd really love to tidy up those artifacts with this technique.



Thanks!

BUMP



(Sorry about the bump. I realised I had replied to what was actually a quite old thread, and that since the question had been answered probably no-one would check it out again.)



I'm still thinking about ways of approaching the problem I mentioned above, but havn't tackled it yet, so any suggestions would be awesome.



I was wondering if there was way of getting a model to use the alpha cue internally, ie to sort it's own polygons. I was considering writing a function that pulls out all the polygons and adds them to their own object. This would work in this case because my polygons are all just billboards, but quite possibly not the best for a complex model.



Thanks

hi, I implemented this to achive decent vegetation rendering sorting:



Index: src/com/jme/renderer/RenderQueue.java
===================================================================
--- src/com/jme/renderer/RenderQueue.java   (revision 5083)
+++ src/com/jme/renderer/RenderQueue.java   (working copy)
@@ -38,6 +38,7 @@
 import com.jme.math.Vector3f;
 import com.jme.scene.Geometry;
 import com.jme.scene.Spatial;
+import com.jme.scene.state.BlendState;
 import com.jme.scene.state.CullState;
 import com.jme.scene.state.RenderState;
 import com.jme.scene.state.TextureState;
@@ -80,10 +81,16 @@
     private CullState tranCull;
    
     /** ZBufferState for two pass transparency rendering. */
-    private ZBufferState tranZBuff;
+    private ZBufferState tranZBuff, noWriteZBuff;
+
+    /** BlendStates for two pass transparency foliage rendering. */
+    private BlendState blend1, blend2;
    
     /** boolean for enabling / disabling two pass transparency rendering. */
-    private boolean twoPassTransparent = true;
+    private boolean twoPassTransparent = false;
+    
+    /** boolean for enabling / disabling two pass transparency foliage rendering. */
+    private boolean twoPassFoliage = false;
    
     private Vector3f tempVector = new Vector3f();
 
@@ -98,6 +105,27 @@
         tranZBuff = r.createZBufferState();
         tranZBuff.setWritable(false);
         tranZBuff.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
+
+        noWriteZBuff = r.createZBufferState();
+        noWriteZBuff.setWritable(false);
+        noWriteZBuff.setFunction(ZBufferState.TestFunction.LessThan);
+        
+        blend1 = r.createBlendState();
+        blend1.setBlendEnabled(false);
+        blend1.setTestEnabled(true);
+        blend1.setTestFunction(BlendState.TestFunction.GreaterThan);
+        blend1.setReference(0.85f);
+        blend1.setEnabled(true);
+        
+        blend2 = r.createBlendState();
+        blend2.setBlendEnabled(true);
+        blend2.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
+        blend2.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
+        blend2.setTestEnabled(true);
+        blend2.setTestFunction(BlendState.TestFunction.GreaterThan);
+        blend2.setReference(8f / 255f);
+        blend2.setEnabled(true);
+        
         setupBuckets();
     }
 
@@ -125,6 +153,27 @@
     }
    
     /**
+     * Enables/Disables two pass transparency foliage rendering. If enabled,
+     * objects in the TRANSPARENT queue will be rendered in two passes. On the
+     * first pass, objects are rendered with alpha killing. On the second pass,
+     * objects are rendered with alpha blending, but with z-write disabled.
+     *
+     * This allows multiple and complex transparent objects like trees and grass
+     * blending into each other and with itself (intersecting triangles) to be
+     * rendered whole without concern as to the order of the faces drawn.
+     *
+     * @param enabled
+     *            set true to turn on two pass transparency foliage rendering
+     */
+    public boolean isTwoPassFoliage() {
+        return twoPassFoliage;
+    }
+
+    public void setTwoPassFoliage(boolean twoPassFoilage) {
+        this.twoPassFoliage = twoPassFoilage;
+    }
+
+    /**
      * Creates the buckets needed.
      */
     private void setupBuckets() {
@@ -262,8 +311,25 @@
             for (int i = 0; i < transparentBucket.listSize; i++) {
                 Spatial obj = transparentBucket.list[i];
 
-                if (twoPassTransparent
-                    && obj instanceof Geometry) {
+                if (twoPassFoliage && obj instanceof Geometry) {
+                    
+                    Geometry geom = (Geometry)obj;
+                    RenderState oldBlendState = geom.states[RenderState.StateType.Blend.ordinal()];
+                    ZBufferState oldZState = (ZBufferState)geom.states[RenderState.StateType.ZBuffer.ordinal()];
+                    
+                    geom.states[RenderState.StateType.Blend.ordinal()] = blend1;
+                    
+                    obj.draw(renderer);
+                    
+                    geom.states[RenderState.StateType.Blend.ordinal()] = blend2;
+                    geom.states[RenderState.StateType.ZBuffer.ordinal()] = tranZBuff;
+                    
+                    obj.draw(renderer);
+
+                    geom.states[RenderState.StateType.ZBuffer.ordinal()] = oldZState;
+                    geom.states[RenderState.StateType.Blend.ordinal()] = oldBlendState;
+                    
+                } else if (twoPassTransparent && obj instanceof Geometry) {
                     Geometry geom = (Geometry)obj;
                     RenderState oldCullState = geom.states[RenderState.StateType.Cull.ordinal()];
                     geom.states[RenderState.StateType.Cull.ordinal()] = tranCull;
@@ -282,7 +348,7 @@
                 } else {
                     // draw as usual
                     obj.draw(renderer);
-                }
+                } // else
                 obj.queueDistance = Float.NEGATIVE_INFINITY;
             }
         transparentBucket.clear();



please, anyone test it and if you like it (if its useful) we can talk about Contributing to the jme2 core.

EDIT: The only thing you have to do is apply the patch and then call  this somewhere in your init methods:


DisplaySystem.getDisplaySystem().getRenderer().getQueue().setTwoPassFoliage(true);