LensFlare & ADDITIVE Shadows

I'm trying to use additive lighting mode for my shadowrenderpass, and I have a lens flare effect (yes it's the jME LensFlare) coming from my point light. Everything is nice when using modulative lighting mode, but the additive somehow breaks the lens flare textures (they appear as non transparent with a black background). Is there any straightforward explanation for this?  :slight_smile:

Additive mode blends multiple renders of your scene together, changing the lighting for each pass.  Try rendering your lens flare separately.

Oki, thanks for the reply.



I've just started to look at the rendering pipeline though, so I am not quite sure how I can single the flare out.  :oops:



I am using a BasicPassManager with a RenderPass and a ShadowedRenderPass, in my GameState.



Any ideas on how that's done, or a pointer to some documentation on the subject would be appreciated.  :slight_smile:

i guess you could attach the flare to a separate rootNode which gets rendered in its own pass

Ok thanks, I've fiddled around with some compositions, but so far something always breaks.  :cry:



I tried a separate (root)node structure for the lens flare, and it did wotk with the transpareny/blanding with the rest. However, occlusion didn't work, which I guess has to do with the fact that if the node isn't connected to the others, there is no way for the renderer to know if this component is supposed to go behind or in front of stuff in the scene… anyone know how to solve that one, if at all?



My second attempt included making a structure under my current rootNode that would accomodate for the occlusion to work properly. It looks like This:



rootNode

—lightEffectNode

— ---occluderNode



And now I put stuff that should be affected by light on lightEffectNode level, things that should also cast shadows on occluderNode level, and stuff that just should be out of the lighting system on rootNode level. rootNode level is where I have my lensflare.

This mysteriously works with the skybox, which is in the lightEffectNode level, but the terrain and all objecte - no matter if they're occluders or not - mess up the lensflare graphics to non transparent black squares.  :expressionless:



I guess I lack a bit of fundamental knowledge about the rendering at this stage to know exactly what's happening.  :stuck_out_tongue:





EDIT: Success!  :lol:



Writing this I kind of realized I might be doing something wrong, and the problem was that I was attaching the flare to a lightnode, which I was placing at the wrong level in the hierarchy. It is now correctly added to the rootNode, and thus the effects of the different levels do not mess up the flare. Thanks for the help guys.  :slight_smile:

hello.



sorry to necro an old topic, but it is the only thread on this forum which deals with my current topic at hand.



i just really need help discovering how whirl fixed his problem.



if i use additive shadows, my shadows look fine, my only problem is that my lensflare gets really  messed up (the pieces of the flare turn into opaque squares, or with some setting the flare only shows the halo rings with really dark coloring.



modulative lighting creates a whole nother world of problems, so fixing this additive lighting option would be amazing for me!



here is how I am attaching my flare:


        // Setup the lensflare textures.
        TextureState[] tex = new TextureState[4];
        tex[0] = display.getRenderer().createTextureState();
        tex[0].setTexture(TextureManager.loadTexture(LensFlare.class
                .getClassLoader()
                .getResource("jmetest/data/texture/flare1.png"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, Image.Format.RGBA8,
                0.0f, true));
        tex[0].setEnabled(true);

        tex[1] = display.getRenderer().createTextureState();
        tex[1].setTexture(TextureManager.loadTexture(LensFlare.class
                .getClassLoader()
                .getResource("jmetest/data/texture/flare2.png"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        tex[1].setEnabled(true);

        tex[2] = display.getRenderer().createTextureState();
        tex[2].setTexture(TextureManager.loadTexture(LensFlare.class
                .getClassLoader()
                .getResource("jmetest/data/texture/flare3.png"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        tex[2].setEnabled(true);

        tex[3] = display.getRenderer().createTextureState();
        tex[3].setTexture(TextureManager.loadTexture(LensFlare.class
                .getClassLoader()
                .getResource("jmetest/data/texture/flare4.png"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        tex[3].setEnabled(true);

        flare = LensFlareFactory.createBasicLensFlare("flare", tex);
        flare.setRootNode(rootNode);
        flare.setTriangleAccurateOcclusion(true);
        flare.setIntensity(1);
        // notice that it comes at the end
        rootNode.attachChild(flare);



any help would be so greatly appreciated!

Hello. I have solved this problem and I intend here to give an explanation for anyone else who experiences a similar problem.



Whirl gave an accurate definition of how to solve the problem, however, it is not specific enough and therefore warrants my explanation.



Attached is some code derived from the shadowpasstest which combines the Lensflare and Additive Shadows together (at least to the user it seems this way) within the code it happens differently.



So, lets identify the problem.


  1. The lens flare was becoming rendered inappropriately when combined with a shadow pass.



    How do we fix this problem?



    There are a couple of steps:

    only apply the shadowpass to the nodes below the rootNode, and not directly to the rootNode itself. This gives the flare a safe nesting place without being destroyed by the shadow pass.



    Put the flare into its own renderpass attach the flare to the rootNode and make sure to add rootNode to the flare for occlusion.



    Well that actually pretty much explains it. Below is a working test for you all to see how to combine two such render passes.







import jmetest.renderer.ShadowTweaker;

import com.jme.app.SimplePassGame;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.image.Image;
import com.jme.image.Texture;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.light.DirectionalLight;
import com.jme.light.LightNode;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.RenderPass;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.scene.Node;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
import com.jmex.effects.LensFlare;
import com.jmex.effects.LensFlareFactory;

/**
 * <code>TestFlareandShadow</code>
 * @author Joshua Aurich
 * Derived from original author Joshua Slack from TestShadowPass
 * @version $Revision: 4130 $
 */
public class TestFlareandShadow extends SimplePassGame {
    private Node occluder;
    private static ShadowedRenderPass sPass = new ShadowedRenderPass();
    public RenderPass rpass = new RenderPass();
    private static boolean debug = true;

    /**
     * Entry point for the test,
     *
     * @param args
     */
    public static void main(String[] args) {
       TestFlareandShadow app = new TestFlareandShadow();
        if (debug) new ShadowTweaker(sPass).setVisible(true);
       
        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        app.start();
    }
   
    TestFlareandShadow() {
        stencilBits = 4; // we need a minimum stencil buffer at least.
       
    }

    /**
     * builds the scene.
     *
     * @see com.jme.app.BaseGame#initGame()
     */
    protected void simpleInitGame() {
        display.setTitle("jME - Shadow and Flare Test : X - enable/disable shadows");
        display.getRenderer().setBackgroundColor(ColorRGBA.gray.clone());

        //Build the Lights
        setupLights();
        //Build the occluder Sphere
        setupOccluder();
       
        rootNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
       
        /** Assign key X to action "toggle_shadows". */
        KeyBindingManager.getKeyBindingManager().set("toggle_shadows",
                KeyInput.KEY_X);
       
        sPass.add(occluder);
        sPass.addOccluder(occluder);
        sPass.setRenderShadows(true);
        sPass.setLightingMethod(ShadowedRenderPass.LightingMethod.Additive);       
        pManager.add(sPass);
        //Builds the flare
        addFlare();
        rootNode.updateGeometricState(0.0f,true);
        rootNode.updateRenderState();
    }
   
    protected void simpleUpdate() {
        if (KeyBindingManager.getKeyBindingManager().isValidCommand(
                "toggle_shadows", false)) {
            sPass.setRenderShadows(!sPass.getRenderShadows());
        }
    }


   
    private void setupLights() {    
       
        DirectionalLight dr = new DirectionalLight();
        dr.setEnabled(true);
        dr.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        dr.setAmbient(new ColorRGBA(.2f, .2f, .2f, .3f));
        dr.setDirection(new Vector3f(0.5f, -0.4f, 0).normalizeLocal());
        dr.setShadowCaster(true);

        PointLight pl = new PointLight();
        pl.setEnabled(true);
        pl.setDiffuse(new ColorRGBA(.7f, .7f, .7f, 1.0f));
        pl.setAmbient(new ColorRGBA(.25f, .25f, .25f, .25f));
        pl.setLocation(new Vector3f(0,500,0));
        pl.setShadowCaster(true);

        DirectionalLight dr2 = new DirectionalLight();
        dr2.setEnabled(true);
        dr2.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        dr2.setAmbient(new ColorRGBA(.2f, .2f, .2f, .4f));
        dr2.setDirection(new Vector3f(-0.2f, -0.3f, .2f).normalizeLocal());
        dr2.setShadowCaster(true);

        CullState cs = display.getRenderer().createCullState();
        cs.setCullFace(CullState.Face.Back);
        cs.setEnabled(true);
        rootNode.setRenderState(cs);

        lightState.detachAll();
        lightState.attach(dr);
        lightState.attach(dr2);
        lightState.attach(pl);
        lightState.setGlobalAmbient(new ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f));
        //rootNode.setRenderState(lightState);
    }

    private void setupOccluder() {

        TextureState ts = display.getRenderer().createTextureState();
        ts.setEnabled(true);
        ts.setTexture(
            TextureManager.loadTexture(
                  TestFlareandShadow.class.getClassLoader().getResource(
            "jmetest/data/texture/rust.jpg"),
            Texture.MinificationFilter.Trilinear,
            Texture.MagnificationFilter.Bilinear));

        occluder = new Node("occs");
        occluder.setRenderState(ts);
        rootNode.attachChild(occluder);
        Sphere s = new Sphere("s",20,20,5);
        s.setModelBound(new BoundingSphere());
        s.updateModelBound();
        s.setLocalTranslation(0,0,15);
        occluder.attachChild(s);
    }
   
    public void addFlare() {
        LightNode lightNode = new LightNode("light");

        Vector3f min2 = new Vector3f(-0.5f, -0.5f, -0.5f);
        Vector3f max2 = new Vector3f(0.5f, 0.5f, 0.5f);
        Box lightBox = new Box("box", min2, max2);
        lightBox.setModelBound(new BoundingBox());
        lightBox.updateModelBound();
        lightNode.attachChild(lightBox);
        lightNode.setLocalTranslation(new Vector3f(-14f, 14f, -14f));
        lightBox.setLightCombineMode(LightCombineMode.Off);

        // Setup the lensflare textures.
        TextureState[] tex = new TextureState[4];
        tex[0] = display.getRenderer().createTextureState();
        tex[0].setTexture(TextureManager.loadTexture(LensFlare.class
                .getClassLoader()
                .getResource("jmetest/data/texture/flare1.png"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, Image.Format.RGBA8,
                0.0f, true));
        tex[0].setEnabled(true);

        tex[1] = display.getRenderer().createTextureState();
        tex[1].setTexture(TextureManager.loadTexture(LensFlare.class
                .getClassLoader()
                .getResource("jmetest/data/texture/flare2.png"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        tex[1].setEnabled(true);

        tex[2] = display.getRenderer().createTextureState();
        tex[2].setTexture(TextureManager.loadTexture(LensFlare.class
                .getClassLoader()
                .getResource("jmetest/data/texture/flare3.png"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        tex[2].setEnabled(true);

        tex[3] = display.getRenderer().createTextureState();
        tex[3].setTexture(TextureManager.loadTexture(LensFlare.class
                .getClassLoader()
                .getResource("jmetest/data/texture/flare4.png"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        tex[3].setEnabled(true);

        LensFlare flare = LensFlareFactory.createBasicLensFlare("flare", tex);
        flare.setRootNode(rootNode);
        flare.setTriangleAccurateOcclusion(true);
        flare.setModelBound(new BoundingSphere());
        flare.updateModelBound();
        rootNode.attachChild(lightNode);
      flare.updateRenderState();
        rpass.add(flare);
        pManager.add(rpass);
        // notice that it comes at the end
        lightNode.attachChild(flare);

    }
}