From 29a58d204b133ef15ea62e0654981d85906018ea Mon Sep 17 00:00:00 2001 From: emil28092005 Date: Thu, 18 Jun 2026 21:48:38 +0300 Subject: [PATCH] feat: ImGui shadow parameters panel with clipboard copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Push constant block expanded: 160B → 176B (added vec4 shadowParams) - shadowParams: x=bias, y=sampleRadius, z=farPlane, w=unused - Fragment shader: bias and sampleRadius now read from push constants (dynamic, adjustable at runtime) - Shadow.frag: uses pc.shadowParams.z for farPlane (dynamic) - Slope-dependent bias: bias + bias*2*(1-NdotL) — adaptive - VulkanRenderer: public ShadowBias, ShadowSampleRadius, ShadowFarPlane properties - Program.cs: ImGui 'Shadow Parameters' panel with sliders: - Light Intensity (0-100), Range (5-100), RGB color - Shadow Bias (0.001-0.5), Sample Radius (0.001-0.1), Far Plane (10-120) - 'Copy Parameters to Clipboard' button — outputs all values as text - 227 tests, all pass --- imgui.ini | 5 ++ src/CortexEngine.App/Program.cs | 57 ++++++++++++++++++ .../Shaders/shadow.frag | 3 +- .../Shaders/shadow.frag.spv | Bin 1096 -> 1236 bytes .../Shaders/shadow.vert | 1 + .../Shaders/shadow.vert.spv | Bin 1536 -> 1588 bytes .../Shaders/triangle.frag | 18 +++--- .../Shaders/triangle.frag.spv | Bin 13124 -> 13472 bytes .../Shaders/triangle.vert | 1 + .../Shaders/triangle.vert.spv | Bin 2264 -> 2316 bytes src/Engine.Graphics.Vulkan/VulkanPipeline.cs | 2 +- src/Engine.Graphics.Vulkan/VulkanRenderer.cs | 20 ++++-- src/Engine.Graphics.Vulkan/VulkanShadowMap.cs | 2 +- 13 files changed, 92 insertions(+), 17 deletions(-) diff --git a/imgui.ini b/imgui.ini index 4e39eef..b286810 100644 --- a/imgui.ini +++ b/imgui.ini @@ -23,3 +23,8 @@ Pos=60,60 Size=226,196 Collapsed=0 +[Window][Shadow Parameters] +Pos=60,60 +Size=318,263 +Collapsed=0 + diff --git a/src/CortexEngine.App/Program.cs b/src/CortexEngine.App/Program.cs index 704a5be..1cd0bad 100644 --- a/src/CortexEngine.App/Program.cs +++ b/src/CortexEngine.App/Program.cs @@ -59,6 +59,7 @@ class Program CreateScene(world); var hasImGui = renderer.GetType().Name == "VulkanRenderer"; + VulkanRenderer? vkRenderer = hasImGui ? (VulkanRenderer)renderer : null; if (hasImGui) { ImGui.GetIO().DisplaySize = new System.Numerics.Vector2(window.Width, window.Height); @@ -170,6 +171,62 @@ class Program ImGui.Text("WASD: move | RMB: look | Q/E: up/down | Shift: boost"); ImGui.End(); + // Shadow parameters panel + ImGui.Begin("Shadow Parameters"); + + var lightEntity2 = world.Lookup("MainLight"); + if ((ulong)lightEntity2.Id != 0) + { + var lightComp = lightEntity2.Get(); + var intensity = lightComp.Intensity; + var range = lightComp.Range; + var colorR = lightComp.Color.X; + var colorG = lightComp.Color.Y; + var colorB = lightComp.Color.Z; + + ImGui.SliderFloat("Light Intensity", ref intensity, 0.0f, 100.0f, "%.1f"); + ImGui.SliderFloat("Light Range", ref range, 5.0f, 100.0f, "%.1f"); + ImGui.SliderFloat("Light R", ref colorR, 0.0f, 1.0f, "%.2f"); + ImGui.SliderFloat("Light G", ref colorG, 0.0f, 1.0f, "%.2f"); + ImGui.SliderFloat("Light B", ref colorB, 0.0f, 1.0f, "%.2f"); + ImGui.Separator(); + + lightComp.Intensity = intensity; + lightComp.Range = range; + lightComp.Color = new Vector3(colorR, colorG, colorB); + lightEntity2.Set(lightComp); + } + + var bias = vkRenderer.ShadowBias; + var sampleRadius = vkRenderer.ShadowSampleRadius; + var farPlane = vkRenderer.ShadowFarPlane; + + ImGui.SliderFloat("Shadow Bias", ref bias, 0.001f, 0.5f, "%.4f"); + ImGui.SliderFloat("Sample Radius", ref sampleRadius, 0.001f, 0.1f, "%.4f"); + ImGui.SliderFloat("Far Plane", ref farPlane, 10.0f, 120.0f, "%.1f"); + ImGui.Separator(); + + vkRenderer.ShadowBias = bias; + vkRenderer.ShadowSampleRadius = sampleRadius; + vkRenderer.ShadowFarPlane = farPlane; + + ImGui.Text($"Current: bias={bias:F4} radius={sampleRadius:F4} far={farPlane:F1}"); + + if (ImGui.Button("Copy Parameters to Clipboard")) + { + var paramsText = $"bias={bias:F4}\nsampleRadius={sampleRadius:F4}\nfarPlane={farPlane:F1}\nshadowMapSize=2048"; + var lightEntity3 = world.Lookup("MainLight"); + if ((ulong)lightEntity3.Id != 0) + { + var lc = lightEntity3.Get(); + paramsText += $"\nlightIntensity={lc.Intensity:F1}\nlightRange={lc.Range:F1}\nlightColor=({lc.Color.X:F2},{lc.Color.Y:F2},{lc.Color.Z:F2})"; + } + ImGui.SetClipboardText(paramsText); + Console.WriteLine("[App] Parameters copied to clipboard:\n" + paramsText); + } + + ImGui.End(); + renderer.EndImGuiFrame(); } diff --git a/src/Engine.Graphics.Vulkan/Shaders/shadow.frag b/src/Engine.Graphics.Vulkan/Shaders/shadow.frag index 0e4f049..a3b124d 100644 --- a/src/Engine.Graphics.Vulkan/Shaders/shadow.frag +++ b/src/Engine.Graphics.Vulkan/Shaders/shadow.frag @@ -8,10 +8,11 @@ layout(push_constant) uniform PC { vec4 lightPos; vec4 lightColor; mat4 lightViewProj; + vec4 shadowParams; } pc; void main() { vec3 toLight = fragPos - pc.lightPos.xyz; float dist = length(toLight); - outDepth = dist / 60.0; + outDepth = dist / pc.shadowParams.z; } diff --git a/src/Engine.Graphics.Vulkan/Shaders/shadow.frag.spv b/src/Engine.Graphics.Vulkan/Shaders/shadow.frag.spv index cea493a38ada11e0c4fb3486c78d087234bc210c..b4fd00eddadfa515bba2b50d77c5cfb965f5cfe5 100644 GIT binary patch delta 248 zcmX@XafOqYnMs+Qfq{{Mn}LHteIqY3qa+&xI|Dxh0|N^X7iT1<x SxE%vCgB(=sFVLG7Knwt>br#D2 delta 112 zcmcb@d4hwNnMs+Qfq{{Mn}LHtX(KN)<76GiCzDl}CJD1K@G~$l@BuL|6!T3!%hb-p u2IMIKF#|(^)8q-vUo<)xSQ$Wqia;z6#0o%c$H2@W2c$t_e}TqZ05Jf2Z4KxE diff --git a/src/Engine.Graphics.Vulkan/Shaders/shadow.vert b/src/Engine.Graphics.Vulkan/Shaders/shadow.vert index 764078e..43c43de 100644 --- a/src/Engine.Graphics.Vulkan/Shaders/shadow.vert +++ b/src/Engine.Graphics.Vulkan/Shaders/shadow.vert @@ -7,6 +7,7 @@ layout(push_constant) uniform PC { vec4 lightPos; vec4 lightColor; mat4 lightViewProj; + vec4 shadowParams; } pc; layout(location = 0) out vec3 fragPos; diff --git a/src/Engine.Graphics.Vulkan/Shaders/shadow.vert.spv b/src/Engine.Graphics.Vulkan/Shaders/shadow.vert.spv index a8c8bec017cef32ad6a320629ac3a2360859d3ef..3e5b97b1266ad9532b13dfd34cedbb62582ab688 100644 GIT binary patch delta 71 zcmZqR*}^m7k0cudI|DZZ0|N^X7iT1<~s&M4@?zzUaD2C7&vIhN@n RBl~21W>H4=&5q3bnE*)&51#-4 delta 26 icmdnO)4(&~&t?h66voLDn9eb>O^#<4-CV)EhY0|JRtWk4 diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag index 8793544..dd9681d 100644 --- a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag +++ b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag @@ -17,11 +17,11 @@ layout(push_constant) uniform PC { vec4 lightPos; vec4 lightColor; mat4 lightViewProj; + vec4 shadowParams; // x=bias, y=sampleRadius, z=farPlane, w=unused } pc; const vec3 AMBIENT = vec3(0.01, 0.01, 0.02); const float PI = 3.14159265359; -const float FAR_PLANE = 60.0; const vec3 POISSON_DISK[16] = vec3[16]( vec3( 0.0000, 0.0000, 0.0000), @@ -94,25 +94,27 @@ mat3 buildTangentBasis(vec3 dir) float calcShadow(vec3 worldPos, vec3 lightPos, vec3 N, vec3 L) { + float bias = pc.shadowParams.x; + float sampleRadius = pc.shadowParams.y; + float farPlane = pc.shadowParams.z; + vec3 dir = worldPos - lightPos; float dist = length(dir); vec3 dirNorm = normalize(dir); - // Slope-dependent bias: small base + larger at grazing angles float NdotL = max(dot(N, L), 0.0); - float bias = 0.08 + 0.15 * (1.0 - NdotL); + float slopeBias = bias + bias * 2.0 * (1.0 - NdotL); - // 16-tap Poisson disk PCF float shadow = 0.0; mat3 basis = buildTangentBasis(dirNorm); - float sampleRadius = clamp(0.015 * (dist / 20.0), 0.003, 0.05); + float radius = clamp(sampleRadius * (dist / 20.0), 0.001, 0.1); for (int i = 0; i < 16; i++) { - vec3 sampleDir = normalize(dirNorm + basis * (POISSON_DISK[i] * sampleRadius)); + vec3 sampleDir = normalize(dirNorm + basis * (POISSON_DISK[i] * radius)); float sampleDepth = texture(shadowCube, sampleDir).r; - float sampleMapped = sampleDepth * FAR_PLANE; - shadow += (dist - bias < sampleMapped) ? 1.0 : 0.0; + float sampleMapped = sampleDepth * farPlane; + shadow += (dist - slopeBias < sampleMapped) ? 1.0 : 0.0; } shadow /= 16.0; diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.frag.spv b/src/Engine.Graphics.Vulkan/Shaders/triangle.frag.spv index 715eb7e0b10ab71e5b48a0a0dad4f55498e7043a..5751d5be87d3802dabf8eff71a86b949c4793a95 100644 GIT binary patch literal 13472 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 b*fHXM90XT85R38EB7Onbct1PDes=s9^i}F- diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.vert b/src/Engine.Graphics.Vulkan/Shaders/triangle.vert index 2c20c93..343bf6b 100644 --- a/src/Engine.Graphics.Vulkan/Shaders/triangle.vert +++ b/src/Engine.Graphics.Vulkan/Shaders/triangle.vert @@ -17,6 +17,7 @@ layout(push_constant) uniform PC { vec4 lightPos; vec4 lightColor; mat4 lightViewProj; + vec4 shadowParams; } pc; void main() { diff --git a/src/Engine.Graphics.Vulkan/Shaders/triangle.vert.spv b/src/Engine.Graphics.Vulkan/Shaders/triangle.vert.spv index 9c4591da88112e87cbab32936229cac49563bdf3..8ff8a8dc723def14f9c876d9e81cda70586305a9 100644 GIT binary patch delta 90 zcmca1*dw%ohf$J^ft`Vyfq{Vqh>J54Q}W9L5{nXZiy1a^GQMUM^k86xODhAFEZF>! WsgzM1qK6A8zz)S=_U1~KcoqQkYz{sE delta 42 rcmeAXx*@oMhjFt8;|s>kD$GTU!fXuO3=9lhK+F!sT$|Uj#IpbZ#@q*F diff --git a/src/Engine.Graphics.Vulkan/VulkanPipeline.cs b/src/Engine.Graphics.Vulkan/VulkanPipeline.cs index 5448e95..6807495 100644 --- a/src/Engine.Graphics.Vulkan/VulkanPipeline.cs +++ b/src/Engine.Graphics.Vulkan/VulkanPipeline.cs @@ -154,7 +154,7 @@ internal sealed unsafe class VulkanPipeline : IDisposable { stageFlags = VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, offset = 0, - size = 160, + size = 176, }; var descLayout = DescriptorSetLayout; diff --git a/src/Engine.Graphics.Vulkan/VulkanRenderer.cs b/src/Engine.Graphics.Vulkan/VulkanRenderer.cs index fd8552d..f6b5d59 100644 --- a/src/Engine.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Engine.Graphics.Vulkan/VulkanRenderer.cs @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; namespace Engine.Graphics.Vulkan; -internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreenshotProvider +public sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreenshotProvider { private readonly VulkanContext _ctx; private readonly VulkanSwapchain _swapchain; @@ -24,10 +24,14 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen private string? _screenshotPath; private float _totalTime; + public float ShadowBias { get; set; } = 0.08f; + public float ShadowSampleRadius { get; set; } = 0.015f; + public float ShadowFarPlane { get; set; } = 60.0f; + public bool IsScreenshotRequested => _screenshotRequested; public IScreenshotProvider ScreenshotProvider => this; - public VulkanRenderer(VulkanContext ctx, VulkanSwapchain swapchain) + internal VulkanRenderer(VulkanContext ctx, VulkanSwapchain swapchain) { _ctx = ctx; _swapchain = swapchain; @@ -266,7 +270,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen Vk.vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBuf, &offset); Vk.vkCmdBindIndexBuffer(cmd, dc.indexBuf, 0, 1); - var pcData = stackalloc byte[160]; + var pcData = stackalloc byte[176]; var modelCopy = dc.model; System.Buffer.MemoryCopy(&modelCopy, pcData, 64, 64); var lpCopy = lightPos; @@ -275,8 +279,10 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen System.Buffer.MemoryCopy(&lcCopy, pcData + 80, 16, 16); var lvpCopy = faceViewProj; System.Buffer.MemoryCopy(&lvpCopy, pcData + 96, 64, 64); + var sp = new Vector4(ShadowBias, ShadowSampleRadius, ShadowFarPlane, 0); + System.Buffer.MemoryCopy(&sp, pcData + 160, 16, 16); - Vk.vkCmdPushConstants(cmd, _shadowMap.PipelineLayout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 0, 160, pcData); + Vk.vkCmdPushConstants(cmd, _shadowMap.PipelineLayout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 0, 176, pcData); Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0); } @@ -376,7 +382,7 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen Vk.vkCmdBindIndexBuffer(cmd, dc.indexBuf, 0, 1); // Pack all push constants: model(64) + lightPos(16) + lightColor(16) + lightViewProj(64) = 160 - var pcData = stackalloc byte[160]; + var pcData = stackalloc byte[176]; var modelCopy = dc.model; System.Buffer.MemoryCopy(&modelCopy, pcData, 64, 64); var lpCopy = lightPos; @@ -385,8 +391,10 @@ internal sealed unsafe class VulkanRenderer : IRenderer, Engine.Graphics.IScreen System.Buffer.MemoryCopy(&lcCopy, pcData + 80, 16, 16); var lvpCopy = lightViewProj; System.Buffer.MemoryCopy(&lvpCopy, pcData + 96, 64, 64); + var sp = new Vector4(ShadowBias, ShadowSampleRadius, ShadowFarPlane, 0); + System.Buffer.MemoryCopy(&sp, pcData + 160, 16, 16); - Vk.vkCmdPushConstants(cmd, _pipeline.PipelineLayout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 0, 160, pcData); + Vk.vkCmdPushConstants(cmd, _pipeline.PipelineLayout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 0, 176, pcData); Vk.vkCmdDrawIndexed(cmd, dc.indexCount, 1, 0, 0, 0); } diff --git a/src/Engine.Graphics.Vulkan/VulkanShadowMap.cs b/src/Engine.Graphics.Vulkan/VulkanShadowMap.cs index 206b508..66d438b 100644 --- a/src/Engine.Graphics.Vulkan/VulkanShadowMap.cs +++ b/src/Engine.Graphics.Vulkan/VulkanShadowMap.cs @@ -323,7 +323,7 @@ internal sealed unsafe class VulkanShadowMap : IDisposable pDynamicStates = dynamicStates, }; - var pushConstantRange = new VkPushConstantRange { stageFlags = VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, offset = 0, size = 160 }; + var pushConstantRange = new VkPushConstantRange { stageFlags = VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, offset = 0, size = 176 }; var layoutInfo = new VkPipelineLayoutCreateInfo {