PSSM broken (?) with GLSL 1.50 and Direct Light problem

Hello.



I’ve got 2 problems:


  1. Looks like PSSM Shadow Renderer is not working at all on video cards supporting GLSL 1.50.

    I’ve got a GTX 460 card for which PostShadowPSSM15.frag shader is used.

    When I run the example program attached below, I get this error after running around for a few seconds:



    WARNING: Common/MatDefs/Shadow/PostShadowPSSM15.frag compile error: 0(90) : error C1115: unable to find compatible overloaded function “textureGather(sampler2DShadow, vec2, float)”



    And shadows are not working at all.



    I’ve checked this decription: http://www.opengl.org/registry/specs/ARB/texture_gather.txt

    And it says the syntax for the textureGather functions is the next:

    gvec4 textureGather(gsampler2D sampler, vec2 coord)

    gvec4 textureGather(gsampler2DArray sampler, vec3 coord)

    gvec4 textureGather(gsamplerCube sampler, vec3 coord)

    gvec4 textureGather(gsamplerCubeArray sampler, vec4 coord)



    So looks like it really takes 2 params and not 3 like in the PostShadowPSSM15.frag file.

    That’s why I think it’s a bug, but I don’t know how to fix it :slight_smile: Please suggest.




  2. DirectionalLight is not working as expected.

    Please check this screenshot from the same example:





    Blue arrow shows the direction of light.

    The wall to the left is lighted as expected. But the wall to the right and the floor are lighted too (see red arrows)! As if the light has passed through the first wall!

    Actually, I can wall off completely building the last 4th wall and if I look from inside one wall will be still light, while it should be completely dark inside! (Please note that ambient light is disabled)

    Is it an expected behaviour or is it a bug too?



    This is how I expect the directional light to work (this is a screenshot generated using another 3D engine - Leadwerks):



    When ambient lighting is disabled and only one directional light is kept, the walls inside this “shaft” are completely dark (black) except the small area on the top which is hit by the light directly.



    I’m using the last JME3 nightly build from 21th February.

    Example code:



    pre type="java"
    package mygame;





    import com.jme3.app.SimpleApplication;


    import com.jme3.bullet.BulletAppState;


    import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;


    import com.jme3.bullet.collision.shapes.CollisionShape;


    import com.jme3.bullet.control.CharacterControl;


    import com.jme3.bullet.control.PhysicsControl;


    import com.jme3.bullet.control.RigidBodyControl;


    import com.jme3.bullet.util.CollisionShapeFactory;


    import com.jme3.input.KeyInput;


    import com.jme3.input.controls.ActionListener;


    import com.jme3.input.controls.KeyTrigger;


    import com.jme3.light.AmbientLight;


    import com.jme3.light.DirectionalLight;


    import com.jme3.material.Material;


    import com.jme3.math.ColorRGBA;


    import com.jme3.math.Vector3f;


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


    import com.jme3.scene.Geometry;


    import com.jme3.scene.Node;


    import com.jme3.scene.shape.Box;


    import com.jme3.shadow.BasicShadowRenderer;


    import com.jme3.shadow.PssmShadowRenderer;


    import java.sql.BatchUpdateException;


    import jme3tools.optimize.GeometryBatchFactory;





    public class Main2 extends SimpleApplication {


        private Vector3f walkDirection = new Vector3f();


        private boolean left = false, right = false, up = false, down = false;





        private BulletAppState appState;


        private CharacterControl player;





        private int cubeCount = 0;





        public static void main(String[] args) {


            new Main2().start();


        }





        @Override


        public void simpleInitApp() {


            appState = new BulletAppState();


            stateManager.attach(appState);





            Material brickMaterial = new Material(assetManager, “Common/MatDefs/Light/Lighting.j3md”);


            brickMaterial.setTexture(“DiffuseMap”, assetManager.loadTexture(“Textures/Terrain/BrickWall/BrickWall.jpg”));


            brickMaterial.setTexture(“NormalMap”, assetManager.loadTexture(“Textures/Terrain/BrickWall/BrickWall_normal.jpg”));


            brickMaterial.setTexture(“ParallaxMap”, assetManager.loadTexture(“Textures/Terrain/BrickWall/BrickWall_height.jpg”));


            brickMaterial.setTexture(“ColorMap”, assetManager.loadTexture(“Textures/Terrain/BrickWall/BrickWall_height.jpg”));


            brickMaterial.setFloat(“Shininess”, 2f);





            Node cubes = new Node(“All Cubes”);





            for (int i = -8; i <= 8; i++) {


                cubes.attachChild(createCube(brickMaterial, 1f, i  2, 2f, -16));


                cubes.attachChild(createCube(brickMaterial, 1f, i 
     2, 2f, 16));


                cubes.attachChild(createCube(brickMaterial, 1f, -16, 2f, i  2));


                cubes.attachChild(createCube(brickMaterial, 1f, 16, 2f, i 
     2));





                for (int j = -8; j <= 8; j++) {


                    cubes.attachChild(createCube(brickMaterial, 1f, i  2, 0f, j  2));


                }


            }





            for (int i = -7; i <= 5; i++) {


                for (int j = 1; j <= 10; j++) {


                    cubes.attachChild(createCube(brickMaterial, 1f, i  2, j  2, 4f));





                    if (i <= 4) {


                        cubes.attachChild(createCube(brickMaterial, 1f, i  2, j  2, -4f));


                    }


                }


            }





            for (int i = -7; i <= 5; i++) {


                for (int k = -3; k <= 3; k++) {


                    cubes.attachChild(createCube(brickMaterial, 1f, i  2, 20, k  2));


                }


            }





            for (int j = 2; j <= 10; j++) {


                for (int k = -3; k <= 3; k++) {


                    cubes.attachChild(createCube(brickMaterial, 1f, -16, j  2, k  2));


                }


            }





            rootNode.attachChild(GeometryBatchFactory.optimize(cubes));





            rootNode.attachChild(createCube(brickMaterial, 1f, 0f, 0f, 0f));





            setupPlayer();


            setupLights();


            setupShadows();


            setupKeys();


        }





        private Geometry createCube(Material mat, float size, float x, float y, float z) {


            cubeCount++;





            Box box = new Box(Vector3f.ZERO, size, size, size);


            Geometry cube = new Geometry("Cube " + cubeCount, box);





            cube.setMaterial(mat);


            cube.setLocalTranslation(x, y, z);


            cube.setShadowMode(ShadowMode.CastAndReceive);





            CollisionShape cubeCollissionShape = CollisionShapeFactory.createMeshShape(cube);


            PhysicsControl cubePhysics = new RigidBodyControl(cubeCollissionShape, 0.0f);


            


            cube.addControl(cubePhysics);





            appState.getPhysicsSpace().add(cubePhysics);





            return cube;


        }





        private void setupLights() {


            DirectionalLight dl = new DirectionalLight();


            dl.setColor(ColorRGBA.White.clone().multLocal(2));


            dl.setDirection(new Vector3f(0f, -1f, -1f).normalizeLocal());


            rootNode.addLight(dl);





            //AmbientLight al = new AmbientLight();


            //al.setColor(new ColorRGBA(1.8f,1.8f,1.8f,1.0f));


            //rootNode.addLight(al);


        }





        private void setupShadows() {


            //BasicShadowRenderer sr = new BasicShadowRenderer(assetManager, 256);





            PssmShadowRenderer sr = new PssmShadowRenderer(assetManager, 512, 2);


            sr.setFilterMode(PssmShadowRenderer.FilterMode.Bilinear);


            sr.setCompareMode(PssmShadowRenderer.CompareMode.Hardware);


            sr.setDirection(new Vector3f(0f, -1f, -1f).normalizeLocal());


            viewPort.addProcessor(sr);


            


            rootNode.setShadowMode(ShadowMode.Off);


        }





        public void setupPlayer() {


            CollisionShape playerShape = new CapsuleCollisionShape(1.3f, 2f, 1);


            player = new CharacterControl(playerShape, 0.05f);





            player.setJumpSpeed(20);


            player.setFallSpeed(30);


            player.setGravity(30);





            player.setPhysicsLocation(new Vector3f(0f, 3f, 0f));





            appState.getPhysicsSpace().add(player);


        }





        private void setupKeys() {


            inputManager.addMapping(“Lefts”, new KeyTrigger(KeyInput.KEY_A));


            inputManager.addMapping(“Rights”, new KeyTrigger(KeyInput.KEY_D));


            inputManager.addMapping(“Ups”, new KeyTrigger(KeyInput.KEY_W));


            inputManager.addMapping(“Downs”, new KeyTrigger(KeyInput.KEY_S));


            inputManager.addMapping(“Jumps”, new KeyTrigger(KeyInput.KEY_SPACE));





            inputManager.addListener(actionListener, new String[] {


                “Lefts”, “Rights”, “Ups”, “Downs”, “Jumps”


            });


        }





        @Override


        public void simpleUpdate(float tpf) {


            Vector3f camDir = cam.getDirection().clone().multLocal(0.2f);


            Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);


            walkDirection.set(0, 0, 0);


            if (left) {


                walkDirection.addLocal(camLeft);


            }


            if (right) {


                walkDirection.addLocal(camLeft.negate());


            }


            if (up) {


                walkDirection.addLocal(camDir);


                walkDirection.y = 0;


            }


            if (down) {


                walkDirection.addLocal(camDir.negate());


                walkDirection.y = 0;


            }


            player.setWalkDirection(walkDirection);


            cam.setLocation(player.getPhysicsLocation());


        }





        private ActionListener actionListener = new ActionListener() {


            public void onAction(String binding, boolean value, float tpf) {


                if (binding.equals(“Lefts”)) {


                    left = value;


                } else if (binding.equals(“Rights”)) {


                    right = value;


                } else if (binding.equals(“Ups”)) {


                    up = value;


                } else if (binding.equals(“Downs”)) {


                    down = value;


                } else if (binding.equals(“Jumps”) && value && player.onGround()) {


                    player.jump();


                }


            }


        };


    }
    /pre

You have to set the shadowmode of the Spatials you want to cast/receive shadows:

setShadowMode(ShadowMode.CastAndReceive);

I already do it. There is a line in my example code:

cube.setShadowMode(ShadowMode.CastAndReceive);

Line #106 to be exact :slight_smile:

Hm, check the latest TestHoveringTank (in the bullet tests), do you see the shadows there?

I see some strange artifacts and the same error:

WARNING: Common/MatDefs/Shadow/PostShadowPSSM15.frag compile error: 0(90) : error C1115: unable to find compatible overloaded function “textureGather(sampler2DShadow, vec2, float)”

Oh ok, well when the shader doesn’t compile at all then shadows wont work… Also, don’t mix up lighting and shadows, they are different things as weird as that may sound. The shadow processor gives the direction of the shadows while the DirectionalLight only gives the direction for the lighting effects of the material. So you could create an “unreal” situation where the light comes from one direction and the shadows are cast to the other direction.

normen said:
Oh ok, well when the shader doesn't compile at all then shadows wont work.

Yes, that's what I'm talking about. The *15.frag version of the shader just doesn't compile. At least on my video card.
Any thoughts on how to fix it?

normen said:
Also, don’t mix up lighting and shadows, they are different things as weird as that may sound. The shadow processor gives the direction of the shadows while the DirectionalLight only gives the direction for the lighting effects of the material.

I understand they are different things, but... shouldn't the lighting effect disappear indoors (inside a "room" which is completely walled off)? It's not about shadows, there is just no light, isn't it? :)
I mean, this should work even without a shadow processor. Or not?
medved said:
I understand they are different things, but... shouldn't the lighting effect disappear indoors (inside a "room" which is completely walled off)? It's not about shadows, there is just no light, isn't it? :)
I mean, this should work even without a shadow processor. Or not?

No, lighting in OpenGL is not done via raycasting so this information (the light is walled off) is just not available. Thats why computing shadows is so expensive (and also the reason its actually needed, otherwise the shadows would be there automatically).
1 Like

@normen, ok, got it,thanks a lot for your explanations!



But what about the PSSM shader compilation problem? :slight_smile:

Is someone going to look into it?

Yeah, I guess @nehon will be interested in this.

hehe sorry, I was few pages late on the forum.



According to GLSL specs, textureGather is not part of GLSL1.5

http://www.opengl.org/registry/doc/GLSLangSpec.1.50.09.pdf



It appear in GLSL 4.0 (which is the version after 1.5)

http://www.opengl.org/registry/doc/GLSLangSpec.4.00.8.clean.pdf



I got to discuss that with Kirill, I guess our cards support glsl 4.0

nehon said:
I guess our cards support glsl 4.0

My card supports it too:

INFO: OpenGL Version: 4.0.0
INFO: Renderer: GeForce GTX 460/PCI/SSE2
INFO: GLSL Ver: 4.00 NVIDIA via Cg compiler

But the shader doesn't compile...

well…then I’m gonna draw the “did you update your drivers lately?” card!! :smiley:

If I recall correctly, textureGather isn’t supposed to be used if the card doesn’t expose the texture_gather extension?

hmm nope it seems to be used no matter what

there is a #extension GL_ARB_texture_gather : enable mention on top then texturegather is used in HARDWARE or SOFTWARE mode

nehon said:
well...then I'm gonna draw the "did you update your drivers lately?" card!! :D

Updated to the latest version of driver. According to jMonkey log even OpenGL 4.1 and GLSL 4.10 are now supported:
INFO: OpenGL Version: 4.1.0
INFO: Renderer: GeForce GTX 460/PCI/SSE2
INFO: GLSL Ver: 4.10 NVIDIA via Cg compiler

But the problem still persists! Same error during shader compilation.
Momoko_Fan said:
If I recall correctly, textureGather isn't supposed to be used if the card doesn't expose the texture_gather extension?

Actually, my card does expose this function, but it expects different number of arguments:

WARNING: Common/MatDefs/Shadow/PostShadowPSSM15.frag compile error: 0(90) : error C1115: unable to find compatible overloaded function “textureGather(sampler2DShadow, vec2, float)”

Removing the third argument makes the shader compile, but shadows do not work :D

Well this method signature is in the spec…that’s strange.



Could you try to switch to software mode please?



pssm.setCompareMOde(CompareMode.Software);

Software mode doesn’t help.



But I found a way to fix it!



As you said, this function is in spec of GLSL 4.00.

But PostShadowPSSM15.frag uses it through the extension GL_ARB_texture_gather for some reason.

I assumed this function from extension had another specifiction (other number of arguments) and disabled it (removed the " #extension GL_ARB_texture_gather : enable" row).

THen I got another error:

WARNING: Common/MatDefs/Shadow/PostShadowPSSM15.frag compile error: 0(90) : error C7532: global function textureGather requires “#version 400” or later



I changed this line in PostShadowPSSM.j3md:

FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag

to:

FragmentShader GLSL400: Common/MatDefs/Shadow/PostShadowPSSM15.frag



Also I had to add some changes to LwjglRenderer class (currently it can detect only GLSL version 1.50 or lower), but it worked!

Shadows are working now!



Now I want to know, how to fix it correctly :slight_smile:

I mean, how to make this shader compatible withh all OpenGL/GLSL version…