From f440527d44015f8185f3ffa266ace7cf9c6804ef Mon Sep 17 00:00:00 2001
From: adfriz <76892624+adfriz@users.noreply.github.com>
Date: Tue, 23 Jun 2026 22:30:13 +0700
Subject: [PATCH] Optimize shadow rendering: static object draw state caching
and low-res far shadows option
---
.../Shadows/CascadedShadowCaster.cs | 8 ++-
.../LibRender2/Shadows/CascadedShadowMap.cs | 27 +++++---
source/LibRender2/Shadows/Shadows.cs | 64 ++++++++++++-------
source/ObjectViewer/formOptions.cs | 31 +++++++++
source/OpenBVE/System/Options.cs | 2 +
.../OpenBVE/UserInterface/formMain.Options.cs | 6 ++
source/OpenBVE/UserInterface/formMain.cs | 19 ++++++
.../System/BaseOptions.OptionsKey.cs | 1 +
source/OpenBveApi/System/BaseOptions.cs | 2 +
source/RouteViewer/formOptions.cs | 36 ++++++++++-
10 files changed, 160 insertions(+), 36 deletions(-)
diff --git a/source/LibRender2/Shadows/CascadedShadowCaster.cs b/source/LibRender2/Shadows/CascadedShadowCaster.cs
index 6c9f63ecc..6cf0eac19 100644
--- a/source/LibRender2/Shadows/CascadedShadowCaster.cs
+++ b/source/LibRender2/Shadows/CascadedShadowCaster.cs
@@ -23,7 +23,7 @@ public class CascadedShadowCaster
public double DepthMargin { get; set; } = 40.0;
/// Active shadow map resolution (used for texel snapping).
- public int Resolution { get; set; } = 2048;
+ public int[] Resolutions { get; set; }
/// Per-cascade light-space VP matrices.
public Matrix4D[] LightSpaceMatrices { get; private set; }
@@ -45,10 +45,12 @@ public CascadedShadowCaster(int cascadeCount = 3)
LightSpaceMatrices = new Matrix4D[cascadeCount];
SplitDistances = new float[cascadeCount];
CascadeBiases = new float[cascadeCount];
+ Resolutions = new int[cascadeCount];
for (int i = 0; i < cascadeCount; i++)
{
LightSpaceMatrices[i] = Matrix4D.Identity;
+ Resolutions[i] = 2048;
}
}
@@ -120,7 +122,7 @@ public void Update(Vector3 lightDirection, Matrix4D cameraView, Matrix4D cameraP
// === Texel snapping to prevent shadow swimming on camera move ===
// Snap the light-space center to texel boundaries
// Each texel covers (2*orthoSize / Resolution) world units
- double worldTexelSize = (orthoSize * 2.0) / (double)Resolution;
+ double worldTexelSize = (orthoSize * 2.0) / (double)Resolutions[i];
// Transform center into light view space to snap it
Vector3 centerLS = TransformPoint(center, lightView);
@@ -145,7 +147,7 @@ public void Update(Vector3 lightDirection, Matrix4D cameraView, Matrix4D cameraP
// Z-Bias: Convert physical texel size into a Depth Buffer fraction.
// This ensures we push the depth exactly enough to cure acne, but no more.
- double texelWorldSize = (orthoSize * 2.0) / (double)Resolution;
+ double texelWorldSize = (orthoSize * 2.0) / (double)Resolutions[i];
double depthRange = zFar - zNear;
double baseBias = texelWorldSize / depthRange;
CascadeBiases[i] = (float)baseBias;
diff --git a/source/LibRender2/Shadows/CascadedShadowMap.cs b/source/LibRender2/Shadows/CascadedShadowMap.cs
index 2f2f505e2..dd6752e71 100644
--- a/source/LibRender2/Shadows/CascadedShadowMap.cs
+++ b/source/LibRender2/Shadows/CascadedShadowMap.cs
@@ -12,8 +12,11 @@ public class CascadedShadowMap : IDisposable
/// Number of cascades (shadow splits).
public int CascadeCount { get; private set; }
- /// Shadow map resolution per cascade.
- public int Resolution { get; private set; }
+ /// Base shadow map resolution.
+ public int BaseResolution { get; private set; }
+
+ /// Resolutions per cascade.
+ public int[] Resolutions { get; private set; }
/// Per-cascade FBO handles.
public int[] FBOs { get; private set; }
@@ -26,15 +29,18 @@ public class CascadedShadowMap : IDisposable
///
/// Number of cascades (typically 3 or 4).
/// Resolution of each cascade's depth texture.
- public CascadedShadowMap(int cascadeCount = 3, int resolution = 2048)
+ /// Whether to downscale resolutions for far cascades.
+ public CascadedShadowMap(int cascadeCount = 3, int resolution = 2048, bool lowResFarShadows = true)
{
CascadeCount = cascadeCount;
- Resolution = resolution;
+ BaseResolution = resolution;
FBOs = new int[cascadeCount];
DepthTextures = new int[cascadeCount];
+ Resolutions = new int[cascadeCount];
for (int i = 0; i < cascadeCount; i++)
{
+ Resolutions[i] = (lowResFarShadows && i > 0) ? Math.Max(128, resolution >> i) : resolution;
CreateCascade(i);
}
}
@@ -45,24 +51,27 @@ public CascadedShadowMap(int cascadeCount = 3, int resolution = 2048)
///
/// New number of cascades.
/// New resolution per cascade.
- public void Resize(int newCascadeCount, int newResolution)
+ /// Whether to downscale resolutions for far cascades.
+ public void Resize(int newCascadeCount, int newResolution, bool lowResFarShadows = true)
{
// Dispose old resources
Dispose();
// Reallocate
CascadeCount = newCascadeCount;
- Resolution = newResolution;
+ BaseResolution = newResolution;
FBOs = new int[newCascadeCount];
DepthTextures = new int[newCascadeCount];
+ Resolutions = new int[newCascadeCount];
for (int i = 0; i < newCascadeCount; i++)
{
+ Resolutions[i] = (lowResFarShadows && i > 0) ? Math.Max(128, newResolution >> i) : newResolution;
CreateCascade(i);
}
Console.WriteLine(
- $"[CSM] Resized to {newCascadeCount} cascades at {newResolution}×{newResolution}");
+ $"[CSM] Resized to {newCascadeCount} cascades with downscaling={lowResFarShadows}");
}
private void CreateCascade(int index)
@@ -72,7 +81,7 @@ private void CreateCascade(int index)
GL.BindTexture(TextureTarget.Texture2D, DepthTextures[index]);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.DepthComponent24,
- Resolution, Resolution, 0,
+ Resolutions[index], Resolutions[index], 0,
PixelFormat.DepthComponent, PixelType.Float, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D,
TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
@@ -118,7 +127,7 @@ private void CreateCascade(int index)
public void BindCascadeForWriting(int cascadeIndex)
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FBOs[cascadeIndex]);
- GL.Viewport(0, 0, Resolution, Resolution);
+ GL.Viewport(0, 0, Resolutions[cascadeIndex], Resolutions[cascadeIndex]);
}
/// Unbinds shadow FBO.
diff --git a/source/LibRender2/Shadows/Shadows.cs b/source/LibRender2/Shadows/Shadows.cs
index 82391bca8..3decddd8a 100644
--- a/source/LibRender2/Shadows/Shadows.cs
+++ b/source/LibRender2/Shadows/Shadows.cs
@@ -59,11 +59,11 @@ public void Initialize()
{
if (Map == null)
{
- Map = new CascadedShadowMap(cascadeCount, resolution);
+ Map = new CascadedShadowMap(cascadeCount, resolution, opts.LowResFarShadows);
}
else
{
- Map.Resize(cascadeCount, resolution);
+ Map.Resize(cascadeCount, resolution, opts.LowResFarShadows);
}
if (Caster == null || cascadeCount != Caster.CascadeCount)
@@ -72,7 +72,7 @@ public void Initialize()
}
Caster.ShadowDistance = shadowDistance;
- Caster.Resolution = resolution;
+ Caster.Resolutions = Map.Resolutions;
Caster.SplitLambda = 0.75;
Caster.DepthMargin = 150.0;
@@ -119,7 +119,7 @@ public void RenderPass()
// 2. Update cascade matrices
// NOTE: We pass renderer.CurrentViewMatrix here which reflects the camera's rotation.
// The Caster will use this to align the shadow frustums with the view direction.
- Caster.Resolution = Map.Resolution;
+ Caster.Resolutions = Map.Resolutions;
if (renderer.currentOptions.ShadowDrawDistance == ShadowDistance.ViewingDistance)
{
Caster.ShadowDistance = renderer.currentOptions.ViewingDistance;
@@ -194,6 +194,9 @@ private void RenderFaces(IEnumerable faces, ref int lastVAO)
private void RenderFacesFiltered(IEnumerable faces, ref int lastVAO, double maxDistanceSquared)
{
Vector3 cameraPos = renderer.Camera.AbsolutePosition;
+ ObjectState lastObject = null;
+ MeshMaterial? lastMaterial = null;
+ int lastTextureId = -1;
foreach (var face in faces)
{
@@ -217,33 +220,50 @@ private void RenderFacesFiltered(IEnumerable faces, ref int lastVAO,
}
}
- DepthShader.SetModelMatrix(state.ModelMatrix * renderer.Camera.TranslationMatrix);
- DepthShader.SetTextureMatrix(state.TextureTranslation);
-
var material = face.Object.Prototype.Mesh.Materials[face.Face.Material];
if ((material.Flags & MaterialFlags.NoShadow) != 0 || material.BlendMode == MeshMaterialBlendMode.Additive)
{
continue;
}
- if (material.DaytimeTexture != null && renderer.currentHost.LoadTexture(ref material.DaytimeTexture, (OpenGlTextureWrapMode)(material.WrapMode ?? OpenGlTextureWrapMode.ClampClamp)))
- {
- GL.ActiveTexture(TextureUnit.Texture0);
- GL.BindTexture(TextureTarget.Texture2D, material.DaytimeTexture.OpenGlTextures[(int)(material.WrapMode ?? OpenGlTextureWrapMode.ClampClamp)].Name);
- DepthShader.SetHasTexture(true);
- }
- else
+
+ // Cache and skip redundant object matrix/animation uniform updates
+ if (state != lastObject)
{
- DepthShader.SetHasTexture(false);
+ DepthShader.SetModelMatrix(state.ModelMatrix * renderer.Camera.TranslationMatrix);
+ DepthShader.SetTextureMatrix(state.TextureTranslation);
+
+ if (state.Matricies != null && state.Matricies.Length > 0)
+ {
+ DepthShader.SetCurrentAnimationMatricies(state);
+ GL.BindBufferBase(BufferTarget.UniformBuffer, 0, state.MatrixBufferIndex);
+ }
+ lastObject = state;
}
- DepthShader.SetAlphaCutoff(0.5f);
- DepthShader.SetMaterialAlpha(material.Color.A / 255.0f);
- DepthShader.SetMaterialFlags(material.Flags);
-
- if (state.Matricies != null && state.Matricies.Length > 0)
+ // Cache and skip redundant material and texture state binds
+ if (!lastMaterial.HasValue || material != lastMaterial.Value)
{
- DepthShader.SetCurrentAnimationMatricies(state);
- GL.BindBufferBase(BufferTarget.UniformBuffer, 0, state.MatrixBufferIndex);
+ int textureId = -1;
+ if (material.DaytimeTexture != null && renderer.currentHost.LoadTexture(ref material.DaytimeTexture, (OpenGlTextureWrapMode)(material.WrapMode ?? OpenGlTextureWrapMode.ClampClamp)))
+ {
+ textureId = material.DaytimeTexture.OpenGlTextures[(int)(material.WrapMode ?? OpenGlTextureWrapMode.ClampClamp)].Name;
+ }
+
+ if (textureId != lastTextureId)
+ {
+ if (textureId != -1)
+ {
+ GL.ActiveTexture(TextureUnit.Texture0);
+ GL.BindTexture(TextureTarget.Texture2D, textureId);
+ }
+ DepthShader.SetHasTexture(textureId != -1);
+ lastTextureId = textureId;
+ }
+
+ DepthShader.SetAlphaCutoff(0.5f);
+ DepthShader.SetMaterialAlpha(material.Color.A / 255.0f);
+ DepthShader.SetMaterialFlags(material.Flags);
+ lastMaterial = material;
}
VertexArrayObject vao = (VertexArrayObject)face.Object.Prototype.Mesh.VAO;
diff --git a/source/ObjectViewer/formOptions.cs b/source/ObjectViewer/formOptions.cs
index aa93a17b6..4397869ef 100644
--- a/source/ObjectViewer/formOptions.cs
+++ b/source/ObjectViewer/formOptions.cs
@@ -87,6 +87,26 @@ private formOptions()
comboBoxBackwards.SelectedItem = Interface.CurrentOptions.CameraMoveBackward;
checkBoxAutoReload.Checked = Interface.CurrentOptions.AutoReloadObjects;
checkBoxShadowFilterCascades.Checked = Interface.CurrentOptions.ShadowFilterCascades;
+
+ // Dynamically add LowResFarShadows checkbox
+ CheckBox checkBoxLowResFarShadows = new CheckBox();
+ checkBoxLowResFarShadows.AutoSize = true;
+ checkBoxLowResFarShadows.Location = new System.Drawing.Point(160, 215);
+ checkBoxLowResFarShadows.Name = "checkBoxLowResFarShadows";
+ checkBoxLowResFarShadows.Size = new System.Drawing.Size(15, 14);
+ checkBoxLowResFarShadows.Text = "";
+ checkBoxLowResFarShadows.Checked = Interface.CurrentOptions.LowResFarShadows;
+ checkBoxLowResFarShadows.Enabled = (Interface.CurrentOptions.ShadowResolution != ShadowMapResolution.Off);
+ checkBoxLowResFarShadows.UseVisualStyleBackColor = true;
+ this.tabPageShadows.Controls.Add(checkBoxLowResFarShadows);
+
+ Label labelLowResFarShadows = new Label();
+ labelLowResFarShadows.AutoSize = true;
+ labelLowResFarShadows.Location = new System.Drawing.Point(6, 215);
+ labelLowResFarShadows.Name = "labelLowResFarShadows";
+ labelLowResFarShadows.Size = new System.Drawing.Size(126, 13);
+ labelLowResFarShadows.Text = "Low-res Far Shadows:";
+ this.tabPageShadows.Controls.Add(labelLowResFarShadows);
}
private void InitializeSunSliders()
@@ -111,6 +131,12 @@ private void UpdateShadowControlsEnabled()
trackBarSunElevation.Enabled = enabled;
checkBoxShadowFilterCascades.Enabled = enabled;
+
+ CheckBox checkBoxLowResFarShadows = this.tabPageShadows.Controls["checkBoxLowResFarShadows"] as CheckBox;
+ if (checkBoxLowResFarShadows != null)
+ {
+ checkBoxLowResFarShadows.Enabled = enabled;
+ }
}
private void comboBoxShadowResolution_SelectedIndexChanged(object sender, EventArgs e)
@@ -281,6 +307,11 @@ private void CloseButton_Click(object sender, EventArgs e)
Interface.CurrentOptions.ShadowBias = (double)numericUpDownShadowBias.Value;
Interface.CurrentOptions.ShadowNormalBias = (double)numericUpDownShadowNormalBias.Value;
Interface.CurrentOptions.ShadowFilterCascades = checkBoxShadowFilterCascades.Checked;
+ CheckBox checkBoxLowResFarShadows = this.tabPageShadows.Controls["checkBoxLowResFarShadows"] as CheckBox;
+ if (checkBoxLowResFarShadows != null)
+ {
+ Interface.CurrentOptions.LowResFarShadows = checkBoxLowResFarShadows.Checked;
+ }
Interface.CurrentOptions.Save(Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/options_ov.cfg"));
Program.RefreshObjects();
diff --git a/source/OpenBVE/System/Options.cs b/source/OpenBVE/System/Options.cs
index db2bd96ce..be8545897 100644
--- a/source/OpenBVE/System/Options.cs
+++ b/source/OpenBVE/System/Options.cs
@@ -326,6 +326,7 @@ public override void Save(string fileName)
Builder.AppendLine("shadowbias = " + ShadowBias.ToString(Culture));
Builder.AppendLine("shadownormalbias = " + ShadowNormalBias.ToString(Culture));
Builder.AppendLine("shadowfiltercascades = " + (ShadowFilterCascades ? "true" : "false"));
+ Builder.AppendLine("lowresfarshadows = " + (LowResFarShadows ? "true" : "false"));
Builder.AppendLine("fpslimit = " + FPSLimit.ToString(Culture));
Builder.AppendLine();
Builder.AppendLine("[objectOptimization]");
@@ -540,6 +541,7 @@ internal static void LoadOptions()
block.TryGetValue(OptionsKey.ShadowNormalBias, ref CurrentOptions.ShadowNormalBias);
if (CurrentOptions.ShadowNormalBias < 0.0) CurrentOptions.ShadowNormalBias = 0.0;
block.GetValue(OptionsKey.ShadowFilterCascades, out Interface.CurrentOptions.ShadowFilterCascades);
+ block.GetValue(OptionsKey.LowResFarShadows, out Interface.CurrentOptions.LowResFarShadows);
block.GetValue(OptionsKey.FPSLimit, out CurrentOptions.FPSLimit);
if (CurrentOptions.FPSLimit < 0)
{
diff --git a/source/OpenBVE/UserInterface/formMain.Options.cs b/source/OpenBVE/UserInterface/formMain.Options.cs
index d6322bd84..d2fa01981 100644
--- a/source/OpenBVE/UserInterface/formMain.Options.cs
+++ b/source/OpenBVE/UserInterface/formMain.Options.cs
@@ -55,6 +55,12 @@ private void comboboxShadowResolution_SelectedIndexChanged(object sender, EventA
updownShadowNormalBias.Enabled = shadowEnabled;
checkboxShadowFilterCascades.Enabled = shadowEnabled;
+ CheckBox checkboxLowResFarShadows = this.groupboxShadows.Controls["checkboxLowResFarShadows"] as CheckBox;
+ if (checkboxLowResFarShadows != null)
+ {
+ checkboxLowResFarShadows.Enabled = shadowEnabled;
+ }
+
if (!shadowEnabled)
{
// Visual hint: grey out the strength label
diff --git a/source/OpenBVE/UserInterface/formMain.cs b/source/OpenBVE/UserInterface/formMain.cs
index f48c5853a..38470336f 100644
--- a/source/OpenBVE/UserInterface/formMain.cs
+++ b/source/OpenBVE/UserInterface/formMain.cs
@@ -499,6 +499,20 @@ private void formMain_Load(object sender, EventArgs e)
checkboxShadowFilterCascades.Checked = Interface.CurrentOptions.ShadowFilterCascades;
// Enable/disable shadow sub-controls based on resolution setting
bool shadowEnabled = Interface.CurrentOptions.ShadowResolution != ShadowMapResolution.Off;
+
+ // Dynamically add LowResFarShadows checkbox
+ this.groupboxShadows.Height = 265;
+ CheckBox checkboxLowResFarShadows = new CheckBox();
+ checkboxLowResFarShadows.AutoSize = true;
+ checkboxLowResFarShadows.Location = new System.Drawing.Point(8, 238);
+ checkboxLowResFarShadows.Name = "checkboxLowResFarShadows";
+ checkboxLowResFarShadows.Size = new System.Drawing.Size(150, 17);
+ checkboxLowResFarShadows.Text = "Low-res Far Shadows";
+ checkboxLowResFarShadows.Checked = Interface.CurrentOptions.LowResFarShadows;
+ checkboxLowResFarShadows.Enabled = shadowEnabled;
+ checkboxLowResFarShadows.UseVisualStyleBackColor = true;
+ this.groupboxShadows.Controls.Add(checkboxLowResFarShadows);
+
comboboxShadowDistance.Enabled = shadowEnabled;
comboboxShadowCascades.Enabled = shadowEnabled;
checkboxShadowFilterCascades.Enabled = shadowEnabled;
@@ -1264,6 +1278,11 @@ private void formMain_FormClosing()
Interface.CurrentOptions.ShadowBias = (double)updownShadowBias.Value;
Interface.CurrentOptions.ShadowNormalBias = (double)updownShadowNormalBias.Value;
Interface.CurrentOptions.ShadowFilterCascades = checkboxShadowFilterCascades.Checked;
+ CheckBox checkboxLowResFarShadows = this.groupboxShadows.Controls["checkboxLowResFarShadows"] as CheckBox;
+ if (checkboxLowResFarShadows != null)
+ {
+ Interface.CurrentOptions.LowResFarShadows = checkboxLowResFarShadows.Checked;
+ }
Interface.CurrentOptions.GameMode = (GameMode)comboboxMode.SelectedIndex;
Interface.CurrentOptions.BlackBox = checkboxBlackBox.Checked;
Interface.CurrentOptions.LoadingSway = checkBoxLoadingSway.Checked;
diff --git a/source/OpenBveApi/System/BaseOptions.OptionsKey.cs b/source/OpenBveApi/System/BaseOptions.OptionsKey.cs
index 9d93fac26..c0d2a2c40 100644
--- a/source/OpenBveApi/System/BaseOptions.OptionsKey.cs
+++ b/source/OpenBveApi/System/BaseOptions.OptionsKey.cs
@@ -57,6 +57,7 @@ public enum OptionsKey
ShadowBias,
ShadowNormalBias,
ShadowFilterCascades,
+ LowResFarShadows,
LightAzimuth,
LightElevation,
diff --git a/source/OpenBveApi/System/BaseOptions.cs b/source/OpenBveApi/System/BaseOptions.cs
index 135f66022..32fa2ed06 100644
--- a/source/OpenBveApi/System/BaseOptions.cs
+++ b/source/OpenBveApi/System/BaseOptions.cs
@@ -96,6 +96,8 @@ public abstract class BaseOptions
public double ShadowNormalBias = 2.0;
/// Whether to filter shadow casters per cascade to improve performance.
public bool ShadowFilterCascades = true;
+ /// Whether to downscale far cascade shadow map resolutions to improve performance.
+ public bool LowResFarShadows = true;
/// The sun azimuth in degrees
diff --git a/source/RouteViewer/formOptions.cs b/source/RouteViewer/formOptions.cs
index 84ff0d240..d1c80ab32 100644
--- a/source/RouteViewer/formOptions.cs
+++ b/source/RouteViewer/formOptions.cs
@@ -89,6 +89,26 @@ public FormOptions()
labelNearClip.Text = Translations.GetInterfaceString(OpenBveApi.Hosts.HostApplication.OpenBve, new[] { "options", "quality_distance_nearclip" });
}
checkBoxShadowFilterCascades.Checked = Interface.CurrentOptions.ShadowFilterCascades;
+
+ // Dynamically add LowResFarShadows checkbox
+ CheckBox checkBoxLowResFarShadows = new CheckBox();
+ checkBoxLowResFarShadows.AutoSize = true;
+ checkBoxLowResFarShadows.Location = new System.Drawing.Point(160, 215);
+ checkBoxLowResFarShadows.Name = "checkBoxLowResFarShadows";
+ checkBoxLowResFarShadows.Size = new System.Drawing.Size(15, 14);
+ checkBoxLowResFarShadows.Text = "";
+ checkBoxLowResFarShadows.Checked = Interface.CurrentOptions.LowResFarShadows;
+ checkBoxLowResFarShadows.Enabled = (Interface.CurrentOptions.ShadowResolution != ShadowMapResolution.Off);
+ checkBoxLowResFarShadows.UseVisualStyleBackColor = true;
+ this.tabPageShadows.Controls.Add(checkBoxLowResFarShadows);
+
+ Label labelLowResFarShadows = new Label();
+ labelLowResFarShadows.AutoSize = true;
+ labelLowResFarShadows.Location = new System.Drawing.Point(6, 215);
+ labelLowResFarShadows.Name = "labelLowResFarShadows";
+ labelLowResFarShadows.Size = new System.Drawing.Size(126, 13);
+ labelLowResFarShadows.Text = "Low-res Far Shadows:";
+ this.tabPageShadows.Controls.Add(labelLowResFarShadows);
}
private void InitializeSunSliders()
@@ -113,6 +133,12 @@ private void UpdateShadowControlsEnabled()
trackBarSunAzimuth.Enabled = enabled;
trackBarSunElevation.Enabled = enabled;
checkBoxShadowFilterCascades.Enabled = enabled;
+
+ CheckBox checkBoxLowResFarShadows = this.tabPageShadows.Controls["checkBoxLowResFarShadows"] as CheckBox;
+ if (checkBoxLowResFarShadows != null)
+ {
+ checkBoxLowResFarShadows.Enabled = enabled;
+ }
}
private void comboBoxShadowResolution_SelectedIndexChanged(object sender, EventArgs e)
@@ -175,6 +201,7 @@ private void button1_Click(object sender, EventArgs e)
double previousShadowBias = Interface.CurrentOptions.ShadowBias;
double previousShadowNormalBias = Interface.CurrentOptions.ShadowNormalBias;
bool previousShadowFilterCascades = Interface.CurrentOptions.ShadowFilterCascades;
+ bool previousLowResFarShadows = Interface.CurrentOptions.LowResFarShadows;
//Interpolation mode
InterpolationMode previousInterpolationMode = Interface.CurrentOptions.Interpolation;
@@ -293,9 +320,14 @@ private void button1_Click(object sender, EventArgs e)
Interface.CurrentOptions.ShadowBias = (double)numericUpDownShadowBias.Value;
Interface.CurrentOptions.ShadowNormalBias = (double)numericUpDownShadowNormalBias.Value;
Interface.CurrentOptions.ShadowFilterCascades = checkBoxShadowFilterCascades.Checked;
+ CheckBox checkBoxLowResFarShadows = this.tabPageShadows.Controls["checkBoxLowResFarShadows"] as CheckBox;
+ if (checkBoxLowResFarShadows != null)
+ {
+ Interface.CurrentOptions.LowResFarShadows = checkBoxLowResFarShadows.Checked;
+ }
+
-
// Sun direction is already updated in real-time via slider events
@@ -313,7 +345,7 @@ private void button1_Click(object sender, EventArgs e)
if (previousInterpolationMode != Interface.CurrentOptions.Interpolation || previousAnisotropicLevel != Interface.CurrentOptions.AnisotropicFilteringLevel || GraphicsModeChanged || Interface.CurrentOptions.ViewingDistance != previousViewingDistance ||
previousShadowResolution != Interface.CurrentOptions.ShadowResolution || previousShadowDistance != Interface.CurrentOptions.ShadowDrawDistance || previousShadowCascades != Interface.CurrentOptions.ShadowCascades ||
previousShadowStrength != Interface.CurrentOptions.ShadowStrength || previousShadowBias != Interface.CurrentOptions.ShadowBias || previousShadowNormalBias != Interface.CurrentOptions.ShadowNormalBias ||
- Interface.CurrentOptions.NearClipBase != previousNearClipBase || previousShadowFilterCascades != Interface.CurrentOptions.ShadowFilterCascades)
+ Interface.CurrentOptions.NearClipBase != previousNearClipBase || previousShadowFilterCascades != Interface.CurrentOptions.ShadowFilterCascades || previousLowResFarShadows != Interface.CurrentOptions.LowResFarShadows)
{
this.DialogResult = DialogResult.OK;
}