# JMonkey Schatten werden durch Terrain hindurch angezeigt



## lord239123 (10. Mrz 2015)

Hallo Leute, 

wie ihr unten im Screenshot sehen könnt, werden die Schatten der Bäume im Hintergrund durch das Terrain hindurch angezeigt, obwohl sie eigentlich nicht sichtbar sein dürften.
Ich verwende zum Anzeigen der Schatten einen DirectionalLightShadowRenderer, welchem ein DirectionalLight hinzugefügt wurde.
Außerdem wird ein AmbientLight verwendet.




Die Klasse Map erbt von Node und enthält alle Spatials, welche zu sehen sind.
Im Konstruktor public Map(TerrainQuad terrain, String name) wird der ShadowMode für das Terrain festgelegt und in der Methode initTrees wird der ShadowMode für die Bäume festgelegt.


```
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package oblivionengine;

import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Node;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.HillHeightMap;
import com.jme3.texture.Texture;
import java.util.ArrayList;

/**
 *
 * @author To
 */
public class Map extends Node{
    
    //Objektvariablen
    private TerrainQuad terrain;
    private float size = 1024;   //Wird nur mit einem Wert belegt, wenn man eine ebene Fläche im Kostruktor erzeugt
    private boolean isUndergroundTextureRepetition = false;
    
    private ArrayList<Building> buildings;
    private ArrayList<Structure> structures;
    
    private AmbientLight ambientLight;
    private DirectionalLight sunLight;
    
    private float gravity;
    private BulletAppState bulletAppState;
    
    //Globales Lager
    public static Lager lager = new Lager(0);

    
    //--------------------------------------------------------------------------
    //Konstruktoren
    
    private Map(String name){
        super(name);
        
        //Physik
        bulletAppState = new BulletAppState();
        activatePhysics(true);
        
        //Einstellungen treffen
        setAmbientLight(true);
        setSunLight(true);
        setSkyColor(new ColorRGBA(6f/255f, 95f/255f, 213f/255f, 1f));
        setGravity(-19.62f);
        
        //Wasser aktivieren
        FilterPostProcessor processor = (FilterPostProcessor)Game.game.getAssetManager().loadFilter("Effects/Wasser.j3f");
        Game.game.getViewPort().addProcessor(processor);
    }
    
    
    public Map(TerrainQuad terrain) {
        this(terrain, "Map");
    }
    

    public Map(TerrainQuad terrain, String name) {
        this(name);
        this.terrain = terrain;
        this.attachChild(terrain);
        
        //Schatten dürfen angenommen werden
        terrain.setShadowMode(ShadowMode.Receive);
        
        //Physik des Terrains einstellen
        RigidBodyControl undergroundPhysic = new RigidBodyControl(0);
        this.terrain.addControl(undergroundPhysic);  
        bulletAppState.getPhysicsSpace().add(undergroundPhysic);
        
        initTrees(300,   "Models/Landschaft/Baum.j3o", true);
        //initTrees(1000,   "Models/Landschaft/Gras.j3o", false);
    }
    
    
    /*
     * Generiert direkt eine zufällige hügelige Fläche
     */
    public Map(float posX, float posZ, float size, String name){
        this(name);
        this.size = size;
        
        
        /*
         * /Terain erstellen
         */
        //Heightmap
        AbstractHeightMap heightMap = null;
        try{
            heightMap = new HillHeightMap((int)(size+1), 100, 50f, 100f, System.currentTimeMillis());
            heightMap.load();
        } catch(Exception e){e.printStackTrace();};
       // HeightMapFilter filter = new HeightMapFilter(heightMap);
        //filter.manipulateEdge();
        
        //Terain
        terrain = new TerrainQuad("terrain", 65, (int)size+1, heightMap.getHeightMap());
        terrain.setShadowMode(ShadowMode.Receive);
        // TerrainLodControl lodControl = new TerrainLodControl(terrain, Game.game.getCamera());
        //terrain.addControl(lodControl);
        this.attachChild(terrain);
        
        /*
         * Material
         */
        Material mat_terrain = new Material(Game.game.getAssetManager(), "Common/MatDefs/Terrain/HeightBasedTerrain.j3md");
        float grassScale = 64;
        float dirtScale = 16;
        float rockScale = 128;
        
        Texture grass = Game.game.getAssetManager().loadTexture("Textures/gras.jpg");
        grass.setWrap(Texture.WrapMode.Repeat);
        mat_terrain.setTexture("region1ColorMap", grass);
        mat_terrain.setVector3("region1", new Vector3f(200, 1, 128));

        // DIRT texture
        Texture dirt = Game.game.getAssetManager().loadTexture("Textures/Erde.png");
        dirt.setWrap(Texture.WrapMode.Repeat);
        mat_terrain.setTexture("region2ColorMap", dirt);
        mat_terrain.setVector3("region2", new Vector3f(0, 20, dirtScale));

        // ROCK texture
        Texture rock = Game.game.getAssetManager().loadTexture("Textures/stein.jpg");
        rock.setWrap(Texture.WrapMode.Repeat);
        mat_terrain.setTexture("region3ColorMap", rock);
        mat_terrain.setVector3("region3", new Vector3f(198, 260, rockScale));
        mat_terrain.setTexture("region4ColorMap", dirt);
        mat_terrain.setVector3("region4", new Vector3f(198, 260, rockScale));
        mat_terrain.setTexture("slopeColorMap", rock);
        mat_terrain.setFloat("slopeTileFactor", 32);
        mat_terrain.setFloat("terrainSize", (int)size+1);
        terrain.setMaterial(mat_terrain);
        
        
        //Physik
        RigidBodyControl undergroundPhysic = new RigidBodyControl(0);
        this.terrain.addControl(undergroundPhysic);  
        bulletAppState.getPhysicsSpace().add(undergroundPhysic);
        
        //Blumen und Bäume
        //initTrees(1000,  "Models/Landschaft/Gras.j3o", false);
        initTrees(100,   "Models/Landschaft/Baum.j3o", true);
        initTrees(1000,   "Models/Landschaft/Gras.j3o", false);
    }
    
    
    //Generiert eine ebene Fläche am Koordinatenursprung
    public Map(float size, String name){
        this(0, 0, size, name);
    }
    
    /*
     * Generiert Baüme und Blumen
     * @param: number: Anzahl der zu generierenden Bäume
     *         path  : Pfad zum Modell des Baumes
     *         castShadow: Soll ein Shatten erzeugt werden und soll das Objekt solide sein?
     */
    public void initTrees(int number, String path, boolean castShadowAndCollision){
        for (int i = 0; i < number; i++) {
            float posX = (float)Math.random()*this.size - size/2;
            float posZ = (float)Math.random()*this.size - size/2;
            float height = terrain.getHeight(new Vector2f(posX, posZ));
            
            
            //Es wird nur bis zur Höhe 5 ein Objekt generiert
            if(height > 10){
                Node tree = (Node)Game.game.getAssetManager().loadModel(path);
                tree.scale((float)Math.random()+1);
                int rotation = (int)(Math.random()*360);
                tree.rotate(0, rotation* FastMath.DEG_TO_RAD, 0);
                tree.setLocalTranslation(posX, height, posZ);
                tree.setShadowMode(ShadowMode.Receive);
                attachChild(tree);
                
                if(castShadowAndCollision){
                    //Schatten einstellen
                    tree.setShadowMode(ShadowMode.CastAndReceive);
                    
                    //Einen Zilinder als Kollisionsmodell verwenden
                    RigidBodyControl control = new RigidBodyControl(new CapsuleCollisionShape(1.2f, 30), 0);
                    tree.addControl(control);
                    bulletAppState.getPhysicsSpace().add(control);
                } 
            }
        }
    }
    
    
    //--------------------------------------------------------------------------
    //Getter und Setter
    
    public TerrainQuad getTerrain() {
        return terrain;
    }

    public ArrayList<Building> getBuildings() {
        return buildings;
    }

    public void addBuildings(Building... buildings) {
        for (Building building : buildings) {
            this.buildings.add(building);
            this.attachChild(building);
        }
    }

    public ArrayList<Structure> getStructures() {
        return structures;
    }

    public void addStructures(Structure... structures) {
        for (Structure structure : structures) {
            this.structures.add(structure);
            this.attachChild(structure);
        }
    }

    public AmbientLight getAmbientLight() {
        return ambientLight;
    }
    
    public void setAmbientLight(boolean isAmbientLight){
        if(ambientLight == null && isAmbientLight){
            ambientLight = new AmbientLight();
            ambientLight.setColor(ColorRGBA.White);
            addLight(ambientLight); 
        } else if(ambientLight != null && !isAmbientLight){
            removeLight(ambientLight);
            ambientLight = null;
        }
    }
    
    public void setAmbientLightColor(ColorRGBA color){
        ambientLight.setColor(color);
    }
    
    public DirectionalLight getSunLight() {
        return sunLight;
    }
    
    public void setSunLight(boolean isSunLight){
        if(sunLight == null && isSunLight){
            sunLight = new DirectionalLight();
            sunLight.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
            sunLight.setColor(ColorRGBA.Yellow);
            addLight(sunLight); 
        } else if(sunLight != null && !isSunLight){
            removeLight(sunLight);
            sunLight = null;
        }
    }
    
    public void setSunLightColor(ColorRGBA color){
        sunLight.setColor(color);
    }
    
    public void setSkyColor(ColorRGBA color){
        Game.game.getViewPort().setBackgroundColor(color);
    }
    
    public ColorRGBA getSkyColor(){
        return Game.game.getViewPort().getBackgroundColor();
    }
    
    public float getGravity() {
        return gravity;
    }

    public void setGravity(float gravity) {
        this.gravity = gravity;
    }

    public BulletAppState getBulletAppState() {
        return bulletAppState;
    }
    
    
    
    //--------------------------------------------------------------------------
    //Klasseninterne Methoden   
 
    public void activatePhysics(boolean value){
        if(value){
            Game.game.getStateManager().attach(bulletAppState);
        } else{
            Game.game.getStateManager().detach(bulletAppState);
        }
    }
}
```

In der Klasse MapState in der Methode activateShadowRenderer wird der DirectionalLightShadowRenderer initialisiert.

```
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package oblivionengine.appstates;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.post.filters.BloomFilter.GlowMode;
import com.jme3.post.filters.DepthOfFieldFilter;
import com.jme3.post.filters.FogFilter;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.renderer.Camera;
import com.jme3.scene.Node;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.ui.Picture;
import oblivionengine.CharakterControl;
import oblivionengine.Game;
import oblivionengine.Map;

/**
 *
 * @author To
 */
public class MapState extends AbstractAppState implements ActionListener, AnalogListener{
    
    //Mappings
    public enum InputMapping{
        RotateLeft, RotateRight, LookUp, LookDown, StrafeLeft, StrafeRight, MoveForward, MoveBackward, Jump, Run;
    }
    
    //--------------------------------------------------------------------------
    //Objektvariablen
    private InputManager inputManager;
    private CharakterControl player;
    private Node playerNode;
    
    private Map map;    //Ist eine Referenz auf activeMap in der Klasse Game
    private Picture cursor;
    
    
    //Filter
    private FilterPostProcessor fpp;
    private DepthOfFieldFilter dofFilter;   //Fokussierung der Kamera
    private SSAOFilter ssaoFilter;          //weiche Schatten
    private BloomFilter bloomFilter;
    private FogFilter fogFilter;
    private DirectionalLightShadowFilter dlsf;
    private DirectionalLightShadowRenderer dlsr;
    
    //--------------------------------------------------------------------------
    //Konstruktoren
    public MapState() {
        //FilterPostProcessor initialisieren
        fpp = new FilterPostProcessor(Game.game.getAssetManager());
        Game.game.getViewPort().addProcessor(fpp);
    }
    
    //--------------------------------------------------------------------------
    //Getter und Setter
    
    public Map getMap() {
        return map;
    }

    public void setMap(Map map) {
        Game.game.getRootNode().detachChild(this.map);
        this.map = map;
        Game.game.setActiveMap(map);
    }  

    //--------------------------------------------------------------------------
    //Klasseninterne Methoden
    
    @Override
    public void initialize(AppStateManager stateManager, Application app){ 
        super.initialize(stateManager, app);
        
        //Map erstellen
        Node a = (Node)Game.game.getAssetManager().loadModel("Scenes/Insel1.j3o");
        map = new Map((TerrainQuad)a.getChild(0), "Startinsel");
        activateShadowRenderer(true);
        Game.game.setActiveMap(map);
        
        Node b = (Node)Game.game.getAssetManager().loadModel("Scenes/Himmel.j3o");
        map.attachChild(b);
        
        //Verhindern, dass gezoomt werden kann
        Game.game.getFlyCam().setZoomSpeed(0);
        
        //Player initialisieren
        Node playerNode = new Node("Player");
        map.attachChild(playerNode);
        player = new CharakterControl(0.5f, 2.5f, 8);
        player.setCamera(Game.game.getCamera());
        playerNode.addControl(player);
        map.getBulletAppState().getPhysicsSpace().add(player);
        
        //InputManager initialisieren
        inputManager = Game.game.getInputManager();
        addInputMappings();
        
        //Spieler zum Startpunkt warpen
        player.warp(new Vector3f(0, map.getTerrain().getHeight(Vector2f.ZERO), 0));
    }
    
    @Override
    public void update(float tpf){
        
        Camera cam = Game.game.getCam();
        
        //Fokussierung der Kamera auf ein Objekt aktualisieren
        if(dofFilter != null){
            //Per RayCasting das anvisierte Objekt ermitteln
            Ray ray = new Ray(cam.getLocation(), cam.getDirection());
            CollisionResults results = new CollisionResults();
            int numCollisions = map.collideWith(ray, results);
            if(numCollisions > 0){
                CollisionResult hit = results.getClosestCollision();
                dofFilter.setFocusDistance(hit.getDistance() / 10);
            }
        }
        
        
        //Bewegung der Sonne
        map.getSunLight().setDirection(map.getSunLight().getDirection().add(tpf, -1f*tpf, 0));
    }
    
    /*
     * wichtige Tasten aktivieren und deaktivieren
     */
    private void addInputMappings(){
        //Mappings erstellen
        inputManager.addMapping(InputMapping.RotateLeft.name(), new MouseAxisTrigger(MouseInput.AXIS_X, true));
        inputManager.addMapping(InputMapping.RotateRight.name(), new MouseAxisTrigger(MouseInput.AXIS_X, false));
        inputManager.addMapping(InputMapping.LookUp.name(), new MouseAxisTrigger(MouseInput.AXIS_Y, false));
        inputManager.addMapping(InputMapping.LookDown.name(), new MouseAxisTrigger(MouseInput.AXIS_Y, true));
        
        inputManager.addMapping(InputMapping.StrafeLeft.name(), new KeyTrigger(KeyInput.KEY_A), new KeyTrigger(KeyInput.KEY_LEFT));
        inputManager.addMapping(InputMapping.StrafeRight.name(), new KeyTrigger(KeyInput.KEY_D), new KeyTrigger(KeyInput.KEY_RIGHT));
        inputManager.addMapping(InputMapping.MoveForward.name(), new KeyTrigger(KeyInput.KEY_W), new KeyTrigger(KeyInput.KEY_UP));
        inputManager.addMapping(InputMapping.MoveBackward.name(), new KeyTrigger(KeyInput.KEY_S), new KeyTrigger(KeyInput.KEY_DOWN));
        inputManager.addMapping(InputMapping.Run.name(), new KeyTrigger(KeyInput.KEY_LSHIFT), new KeyTrigger(KeyInput.KEY_RCONTROL));
        inputManager.addMapping(InputMapping.Jump.name(), new KeyTrigger(KeyInput.KEY_SPACE));
   
        //Listener aktivieren
        for(InputMapping i: InputMapping.values()){
            inputManager.addListener(this, i.name());
        }
    }
    
    
    @Override
    public void onAction(String name, boolean isPressed, float tpf) {
        if(player != null){
            player.onAction(name, isPressed, tpf);
        }
    }

    @Override
    public void onAnalog(String name, float value, float tpf) {
        if(player != null){
            player.onAnalog(name, value, tpf);
        }
    }
    
    

    @Override
    public void cleanup() {
        super.cleanup(); 
        for (InputMapping i : InputMapping.values()) {
            if(inputManager.hasMapping(i.name())){
                inputManager.deleteMapping(i.name());
            }
        }
        inputManager.removeListener(this);
    }
    
    
    
    /*
     * Fokussierung der Kamera auf ein Objekt aktivieren
     */
    public void activateDepthOfFieldFilter(boolean value){
        if(value){
            if(dofFilter == null){
                dofFilter = new DepthOfFieldFilter();
                fpp.addFilter(dofFilter);
            }
        } else{
            if(dofFilter != null){
                fpp.removeFilter(dofFilter);
                dofFilter = null;
            }
        }
    }
    
    
    public void activateDepthOfFieldFilter(DepthOfFieldFilter dofFilter){
        if(this.dofFilter == null){
            this.dofFilter = dofFilter;
            fpp.addFilter(this.dofFilter);
        }
    }
    
    /*
     * Aktiviert weiche Schatten
     */
    public void activateSSAOFilter(boolean value){
        if(value){
            if(ssaoFilter == null){
                ssaoFilter = new SSAOFilter(1f, 1.5f, 0.1f, 0.1f);
                fpp.addFilter(ssaoFilter);
            }
        } else{
            if(ssaoFilter != null){
                fpp.removeFilter(ssaoFilter);
                ssaoFilter = null;
            }
        }
    }
    
    public void activateSSAOFilter(float sampleRadius, float intensity, float scale, float bias){
        if(ssaoFilter == null){
            ssaoFilter = new SSAOFilter(sampleRadius, intensity, scale, bias);
            fpp.addFilter(ssaoFilter);
        }
    }
    
    public void activateSSAOFilter(SSAOFilter ssaoFilter){
        if(this.ssaoFilter == null){
            this.ssaoFilter = ssaoFilter;
            fpp.addFilter(this.ssaoFilter);
        }
    }
    
    /*
     * Lässt die Szene leuchten
     */
    public void activateBloomFilter(boolean value){
        if(value){
            if(bloomFilter == null){
                bloomFilter = new BloomFilter();
                fpp.addFilter(bloomFilter);
            }
        } else{
            if(bloomFilter != null){
                fpp.removeFilter(bloomFilter);
                bloomFilter = null;
            }
        }
    }
    
    public void activateBloomFilter(GlowMode glowMode){
        if(bloomFilter == null){
            bloomFilter = new BloomFilter(glowMode);
            fpp.addFilter(bloomFilter);
        }
    }
    
    public void activateBloomFilter(BloomFilter bloomFilter){
        if(this.bloomFilter == null){
            this.bloomFilter = bloomFilter;
            fpp.addFilter(this.bloomFilter);
        }
    }
    
    //Schatten für ein Sonnenlicht
    public void activateShadowRenderer(boolean value){
        if(value){
            if(dlsr == null){
                dlsr = new DirectionalLightShadowRenderer(Game.game.getAssetManager(), 1024, 3);
                dlsr.setLight(map.getSunLight());
                Game.game.getViewPort().addProcessor(dlsr); 
 
            }
        } else{
            if(dlsr != null){
                Game.game.getViewPort().removeProcessor(dlsr);
                dlsr = null;
            }
        }
    }
    
    public void activateShadowFilter(boolean value){
        if(value){
            if(dlsf == null && map.getSunLight() != null){
                dlsf = new DirectionalLightShadowFilter(Game.game.getAssetManager(), 1024, 1);
                dlsf.setLight(map.getSunLight());
                fpp.addFilter(dlsf);
            }
        } else{
            if(dlsf != null){
                fpp.removeFilter(dlsf);
                dlsf = null;
            }
        }
    }
    
    /*
     * Fadenkreuz anzeigen
     */
    public void activateCursor(boolean value) {
        if(value){
            if(cursor == null){
                cursor = new Picture("Cursor");
                cursor.setImage(Game.game.getAssetManager(), "Interface/Cursor.png", true);
                cursor.move(Game.game.getSettings().getWidth()/2-30, Game.game.getSettings().getHeight()/2-30, 0);
                cursor.setWidth(60);
                cursor.setHeight(60);
                Game.game.getGuiNode().attachChild(cursor);
            }
        } else{
            if(cursor != null){
                Game.game.getGuiNode().detachChild(cursor);
                cursor = null;
            }
        }
    }
    
    public void activateCursor(String path) {
        if(cursor == null){
            cursor = new Picture("Cursor");
            cursor.setImage(Game.game.getAssetManager(), path, true);
            cursor.move(Game.game.getSettings().getWidth()/2-30, 0, Game.game.getSettings().getHeight()/2-30);
            cursor.setWidth(60);
            cursor.setHeight(60);
            Game.game.getGuiNode().attachChild(cursor);
        }
    }
}
```

Hoffentlich kann mir einer von euch bei dem Problem helfen.
Danke schon mal im Vorraus.

Mit freundlichen Grüßen
lord239123


----------



## lord239123 (12. Mrz 2015)

Ich konnte das Problem jetzt beheben.
Das muss wohl an der Reihenfolge gelegen haben, mit der ich die Filter dem Viewport hinzugefügt habe.

Damit kann das Thema jetzt geschlossen werden.


----------

