Filter appears to have outdated scene texture information

My deferred lighting system is nearly finished for my personal needs, but I’ve run into a bit of a problem. Here’s my process:
Render the whole scene unshaded with a pure white ambient light
In my DeferredFilter, use a similar system to the SSAOFilter - render the normals in a Pass in the postQueue method. I also use this to render my specular map.
Pass the Depth, Normals, and Specular map all to each light. Lights are attached to their own Node, and this Node is part of its own ViewPort:


    protected void createLightBuffer() {
        lightBuffer = new FrameBuffer(screenWidth, screenHeight, 1);
        lights = new Texture2D(screenWidth, screenHeight, Format.RGBA8);
        lightBuffer.setColorTexture(lights);
        lightBuffer.setDepthTexture((Texture2D) viewPort.getOutputFrameBuffer().getDepthBuffer().getTexture()); //Pretty sure there's a better way to do this

        lightViewPort = renderManager.createPreView("LightBuffer", cam);
        lightViewPort.attachScene(lightNode);
        lightViewPort.setOutputFrameBuffer(lightBuffer);
        lightViewPort.setClearColor(true);

        material.setTexture("LightMap", lights);
    }

Each light does its calculations, adds its effect to the LightMap, and the actual filter takes the rendered texture from the light viewport and multiplies it by the unshaded scene.
So far I’m able to render 2500+ point lights without any framerate problems on a pretty old GPU (HD 6670 1GB), and it’s working perfectly…except one thing.


That’s my test scene when the camera remains still - that is, neither moving nor looking around. (I did 40K cubes and 10K lights to intentionally drop the framerate so the problem was more noticeable). Now when I look around…

The lights move correctly, and without delay…but the initial unshaded render appears to be one frame behind.

I took that one while panning my camera down. As you can see, the light map moved correctly, but the initial unshaded pass remained one frame behind, which distorts the result. At higher framerates, this problem is unnoticeable as frame times are much shorter. If need be, I can post the code to my whole Deferred Filter but I feel like the problem is somehow related to the order in which the two viewports are rendered. I’ve tried creating the lightViewPort using .createPreView, .createPostView, and .createMainView but they all have identical problems. There’s gotta be something I’m missing here.

Post the code yeah. But my guess is that you’re using another viewport in a processor post queue and let the normal render flow render to it. Or something along those lines. We had a similar issue at first with the water reflection processor. Reflection was one frame late.
If you need additional renders, render them on the spot in the post queue like it’s done in the SSAO filter with the normals for example.

I would say its because you are rendering your lights in a “PREview viewport”. At least thats the info i get out of the snippet. In that case you would render your lightmap based on the depth/normal from the previous frame

@zzuegg I tried createPostView and createMainView, both had the same result.

@nehon Keep in mind it’s kinda messy and mostly experimental. Here’s the most relevant code portions:
PostQueue:


    @Override
    protected void postQueue(RenderQueue queue) {
        if (lightBuffer == null) {
            createLightBuffer();
        }
        //Render the normal map
        Renderer r = renderManager.getRenderer();
        r.setFrameBuffer(normalPass.getRenderFrameBuffer());
        renderManager.getRenderer().clearBuffers(true, true, true);
        renderManager.setForcedTechnique("CustomNormalPass");
        renderManager.renderViewPortQueues(viewPort, false);
        //Render the specular map
        r.setFrameBuffer(specularPass.getRenderFrameBuffer());
        renderManager.getRenderer().clearBuffers(true, true, true);
        renderManager.setForcedTechnique("SpecularPass");
        renderManager.renderViewPortQueues(viewPort, false);
        //Restore default viewport and technique
        renderManager.setForcedTechnique(null);
        renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());
    }

PostFrame:


    protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
        for (Spatial child : lightNode.getChildren()) {
            Light light = (Light) child;
            if (!light.getIsMaterialConfigured()) {
//                light.setMaterialConfigured(true);
                light.getMaterial().setColor("LightColor", light.getColor());
                light.getMaterial().setTexture("DepthTexture", normalPass.getDepthTexture());
                light.getMaterial().setTexture("NormalTexture", normalPass.getRenderedTexture());
                light.getMaterial().setTexture("SpecularTexture", specularPass.getRenderedTexture());
                light.getMaterial().setMatrix4("InverseViewProjectionMatrix", lightViewPort.getCamera().getViewProjectionMatrix().invert());
//                light.getMaterial().setVector3("CameraDirection", viewPort.getCamera().getDirection());
                light.getMaterial().setVector3("CameraPosition", viewPort.getCamera().getLocation());
                light.getMaterial().setVector2("Resolution", new Vector2f(screenWidth, screenHeight));
                light.getMaterial().setVector3("FrustumCorner", frustumCorner);
                light.getMaterial().setVector2("FrustumNearFar", frustumNearFar);
                if (light instanceof PointLight) {
                    light.getMaterial().getAdditionalRenderState().setFaceCullMode(viewPort.getCamera().getLocation().distance(((PointLight) light).getPosition()) > light.getLocalScale().x + viewPort.getCamera().getFrustumNear() ? FaceCullMode.Back : FaceCullMode.Front);
                    light.getMaterial().getAdditionalRenderState().setDepthTest(viewPort.getCamera().getLocation().distance(((PointLight) light).getPosition()) > light.getLocalScale().x + viewPort.getCamera().getFrustumNear());
                    light.getMaterial().setFloat("Radius", ((PointLight) light).getRadius());
                    light.getMaterial().setVector3("LightPosition", ((PointLight) light).getPosition());
                    light.getMaterial().setFloat("SpecularMult", ((PointLight) light).getPosition().distance(viewPort.getCamera().getLocation()) > settings.getLightRange() * 2.0F ? 0.0F : 1.0F);

                } else if (light instanceof SpotLight) {
                    light.getMaterial().getAdditionalRenderState().setFaceCullMode(viewPort.getCamera().getLocation().distance(((SpotLight) light).getPosition()) > light.getLocalScale().x + viewPort.getCamera().getFrustumNear() ? FaceCullMode.Back : FaceCullMode.Front);
                    light.getMaterial().getAdditionalRenderState().setDepthTest(viewPort.getCamera().getLocation().distance(((SpotLight) light).getPosition()) > light.getLocalScale().x + viewPort.getCamera().getFrustumNear());
                    light.getMaterial().setFloat("Range", ((SpotLight) light).getRange());
                    light.getMaterial().setVector3("LightPosition", ((SpotLight) light).getPosition());
                    light.getMaterial().setVector3("LightDirection", ((SpotLight) light).getDirection());
                    light.getMaterial().setFloat("SpecularMult", ((SpotLight) light).getPosition().distance(viewPort.getCamera().getLocation()) > settings.getLightRange() * 2.0F ? 0.0F : 1.0F);
                    light.getMaterial().setFloat("InnerLimit", FastMath.cos(((SpotLight) light).getInnerLimit() * FastMath.DEG_TO_RAD * 0.5F));
                    light.getMaterial().setFloat("OuterLimit", FastMath.cos(((SpotLight) light).getOuterLimit() * FastMath.DEG_TO_RAD * 0.5F)); //Fix to only calculate these once instead of every frame
                } else if (light instanceof DirectionalLight) {
//                    light.setLocalTranslation(cam.getLocation());
                    light.getMaterial().setVector3("LightDirection", ((DirectionalLight) light).getDirection());
                    //light.getMaterial().setTexture("LightDepth", ((DirectionalLight) light).getShadowDepthMap();
                }
            }
        }

InitFilter:


    @Override
    protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
        this.manager = manager;
        this.renderManager = renderManager;
        this.viewPort = vp;
        this.screenWidth = w;
        this.screenHeight = h;
        postRenderPasses = new ArrayList();

        normalPass = new Pass();
        normalPass.init(renderManager.getRenderer(), w, h, Image.Format.RGBA8, Image.Format.Depth, 1, true);
        normalPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Nearest);
        normalPass.getRenderedTexture().setMinFilter(Texture.MinFilter.NearestNoMipMaps);

        specularPass = new Pass();
        specularPass.init(renderManager.getRenderer(), w, h, Image.Format.Luminance8Alpha8, Image.Format.Depth);
        specularPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Nearest);
        specularPass.getRenderedTexture().setMinFilter(Texture.MinFilter.NearestNoMipMaps);

        material = new Material(manager, "MatDefs/DeferredLighting.j3md");
    }

creatLightBuffer:


    protected void createLightBuffer() {
        lightBuffer = new FrameBuffer(screenWidth, screenHeight, 1);
        lights = new Texture2D(screenWidth, screenHeight, Format.RGBA8);
        lightBuffer.setColorTexture(lights);

        lightViewPort = renderManager.createMainView("LightBuffer", cam);
        lightViewPort.attachScene(lightNode);
        lightViewPort.setOutputFrameBuffer(lightBuffer);
        lightViewPort.setClearColor(true);

        material.setTexture("LightMap", lights);
    }

Also, apologies for the few days late reply. I guess the new forum automatically unchecks the “notify me of replies” box. Thanks in advance for any help!

What @zzuegg says makes a lot of sense.
When do you call your createLightBuffer method?

Also on a side note for your first pass you should use multi render target instead of doing one geometry pass for each buffer.

@nehon I already tried using renderManager.createPostView and .createMainView, both had an identical problem. I call createLightBuffer when it passes through the postQueue for the first time, but I’ve tried putting it in initFilter and postFrame but all have the same problem.

And for the multi render target, I’ll get started on that. It’ll be a lot simpler and more efficient than what I have right now, that’s for sure! Thanks for the help so far.

Could it be that I’m using the default ViewPort as my primary render? It renders the whole scene with a white ambient light, then I multiply by the light map. Could that possibly be part of the problem? I remember when I was researching and poking around in DMonkey’s code by kwando, he made a totally separate ViewPort for the Gbuffer. Since I’m starting on moving stuff from geometry passes to multi render targets, I’ll probably have to do that anyway. I suppose we’ll see though.