Schatten System verbessert und Schatten qualität einstellbar gemacht
This commit is contained in:
@@ -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;
|
||||
@@ -49,6 +50,7 @@ public class GraphicsScreen extends MenuScreen {
|
||||
edit.vsync = live.vsync;
|
||||
edit.samples = live.samples;
|
||||
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,16 +162,22 @@ 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 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;
|
||||
|| live.samples != edit.samples
|
||||
|| shadowChanged;
|
||||
|
||||
live.width = edit.width;
|
||||
live.height = edit.height;
|
||||
@@ -171,6 +185,7 @@ public class GraphicsScreen extends MenuScreen {
|
||||
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);
|
||||
|
||||
@@ -7,6 +7,36 @@ public class GraphicsSettings {
|
||||
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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ────────────────────────────────────────────────────
|
||||
|
||||
@@ -54,6 +54,10 @@ menu.graphics.val.off=Aus
|
||||
menu.graphics.val.viewdist.short=Kurz
|
||||
menu.graphics.val.viewdist.normal=Normal
|
||||
menu.graphics.val.viewdist.far=Weit
|
||||
menu.graphics.row.shadow=Schattenqualität
|
||||
menu.graphics.val.shadow.low=Niedrig
|
||||
menu.graphics.val.shadow.medium=Mittel
|
||||
menu.graphics.val.shadow.high=Hoch
|
||||
menu.graphics.btn.apply=Übernehmen
|
||||
menu.graphics.btn.cancel=Abbrechen
|
||||
|
||||
|
||||
@@ -57,6 +57,10 @@ menu.graphics.val.off=Off
|
||||
menu.graphics.val.viewdist.short=Short
|
||||
menu.graphics.val.viewdist.normal=Normal
|
||||
menu.graphics.val.viewdist.far=Far
|
||||
menu.graphics.row.shadow=Shadow Quality
|
||||
menu.graphics.val.shadow.low=Low
|
||||
menu.graphics.val.shadow.medium=Medium
|
||||
menu.graphics.val.shadow.high=High
|
||||
menu.graphics.btn.apply=Apply
|
||||
menu.graphics.btn.cancel=Cancel
|
||||
|
||||
|
||||
339
com/jme3/shadow/AbstractShadowFilter.java
Normal file
339
com/jme3/shadow/AbstractShadowFilter.java
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2025 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.shadow;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.math.Vector4f;
|
||||
import com.jme3.post.Filter;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.ViewPort;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.texture.FrameBuffer;
|
||||
import com.jme3.util.TempVars;
|
||||
import com.jme3.util.clone.Cloner;
|
||||
import com.jme3.util.clone.JmeCloneable;
|
||||
|
||||
/**
|
||||
* Generic abstract filter that holds common implementations for the different
|
||||
* shadow filters
|
||||
*
|
||||
* @author Rémy Bouquet aka Nehon
|
||||
*/
|
||||
public abstract class AbstractShadowFilter<T extends AbstractShadowRenderer> extends Filter implements JmeCloneable {
|
||||
|
||||
protected T shadowRenderer;
|
||||
protected ViewPort viewPort;
|
||||
|
||||
private final Vector4f tempVec4 = new Vector4f();
|
||||
private final Matrix4f tempMat4 = new Matrix4f();
|
||||
|
||||
/**
|
||||
* For serialization only. Do not use.
|
||||
*/
|
||||
protected AbstractShadowFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AbstractShadowFilter. Subclasses invoke this constructor.
|
||||
*
|
||||
* @param assetManager The application's asset manager.
|
||||
* @param shadowMapSize The size of the rendered shadow maps (e.g., 512, 1024, 2048).
|
||||
* @param shadowRenderer The shadowRenderer to use for this Filter
|
||||
*/
|
||||
protected AbstractShadowFilter(AssetManager assetManager, int shadowMapSize, T shadowRenderer) {
|
||||
super("Post Shadow");
|
||||
this.shadowRenderer = shadowRenderer;
|
||||
// this is legacy setting for shadows with backface shadows
|
||||
this.shadowRenderer.setRenderBackFacesShadows(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRequiresDepthTexture() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getMaterial()} instead.
|
||||
* @return The Material used by this filter.
|
||||
*/
|
||||
@Deprecated
|
||||
public Material getShadowMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preFrame(float tpf) {
|
||||
shadowRenderer.preFrame(tpf);
|
||||
Matrix4f m = viewPort.getCamera().getViewProjectionMatrix();
|
||||
material.setMatrix4("ViewProjectionMatrixInverse", tempMat4.set(m).invertLocal());
|
||||
material.setVector4("ViewProjectionMatrixRow2", tempVec4.set(m.m20, m.m21, m.m22, m.m23));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postQueue(RenderQueue queue) {
|
||||
shadowRenderer.postQueue(queue);
|
||||
if (shadowRenderer.skipPostPass) {
|
||||
// removing the shadow map so that the post pass is skipped
|
||||
material.setTexture("ShadowMap0", null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
|
||||
if (!shadowRenderer.skipPostPass) {
|
||||
shadowRenderer.setPostShadowParams();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
|
||||
shadowRenderer.needsfallBackMaterial = true;
|
||||
material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md");
|
||||
shadowRenderer.setPostShadowMaterial(material);
|
||||
shadowRenderer.initialize(renderManager, vp);
|
||||
this.viewPort = vp;
|
||||
}
|
||||
|
||||
/**
|
||||
* How far the shadows are rendered in the view
|
||||
*
|
||||
* @return shadowZExtend
|
||||
* @see #setShadowZExtend(float zFar)
|
||||
*/
|
||||
public float getShadowZExtend() {
|
||||
return shadowRenderer.getShadowZExtend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the distance from the eye where the shadows will be rendered default
|
||||
* value is dynamically computed to the shadow casters/receivers union bound
|
||||
* zFar, capped to view frustum far value.
|
||||
*
|
||||
* @param zFar the zFar values that override the computed one
|
||||
*/
|
||||
public void setShadowZExtend(float zFar) {
|
||||
shadowRenderer.setShadowZExtend(zFar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the length over which the shadow will fade out when using a
|
||||
* shadowZextend
|
||||
*
|
||||
* @param length the fade length in world units
|
||||
*/
|
||||
public void setShadowZFadeLength(float length) {
|
||||
shadowRenderer.setShadowZFadeLength(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the length over which the shadow will fade out when using a
|
||||
* shadowZextend
|
||||
*
|
||||
* @return the fade length in world units
|
||||
*/
|
||||
public float getShadowZFadeLength() {
|
||||
return shadowRenderer.getShadowZFadeLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the shadow intensity
|
||||
*
|
||||
* @see #setShadowIntensity(float shadowIntensity)
|
||||
* @return shadowIntensity
|
||||
*/
|
||||
public float getShadowIntensity() {
|
||||
return shadowRenderer.getShadowIntensity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the shadowIntensity, the value should be between 0 and 1, a 0 value
|
||||
* gives a bright and invisible shadow, a 1 value gives a pitch black
|
||||
* shadow, default is 0.7
|
||||
*
|
||||
* @param shadowIntensity the darkness of the shadow
|
||||
*/
|
||||
final public void setShadowIntensity(float shadowIntensity) {
|
||||
shadowRenderer.setShadowIntensity(shadowIntensity);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the edges thickness <br>
|
||||
*
|
||||
* @see #setEdgesThickness(int edgesThickness)
|
||||
* @return edgesThickness
|
||||
*/
|
||||
public int getEdgesThickness() {
|
||||
return shadowRenderer.getEdgesThickness();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the shadow edges thickness. Default is 10. Setting it to lower values
|
||||
* can help reduce the jagged effect of shadow edges.
|
||||
*
|
||||
* @param edgesThickness the desired thickness (in tenths of a pixel, default=10)
|
||||
*/
|
||||
public void setEdgesThickness(int edgesThickness) {
|
||||
shadowRenderer.setEdgesThickness(edgesThickness);
|
||||
}
|
||||
|
||||
/**
|
||||
* isFlushQueues does nothing and is kept only for backward compatibility
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isFlushQueues() {
|
||||
return shadowRenderer.isFlushQueues();
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the shadow compare mode see {@link CompareMode} for more info
|
||||
*
|
||||
* @param compareMode the desired mode
|
||||
*/
|
||||
final public void setShadowCompareMode(CompareMode compareMode) {
|
||||
shadowRenderer.setShadowCompareMode(compareMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the shadow compare mode
|
||||
*
|
||||
* @see CompareMode
|
||||
* @return the shadowCompareMode
|
||||
*/
|
||||
public CompareMode getShadowCompareMode() {
|
||||
return shadowRenderer.getShadowCompareMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filtering mode for shadow edges see {@link EdgeFilteringMode}
|
||||
* for more info
|
||||
*
|
||||
* @param filterMode the desired mode
|
||||
*/
|
||||
final public void setEdgeFilteringMode(EdgeFilteringMode filterMode) {
|
||||
shadowRenderer.setEdgeFilteringMode(filterMode);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* !! WARNING !! this parameter is defaulted to true for the ShadowFilter.
|
||||
* Setting it to true, may produce edges artifacts on shadows.
|
||||
*
|
||||
* Set to true if you want back faces shadows on geometries.
|
||||
* Note that back faces shadows will be blended over dark lighten areas and may produce overly dark lighting.
|
||||
*
|
||||
* Setting this parameter will override this parameter for ALL materials in the scene.
|
||||
* This also will automatically adjust the faceCullMode and the PolyOffset of the pre shadow pass.
|
||||
* You can modify them by using {@link #getPreShadowForcedRenderState()}
|
||||
*
|
||||
* If you want to set it differently for each material in the scene you have to use the ShadowRenderer instead
|
||||
* of the shadow filter.
|
||||
*
|
||||
* @param renderBackFacesShadows true or false.
|
||||
*/
|
||||
public void setRenderBackFacesShadows(boolean renderBackFacesShadows) {
|
||||
shadowRenderer.setRenderBackFacesShadows(renderBackFacesShadows);
|
||||
}
|
||||
|
||||
/**
|
||||
* if this filter renders back faces shadows
|
||||
* @return true if this filter renders back faces shadows
|
||||
*/
|
||||
public boolean isRenderBackFacesShadows() {
|
||||
return shadowRenderer.isRenderBackFacesShadows();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the pre shadows pass render state.
|
||||
* use it to adjust the RenderState parameters of the pre shadow pass.
|
||||
* Note that this will be overridden if the preShadow technique in the material has a ForcedRenderState
|
||||
* @return the pre shadow render state.
|
||||
*/
|
||||
public RenderState getPreShadowForcedRenderState() {
|
||||
return shadowRenderer.getPreShadowForcedRenderState();
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the edge filtering mode
|
||||
*
|
||||
* @see EdgeFilteringMode
|
||||
* @return the enum value
|
||||
*/
|
||||
public EdgeFilteringMode getEdgeFilteringMode() {
|
||||
return shadowRenderer.getEdgeFilteringMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the number of shadow maps rendered by this filter.
|
||||
*
|
||||
* @return count
|
||||
*/
|
||||
public int getNumShadowMaps() {
|
||||
return shadowRenderer.getNumShadowMaps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the size of each shadow map rendered by this filter.
|
||||
*
|
||||
* @return a map's height (which is also its width, in pixels)
|
||||
*/
|
||||
public int getShadowMapSize() {
|
||||
return shadowRenderer.getShadowMapSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public AbstractShadowFilter<T> jmeClone() {
|
||||
try {
|
||||
return (AbstractShadowFilter<T>) super.clone();
|
||||
} catch (final CloneNotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cloneFields(final Cloner cloner, final Object original) {
|
||||
material = cloner.clone(material);
|
||||
shadowRenderer = cloner.clone(shadowRenderer);
|
||||
shadowRenderer.setPostShadowMaterial(material);
|
||||
}
|
||||
|
||||
}
|
||||
157
com/jme3/shadow/DirectionalLightShadowFilter.java
Normal file
157
com/jme3/shadow/DirectionalLightShadowFilter.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2024 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.shadow;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.export.InputCapsule;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.OutputCapsule;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This Filter does basically the same as a DirectionalLightShadowRenderer
|
||||
* except it renders the post shadow pass as a fullscreen quad pass instead of a
|
||||
* geometry pass. It's mostly faster than PssmShadowRenderer as long as you have
|
||||
* more than about ten shadow receiving objects. The expense is the drawback
|
||||
* that the shadow Receive mode set on spatial is ignored. So basically all and
|
||||
* only objects that render depth in the scene receive shadows.
|
||||
*
|
||||
* API is basically the same as the PssmShadowRenderer.
|
||||
*
|
||||
* @author Rémy Bouquet aka Nehon
|
||||
*/
|
||||
public class DirectionalLightShadowFilter extends AbstractShadowFilter<DirectionalLightShadowRenderer> {
|
||||
|
||||
/**
|
||||
* For serialization only. Do not use.
|
||||
*
|
||||
* @see #DirectionalLightShadowFilter(AssetManager assetManager, int shadowMapSize, int nbSplits)
|
||||
*/
|
||||
public DirectionalLightShadowFilter() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DirectionalLightShadowFilter.
|
||||
*
|
||||
* @param assetManager the application's asset manager
|
||||
* @param shadowMapSize the size of the rendered shadow maps (512, 1024, 2048, etc...)
|
||||
* @param nbSplits the number of shadow maps rendered (more shadow maps = better quality, but slower)
|
||||
*
|
||||
* @throws IllegalArgumentException if the provided 'nbSplits' is not within the valid range of 1 to 4.
|
||||
*/
|
||||
public DirectionalLightShadowFilter(AssetManager assetManager, int shadowMapSize, int nbSplits) {
|
||||
super(assetManager, shadowMapSize, new DirectionalLightShadowRenderer(assetManager, shadowMapSize, nbSplits));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the light used to cast shadows.
|
||||
*
|
||||
* @return the DirectionalLight
|
||||
*/
|
||||
public DirectionalLight getLight() {
|
||||
return shadowRenderer.getLight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the light to use to cast shadows.
|
||||
*
|
||||
* @param light a DirectionalLight
|
||||
*/
|
||||
public void setLight(DirectionalLight light) {
|
||||
shadowRenderer.setLight(light);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lambda parameter.
|
||||
*
|
||||
* @see #setLambda(float lambda)
|
||||
* @return lambda
|
||||
*/
|
||||
public float getLambda() {
|
||||
return shadowRenderer.getLambda();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the partition of the shadow extend into shadow maps. Lambda is
|
||||
* usually between 0 and 1.
|
||||
* <p>
|
||||
* A low value gives a more linear partition, resulting in consistent shadow
|
||||
* quality over the extend, but near shadows could look very jagged. A high
|
||||
* value gives a more logarithmic partition, resulting in high quality for near
|
||||
* shadows, but quality decreases rapidly with distance.
|
||||
* <p>
|
||||
* The default value is 0.65 (the theoretical optimum).
|
||||
*
|
||||
* @param lambda the lambda value
|
||||
*/
|
||||
public void setLambda(float lambda) {
|
||||
shadowRenderer.setLambda(lambda);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if stabilization is enabled.
|
||||
*
|
||||
* @return true if stabilization is enabled
|
||||
*/
|
||||
public boolean isEnabledStabilization() {
|
||||
return shadowRenderer.isEnabledStabilization();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the stabilization of the shadow's edges. (default is true)
|
||||
* This prevents shadow edges from flickering when the camera moves.
|
||||
* However, it can lead to some loss of shadow quality in particular scenes.
|
||||
*
|
||||
* @param stabilize true to stabilize, false to disable stabilization
|
||||
*/
|
||||
public void setEnabledStabilization(boolean stabilize) {
|
||||
shadowRenderer.setEnabledStabilization(stabilize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JmeExporter ex) throws IOException {
|
||||
super.write(ex);
|
||||
OutputCapsule oc = ex.getCapsule(this);
|
||||
oc.write(shadowRenderer, "shadowRenderer", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(JmeImporter im) throws IOException {
|
||||
super.read(im);
|
||||
InputCapsule ic = im.getCapsule(this);
|
||||
shadowRenderer = (DirectionalLightShadowRenderer) ic.readSavable("shadowRenderer", null);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user