From cfc2c96d1a4a5f742350d30a8b1b41c8f8ae651c Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Mon, 23 Jun 2025 17:05:04 +0200 Subject: [PATCH 1/4] Update DirectionalLightShadowRenderer.java --- .../DirectionalLightShadowRenderer.java | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java index 0b50f8be4b..d09c1cbb76 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2021 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -59,37 +59,40 @@ * shadow mapping.
for more information on this read https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html
* - * @author Rémy Bouquet aka Nehon + * @author Nehon */ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer { - protected float lambda = 0.65f; + // Default lambda value, optimizing shadow partition + protected float lambda = 0.65f; protected Camera shadowCam; + // Stores the normalized split distances for shader use (RGBA channels) protected ColorRGBA splits; + // Stores the actual split distances in world space protected float[] splitsArray; protected DirectionalLight light; - protected Vector3f[] points = new Vector3f[8]; - //Holding the info for fading shadows in the far distance + // Reusable array for frustum points to avoid repeated allocations + protected final Vector3f[] points = new Vector3f[8]; + // Reusable temporary vector to avoid repeated allocations + protected final Vector3f tempVec = new Vector3f(); + // Flag to enable or disable shadow edge stabilization private boolean stabilize = true; /** - * Used for serialization use - * DirectionalLightShadowRenderer#DirectionalLightShadowRenderer(AssetManager - * assetManager, int shadowMapSize, int nbSplits) + * For serialization only. Do not use. */ protected DirectionalLightShadowRenderer() { super(); } /** - * Creates a DirectionalLight shadow renderer. More info on the technique at https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html + * Creates a DirectionalLight shadow renderer. This renderer implements the + * Parallel Split Shadow Mapping (PSSM) technique. * - * @param assetManager the application's asset manager - * @param shadowMapSize the size of the rendered shadowmaps (512, 1024, 2048, - * etcetera) - * @param nbSplits the number of shadow maps rendered (More shadow maps yield - * better quality, fewer fps.) + * @param assetManager The application's asset manager. + * @param shadowMapSize The size of the rendered shadow maps (e.g., 512, 1024, 2048). + * @param nbSplits The number of shadow maps to render (1 to 4). More maps + * improve quality but can reduce performance. */ public DirectionalLightShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbSplits) { super(assetManager, shadowMapSize, nbSplits); @@ -151,16 +154,16 @@ protected void updateShadowCams(Camera viewCam) { ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points); shadowCam.setFrustumFar(zFar); - shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp()); + shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp(tempVec)); shadowCam.update(); shadowCam.updateViewProjection(); PssmShadowUtil.updateFrustumSplits(splitsArray, frustumNear, zFar, lambda); // in parallel projection shadow position goe from 0 to 1 - if(viewCam.isParallelProjection()){ + if (viewCam.isParallelProjection()) { for (int i = 0; i < nbShadowMaps; i++) { - splitsArray[i] = splitsArray[i]/(zFar- frustumNear); + splitsArray[i] = splitsArray[i] / (zFar - frustumNear); } } @@ -176,7 +179,6 @@ protected void updateShadowCams(Camera viewCam) { splits.r = splitsArray[1]; break; } - } @Override @@ -185,20 +187,21 @@ protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sha // update frustum points based on current camera and split ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], 1.0f, points); - //Updating shadow cam with current split frusta - if (lightReceivers.size()==0) { + // If light receivers haven't been identified yet, find them within the view frustum + if (lightReceivers.size() == 0) { for (Spatial scene : viewPort.getScenes()) { - ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), RenderQueue.ShadowMode.Receive, lightReceivers); + ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), RenderQueue.ShadowMode.Receive, lightReceivers); } } - ShadowUtil.updateShadowCamera(viewPort, lightReceivers, shadowCam, points, shadowMapOccluders, stabilize?shadowMapSize:0); + // Update the shadow camera's projection based on the occluders and stabilization setting + ShadowUtil.updateShadowCamera(viewPort, lightReceivers, shadowCam, points, shadowMapOccluders, stabilize ? shadowMapSize : 0); return shadowMapOccluders; } @Override protected void getReceivers(GeometryList lightReceivers) { - if (lightReceivers.size()==0) { + if (lightReceivers.size() == 0) { for (Spatial scene : viewPort.getScenes()) { ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), RenderQueue.ShadowMode.Receive, lightReceivers); } From 7d5a0d73f2e4dcf23123a24c27f484fc411abef5 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Tue, 24 Jun 2025 15:07:43 +0200 Subject: [PATCH 2/4] DirectionalLightShadowRenderer: fix javadoc --- .../DirectionalLightShadowRenderer.java | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java index d09c1cbb76..adb2136539 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java @@ -37,6 +37,7 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.light.DirectionalLight; +import com.jme3.light.SpotLight; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; @@ -51,14 +52,19 @@ import java.io.IOException; /** - * DirectionalLightShadowRenderer renderer use Parallel Split Shadow Mapping - * technique (pssm)
It splits the view frustum in several parts and compute - * a shadow map for each one.
splits are distributed so that the closer they - * are from the camera, the smaller they are to maximize the resolution used of - * the shadow map.
This results in a better quality shadow than standard - * shadow mapping.
for more information on this read https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html
+ * Implements a shadow renderer specifically for {@link DirectionalLight DirectionalLight} + * using the **Parallel Split Shadow Mapping (PSSM)** technique. * + *

PSSM divides the camera's view frustum into multiple sections, + * generating a separate shadow map for each. These splits are + * intelligently distributed, with smaller, higher-resolution maps for areas + * closer to the camera and larger, lower-resolution maps for distant areas. + * This approach optimizes shadow map usage, leading to superior shadow quality + * compared to standard shadow mapping techniques. + * + *

For a detailed explanation of PSSM, refer to: + * GPU Gems 3, Chapter 10: Parallel-Split Shadow Maps on Programmable GPUs + * * @author Nehon */ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer { @@ -89,10 +95,11 @@ protected DirectionalLightShadowRenderer() { * Creates a DirectionalLight shadow renderer. This renderer implements the * Parallel Split Shadow Mapping (PSSM) technique. * - * @param assetManager The application's asset manager. + * @param assetManager The application's asset manager. * @param shadowMapSize The size of the rendered shadow maps (e.g., 512, 1024, 2048). - * @param nbSplits The number of shadow maps to render (1 to 4). More maps - * improve quality but can reduce performance. + * Higher values produce better quality shadows but may impact performance. + * @param nbSplits The number of shadow maps to render (1 to 4). More maps + * improve quality but can reduce performance. */ public DirectionalLightShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbSplits) { super(assetManager, shadowMapSize, nbSplits); @@ -100,10 +107,12 @@ public DirectionalLightShadowRenderer(AssetManager assetManager, int shadowMapSi } private void init(int nbSplits, int shadowMapSize) { - nbShadowMaps = Math.max(Math.min(nbSplits, 4), 1); - if (nbShadowMaps != nbSplits) { - throw new IllegalArgumentException("Number of splits must be between 1 and 4. Given value : " + nbSplits); + // Ensure the number of shadow maps is within the valid range [1, 4] + if (nbSplits < 1 || nbSplits > 4) { + throw new IllegalArgumentException("Number of splits must be between 1 and 4. Given value: " + nbSplits); } + + nbShadowMaps = nbSplits; splits = new ColorRGBA(); splitsArray = new float[nbSplits + 1]; shadowCam = new Camera(shadowMapSize, shadowMapSize); From 2b06cbcc3da2a6330c4a1e4d4c4bc7ec69a53748 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Tue, 24 Jun 2025 15:14:42 +0200 Subject: [PATCH 3/4] DirectionalLightShadowRenderer: remove unused imports --- .../java/com/jme3/shadow/DirectionalLightShadowRenderer.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java index adb2136539..d1273694db 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java @@ -37,7 +37,6 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.light.DirectionalLight; -import com.jme3.light.SpotLight; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; @@ -61,10 +60,10 @@ * closer to the camera and larger, lower-resolution maps for distant areas. * This approach optimizes shadow map usage, leading to superior shadow quality * compared to standard shadow mapping techniques. - * + * *

For a detailed explanation of PSSM, refer to: * GPU Gems 3, Chapter 10: Parallel-Split Shadow Maps on Programmable GPUs - * + * * @author Nehon */ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer { From 00d8c624fe75ababc93e21bbbd44383d3efe6a3d Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Thu, 10 Jul 2025 19:06:39 +0200 Subject: [PATCH 4/4] DirectionalLightShadowRenderer: restored original author name --- .../java/com/jme3/shadow/DirectionalLightShadowRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java index d1273694db..ba276857c8 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java @@ -64,7 +64,7 @@ *

For a detailed explanation of PSSM, refer to: * GPU Gems 3, Chapter 10: Parallel-Split Shadow Maps on Programmable GPUs * - * @author Nehon + * @author Rémy Bouquet aka Nehon */ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {