Schatten System verbessert und Schatten qualität einstellbar gemacht

This commit is contained in:
2026-07-19 15:00:40 +02:00
parent ad37720edd
commit 4328d66fe7
8 changed files with 588 additions and 23 deletions

View File

@@ -20,7 +20,8 @@ public class GraphicsScreen extends MenuScreen {
private static final int ROW_VSYNC = 2;
private static final int ROW_AA = 3;
private static final int ROW_VIEWDIST = 4;
private static final int ROW_COUNT = 5;
private static final int ROW_SHADOW = 5;
private static final int ROW_COUNT = 6;
private final GraphicsSettings live;
private GraphicsSettings edit;
@@ -48,7 +49,8 @@ public class GraphicsScreen extends MenuScreen {
edit.fullscreen = live.fullscreen;
edit.vsync = live.vsync;
edit.samples = live.samples;
edit.viewDistance = live.viewDistance;
edit.viewDistance = live.viewDistance;
edit.shadowQuality = live.shadowQuality;
resIdx = 0;
for (int i = 0; i < RESOLUTIONS.length; i++) {
@@ -71,7 +73,8 @@ public class GraphicsScreen extends MenuScreen {
"menu.graphics.row.fullscreen",
"menu.graphics.row.vsync",
"menu.graphics.row.aa",
"menu.graphics.row.viewdist"
"menu.graphics.row.viewdist",
"menu.graphics.row.shadow"
};
float lblX = CONT_X + 30f;
@@ -125,6 +128,11 @@ public class GraphicsScreen extends MenuScreen {
case NORMAL -> t("menu.graphics.val.viewdist.normal");
case FAR -> t("menu.graphics.val.viewdist.far");
};
case ROW_SHADOW -> switch (edit.shadowQuality) {
case LOW -> t("menu.graphics.val.shadow.low");
case MEDIUM -> t("menu.graphics.val.shadow.medium");
case HIGH -> t("menu.graphics.val.shadow.high");
};
default -> "";
};
BitmapText vt = valTexts[row];
@@ -154,23 +162,30 @@ public class GraphicsScreen extends MenuScreen {
GraphicsSettings.ViewDistance[] vds = GraphicsSettings.ViewDistance.values();
edit.viewDistance = vds[(edit.viewDistance.ordinal() + dir + vds.length) % vds.length];
break;
case ROW_SHADOW:
GraphicsSettings.ShadowQuality[] sqs = GraphicsSettings.ShadowQuality.values();
edit.shadowQuality = sqs[(edit.shadowQuality.ordinal() + dir + sqs.length) % sqs.length];
break;
}
refreshText(row);
}
private void applyAndSave() {
boolean needsRestart = live.width != edit.width
|| live.height != edit.height
|| live.fullscreen != edit.fullscreen
|| live.vsync != edit.vsync
|| live.samples != edit.samples;
boolean shadowChanged = live.shadowQuality != edit.shadowQuality;
boolean needsRestart = live.width != edit.width
|| live.height != edit.height
|| live.fullscreen != edit.fullscreen
|| live.vsync != edit.vsync
|| live.samples != edit.samples
|| shadowChanged;
live.width = edit.width;
live.height = edit.height;
live.fullscreen = edit.fullscreen;
live.vsync = edit.vsync;
live.samples = edit.samples;
live.viewDistance = edit.viewDistance;
live.width = edit.width;
live.height = edit.height;
live.fullscreen = edit.fullscreen;
live.vsync = edit.vsync;
live.samples = edit.samples;
live.viewDistance = edit.viewDistance;
live.shadowQuality = edit.shadowQuality;
GraphicsStore.save(live);
WeatherState ws = getApplication().getStateManager().getState(WeatherState.class);

View File

@@ -1,12 +1,42 @@
package de.blight.game.config;
public class GraphicsSettings {
public int width = 1280;
public int height = 720;
public boolean fullscreen = false;
public boolean vsync = false;
public int samples = 4;
public ViewDistance viewDistance = ViewDistance.NORMAL;
public int width = 1280;
public int height = 720;
public boolean fullscreen = false;
public boolean vsync = false;
public int samples = 4;
public ViewDistance viewDistance = ViewDistance.NORMAL;
public ShadowQuality shadowQuality = ShadowQuality.MEDIUM;
/**
* Schattenqualitäts-Stufen.
* mapSize/splits sind Konstruktor-Parameter des Shadow-Filters → Neustart nötig.
* lambda/zExtend/zFade/backface können zur Laufzeit angepasst werden.
*/
public enum ShadowQuality {
// mapSize splits lambda zExtend zFade backface
LOW ( 1024, 2, 0.70f, 40f, 5f, false),
MEDIUM ( 2048, 3, 0.75f, 60f, 8f, true ),
HIGH ( 4096, 4, 0.80f, 80f, 10f, true );
public final int shadowMapSize;
public final int splits;
public final float lambda;
public final float zExtend;
public final float zFade;
public final boolean backface;
ShadowQuality(int shadowMapSize, int splits, float lambda,
float zExtend, float zFade, boolean backface) {
this.shadowMapSize = shadowMapSize;
this.splits = splits;
this.lambda = lambda;
this.zExtend = zExtend;
this.zFade = zFade;
this.backface = backface;
}
}
public enum ViewDistance {
SHORT (0.55f, 0, 2),

View File

@@ -535,6 +535,15 @@ public class WorldScene extends BaseAppState {
* Setzt Ambient=(1,1,1,1) auf allen Lighting.j3md-Materialien eines Modells,
* damit es den globalen AmbientLight korrekt empfängt (Blender-Export setzt Ambient oft auf schwarz).
*/
private static void applyShadowModeRecursive(Spatial s, RenderQueue.ShadowMode mode) {
s.setShadowMode(mode);
if (s instanceof Node n) {
for (Spatial child : n.getChildren()) {
applyShadowModeRecursive(child, mode);
}
}
}
public static void applyFullAmbient(Spatial s) {
if (s instanceof Geometry g) {
com.jme3.material.Material m = g.getMaterial();
@@ -575,7 +584,7 @@ public class WorldScene extends BaseAppState {
try {
Spatial loaded = assetManager.loadModel(mc.getModelPath());
stripEmbeddedClips(loaded, mc.getModelPath());
loaded.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
applyShadowModeRecursive(loaded, RenderQueue.ShadowMode.CastAndReceive);
applyFullAmbient(loaded);
collectPbrMaterials(loaded, characterPbrMaterials);
log.info("[WorldScene] {} PBR-Materialien im Charakter-Modell gefunden.", characterPbrMaterials.size());
@@ -685,8 +694,15 @@ public class WorldScene extends BaseAppState {
// Licht, Ambient und Sky kommen von DayNightState (der wurde in initialize() attached).
// Wir erstellen hier nur den Shadow-Filter und übergeben ihn an DayNightState,
// damit dieser Intensität und Lichtbindung dynamisch verwaltet.
GraphicsSettings.ShadowQuality sq = graphicsSettings.shadowQuality;
DirectionalLightShadowFilter shadowFilter =
new DirectionalLightShadowFilter(assetManager, 2048, 3);
new DirectionalLightShadowFilter(assetManager, sq.shadowMapSize, sq.splits);
shadowFilter.setEdgeFilteringMode(
sq == GraphicsSettings.ShadowQuality.LOW ? EdgeFilteringMode.PCF4 : EdgeFilteringMode.PCFPOISSON);
shadowFilter.setLambda(sq.lambda);
shadowFilter.setShadowZExtend(sq.zExtend);
shadowFilter.setShadowZFadeLength(sq.zFade);
shadowFilter.setRenderBackFacesShadows(sq.backface);
shadowFilter.setShadowIntensity(0.4f);
shadowFilter.setEnabled(true);
dayNight.setShadowFilter(shadowFilter); // bindet sun + übernimmt Intensitäts-Updates

View File

@@ -45,7 +45,7 @@ public class DayNightState extends BaseAppState implements TimeListener {
// ── Sonnenrichtungs-Drosselung ────────────────────────────────────────────
private static final float SUN_DIR_INTERVAL = 0.25f;
private static final float SUN_DIR_INTERVAL = 2.0f;
private float sunDirTimer = SUN_DIR_INTERVAL; // sofort bereit beim ersten Aufruf
// ── Höhlen-Abdunkelung ────────────────────────────────────────────────────