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.
In der Klasse MapState in der Methode activateShadowRenderer wird der DirectionalLightShadowRenderer initialisiert.
Hoffentlich kann mir einer von euch bei dem Problem helfen.
Danke schon mal im Vorraus.
Mit freundlichen Grüßen
lord239123
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.
Java:
/*
* 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.
Java:
/*
* 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