From 2fb02fe512e3bc341efd7aeaa34e8d6b225d2b44 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 001/133] [init] create basic test world --- source/editor/GeneralWindows.cpp | 3 ++- source/runtime/Core/Debugging.h | 2 +- source/runtime/Game/Game.cpp | 18 ++++++++++++++++++ source/runtime/Game/Game.h | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/source/editor/GeneralWindows.cpp b/source/editor/GeneralWindows.cpp index 9d2410fae4..ee72d25bad 100644 --- a/source/editor/GeneralWindows.cpp +++ b/source/editor/GeneralWindows.cpp @@ -416,7 +416,8 @@ namespace "4. Sponza 4k (high-resolution textures & meshes) - demanding", "5. Subway (gi test, no lights, only emissive textures) - moderate", "6. Minecraft (blocky aesthetic) - light", - "7. Basic (light, camera, floor) - light" + "7. Basic (light, camera, floor) - light", + "8. Water (light, camera, ocean) - ?" }; int world_index = 0; diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 9c6a0074c9..049fc87ee7 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -40,7 +40,7 @@ namespace spartan inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd - inline static bool m_renderdoc_enabled = false; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping + inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost inline static bool m_gpu_timing_enabled = true; // enables gpu performance timing with negligible performance cost inline static bool m_shader_optimization_enabled = true; // controls shader optimization, disabling has significant performance impact diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 9808f3a614..4fdb31da37 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1581,6 +1581,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() @@ -1642,6 +1659,7 @@ namespace spartan case DefaultWorld::Showroom: worlds::showroom::create(); break; case DefaultWorld::LiminalSpace: worlds::liminal_space::create(); break; case DefaultWorld::Basic: worlds::basic::create(); break; + case DefaultWorld::Water: worlds::water::create(); break; default: SP_ASSERT_MSG(false, "Unhandled default world"); break; } diff --git a/source/runtime/Game/Game.h b/source/runtime/Game/Game.h index 90cbecb410..c5bd61c9f0 100644 --- a/source/runtime/Game/Game.h +++ b/source/runtime/Game/Game.h @@ -32,6 +32,7 @@ namespace spartan Subway, Minecraft, Basic, + Water, Max }; From b3e1b44ee040ba5bcd301473900cb0ad5268d700 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 002/133] [init] create basic test world --- source/editor/GeneralWindows.cpp | 3 ++- source/runtime/Core/Debugging.h | 2 +- source/runtime/Game/Game.cpp | 18 ++++++++++++++++++ source/runtime/Game/Game.h | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/source/editor/GeneralWindows.cpp b/source/editor/GeneralWindows.cpp index 8424e2ae01..3d91bbd700 100644 --- a/source/editor/GeneralWindows.cpp +++ b/source/editor/GeneralWindows.cpp @@ -415,7 +415,8 @@ namespace "4. Sponza 4k (high-resolution textures & meshes) - demanding", "5. Subway (gi test, no lights, only emissive textures) - moderate", "6. Minecraft (blocky aesthetic) - light", - "7. Basic (light, camera, floor) - light" + "7. Basic (light, camera, floor) - light", + "8. Water (light, camera, ocean) - ?" }; int world_index = 0; diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 9c6a0074c9..049fc87ee7 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -40,7 +40,7 @@ namespace spartan inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd - inline static bool m_renderdoc_enabled = false; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping + inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost inline static bool m_gpu_timing_enabled = true; // enables gpu performance timing with negligible performance cost inline static bool m_shader_optimization_enabled = true; // controls shader optimization, disabling has significant performance impact diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 7eb38d5171..d4cf4a31df 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1588,6 +1588,23 @@ namespace spartan entities::material_ball(Vector3::Zero); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() @@ -1649,6 +1666,7 @@ namespace spartan case DefaultWorld::Showroom: worlds::showroom::create(); break; case DefaultWorld::LiminalSpace: worlds::liminal_space::create(); break; case DefaultWorld::Basic: worlds::basic::create(); break; + case DefaultWorld::Water: worlds::water::create(); break; default: SP_ASSERT_MSG(false, "Unhandled default world"); break; } diff --git a/source/runtime/Game/Game.h b/source/runtime/Game/Game.h index 90cbecb410..c5bd61c9f0 100644 --- a/source/runtime/Game/Game.h +++ b/source/runtime/Game/Game.h @@ -32,6 +32,7 @@ namespace spartan Subway, Minecraft, Basic, + Water, Max }; From d896ea11ca6e8da403738fdbcc4fde525fcc2877 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:19:24 +0200 Subject: [PATCH 003/133] [world] removed duplicated code --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index c42d3d6b40..d4cf4a31df 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,23 +1605,6 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } } void Game::Shutdown() From cbc8eb446681bad7428034e4c7fe1674efb1ef84 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:30:19 +0200 Subject: [PATCH 004/133] [world] fixed implicit conversion warning --- source/runtime/Game/Game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index d4cf4a31df..5a801a7b81 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1598,7 +1598,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); water->SetParent(entity.get()); From 6ac4d29bf5c7cc2ae723515f1b5a6f89de5c0cd3 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 005/133] [init] create basic test world --- source/editor/GeneralWindows.cpp | 3 ++- source/runtime/Core/Debugging.h | 2 +- source/runtime/Game/Game.cpp | 18 ++++++++++++++++++ source/runtime/Game/Game.h | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/source/editor/GeneralWindows.cpp b/source/editor/GeneralWindows.cpp index 8424e2ae01..3d91bbd700 100644 --- a/source/editor/GeneralWindows.cpp +++ b/source/editor/GeneralWindows.cpp @@ -415,7 +415,8 @@ namespace "4. Sponza 4k (high-resolution textures & meshes) - demanding", "5. Subway (gi test, no lights, only emissive textures) - moderate", "6. Minecraft (blocky aesthetic) - light", - "7. Basic (light, camera, floor) - light" + "7. Basic (light, camera, floor) - light", + "8. Water (light, camera, ocean) - ?" }; int world_index = 0; diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 9c6a0074c9..049fc87ee7 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -40,7 +40,7 @@ namespace spartan inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd - inline static bool m_renderdoc_enabled = false; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping + inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost inline static bool m_gpu_timing_enabled = true; // enables gpu performance timing with negligible performance cost inline static bool m_shader_optimization_enabled = true; // controls shader optimization, disabling has significant performance impact diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index b52144fcd3..b0d428a9fe 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1588,6 +1588,23 @@ namespace spartan create_entity::material_ball(Vector3::Zero); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() @@ -1649,6 +1666,7 @@ namespace spartan case DefaultWorld::Showroom: worlds::showroom::create(); break; case DefaultWorld::LiminalSpace: worlds::liminal_space::create(); break; case DefaultWorld::Basic: worlds::basic::create(); break; + case DefaultWorld::Water: worlds::water::create(); break; default: SP_ASSERT_MSG(false, "Unhandled default world"); break; } diff --git a/source/runtime/Game/Game.h b/source/runtime/Game/Game.h index 90cbecb410..c5bd61c9f0 100644 --- a/source/runtime/Game/Game.h +++ b/source/runtime/Game/Game.h @@ -32,6 +32,7 @@ namespace spartan Subway, Minecraft, Basic, + Water, Max }; From 3eb5fc9d0d6f7c0bfcdb7121804f29bc0c90e64d Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 006/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index b0d428a9fe..5b13705ffc 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,6 +1605,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() From dfa337bd37ca19d5591328c5790adf08b502224b Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:19:24 +0200 Subject: [PATCH 007/133] [world] removed duplicated code --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 5b13705ffc..b0d428a9fe 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,23 +1605,6 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } } void Game::Shutdown() From 6366b2edc68d9c51af03a54431f14adfeedc279e Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:30:19 +0200 Subject: [PATCH 008/133] [world] fixed implicit conversion warning --- source/runtime/Game/Game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index b0d428a9fe..8981f99a12 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1598,7 +1598,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); water->SetParent(entity.get()); From 82a87001455c75649dc790e9605b3569ee25404b Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 18:58:20 +0200 Subject: [PATCH 009/133] [game] small fixes after rebase --- source/runtime/Game/Game.cpp | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index db4d5da4d4..eabab46b27 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1593,31 +1593,14 @@ namespace spartan { void create() { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); + create_entity::camera(); + create_entity::sun(true); auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = create_entity::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); - water->SetParent(entity.get()); + water->SetParent(entity); default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } From db9e8f9699513e5c6eff1bfffced7055cfa8853c Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 20:40:04 +0200 Subject: [PATCH 010/133] [wip] basic compute shader --- data/shaders/ocean/initial_spectrum.hlsl | 7 ++++++ source/runtime/Core/Debugging.h | 2 +- source/runtime/Rendering/Renderer.h | 2 ++ .../runtime/Rendering/Renderer_Definitions.h | 3 +++ source/runtime/Rendering/Renderer_Passes.cpp | 25 +++++++++++++++++++ .../runtime/Rendering/Renderer_Resources.cpp | 12 +++++++++ 6 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 data/shaders/ocean/initial_spectrum.hlsl diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl new file mode 100644 index 0000000000..239ab7f52f --- /dev/null +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -0,0 +1,7 @@ +RWTexture2D initial_spectrum : register(u9); + +[numthreads(8, 8, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + initial_spectrum[thread_id.xy] = float4(1.0f, 0.0f, 1.0f, 1.0f); +} diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 049fc87ee7..afdcc4be90 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -38,7 +38,7 @@ namespace spartan private: inline static bool m_validation_layer_enabled = false; // enables vulkan diagnostic layers, incurs significant per-draw cpu performance overhead inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact - inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations + inline static bool m_logging_to_file_enabled = true; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index a51b4e1ed5..e3ad10b28e 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -161,6 +161,8 @@ namespace spartan static void Pass_Light_ImageBased(RHI_CommandList* cmd_list); static void Pass_Lut_BrdfSpecular(RHI_CommandList* cmd_list); static void Pass_Lut_AtmosphericScattering(RHI_CommandList* cmd_list); + // passes - ocean + static void Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 691ab227d8..7a4f349c88 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -137,6 +137,7 @@ namespace spartan visibility = 6, sb_spd = 7, tex_spd = 8, + ocean_initial_spectrum = 9, }; enum class Renderer_Shader : uint8_t @@ -190,6 +191,7 @@ namespace spartan icon_c, dithering_c, transparency_reflection_refraction_c, + ocean_initial_spectrum_c, max }; @@ -222,6 +224,7 @@ namespace spartan outline, shading_rate, shadow_atlas, + ocean_initial_spectrum, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 73c919faf1..e08396e722 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -115,6 +115,11 @@ namespace spartan if (m_transparents_present) { bool is_transparent = true; + + /*static bool compute_ocean_spectrum = true; + if (compute_ocean_spectrum)*/ + Pass_ComputeInitialSpectrum(cmd_list_graphics_present); + Pass_GBuffer(cmd_list_graphics_present, is_transparent); Pass_Light(cmd_list_graphics_present, is_transparent); Pass_Light_Composition(cmd_list_graphics_present, is_transparent); @@ -1068,6 +1073,26 @@ namespace spartan cmd_list->EndTimeblock(); } + void Renderer::Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list) + { + RHI_Texture* initial_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_initial_spectrum); + + cmd_list->BeginTimeblock("ocean_intial_spectrum"); + { + RHI_PipelineState pso; + pso.name = "ocean_initial_spectrum"; + pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_initial_spectrum_c); + cmd_list->SetPipelineState(pso); + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); + cmd_list->Dispatch(initial_spectrum); + + // for the lifetime of the engine, this will be read as an srv, so transition here + initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } + void Renderer::Pass_PostProcess(RHI_CommandList* cmd_list) { // acquire render targets diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 01f6d8c5a8..e0d09099e5 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -221,6 +221,12 @@ namespace spartan render_target(Renderer_RenderTarget::light_volumetric) = make_shared(RHI_Texture_Type::Type2D, width_render, height_render, 1, 1, RHI_Format::R11G11B10_Float, flags, "light_volumetric"); } + // ocean + { + uint32_t flags = RHI_Texture_Uav | RHI_Texture_Srv; + render_target(Renderer_RenderTarget::ocean_initial_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_initial_spectrum"); + } + // occlusion { // note #1: amd is very specific with depth formats, so if something is a depth render target, it can only have one mip and flags like RHI_Texture_Uav @@ -367,6 +373,12 @@ namespace spartan shader(Renderer_Shader::light_image_based_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "light_image_based.hlsl", async); } + // ocean + { + //shader(Renderer_Shader::ocean_initial_spectrum_c) = make_shared(); + //shader(Renderer_Shader::light_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\initial_spectrum.hlsl", async); + } + // blur { // gaussian From a263d4be08c33b1a270a776a83b2572709626e94 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 011/133] [init] create basic test world --- source/editor/GeneralWindows.cpp | 3 ++- source/runtime/Core/Debugging.h | 2 +- source/runtime/Game/Game.cpp | 18 ++++++++++++++++++ source/runtime/Game/Game.h | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/source/editor/GeneralWindows.cpp b/source/editor/GeneralWindows.cpp index 8424e2ae01..3d91bbd700 100644 --- a/source/editor/GeneralWindows.cpp +++ b/source/editor/GeneralWindows.cpp @@ -415,7 +415,8 @@ namespace "4. Sponza 4k (high-resolution textures & meshes) - demanding", "5. Subway (gi test, no lights, only emissive textures) - moderate", "6. Minecraft (blocky aesthetic) - light", - "7. Basic (light, camera, floor) - light" + "7. Basic (light, camera, floor) - light", + "8. Water (light, camera, ocean) - ?" }; int world_index = 0; diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 9c6a0074c9..049fc87ee7 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -40,7 +40,7 @@ namespace spartan inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd - inline static bool m_renderdoc_enabled = false; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping + inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost inline static bool m_gpu_timing_enabled = true; // enables gpu performance timing with negligible performance cost inline static bool m_shader_optimization_enabled = true; // controls shader optimization, disabling has significant performance impact diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 3a291558fa..980b8f1756 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1588,6 +1588,23 @@ namespace spartan entities::material_ball(Vector3::Zero); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() @@ -1646,6 +1663,7 @@ namespace spartan case DefaultWorld::Showroom: worlds::showroom::create(); break; case DefaultWorld::LiminalSpace: worlds::liminal_space::create(); break; case DefaultWorld::Basic: worlds::basic::create(); break; + case DefaultWorld::Water: worlds::water::create(); break; default: SP_ASSERT_MSG(false, "Unhandled default world"); break; } diff --git a/source/runtime/Game/Game.h b/source/runtime/Game/Game.h index 90cbecb410..c5bd61c9f0 100644 --- a/source/runtime/Game/Game.h +++ b/source/runtime/Game/Game.h @@ -32,6 +32,7 @@ namespace spartan Subway, Minecraft, Basic, + Water, Max }; From a6b737c07c9c0654e05d42ac6ad7d9d23e389bd7 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 012/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 980b8f1756..9fe1a2bb72 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,6 +1605,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() From 7f236af75b3a0be8739b6ae0dcf93c38701ec2a8 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:19:24 +0200 Subject: [PATCH 013/133] [world] removed duplicated code --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 9fe1a2bb72..980b8f1756 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,23 +1605,6 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } } void Game::Shutdown() From 4fa583a8c89734152cdbf82bd3e68a4ceca9f9d9 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:30:19 +0200 Subject: [PATCH 014/133] [world] fixed implicit conversion warning --- source/runtime/Game/Game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 980b8f1756..76036b897f 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1598,7 +1598,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); water->SetParent(entity.get()); From 9bbd39af00f674412ceb284aa7c5027decd6eb60 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 015/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 76036b897f..7e936c3a98 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,6 +1605,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() From ce1dfdf1867da58223ea4397e900eb71b53aadb6 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:19:24 +0200 Subject: [PATCH 016/133] [world] removed duplicated code --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 7e936c3a98..76036b897f 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,23 +1605,6 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } } void Game::Shutdown() From cb8036b2232dcd802fa263eee73669e64edbc17c Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 20:40:04 +0200 Subject: [PATCH 017/133] [wip] basic compute shader --- data/shaders/ocean/initial_spectrum.hlsl | 7 ++++++ source/runtime/Core/Debugging.h | 2 +- source/runtime/Rendering/Renderer.h | 2 ++ .../runtime/Rendering/Renderer_Definitions.h | 3 +++ source/runtime/Rendering/Renderer_Passes.cpp | 25 +++++++++++++++++++ .../runtime/Rendering/Renderer_Resources.cpp | 12 +++++++++ 6 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 data/shaders/ocean/initial_spectrum.hlsl diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl new file mode 100644 index 0000000000..239ab7f52f --- /dev/null +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -0,0 +1,7 @@ +RWTexture2D initial_spectrum : register(u9); + +[numthreads(8, 8, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + initial_spectrum[thread_id.xy] = float4(1.0f, 0.0f, 1.0f, 1.0f); +} diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 049fc87ee7..afdcc4be90 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -38,7 +38,7 @@ namespace spartan private: inline static bool m_validation_layer_enabled = false; // enables vulkan diagnostic layers, incurs significant per-draw cpu performance overhead inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact - inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations + inline static bool m_logging_to_file_enabled = true; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index a51b4e1ed5..e3ad10b28e 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -161,6 +161,8 @@ namespace spartan static void Pass_Light_ImageBased(RHI_CommandList* cmd_list); static void Pass_Lut_BrdfSpecular(RHI_CommandList* cmd_list); static void Pass_Lut_AtmosphericScattering(RHI_CommandList* cmd_list); + // passes - ocean + static void Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 691ab227d8..7a4f349c88 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -137,6 +137,7 @@ namespace spartan visibility = 6, sb_spd = 7, tex_spd = 8, + ocean_initial_spectrum = 9, }; enum class Renderer_Shader : uint8_t @@ -190,6 +191,7 @@ namespace spartan icon_c, dithering_c, transparency_reflection_refraction_c, + ocean_initial_spectrum_c, max }; @@ -222,6 +224,7 @@ namespace spartan outline, shading_rate, shadow_atlas, + ocean_initial_spectrum, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 0f89d46f4f..ea2c667b74 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -115,6 +115,11 @@ namespace spartan if (m_transparents_present) { bool is_transparent = true; + + /*static bool compute_ocean_spectrum = true; + if (compute_ocean_spectrum)*/ + Pass_ComputeInitialSpectrum(cmd_list_graphics_present); + Pass_GBuffer(cmd_list_graphics_present, is_transparent); Pass_Light(cmd_list_graphics_present, is_transparent); Pass_Light_Composition(cmd_list_graphics_present, is_transparent); @@ -1068,6 +1073,26 @@ namespace spartan cmd_list->EndTimeblock(); } + void Renderer::Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list) + { + RHI_Texture* initial_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_initial_spectrum); + + cmd_list->BeginTimeblock("ocean_intial_spectrum"); + { + RHI_PipelineState pso; + pso.name = "ocean_initial_spectrum"; + pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_initial_spectrum_c); + cmd_list->SetPipelineState(pso); + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); + cmd_list->Dispatch(initial_spectrum); + + // for the lifetime of the engine, this will be read as an srv, so transition here + initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } + void Renderer::Pass_PostProcess(RHI_CommandList* cmd_list) { // acquire render targets diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 01f6d8c5a8..e0d09099e5 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -221,6 +221,12 @@ namespace spartan render_target(Renderer_RenderTarget::light_volumetric) = make_shared(RHI_Texture_Type::Type2D, width_render, height_render, 1, 1, RHI_Format::R11G11B10_Float, flags, "light_volumetric"); } + // ocean + { + uint32_t flags = RHI_Texture_Uav | RHI_Texture_Srv; + render_target(Renderer_RenderTarget::ocean_initial_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_initial_spectrum"); + } + // occlusion { // note #1: amd is very specific with depth formats, so if something is a depth render target, it can only have one mip and flags like RHI_Texture_Uav @@ -367,6 +373,12 @@ namespace spartan shader(Renderer_Shader::light_image_based_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "light_image_based.hlsl", async); } + // ocean + { + //shader(Renderer_Shader::ocean_initial_spectrum_c) = make_shared(); + //shader(Renderer_Shader::light_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\initial_spectrum.hlsl", async); + } + // blur { // gaussian From 622b1d1df209b9fe09330a83d4593fd33e81404e Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 20:45:26 +0200 Subject: [PATCH 018/133] [game] fixed errors caused by rebase --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 190e361321..960690f4dd 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1600,23 +1600,6 @@ namespace spartan auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } - - namespace water - { - void create() - { - create_entity::camera(); - create_entity::sun(true); - - auto entity = World::CreateEntity(); - - auto water = create_entity::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); - water->SetParent(entity); default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); From 26e6999646441c2dc59194fcfe5c752e89de0590 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 019/133] [init] create basic test world --- source/editor/GeneralWindows.cpp | 3 ++- source/runtime/Core/Debugging.h | 2 +- source/runtime/Game/Game.cpp | 18 ++++++++++++++++++ source/runtime/Game/Game.h | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/source/editor/GeneralWindows.cpp b/source/editor/GeneralWindows.cpp index 8424e2ae01..3d91bbd700 100644 --- a/source/editor/GeneralWindows.cpp +++ b/source/editor/GeneralWindows.cpp @@ -415,7 +415,8 @@ namespace "4. Sponza 4k (high-resolution textures & meshes) - demanding", "5. Subway (gi test, no lights, only emissive textures) - moderate", "6. Minecraft (blocky aesthetic) - light", - "7. Basic (light, camera, floor) - light" + "7. Basic (light, camera, floor) - light", + "8. Water (light, camera, ocean) - ?" }; int world_index = 0; diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 9c6a0074c9..049fc87ee7 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -40,7 +40,7 @@ namespace spartan inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd - inline static bool m_renderdoc_enabled = false; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping + inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost inline static bool m_gpu_timing_enabled = true; // enables gpu performance timing with negligible performance cost inline static bool m_shader_optimization_enabled = true; // controls shader optimization, disabling has significant performance impact diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 3a291558fa..980b8f1756 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1588,6 +1588,23 @@ namespace spartan entities::material_ball(Vector3::Zero); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() @@ -1646,6 +1663,7 @@ namespace spartan case DefaultWorld::Showroom: worlds::showroom::create(); break; case DefaultWorld::LiminalSpace: worlds::liminal_space::create(); break; case DefaultWorld::Basic: worlds::basic::create(); break; + case DefaultWorld::Water: worlds::water::create(); break; default: SP_ASSERT_MSG(false, "Unhandled default world"); break; } diff --git a/source/runtime/Game/Game.h b/source/runtime/Game/Game.h index 90cbecb410..c5bd61c9f0 100644 --- a/source/runtime/Game/Game.h +++ b/source/runtime/Game/Game.h @@ -32,6 +32,7 @@ namespace spartan Subway, Minecraft, Basic, + Water, Max }; From d1bee10f7a434338fb6e96bd416f7bbbfb3f7712 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 020/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 980b8f1756..9fe1a2bb72 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,6 +1605,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() From 11989e304d9e8da946b5d56a6d0e418894add4b4 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:19:24 +0200 Subject: [PATCH 021/133] [world] removed duplicated code --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 9fe1a2bb72..980b8f1756 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,23 +1605,6 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } } void Game::Shutdown() From ffa276115e4322ed8a575c63dd85d500c4c91938 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:30:19 +0200 Subject: [PATCH 022/133] [world] fixed implicit conversion warning --- source/runtime/Game/Game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 980b8f1756..76036b897f 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1598,7 +1598,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); water->SetParent(entity.get()); From 35f19babd4cfa82ac2645760ae9d30b75f10ecbd Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 023/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 76036b897f..7e936c3a98 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,6 +1605,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() From a0d155b94d7ab43f5791ecb8c086561ab68fbe2e Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:19:24 +0200 Subject: [PATCH 024/133] [world] removed duplicated code --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 7e936c3a98..76036b897f 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,23 +1605,6 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } } void Game::Shutdown() From a3c154c79a5c37fa142af824edec83508c06279d Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 20:40:04 +0200 Subject: [PATCH 025/133] [wip] basic compute shader --- data/shaders/ocean/initial_spectrum.hlsl | 7 ++++++ source/runtime/Core/Debugging.h | 2 +- source/runtime/Rendering/Renderer.h | 2 ++ .../runtime/Rendering/Renderer_Definitions.h | 3 +++ source/runtime/Rendering/Renderer_Passes.cpp | 25 +++++++++++++++++++ .../runtime/Rendering/Renderer_Resources.cpp | 12 +++++++++ 6 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 data/shaders/ocean/initial_spectrum.hlsl diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl new file mode 100644 index 0000000000..239ab7f52f --- /dev/null +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -0,0 +1,7 @@ +RWTexture2D initial_spectrum : register(u9); + +[numthreads(8, 8, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + initial_spectrum[thread_id.xy] = float4(1.0f, 0.0f, 1.0f, 1.0f); +} diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 049fc87ee7..afdcc4be90 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -38,7 +38,7 @@ namespace spartan private: inline static bool m_validation_layer_enabled = false; // enables vulkan diagnostic layers, incurs significant per-draw cpu performance overhead inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact - inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations + inline static bool m_logging_to_file_enabled = true; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index a51b4e1ed5..e3ad10b28e 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -161,6 +161,8 @@ namespace spartan static void Pass_Light_ImageBased(RHI_CommandList* cmd_list); static void Pass_Lut_BrdfSpecular(RHI_CommandList* cmd_list); static void Pass_Lut_AtmosphericScattering(RHI_CommandList* cmd_list); + // passes - ocean + static void Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 691ab227d8..7a4f349c88 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -137,6 +137,7 @@ namespace spartan visibility = 6, sb_spd = 7, tex_spd = 8, + ocean_initial_spectrum = 9, }; enum class Renderer_Shader : uint8_t @@ -190,6 +191,7 @@ namespace spartan icon_c, dithering_c, transparency_reflection_refraction_c, + ocean_initial_spectrum_c, max }; @@ -222,6 +224,7 @@ namespace spartan outline, shading_rate, shadow_atlas, + ocean_initial_spectrum, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 0f89d46f4f..ea2c667b74 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -115,6 +115,11 @@ namespace spartan if (m_transparents_present) { bool is_transparent = true; + + /*static bool compute_ocean_spectrum = true; + if (compute_ocean_spectrum)*/ + Pass_ComputeInitialSpectrum(cmd_list_graphics_present); + Pass_GBuffer(cmd_list_graphics_present, is_transparent); Pass_Light(cmd_list_graphics_present, is_transparent); Pass_Light_Composition(cmd_list_graphics_present, is_transparent); @@ -1068,6 +1073,26 @@ namespace spartan cmd_list->EndTimeblock(); } + void Renderer::Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list) + { + RHI_Texture* initial_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_initial_spectrum); + + cmd_list->BeginTimeblock("ocean_intial_spectrum"); + { + RHI_PipelineState pso; + pso.name = "ocean_initial_spectrum"; + pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_initial_spectrum_c); + cmd_list->SetPipelineState(pso); + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); + cmd_list->Dispatch(initial_spectrum); + + // for the lifetime of the engine, this will be read as an srv, so transition here + initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } + void Renderer::Pass_PostProcess(RHI_CommandList* cmd_list) { // acquire render targets diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 01f6d8c5a8..e0d09099e5 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -221,6 +221,12 @@ namespace spartan render_target(Renderer_RenderTarget::light_volumetric) = make_shared(RHI_Texture_Type::Type2D, width_render, height_render, 1, 1, RHI_Format::R11G11B10_Float, flags, "light_volumetric"); } + // ocean + { + uint32_t flags = RHI_Texture_Uav | RHI_Texture_Srv; + render_target(Renderer_RenderTarget::ocean_initial_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_initial_spectrum"); + } + // occlusion { // note #1: amd is very specific with depth formats, so if something is a depth render target, it can only have one mip and flags like RHI_Texture_Uav @@ -367,6 +373,12 @@ namespace spartan shader(Renderer_Shader::light_image_based_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "light_image_based.hlsl", async); } + // ocean + { + //shader(Renderer_Shader::ocean_initial_spectrum_c) = make_shared(); + //shader(Renderer_Shader::light_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\initial_spectrum.hlsl", async); + } + // blur { // gaussian From ca6e6a2151fcaffbc029ef320aeb4f4623060cd9 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 026/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 76036b897f..7e936c3a98 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,6 +1605,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() From 85913ef498b074963fed92147cecb3100a9c3c68 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 027/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 7e936c3a98..d85ffacce6 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1622,6 +1622,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() From 459559f66f24b419ef1f0e8de0f51913e4470b4f Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:19:24 +0200 Subject: [PATCH 028/133] [world] removed duplicated code --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index d85ffacce6..7e936c3a98 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1622,23 +1622,6 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } } void Game::Shutdown() From b0cfe5a32ab969a051f69cb9558d2cfaae929007 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:30:19 +0200 Subject: [PATCH 029/133] [world] fixed implicit conversion warning --- source/runtime/Game/Game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 7e936c3a98..efd3692391 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1615,7 +1615,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); water->SetParent(entity.get()); From 13bb38abe1114a4674111335cddea6f198387947 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 030/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index efd3692391..980b8f1756 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1598,24 +1598,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); water->SetParent(entity.get()); From f6bcd1f1b90c35c04d84693d0faca4a8ed7d4bd4 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 4 Sep 2025 16:48:43 +0200 Subject: [PATCH 031/133] [init] create basic test world --- source/runtime/Game/Game.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 980b8f1756..9fe1a2bb72 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,6 +1605,23 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } + + namespace water + { + void create() + { + entities::camera(); + entities::sun(true); + + auto entity = World::CreateEntity(); + + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + + water->SetParent(entity.get()); + + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + } } void Game::Shutdown() From 4319c1a3b58b5bc170d1fffd121cfac21c824d81 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:19:24 +0200 Subject: [PATCH 032/133] [world] removed duplicated code --- source/runtime/Game/Game.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 9fe1a2bb72..980b8f1756 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1605,23 +1605,6 @@ namespace spartan default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } } - - namespace water - { - void create() - { - entities::camera(); - entities::sun(true); - - auto entity = World::CreateEntity(); - - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); - - water->SetParent(entity.get()); - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - } } void Game::Shutdown() From 0ef5b2359c272722d42f5959053b0b85eb8e474d Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 7 Sep 2025 01:30:19 +0200 Subject: [PATCH 033/133] [world] fixed implicit conversion warning --- source/runtime/Game/Game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 980b8f1756..76036b897f 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1598,7 +1598,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2.0f, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); water->SetParent(entity.get()); From 2f88a59fe1b3bfea91d04a319ff1b656cded8633 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 18:58:20 +0200 Subject: [PATCH 034/133] [game] small fixes after rebase --- source/runtime/Game/Game.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 76036b897f..e329f3f70c 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1582,10 +1582,10 @@ namespace spartan { void create() { - entities::camera(); - entities::floor(); - entities::sun(true); - entities::material_ball(Vector3::Zero); + create_entity::camera(); + create_entity::floor(); + create_entity::sun(true); + create_entity::material_ball(Vector3::Zero); } } @@ -1593,14 +1593,14 @@ namespace spartan { void create() { - entities::camera(); - entities::sun(true); + create_entity::camera(); + create_entity::sun(true); auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = create_entity::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); - water->SetParent(entity.get()); + water->SetParent(entity); default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } From 08adc1bbabee085e9aea81ccc0ef263b1293ad26 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 20:45:26 +0200 Subject: [PATCH 035/133] [game] fixed errors caused by rebase --- source/runtime/Game/Game.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index e329f3f70c..960690f4dd 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1582,10 +1582,10 @@ namespace spartan { void create() { - create_entity::camera(); - create_entity::floor(); - create_entity::sun(true); - create_entity::material_ball(Vector3::Zero); + entities::camera(); + entities::floor(); + entities::sun(true); + entities::material_ball(Vector3::Zero); } } @@ -1593,12 +1593,12 @@ namespace spartan { void create() { - create_entity::camera(); - create_entity::sun(true); + entities::camera(); + entities::sun(true); auto entity = World::CreateEntity(); - auto water = create_entity::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); water->SetParent(entity); From 15c9be2b591e1848e41364920ac3f1cee608d68b Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 21:05:31 +0200 Subject: [PATCH 036/133] [shaders] uncommented shader loading --- source/runtime/Rendering/Renderer_Resources.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index e0d09099e5..7904d0b8e1 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -375,8 +375,8 @@ namespace spartan // ocean { - //shader(Renderer_Shader::ocean_initial_spectrum_c) = make_shared(); - //shader(Renderer_Shader::light_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\initial_spectrum.hlsl", async); + shader(Renderer_Shader::ocean_initial_spectrum_c) = make_shared(); + shader(Renderer_Shader::light_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\initial_spectrum.hlsl", false); } // blur From 0623ab73a51046107ed3b98d31ed691f16ee71d1 Mon Sep 17 00:00:00 2001 From: Panos Karabelas Date: Mon, 8 Sep 2025 20:27:10 +0100 Subject: [PATCH 037/133] [shaders] fixed compilation of ocean initial spectrum --- source/runtime/Rendering/Renderer_Resources.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 7904d0b8e1..9440d970de 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -376,7 +376,7 @@ namespace spartan // ocean { shader(Renderer_Shader::ocean_initial_spectrum_c) = make_shared(); - shader(Renderer_Shader::light_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\initial_spectrum.hlsl", false); + shader(Renderer_Shader::ocean_initial_spectrum_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\initial_spectrum.hlsl", false); } // blur From 293f9e9e4dee420d5228bf436a798b1c800a854c Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 8 Sep 2025 21:39:40 +0200 Subject: [PATCH 038/133] [shaders] updated color, closes #14 --- data/shaders/ocean/initial_spectrum.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index 239ab7f52f..5e1df510cc 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -3,5 +3,5 @@ RWTexture2D initial_spectrum : register(u9); [numthreads(8, 8, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { - initial_spectrum[thread_id.xy] = float4(1.0f, 0.0f, 1.0f, 1.0f); + initial_spectrum[thread_id.xy] = float4(1.0f, 1.0f, 0.0f, 1.0f); } From d07d3b7efc93b2a6c36999bfd4560ed02691b075 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Tue, 9 Sep 2025 09:26:13 +0200 Subject: [PATCH 039/133] [debug] removed file logging --- source/runtime/Core/Debugging.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index afdcc4be90..049fc87ee7 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -38,7 +38,7 @@ namespace spartan private: inline static bool m_validation_layer_enabled = false; // enables vulkan diagnostic layers, incurs significant per-draw cpu performance overhead inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact - inline static bool m_logging_to_file_enabled = true; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations + inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost From 627f0c75fe09573f76c53dbb7c1d7116148cc717 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Tue, 9 Sep 2025 15:18:26 +0200 Subject: [PATCH 040/133] [ocean] initial spectrum compute pass --- data/shaders/common_resources.hlsl | 17 ++ data/shaders/common_structs.hlsl | 34 ++++ data/shaders/ocean/initial_spectrum.hlsl | 158 ++++++++++++++++++- source/runtime/Game/Game.cpp | 89 ++++++++++- source/runtime/Game/Game.h | 2 +- source/runtime/Rendering/Material.cpp | 45 ++++++ source/runtime/Rendering/Material.h | 25 +++ source/runtime/Rendering/Renderer.cpp | 16 ++ source/runtime/Rendering/Renderer_Buffers.h | 17 ++ source/runtime/Rendering/Renderer_Passes.cpp | 21 ++- 10 files changed, 414 insertions(+), 10 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index 095562e52e..2260c06f51 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -108,6 +108,23 @@ struct MaterialParameters float anisotropic_rotation; float clearcoat; float clearcoat_roughness; + + struct JonswapParameters + { + float scale; + float spreadBlend; + float swell; + float gamma; + float shortWavesFade; + + float windDirection; + float fetch; + float windSpeed; + float repeatTime; + float angle; + float alpha; + float peakOmega; + } jonswap_parameters; bool has_texture_occlusion() { return (flags & (1 << 7)) != 0; } bool has_texture_roughness() { return (flags & (1 << 3)) != 0; } diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index fa8e262885..126f2be2b4 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -26,6 +26,23 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SPARTAN_COMMON_STRUCT #define SPARTAN_COMMON_STRUCT +struct JonswapParameters +{ + float scale; + float spreadBlend; + float swell; + float gamma; + float shortWavesFade; + + float windDirection; + float fetch; + float windSpeed; + float repeatTime; + float angle; + float alpha; + float peakOmega; +}; + struct Surface { // properties @@ -55,6 +72,8 @@ struct Surface float3 specular_energy; float3 diffuse_energy; + JonswapParameters jonswap_parameters; + // easy access to certain properties bool has_texture_height() { return flags & uint(1U << 0); } bool has_texture_normal() { return flags & uint(1U << 1); } @@ -70,6 +89,7 @@ struct Surface bool is_grass_blade() { return flags & uint(1U << 11); } bool is_water() { return flags & uint(1U << 12); } bool is_tessellated() { return flags & uint(1U << 13); } + bool is_ocean() { return flags & uint(1U << 15); } bool is_sky() { return alpha == 0.0f; } bool is_opaque() { return alpha == 1.0f; } bool is_transparent() { return alpha > 0.0f && alpha < 1.0f; } @@ -105,6 +125,20 @@ struct Surface subsurface_scattering = material.subsurface_scattering; specular_energy = 1.0f; diffuse_energy = 1.0f; + + // jonswap parameters + jonswap_parameters.alpha = material.jonswap_parameters.alpha; + jonswap_parameters.angle = material.jonswap_parameters.angle; + jonswap_parameters.fetch = material.jonswap_parameters.fetch; + jonswap_parameters.gamma = material.jonswap_parameters.gamma; + jonswap_parameters.peakOmega = material.jonswap_parameters.peakOmega; + jonswap_parameters.repeatTime = material.jonswap_parameters.repeatTime; + jonswap_parameters.scale = material.jonswap_parameters.scale; + jonswap_parameters.shortWavesFade = material.jonswap_parameters.shortWavesFade; + jonswap_parameters.spreadBlend = material.jonswap_parameters.spreadBlend; + jonswap_parameters.swell = material.jonswap_parameters.swell; + jonswap_parameters.windDirection = material.jonswap_parameters.windDirection; + jonswap_parameters.windSpeed = material.jonswap_parameters.windSpeed; // roughness is authored as perceptual roughness, as is convention roughness_alpha = roughness * roughness; diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index 5e1df510cc..cfaf56e361 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -1,7 +1,163 @@ +//static const float PI = 3.14159265f; + +#include "../common.hlsl" + +static const float G = 9.81f; +static const uint SPECTRUM_TEX_SIZE = 512; +static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; + +static const float depth = 10.0f; +static const float lowCutoff = 0.01f; +static const float highCutoff = 1000.0f; + RWTexture2D initial_spectrum : register(u9); +// Heavily Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute +float2 UniformToGaussian(float u1, float u2) +{ + float R = sqrt(-2.0f * log(u1)); + float theta = 2.0f * PI * u2; + + return float2(R * cos(theta), R * sin(theta)); +} + +float Dispersion(float kMag) +{ + return sqrt(G * kMag * tanh(min(kMag * depth, 20))); +} + +float DispersionDerivative(float kMag) +{ + float th = tanh(min(kMag * depth, 20)); + float ch = cosh(kMag * depth); + return G * (depth * kMag / ch / ch + th) / Dispersion(kMag) / 2.0f; +} + +float NormalizationFactor(float s) +{ + float s2 = s * s; + float s3 = s2 * s; + float s4 = s3 * s; + if (s < 5) + return -0.000564f * s4 + 0.00776f * s3 - 0.044f * s2 + 0.192f * s + 0.163f; + else + return -4.80e-08f * s4 + 1.07e-05f * s3 - 9.53e-04f * s2 + 5.90e-02f * s + 3.93e-01f; +} + +float DonelanBannerBeta(float x) +{ + if (x < 0.95f) + return 2.61f * pow(abs(x), 1.3f); + if (x < 1.6f) + return 2.28f * pow(abs(x), -1.3f); + + float p = -0.4f + 0.8393f * exp(-0.567f * log(x * x)); + return pow(10.0f, p); +} + +float DonelanBanner(float theta, float omega, float peakOmega) +{ + float beta = DonelanBannerBeta(omega / peakOmega); + float sech = 1.0f / cosh(beta * theta); + return beta / 2.0f / tanh(beta * 3.1416f) * sech * sech; +} + +float Cosine2s(float theta, float s) +{ + return NormalizationFactor(s) * pow(abs(cos(0.5f * theta)), 2.0f * s); +} + +float SpreadPower(float omega, float peakOmega) +{ + if (omega > peakOmega) + return 9.77f * pow(abs(omega / peakOmega), -2.5f); + else + return 6.97f * pow(abs(omega / peakOmega), 5.0f); +} + +float DirectionSpectrum(float theta, float omega, float peakOmega, float swell, float spreadBlend, float angle) +{ + float s = SpreadPower(omega, peakOmega) + 16 * tanh(min(omega / peakOmega, 20)) * swell * swell; + + return lerp(2.0f / 3.1415f * cos(theta) * cos(theta), Cosine2s(theta - angle, s), spreadBlend); +} + +float TMACorrection(float omega) +{ + float omegaH = omega * sqrt(depth / G); + if (omegaH <= 1.0f) + return 0.5f * omegaH * omegaH; + if (omegaH < 2.0f) + return 1.0f - 0.5f * (2.0f - omegaH) * (2.0f - omegaH); + + return 1.0f; +} + +float JONSWAP(float omega, float peakOmega, float gamma, float scale, float alpha) +{ + float sigma = (omega <= peakOmega) ? 0.07f : 0.09f; + + float r = exp(-(omega - peakOmega) * (omega - peakOmega) / 2.0f / sigma / sigma / peakOmega / peakOmega); + + float oneOverOmega = 1.0f / (omega + 1e-6f); + float peakOmegaOverOmega = peakOmega / omega; + return scale * TMACorrection(omega) * alpha * G * G + * oneOverOmega * oneOverOmega * oneOverOmega * oneOverOmega * oneOverOmega + * exp(-1.25f * peakOmegaOverOmega * peakOmegaOverOmega * peakOmegaOverOmega * peakOmegaOverOmega) + * pow(abs(gamma), r); +} + +float ShortWavesFade(float kLength, float shortWavesFade) +{ + return exp(-shortWavesFade * shortWavesFade * kLength * kLength); +} + +float hash(uint n) +{ + // integer hash copied from Hugo Elias + n = (n << 13U) ^ n; + n = n * (n * n * 15731U + 0x789221U) + 0x1376312589U; + return float(n & uint(0x7fffffffU)) / float(0x7fffffff); +} + [numthreads(8, 8, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { - initial_spectrum[thread_id.xy] = float4(1.0f, 1.0f, 0.0f, 1.0f); + float2 resolution_out; + initial_spectrum.GetDimensions(resolution_out.x, resolution_out.y); + Surface surface; + surface.Build(thread_id.xy, resolution_out, true, false); + + uint seed = thread_id.x + SPECTRUM_TEX_SIZE * thread_id.y + SPECTRUM_TEX_SIZE; + //seed += srt - > frame; + seed += 0; + + JonswapParameters params = surface.jonswap_parameters; + + float halfN = SPECTRUM_TEX_SIZE / 2.0f; + + float deltaK = 2.0f * PI / LENGTH_SCALE; + float2 K = (thread_id.xy - halfN) * deltaK; + float kLength = length(K); + + seed += hash(seed) * 10; + float4 uniformRandSamples = float4(hash(seed), hash(seed * 2), hash(seed * 3), hash(seed * 4)); + float2 gauss1 = UniformToGaussian(uniformRandSamples.x, uniformRandSamples.y); + float2 gauss2 = UniformToGaussian(uniformRandSamples.z, uniformRandSamples.w); + + if (lowCutoff <= kLength && kLength <= highCutoff) + { + float kAngle = atan2(K.y, K.x); + float omega = Dispersion(kLength); + float dOmegadk = DispersionDerivative(kLength); + + float spectrum = JONSWAP(omega, params.peakOmega, params.gamma, params.scale, params.alpha) * DirectionSpectrum(kAngle, omega, params.peakOmega, params.swell, params.spreadBlend, params.angle) * ShortWavesFade(kLength, params.shortWavesFade); + + initial_spectrum[thread_id.xy] = float4(float2(gauss2.x, gauss1.y) * float2(sqrt(2 * spectrum * abs(dOmegadk) / kLength * deltaK * deltaK).xx), 0.0f, 0.0f); + } + else + { + initial_spectrum[thread_id.xy] = float4(0.0f, 0.0f, 0.0f, 0.0f); + } } diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 960690f4dd..f32e17c07c 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -295,6 +295,89 @@ namespace spartan return water; } + + Entity* ocean(const Vector3& position, float dimension, uint32_t density) + { + // entity + Entity* water = World::CreateEntity(); + water->SetObjectName("ocean"); + water->SetPosition(position); + + // material + shared_ptr material = make_shared(); + { + material->SetObjectName("material_ocean"); + material->SetResourceFilePath("ocean" + string(EXTENSION_MATERIAL)); + + float windDir = 100.0f; + float angle = windDir / 180.0f * pi; + + material->SetColor(Color(0.0f, 150.0f / 255.0f, 130.0f / 255.0f, 150.0f / 255.0f)); + material->SetProperty(MaterialProperty::IsOcean, 1.0f); + material->SetOceanProperty(JonswapParameters::Scale, 2.5f); + material->SetOceanProperty(JonswapParameters::SpreadBlend, 0.9f); + material->SetOceanProperty(JonswapParameters::Swell, 0.6f); + material->SetOceanProperty(JonswapParameters::Fetch, 10000.0f); + material->SetOceanProperty(JonswapParameters::WindDirection, windDir); + material->SetOceanProperty(JonswapParameters::WindSpeed, 100.0f); + material->SetOceanProperty(JonswapParameters::Gamma, 3.3f); + material->SetOceanProperty(JonswapParameters::ShortWavesFade, 0.0f); + material->SetOceanProperty(JonswapParameters::RepeatTime, 200.0f); + + material->SetOceanProperty(JonswapParameters::Angle, angle); + material->SetOceanProperty(JonswapParameters::Alpha, 0.0f); // handled internally + material->SetOceanProperty(JonswapParameters::PeakOmega, 0.0f); // handled internally + } + + // geometry + { + // generate grid + const uint32_t grid_points_per_dimension = density; + vector vertices; + vector indices; + geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, dimension); + + // split into tiles + const uint32_t tile_count = std::max(1u, density / 6); // dynamic tile count based on density, minimum 1 + vector> tiled_vertices; + vector> tiled_indices; + vector tile_offsets; + spartan::geometry_processing::split_surface_into_tiles(vertices, indices, tile_count, tiled_vertices, tiled_indices, tile_offsets); + + for (uint32_t tile_index = 0; tile_index < static_cast(tiled_vertices.size()); tile_index++) + { + string name = "tile_" + to_string(tile_index); + + // create mesh if it doesn't exist + shared_ptr mesh = meshes.emplace_back(make_shared()); + mesh->SetObjectName(name); + mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); + mesh->AddGeometry(tiled_vertices[tile_index], tiled_indices[tile_index], false); + mesh->CreateGpuBuffers(); + + // create a child entity, add a renderable, and this mesh tile to it + { + Entity* entity_tile = World::CreateEntity(); + entity_tile->SetObjectName(name); + entity_tile->SetParent(water); + entity_tile->SetPosition(tile_offsets[tile_index]); + + if (Renderable* renderable = entity_tile->AddComponent()) + { + renderable->SetMesh(mesh.get()); + renderable->SetMaterial(material); + renderable->SetFlag(RenderableFlags::CastsShadows, false); + } + + // enable buoyancy + Physics* physics = entity_tile->AddComponent(); + physics->SetBodyType(BodyType::Water); + } + } + } + + return water; + } } void set_base_renderer_options() @@ -1589,7 +1672,7 @@ namespace spartan } } - namespace water + namespace ocean { void create() { @@ -1598,7 +1681,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::water({ 0.0f, 0.0f, 0.0f }, 20.0f, 2, spartan::Color::standard_blue, 2.0f, 0.1f); + auto water = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 2); water->SetParent(entity); @@ -1663,7 +1746,7 @@ namespace spartan case DefaultWorld::Showroom: worlds::showroom::create(); break; case DefaultWorld::LiminalSpace: worlds::liminal_space::create(); break; case DefaultWorld::Basic: worlds::basic::create(); break; - case DefaultWorld::Water: worlds::water::create(); break; + case DefaultWorld::Ocean: worlds::ocean::create(); break; default: SP_ASSERT_MSG(false, "Unhandled default world"); break; } diff --git a/source/runtime/Game/Game.h b/source/runtime/Game/Game.h index c5bd61c9f0..f787a80e38 100644 --- a/source/runtime/Game/Game.h +++ b/source/runtime/Game/Game.h @@ -32,7 +32,7 @@ namespace spartan Subway, Minecraft, Basic, - Water, + Ocean, Max }; diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index 7eb1f4b92e..7e9d1774e7 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -101,6 +101,9 @@ namespace spartan } } } + + float jonswap_alpha(float fetch, float windSpeed) { return 0.076f * pow(9.81f * fetch / windSpeed / windSpeed, -0.22f); } + float jonswap_peak_frequency(float fetch, float windSpeed) { return 22 * pow(windSpeed * fetch / 9.81f / 9.81f, -0.33f); } } namespace texture_processing @@ -730,6 +733,48 @@ namespace spartan } } + float Material::GetOceanProperty(const JonswapParameters property_type) const + { + SP_ASSERT_MSG(m_properties[static_cast(MaterialProperty::IsOcean)] == 1.0f, "Only ocean materials can have ocean properties"); + + return m_ocean_properties[static_cast(property_type)]; + } + + void Material::SetOceanProperty(const JonswapParameters property_type, const float value) + { + SP_ASSERT_MSG(m_properties[static_cast(MaterialProperty::IsOcean)] == 1.0f, "Only ocean materials can have ocean properties"); + + // special cases + if (property_type == JonswapParameters::Alpha) + { + float fetch = m_ocean_properties[static_cast(JonswapParameters::Fetch)]; + float windSpeed = m_ocean_properties[static_cast(JonswapParameters::WindSpeed)]; + + m_ocean_properties[static_cast(property_type)] = jonswap_alpha(fetch, windSpeed); + } + else if (property_type == JonswapParameters::PeakOmega) + { + float fetch = m_ocean_properties[static_cast(JonswapParameters::Fetch)]; + float windSpeed = m_ocean_properties[static_cast(JonswapParameters::WindSpeed)]; + + m_ocean_properties[static_cast(property_type)] = jonswap_peak_frequency(fetch, windSpeed); + } + else + { + if (m_ocean_properties[static_cast(property_type)] == value) + return; + + m_ocean_properties[static_cast(property_type)] = value; + + // if the world is loading, don't fire an event as we will spam the event system + // also the renderer will check all the materials after loading anyway + if (!ProgressTracker::GetProgress(ProgressType::World).IsProgressing()) + { + SP_FIRE_EVENT(EventType::MaterialOnChanged); + } + } + } + void Material::SetColor(const Color& color) { SetProperty(MaterialProperty::ColorR, color.r); diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 23e669d0ce..d423b18321 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -88,6 +88,7 @@ namespace spartan WindAnimation, // vertex wind animation ColorVariationFromInstance, // per-instance color variation IsWater, // water flow animation + IsOcean, // fft ocean rendering // render settings CullMode, // face culling mode @@ -107,6 +108,27 @@ namespace spartan Max }; + // used for ocean calculations + enum class JonswapParameters + { + Scale, // used to scale the Spectrum [1.0f, 5.0f] --> Value Range + SpreadBlend, // used to blend between agitated water motion, and windDirection [0.0f, 1.0f] + Swell, // influences wave choppines, the bigger the swell, the longer the wave length [0.0f, 1.0f] + Gamma, // defines the Spectrum Peak [0.0f, 7.0f] + + ShortWavesFade, // [0.0f, 1.0f] + WindDirection, // [0.0f, 360.0f] + Fetch, // distance over which Wind impacts Wave Formation [0.0f, 10000.0f] + WindSpeed, // [0.0f, 100.0f] + + RepeatTime, + Angle, + Alpha, + PeakOmega, + + Max + }; + class Material : public IResource { public: @@ -134,6 +156,8 @@ namespace spartan // properties float GetProperty(const MaterialProperty property_type) const { return m_properties[static_cast(property_type)]; } void SetProperty(const MaterialProperty property_type, const float value); + float GetOceanProperty(const JonswapParameters property_type) const; + void SetOceanProperty(const JonswapParameters property_type, const float value); void SetColor(const Color& color); bool IsTransparent() const { return GetProperty(MaterialProperty::ColorA) < 1.0f; } bool IsAlphaTested(); @@ -149,6 +173,7 @@ namespace spartan private: std::array(MaterialTextureType::Max) * slots_per_texture_type> m_textures; std::array(MaterialProperty::Max)> m_properties; + std::array(JonswapParameters::Max)> m_ocean_properties; uint32_t m_index = 0; }; } diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index 5e2fa7f785..4d28d8132d 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -868,6 +868,21 @@ namespace spartan properties[count].subsurface_scattering = material->GetProperty(MaterialProperty::SubsurfaceScattering); properties[count].world_space_uv = material->GetProperty(MaterialProperty::WorldSpaceUv); + // ocean + properties[count].jonswap_parameters.alpha = material->GetOceanProperty(JonswapParameters::Alpha); + properties[count].jonswap_parameters.angle = material->GetOceanProperty(JonswapParameters::Angle); + properties[count].jonswap_parameters.fetch = material->GetOceanProperty(JonswapParameters::Fetch); + properties[count].jonswap_parameters.gamma = material->GetOceanProperty(JonswapParameters::Gamma); + properties[count].jonswap_parameters.peakOmega = material->GetOceanProperty(JonswapParameters::PeakOmega); + properties[count].jonswap_parameters.repeatTime = material->GetOceanProperty(JonswapParameters::RepeatTime); + properties[count].jonswap_parameters.scale = material->GetOceanProperty(JonswapParameters::Scale); + properties[count].jonswap_parameters.shortWavesFade = material->GetOceanProperty(JonswapParameters::ShortWavesFade); + properties[count].jonswap_parameters.spreadBlend = material->GetOceanProperty(JonswapParameters::SpreadBlend); + properties[count].jonswap_parameters.swell = material->GetOceanProperty(JonswapParameters::Swell); + properties[count].jonswap_parameters.windDirection = material->GetOceanProperty(JonswapParameters::WindDirection); + properties[count].jonswap_parameters.windSpeed = material->GetOceanProperty(JonswapParameters::WindSpeed); + + // flags properties[count].flags = material->HasTextureOfType(MaterialTextureType::Height) ? (1U << 0) : 0; properties[count].flags |= material->HasTextureOfType(MaterialTextureType::Normal) ? (1U << 1) : 0; @@ -884,6 +899,7 @@ namespace spartan properties[count].flags |= material->GetProperty(MaterialProperty::IsWater) ? (1U << 12) : 0; properties[count].flags |= material->GetProperty(MaterialProperty::Tessellation) ? (1U << 13) : 0; properties[count].flags |= material->GetProperty(MaterialProperty::EmissiveFromAlbedo) ? (1U << 14) : 0; + properties[count].flags |= material->GetProperty(MaterialProperty::IsOcean) ? (1U << 15) : 0; // when changing the bit flags, ensure that you also update the Surface struct in common_structs.hlsl, so that it reads those flags as expected } diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index 356a06f156..ae08f93442 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -181,6 +181,23 @@ namespace spartan float anisotropic_rotation; float clearcoat; float clearcoat_roughness; + + struct JonswapParameters + { + float scale; + float spreadBlend; + float swell; + float gamma; + float shortWavesFade; + + float windDirection; + float fetch; + float windSpeed; + float repeatTime; + float angle; + float alpha; + float peakOmega; + } jonswap_parameters; }; struct Sb_Light diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index c1a5aa9dce..82350897b9 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -118,7 +118,7 @@ namespace spartan /*static bool compute_ocean_spectrum = true; if (compute_ocean_spectrum)*/ - Pass_ComputeInitialSpectrum(cmd_list_graphics_present); + Pass_ComputeInitialSpectrum(cmd_list_graphics_present); Pass_GBuffer(cmd_list_graphics_present, is_transparent); Pass_Light(cmd_list_graphics_present, is_transparent); @@ -1083,10 +1083,21 @@ namespace spartan pso.name = "ocean_initial_spectrum"; pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_initial_spectrum_c); cmd_list->SetPipelineState(pso); - - cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); - cmd_list->Dispatch(initial_spectrum); - + + for (uint32_t i = 0; i < m_draw_call_count; i++) + { + const Renderer_DrawCall& draw_call = m_draw_calls[i]; + Renderable* renderable = draw_call.renderable; + Material* material = renderable->GetMaterial(); + + // get ocean material + if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) + continue; + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); + cmd_list->Dispatch(initial_spectrum); + } + // for the lifetime of the engine, this will be read as an srv, so transition here initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } From fb4b201c1af7057b950dffde53c350ff63ad418f Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Tue, 9 Sep 2025 16:31:49 +0200 Subject: [PATCH 041/133] [ocean material] exposed spectrum variables to editor --- source/editor/Widgets/Properties.cpp | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 5844bf284e..d94d8c85f2 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -822,6 +822,56 @@ void Properties::ShowMaterial(Material* material) const show_property("Sheen", "Amount of soft velvet like reflection near edges", MaterialTextureType::Max, MaterialProperty::Sheen); show_property("Subsurface scattering","Amount of translucency", MaterialTextureType::Max, MaterialProperty::SubsurfaceScattering); } + + // ocean properties + if (material->GetProperty(MaterialProperty::IsOcean)) + { + const auto show_jonswap_params = [this, &material](const char* name, const char* tooltip, const JonswapParameters params) + { + bool show_modifier = params != JonswapParameters::Max; + + // name + if (name) + { + ImGui::Text(name); + + if (tooltip) + { + ImGuiSp::tooltip(tooltip); + } + + if (show_modifier) + { + ImGui::SameLine(column_pos_x); + } + } + + if (show_modifier) + { + float value = material->GetOceanProperty(params); + + float min = 0.0f; + + // this custom slider already has a unique id + ImGuiSp::draw_float_wrap("", &value, 0.004f, min); + + material->SetOceanProperty(params, value); + } + }; + + show_jonswap_params("Alpha", "", JonswapParameters::Alpha); + show_jonswap_params("Angle", "", JonswapParameters::Angle); + show_jonswap_params("Fetch", "", JonswapParameters::Fetch); + show_jonswap_params("Gamma", "", JonswapParameters::Gamma); + show_jonswap_params("Peak Omega", "", JonswapParameters::PeakOmega); + show_jonswap_params("Repeat Time", "", JonswapParameters::RepeatTime); + show_jonswap_params("Scale", "", JonswapParameters::Scale); + show_jonswap_params("Short Waves Fade", "", JonswapParameters::ShortWavesFade); + show_jonswap_params("Spread Blend", "", JonswapParameters::SpreadBlend); + show_jonswap_params("Swell", "", JonswapParameters::Swell); + show_jonswap_params("Wind Direction", "", JonswapParameters::WindDirection); + show_jonswap_params("Wind Speed", "", JonswapParameters::WindSpeed); + } // uv { From 30f86dd31025b47dde7c780c9e33b49bf1a58c75 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 10 Sep 2025 11:38:47 +0200 Subject: [PATCH 042/133] [ocean] fixed jonswap peak frequency function --- source/runtime/Rendering/Material.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index 7e9d1774e7..b411291317 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -103,7 +103,11 @@ namespace spartan } float jonswap_alpha(float fetch, float windSpeed) { return 0.076f * pow(9.81f * fetch / windSpeed / windSpeed, -0.22f); } - float jonswap_peak_frequency(float fetch, float windSpeed) { return 22 * pow(windSpeed * fetch / 9.81f / 9.81f, -0.33f); } + float jonswap_peak_frequency(float fetch, float windSpeed) { + float g = 9.81f; + float dimensionlessFetch = g * fetch / (windSpeed * windSpeed); + return 22.0f * (g / windSpeed) * pow(dimensionlessFetch, -0.33f); + } } namespace texture_processing From f783b1bbe480d40b78bf9e873b8d6946b59b9fd3 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 10 Sep 2025 14:51:56 +0200 Subject: [PATCH 043/133] [ocean] added a few more parameters required for the initial spectrum --- data/shaders/common_resources.hlsl | 4 ++++ data/shaders/common_structs.hlsl | 7 ++++++ data/shaders/ocean/initial_spectrum.hlsl | 24 +++++++++------------ source/editor/Widgets/Properties.cpp | 3 +++ source/runtime/Rendering/Material.h | 4 ++++ source/runtime/Rendering/Renderer.cpp | 4 +++- source/runtime/Rendering/Renderer_Buffers.h | 4 ++++ 7 files changed, 35 insertions(+), 15 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index 2260c06f51..a79408f722 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -124,6 +124,10 @@ struct MaterialParameters float angle; float alpha; float peakOmega; + + float depth; + float lowCutoff; + float highCutoff; } jonswap_parameters; bool has_texture_occlusion() { return (flags & (1 << 7)) != 0; } diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index 126f2be2b4..b0899a2bc8 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -41,6 +41,10 @@ struct JonswapParameters float angle; float alpha; float peakOmega; + + float depth; + float lowCutoff; + float highCutoff; }; struct Surface @@ -139,6 +143,9 @@ struct Surface jonswap_parameters.swell = material.jonswap_parameters.swell; jonswap_parameters.windDirection = material.jonswap_parameters.windDirection; jonswap_parameters.windSpeed = material.jonswap_parameters.windSpeed; + jonswap_parameters.depth = material.jonswap_parameters.depth; + jonswap_parameters.lowCutoff = material.jonswap_parameters.lowCutoff; + jonswap_parameters.highCutoff = material.jonswap_parameters.highCutoff; // roughness is authored as perceptual roughness, as is convention roughness_alpha = roughness * roughness; diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index cfaf56e361..40283f0fbd 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -6,10 +6,6 @@ static const float G = 9.81f; static const uint SPECTRUM_TEX_SIZE = 512; static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; -static const float depth = 10.0f; -static const float lowCutoff = 0.01f; -static const float highCutoff = 1000.0f; - RWTexture2D initial_spectrum : register(u9); // Heavily Inspired by Acerola's Implementation: @@ -22,16 +18,16 @@ float2 UniformToGaussian(float u1, float u2) return float2(R * cos(theta), R * sin(theta)); } -float Dispersion(float kMag) +float Dispersion(float kMag, float depth) { return sqrt(G * kMag * tanh(min(kMag * depth, 20))); } -float DispersionDerivative(float kMag) +float DispersionDerivative(float kMag, float depth) { float th = tanh(min(kMag * depth, 20)); float ch = cosh(kMag * depth); - return G * (depth * kMag / ch / ch + th) / Dispersion(kMag) / 2.0f; + return G * (depth * kMag / ch / ch + th) / Dispersion(kMag, depth) / 2.0f; } float NormalizationFactor(float s) @@ -83,7 +79,7 @@ float DirectionSpectrum(float theta, float omega, float peakOmega, float swell, return lerp(2.0f / 3.1415f * cos(theta) * cos(theta), Cosine2s(theta - angle, s), spreadBlend); } -float TMACorrection(float omega) +float TMACorrection(float omega, float depth) { float omegaH = omega * sqrt(depth / G); if (omegaH <= 1.0f) @@ -94,7 +90,7 @@ float TMACorrection(float omega) return 1.0f; } -float JONSWAP(float omega, float peakOmega, float gamma, float scale, float alpha) +float JONSWAP(float omega, float peakOmega, float gamma, float scale, float alpha, float depth) { float sigma = (omega <= peakOmega) ? 0.07f : 0.09f; @@ -102,7 +98,7 @@ float JONSWAP(float omega, float peakOmega, float gamma, float scale, float alph float oneOverOmega = 1.0f / (omega + 1e-6f); float peakOmegaOverOmega = peakOmega / omega; - return scale * TMACorrection(omega) * alpha * G * G + return scale * TMACorrection(omega, depth) * alpha * G * G * oneOverOmega * oneOverOmega * oneOverOmega * oneOverOmega * oneOverOmega * exp(-1.25f * peakOmegaOverOmega * peakOmegaOverOmega * peakOmegaOverOmega * peakOmegaOverOmega) * pow(abs(gamma), r); @@ -146,13 +142,13 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float2 gauss1 = UniformToGaussian(uniformRandSamples.x, uniformRandSamples.y); float2 gauss2 = UniformToGaussian(uniformRandSamples.z, uniformRandSamples.w); - if (lowCutoff <= kLength && kLength <= highCutoff) + if (params.lowCutoff <= kLength && kLength <= params.highCutoff) { float kAngle = atan2(K.y, K.x); - float omega = Dispersion(kLength); - float dOmegadk = DispersionDerivative(kLength); + float omega = Dispersion(kLength, params.depth); + float dOmegadk = DispersionDerivative(kLength, params.depth); - float spectrum = JONSWAP(omega, params.peakOmega, params.gamma, params.scale, params.alpha) * DirectionSpectrum(kAngle, omega, params.peakOmega, params.swell, params.spreadBlend, params.angle) * ShortWavesFade(kLength, params.shortWavesFade); + float spectrum = JONSWAP(omega, params.peakOmega, params.gamma, params.scale, params.alpha, params.depth) * DirectionSpectrum(kAngle, omega, params.peakOmega, params.swell, params.spreadBlend, params.angle) * ShortWavesFade(kLength, params.shortWavesFade); initial_spectrum[thread_id.xy] = float4(float2(gauss2.x, gauss1.y) * float2(sqrt(2 * spectrum * abs(dOmegadk) / kLength * deltaK * deltaK).xx), 0.0f, 0.0f); } diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index d94d8c85f2..99d2c16d72 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -871,6 +871,9 @@ void Properties::ShowMaterial(Material* material) const show_jonswap_params("Swell", "", JonswapParameters::Swell); show_jonswap_params("Wind Direction", "", JonswapParameters::WindDirection); show_jonswap_params("Wind Speed", "", JonswapParameters::WindSpeed); + show_jonswap_params("Depth", "", JonswapParameters::Depth); + show_jonswap_params("Low Cutoff", "", JonswapParameters::LowCutoff); + show_jonswap_params("High Cutoff", "", JonswapParameters::HighCutoff); } // uv diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index d423b18321..6608e88403 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -126,6 +126,10 @@ namespace spartan Alpha, PeakOmega, + Depth, + LowCutoff, + HighCutoff, + Max }; diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index de31bfbefe..33ac390529 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -881,7 +881,9 @@ namespace spartan properties[count].jonswap_parameters.swell = material->GetOceanProperty(JonswapParameters::Swell); properties[count].jonswap_parameters.windDirection = material->GetOceanProperty(JonswapParameters::WindDirection); properties[count].jonswap_parameters.windSpeed = material->GetOceanProperty(JonswapParameters::WindSpeed); - + properties[count].jonswap_parameters.depth = material->GetOceanProperty(JonswapParameters::Depth); + properties[count].jonswap_parameters.lowCutoff = material->GetOceanProperty(JonswapParameters::LowCutoff); + properties[count].jonswap_parameters.highCutoff = material->GetOceanProperty(JonswapParameters::HighCutoff); // flags properties[count].flags = material->HasTextureOfType(MaterialTextureType::Height) ? (1U << 0) : 0; diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index ae08f93442..df95505d80 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -197,6 +197,10 @@ namespace spartan float angle; float alpha; float peakOmega; + + float depth; + float lowCutoff; + float highCutoff; } jonswap_parameters; }; From fab534a846571c14060ddf68cd39bd8d4ca7e1b5 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 10 Sep 2025 14:53:10 +0200 Subject: [PATCH 044/133] [ocean] fixed parameter update logic for alpha, peakOmega and angle --- source/runtime/Game/Game.cpp | 12 +++++++---- source/runtime/Rendering/Material.cpp | 30 +++++++++++++++++---------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index f32e17c07c..9e0031b7b6 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -310,10 +310,14 @@ namespace spartan material->SetResourceFilePath("ocean" + string(EXTENSION_MATERIAL)); float windDir = 100.0f; - float angle = windDir / 180.0f * pi; material->SetColor(Color(0.0f, 150.0f / 255.0f, 130.0f / 255.0f, 150.0f / 255.0f)); material->SetProperty(MaterialProperty::IsOcean, 1.0f); + + material->SetOceanProperty(JonswapParameters::Angle, 0.0f); //handled internally + material->SetOceanProperty(JonswapParameters::Alpha, 0.0f); // handled internally + material->SetOceanProperty(JonswapParameters::PeakOmega, 0.0f); // handled internally + material->SetOceanProperty(JonswapParameters::Scale, 2.5f); material->SetOceanProperty(JonswapParameters::SpreadBlend, 0.9f); material->SetOceanProperty(JonswapParameters::Swell, 0.6f); @@ -324,9 +328,9 @@ namespace spartan material->SetOceanProperty(JonswapParameters::ShortWavesFade, 0.0f); material->SetOceanProperty(JonswapParameters::RepeatTime, 200.0f); - material->SetOceanProperty(JonswapParameters::Angle, angle); - material->SetOceanProperty(JonswapParameters::Alpha, 0.0f); // handled internally - material->SetOceanProperty(JonswapParameters::PeakOmega, 0.0f); // handled internally + material->SetOceanProperty(JonswapParameters::Depth, 20.0f); + material->SetOceanProperty(JonswapParameters::LowCutoff, 0.001f); + material->SetOceanProperty(JonswapParameters::HighCutoff, 1000.0f); } // geometry diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index b411291317..95f7861816 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -749,19 +749,27 @@ namespace spartan SP_ASSERT_MSG(m_properties[static_cast(MaterialProperty::IsOcean)] == 1.0f, "Only ocean materials can have ocean properties"); // special cases - if (property_type == JonswapParameters::Alpha) + if (property_type == JonswapParameters::Fetch || property_type == JonswapParameters::WindSpeed) { + // update fetch or windspeed + m_ocean_properties[static_cast(property_type)] = value; + + // get fetch and windspeed float fetch = m_ocean_properties[static_cast(JonswapParameters::Fetch)]; float windSpeed = m_ocean_properties[static_cast(JonswapParameters::WindSpeed)]; - m_ocean_properties[static_cast(property_type)] = jonswap_alpha(fetch, windSpeed); + // update alpha and peakOmega + m_ocean_properties[static_cast(JonswapParameters::Alpha)] = jonswap_alpha(fetch, windSpeed); + m_ocean_properties[static_cast(JonswapParameters::PeakOmega)] = jonswap_peak_frequency(fetch, windSpeed); } - else if (property_type == JonswapParameters::PeakOmega) + else if (property_type == JonswapParameters::WindDirection) { - float fetch = m_ocean_properties[static_cast(JonswapParameters::Fetch)]; - float windSpeed = m_ocean_properties[static_cast(JonswapParameters::WindSpeed)]; + // update wind direction + m_ocean_properties[static_cast(property_type)] = value; - m_ocean_properties[static_cast(property_type)] = jonswap_peak_frequency(fetch, windSpeed); + // update angle, based on the wind direction + float angle = value / 180.0f * pi; + m_ocean_properties[static_cast(JonswapParameters::Angle)] = angle; } else { @@ -769,13 +777,13 @@ namespace spartan return; m_ocean_properties[static_cast(property_type)] = value; + } - // if the world is loading, don't fire an event as we will spam the event system + // if the world is loading, don't fire an event as we will spam the event system // also the renderer will check all the materials after loading anyway - if (!ProgressTracker::GetProgress(ProgressType::World).IsProgressing()) - { - SP_FIRE_EVENT(EventType::MaterialOnChanged); - } + if (!ProgressTracker::GetProgress(ProgressType::World).IsProgressing()) + { + SP_FIRE_EVENT(EventType::MaterialOnChanged); } } From 65060c38efab6e63cacef58acb9d456042da932a Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 10 Sep 2025 18:08:15 +0200 Subject: [PATCH 045/133] [ocean] spectrum is now computed only once per parameter change, instead of per frame --- source/editor/ImGui/ImGui_Extension.h | 6 ++++-- source/editor/Widgets/Properties.cpp | 5 ++--- source/runtime/Rendering/Material.cpp | 3 +++ source/runtime/Rendering/Material.h | 4 ++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/source/editor/ImGui/ImGui_Extension.h b/source/editor/ImGui/ImGui_Extension.h index d6262d2b20..f1b07450c4 100644 --- a/source/editor/ImGui/ImGui_Extension.h +++ b/source/editor/ImGui/ImGui_Extension.h @@ -271,7 +271,7 @@ namespace ImGuiSp } // a drag float which will wrap the mouse cursor around the edges of the screen - static void draw_float_wrap(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const ImGuiSliderFlags flags = 0) + static bool draw_float_wrap(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const ImGuiSliderFlags flags = 0) { static const uint32_t screen_edge_padding = 10; ImGuiIO& io = ImGui::GetIO(); @@ -315,8 +315,10 @@ namespace ImGuiSp } ImGui::PushID(static_cast(ImGui::GetCursorPosX() + ImGui::GetCursorPosY())); - ImGui::DragFloat(label, v, v_speed, v_min, v_max, format, flags); + bool result = ImGui::DragFloat(label, v, v_speed, v_min, v_max, format, flags); ImGui::PopID(); + + return result; } diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 99d2c16d72..5983f77556 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -853,9 +853,8 @@ void Properties::ShowMaterial(Material* material) const float min = 0.0f; // this custom slider already has a unique id - ImGuiSp::draw_float_wrap("", &value, 0.004f, min); - - material->SetOceanProperty(params, value); + if (ImGuiSp::draw_float_wrap("", &value, 0.004f, min)) + material->SetOceanProperty(params, value); } }; diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index 95f7861816..96a91f259a 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -779,6 +779,9 @@ namespace spartan m_ocean_properties[static_cast(property_type)] = value; } + // mark for spectrum re-computation + m_should_compute_spectrum = true; + // if the world is loading, don't fire an event as we will spam the event system // also the renderer will check all the materials after loading anyway if (!ProgressTracker::GetProgress(ProgressType::World).IsProgressing()) diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 6608e88403..09d88ae776 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -165,12 +165,15 @@ namespace spartan void SetColor(const Color& color); bool IsTransparent() const { return GetProperty(MaterialProperty::ColorA) < 1.0f; } bool IsAlphaTested(); + bool IsOcean() const { return GetProperty(MaterialProperty::IsOcean) == 1.0f; } // misc void PrepareForGpu(); uint32_t GetUsedSlotCount() const; void SetIndex(const uint32_t index) { m_index = index; } uint32_t GetIndex() const { return m_index; } + bool ShouldComputeSpectrum() const { return m_should_compute_spectrum; } + void MarkSpectrumAsComputed() { m_should_compute_spectrum = false; } static const uint32_t slots_per_texture_type = 4; @@ -179,5 +182,6 @@ namespace spartan std::array(MaterialProperty::Max)> m_properties; std::array(JonswapParameters::Max)> m_ocean_properties; uint32_t m_index = 0; + bool m_should_compute_spectrum = true; }; } From 11a0069e60bc0026bc7471075831c10d531254ca Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 10 Sep 2025 18:09:05 +0200 Subject: [PATCH 046/133] [ocean] compute conjugate and pack into spectrum texture --- data/shaders/ocean/pack_spectrum.hlsl | 16 ++++++ source/runtime/Rendering/Renderer.h | 1 + .../runtime/Rendering/Renderer_Definitions.h | 1 + source/runtime/Rendering/Renderer_Passes.cpp | 55 ++++++++++++++----- .../runtime/Rendering/Renderer_Resources.cpp | 3 + 5 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 data/shaders/ocean/pack_spectrum.hlsl diff --git a/data/shaders/ocean/pack_spectrum.hlsl b/data/shaders/ocean/pack_spectrum.hlsl new file mode 100644 index 0000000000..0a1f6a0f5e --- /dev/null +++ b/data/shaders/ocean/pack_spectrum.hlsl @@ -0,0 +1,16 @@ +#include "../common.hlsl" + +static const uint SPECTRUM_TEX_SIZE = 512; + +RWTexture2D initial_spectrum : register(u9); + +[numthreads(8, 8, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + // Heavily Inspired by Acerola's Implementation: + // https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + float2 h0 = initial_spectrum[thread_id.xy].rg; + float2 h0conj = initial_spectrum[uint2((SPECTRUM_TEX_SIZE - thread_id.x) % SPECTRUM_TEX_SIZE, (SPECTRUM_TEX_SIZE - thread_id.y) % SPECTRUM_TEX_SIZE)].rg; + + initial_spectrum[thread_id.xy] = float4(h0, h0conj.x, -h0conj.y); +} diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index e3ad10b28e..c35859bd16 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -163,6 +163,7 @@ namespace spartan static void Pass_Lut_AtmosphericScattering(RHI_CommandList* cmd_list); // passes - ocean static void Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list); + static void Pass_PackSpectrum(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 7a4f349c88..baec2ef551 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -192,6 +192,7 @@ namespace spartan dithering_c, transparency_reflection_refraction_c, ocean_initial_spectrum_c, + ocean_pack_spectrum_c, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 82350897b9..ccc63d7c1b 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -116,9 +116,27 @@ namespace spartan { bool is_transparent = true; - /*static bool compute_ocean_spectrum = true; - if (compute_ocean_spectrum)*/ - Pass_ComputeInitialSpectrum(cmd_list_graphics_present); + // Ocean Passes + for (uint32_t i = 0; i < m_draw_call_count; i++) + { + const Renderer_DrawCall& draw_call = m_draw_calls[i]; + Renderable* renderable = draw_call.renderable; + Material* material = renderable->GetMaterial(); + + // get ocean material + if (!material->IsOcean()) + continue; + + if (material->ShouldComputeSpectrum()) + { + SP_LOG_INFO("Computing Ocean Spectrum..."); + Pass_ComputeInitialSpectrum(cmd_list_graphics_present); + // calculates conjugate and stores it in BA channels of the initial spectrum + Pass_PackSpectrum(cmd_list_graphics_present); + + material->MarkSpectrumAsComputed(); + } + } Pass_GBuffer(cmd_list_graphics_present, is_transparent); Pass_Light(cmd_list_graphics_present, is_transparent); @@ -1084,19 +1102,28 @@ namespace spartan pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_initial_spectrum_c); cmd_list->SetPipelineState(pso); - for (uint32_t i = 0; i < m_draw_call_count; i++) - { - const Renderer_DrawCall& draw_call = m_draw_calls[i]; - Renderable* renderable = draw_call.renderable; - Material* material = renderable->GetMaterial(); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); + cmd_list->Dispatch(initial_spectrum); - // get ocean material - if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) - continue; + // for the lifetime of the engine, this will be read as an srv, so transition here + //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } - cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); - cmd_list->Dispatch(initial_spectrum); - } + void Renderer::Pass_PackSpectrum(RHI_CommandList* cmd_list) + { + RHI_Texture* initial_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_initial_spectrum); + + cmd_list->BeginTimeblock("ocean_pack_spectrum"); + { + RHI_PipelineState pso; + pso.name = "ocean_pack_spectrum"; + pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_pack_spectrum_c); + cmd_list->SetPipelineState(pso); + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); + cmd_list->Dispatch(initial_spectrum); // for the lifetime of the engine, this will be read as an srv, so transition here initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 40ff00d8f6..2ca625b978 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -380,6 +380,9 @@ namespace spartan { shader(Renderer_Shader::ocean_initial_spectrum_c) = make_shared(); shader(Renderer_Shader::ocean_initial_spectrum_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\initial_spectrum.hlsl", false); + + shader(Renderer_Shader::ocean_pack_spectrum_c) = make_shared(); + shader(Renderer_Shader::ocean_pack_spectrum_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\pack_spectrum.hlsl", false); } // blur From d91fa04e10a7718267938d65708b610e134f9d98 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 11 Sep 2025 10:07:04 +0200 Subject: [PATCH 047/133] [ocean] added ocean properties to material update function --- source/runtime/Rendering/Material.cpp | 7 ------- source/runtime/Rendering/Material.h | 1 + source/runtime/World/World.cpp | 4 ++++ 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index eb9ff8815c..1e464c2887 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -769,13 +769,6 @@ namespace spartan // mark for spectrum re-computation m_should_compute_spectrum = true; - - // if the world is loading, don't fire an event as we will spam the event system - // also the renderer will check all the materials after loading anyway - if (!ProgressTracker::GetProgress(ProgressType::World).IsProgressing()) - { - SP_FIRE_EVENT(EventType::MaterialOnChanged); - } } void Material::SetColor(const Color& color) diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index c5764006c6..19b5a8c3c8 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -179,6 +179,7 @@ namespace spartan void MarkSpectrumAsComputed() { m_should_compute_spectrum = false; } const std::array(MaterialProperty::Max)>& GetProperties() const { return m_properties; } + const std::array(JonswapParameters::Max)>& GetOceanProperties() const { return m_ocean_properties; } private: std::array(MaterialTextureType::Max) * slots_per_texture> m_textures; diff --git a/source/runtime/World/World.cpp b/source/runtime/World/World.cpp index fbbbe55b42..661db332de 100644 --- a/source/runtime/World/World.cpp +++ b/source/runtime/World/World.cpp @@ -89,6 +89,10 @@ namespace spartan { hash = (hash * 31) ^ std::hash{}(prop); } + for (const float oceanProp : material->GetOceanProperties()) + { + hash = (hash * 31) ^ std::hash{}(oceanProp); + } return hash; } From 579303394276b5008dd18e21a4122aeadab6ff29 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 11 Sep 2025 11:06:57 +0200 Subject: [PATCH 048/133] [ocean] added pass for generating slope and displacement spectrums --- data/shaders/ocean/advance_spectrum.hlsl | 72 +++++++++++++++++++ source/runtime/Rendering/Renderer.h | 1 + .../runtime/Rendering/Renderer_Definitions.h | 7 +- source/runtime/Rendering/Renderer_Passes.cpp | 27 +++++++ .../runtime/Rendering/Renderer_Resources.cpp | 8 +++ 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 data/shaders/ocean/advance_spectrum.hlsl diff --git a/data/shaders/ocean/advance_spectrum.hlsl b/data/shaders/ocean/advance_spectrum.hlsl new file mode 100644 index 0000000000..ebb1f16804 --- /dev/null +++ b/data/shaders/ocean/advance_spectrum.hlsl @@ -0,0 +1,72 @@ +#include "../common.hlsl" + +static const float G = 9.81f; +static const uint SPECTRUM_TEX_SIZE = 512; +static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; + +RWTexture2D initial_spectrum : register(u9); +RWTexture2D displacement_spectrum : register(u10); +RWTexture2D slope_spectrum : register(u11); + +float2 ComplexMult(float2 a, float2 b) +{ + return float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); +} + +float2 EulerFormula(float x) +{ + return float2(cos(x), sin(x)); +} + +[numthreads(8, 8, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + float4 initialSignal = initial_spectrum[thread_id.xy]; + float2 h0 = initialSignal.rg; + float2 h0conj = initialSignal.ba; + + float halfN = SPECTRUM_TEX_SIZE / 2.0f; + float2 K = (thread_id.xy - halfN) * 2.0f * PI / LENGTH_SCALE; + float kMag = length(K); + float kMagRcp = rcp(kMag); + + if (kMag < 0.0001f) + { + kMagRcp = 1.0f; + } + + float2 resolution_out; + initial_spectrum.GetDimensions(resolution_out.x, resolution_out.y); + Surface surface; + surface.Build(thread_id.xy, resolution_out, true, false); + JonswapParameters params = surface.jonswap_parameters; + + float repeatTime = params.repeatTime; //frac(srt->time); + float w_0 = 2.0f * PI / repeatTime; + float dispersion = floor(sqrt(G * kMag) / w_0) * w_0 * 0.16f; //srt - > time; + + float2 exponent = EulerFormula(dispersion); + + float2 htilde = ComplexMult(h0, exponent) + ComplexMult(h0conj, float2(exponent.x, -exponent.y)); + float2 ih = float2(-htilde.y, htilde.x); + + float2 displacementX = ih * K.x * kMagRcp; + float2 displacementY = htilde; + float2 displacementZ = ih * K.y * kMagRcp; + + float2 displacementX_dx = -htilde * K.x * K.x * kMagRcp; + float2 displacementY_dx = ih * K.x; + float2 displacementZ_dx = -htilde * K.x * K.y * kMagRcp; + + float2 displacementY_dz = ih * K.y; + float2 displacementZ_dz = -htilde * K.y * K.y * kMagRcp; + + float2 htildeDisplacementX = float2(displacementX.x - displacementZ.y, displacementX.y + displacementZ.x); + float2 htildeDisplacementZ = float2(displacementY.x - displacementZ_dx.y, displacementY.y + displacementZ_dx.x); + + float2 htildeSlopeX = float2(displacementY_dx.x - displacementY_dz.y, displacementY_dx.y + displacementY_dz.x); + float2 htildeSlopeZ = float2(displacementX_dx.x - displacementZ_dz.y, displacementX_dx.y + displacementZ_dz.x); + + displacement_spectrum[thread_id.xy] = float4(htildeDisplacementX, htildeDisplacementZ); + slope_spectrum[thread_id.xy] = float4(htildeSlopeX, htildeSlopeZ); +} diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index c5439c48df..99c0ad1cbf 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -164,6 +164,7 @@ namespace spartan // passes - ocean static void Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list); static void Pass_PackSpectrum(RHI_CommandList* cmd_list); + static void Pass_AdvanceSpectrum(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index baec2ef551..d827543dae 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -137,7 +137,9 @@ namespace spartan visibility = 6, sb_spd = 7, tex_spd = 8, - ocean_initial_spectrum = 9, + ocean_initial_spectrum = 9, + ocean_displacement_spectrum = 10, + ocean_slope_spectrum = 11, }; enum class Renderer_Shader : uint8_t @@ -193,6 +195,7 @@ namespace spartan transparency_reflection_refraction_c, ocean_initial_spectrum_c, ocean_pack_spectrum_c, + ocean_advance_spectrum_c, max }; @@ -226,6 +229,8 @@ namespace spartan shading_rate, shadow_atlas, ocean_initial_spectrum, + ocean_displacement_spectrum, + ocean_slope_spectrum, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index ccc63d7c1b..a3a061453f 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -136,6 +136,9 @@ namespace spartan material->MarkSpectrumAsComputed(); } + + // computes displacement and slope maps + Pass_AdvanceSpectrum(cmd_list_graphics_present); } Pass_GBuffer(cmd_list_graphics_present, is_transparent); @@ -1131,6 +1134,30 @@ namespace spartan cmd_list->EndTimeblock(); } + void Renderer::Pass_AdvanceSpectrum(RHI_CommandList* cmd_list) + { + RHI_Texture* initial_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_initial_spectrum); + RHI_Texture* displacement_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_spectrum); + RHI_Texture* slope_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_slope_spectrum); + + cmd_list->BeginTimeblock("ocean_advance_spectrum"); + { + RHI_PipelineState pso; + pso.name = "ocean_advance_spectrum"; + pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_advance_spectrum_c); + cmd_list->SetPipelineState(pso); + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_spectrum, displacement_spectrum); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); + cmd_list->Dispatch(initial_spectrum); + + // for the lifetime of the engine, this will be read as an srv, so transition here + initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } + void Renderer::Pass_PostProcess(RHI_CommandList* cmd_list) { // acquire render targets diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 2ca625b978..b547f764f6 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -228,6 +228,10 @@ namespace spartan { uint32_t flags = RHI_Texture_Uav | RHI_Texture_Srv; render_target(Renderer_RenderTarget::ocean_initial_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_initial_spectrum"); + + render_target(Renderer_RenderTarget::ocean_displacement_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_spectrum"); + + render_target(Renderer_RenderTarget::ocean_slope_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_spectrum"); } // occlusion @@ -383,6 +387,10 @@ namespace spartan shader(Renderer_Shader::ocean_pack_spectrum_c) = make_shared(); shader(Renderer_Shader::ocean_pack_spectrum_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\pack_spectrum.hlsl", false); + + shader(Renderer_Shader::ocean_advance_spectrum_c) = make_shared(); + shader(Renderer_Shader::ocean_advance_spectrum_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\advance_spectrum.hlsl", false); + } // blur From 3b2a05eb11133ec66f462f697d70f367944b1fd6 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 11 Sep 2025 13:38:44 +0200 Subject: [PATCH 049/133] [ocean] fft shaders --- data/shaders/ocean/fft_common.hlsl | 54 +++++++++++++++++++ data/shaders/ocean/horizontal_fft.hlsl | 11 ++++ data/shaders/ocean/vertical_fft.hlsl | 11 ++++ source/runtime/Rendering/Renderer.h | 2 + .../runtime/Rendering/Renderer_Definitions.h | 2 + source/runtime/Rendering/Renderer_Passes.cpp | 48 ++++++++++++++++- .../runtime/Rendering/Renderer_Resources.cpp | 5 ++ 7 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 data/shaders/ocean/fft_common.hlsl create mode 100644 data/shaders/ocean/horizontal_fft.hlsl create mode 100644 data/shaders/ocean/vertical_fft.hlsl diff --git a/data/shaders/ocean/fft_common.hlsl b/data/shaders/ocean/fft_common.hlsl new file mode 100644 index 0000000000..37cbda88c3 --- /dev/null +++ b/data/shaders/ocean/fft_common.hlsl @@ -0,0 +1,54 @@ +#ifndef SPARTAN_FFT_COMMON +#define SPARTAN_FFT_COMMON + +static const uint SPECTRUM_TEX_SIZE = 512; +static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; + +static const uint LOG_SIZE = log(SPECTRUM_TEX_SIZE) / log(2); // result of Log base 2 of SPECTRUM_TEX_SIZE + +groupshared float4 fftGroupBuffer[2][SPECTRUM_TEX_SIZE]; + +void ButterflyValues(uint step, uint index, out uint2 indices, out float2 twiddle) +{ + const float twoPi = 6.28318530718; + uint b = SPECTRUM_TEX_SIZE >> (step + 1); + uint w = b * (index / b); + uint i = (w + index) % SPECTRUM_TEX_SIZE; + sincos(-twoPi / SPECTRUM_TEX_SIZE * w, twiddle.y, twiddle.x); + + // This is what makes it the inverse FFT + twiddle.y = -twiddle.y; + indices = uint2(i, i + b); +} + +float2 ComplexMult(float2 a, float2 b) +{ + return float2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); +} + +float4 FFT(uint threadIndex, float4 input) +{ + fftGroupBuffer[0][threadIndex] = input; + GroupMemoryBarrierWithGroupSync(); + + bool flag = false; + + /*[unroll]*/ + for (uint step = 0; step < LOG_SIZE; ++step) + { + uint2 inputsIndices; + float2 twiddle; + ButterflyValues(step, threadIndex, inputsIndices, twiddle); + + float4 v = fftGroupBuffer[flag][inputsIndices.y]; + fftGroupBuffer[!flag][threadIndex] = + fftGroupBuffer[flag][inputsIndices.x] + float4(ComplexMult(twiddle, v.xy), ComplexMult(twiddle, v.zw)); + + flag = !flag; + GroupMemoryBarrierWithGroupSync(); + } + + return fftGroupBuffer[flag][threadIndex]; +} + +#endif // SPARTAN_FFT_COMMON diff --git a/data/shaders/ocean/horizontal_fft.hlsl b/data/shaders/ocean/horizontal_fft.hlsl new file mode 100644 index 0000000000..4f2d9e04ae --- /dev/null +++ b/data/shaders/ocean/horizontal_fft.hlsl @@ -0,0 +1,11 @@ +#include "fft_common.hlsl" + +RWTexture2D displacement_spectrum : register(u10); +RWTexture2D slope_spectrum : register(u11); + +[numthreads(512, 1, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + displacement_spectrum[thread_id.xy] = FFT(thread_id.x, displacement_spectrum[thread_id.xy]); + slope_spectrum[thread_id.xy] = FFT(thread_id.x, slope_spectrum[thread_id.xy]); +} diff --git a/data/shaders/ocean/vertical_fft.hlsl b/data/shaders/ocean/vertical_fft.hlsl new file mode 100644 index 0000000000..eba24506c5 --- /dev/null +++ b/data/shaders/ocean/vertical_fft.hlsl @@ -0,0 +1,11 @@ +#include "fft_common.hlsl" + +RWTexture2D displacement_spectrum : register(u10); +RWTexture2D slope_spectrum : register(u11); + +[numthreads(512, 1, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + displacement_spectrum[thread_id.yx] = FFT(thread_id.x, displacement_spectrum[thread_id.yx]); + slope_spectrum[thread_id.yx] = FFT(thread_id.x, slope_spectrum[thread_id.yx]); +} diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index 99c0ad1cbf..314a3d1040 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -165,6 +165,8 @@ namespace spartan static void Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list); static void Pass_PackSpectrum(RHI_CommandList* cmd_list); static void Pass_AdvanceSpectrum(RHI_CommandList* cmd_list); + static void Pass_ApplyHorizontalFFT(RHI_CommandList* cmd_list); + static void Pass_ApplyVerticalFFT(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index d827543dae..9d3e38592c 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -196,6 +196,8 @@ namespace spartan ocean_initial_spectrum_c, ocean_pack_spectrum_c, ocean_advance_spectrum_c, + ocean_horizontal_fft_c, + ocean_vertical_fft_c, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index a3a061453f..210400ec13 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -139,6 +139,8 @@ namespace spartan // computes displacement and slope maps Pass_AdvanceSpectrum(cmd_list_graphics_present); + Pass_ApplyHorizontalFFT(cmd_list_graphics_present); + Pass_ApplyVerticalFFT(cmd_list_graphics_present); } Pass_GBuffer(cmd_list_graphics_present, is_transparent); @@ -1153,7 +1155,51 @@ namespace spartan cmd_list->Dispatch(initial_spectrum); // for the lifetime of the engine, this will be read as an srv, so transition here - initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } + + void Renderer::Pass_ApplyHorizontalFFT(RHI_CommandList* cmd_list) + { + RHI_Texture* displacement_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_spectrum); + RHI_Texture* slope_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_slope_spectrum); + + cmd_list->BeginTimeblock("ocean_horizontal_fft"); + { + RHI_PipelineState pso; + pso.name = "ocean_horizontal_fft"; + pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_horizontal_fft_c); + cmd_list->SetPipelineState(pso); + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_spectrum, displacement_spectrum); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); + cmd_list->Dispatch(1, displacement_spectrum->GetHeight(), 1); + + // for the lifetime of the engine, this will be read as an srv, so transition here + //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } + + void Renderer::Pass_ApplyVerticalFFT(RHI_CommandList* cmd_list) + { + RHI_Texture* displacement_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_spectrum); + RHI_Texture* slope_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_slope_spectrum); + + cmd_list->BeginTimeblock("ocean_vertical_fft"); + { + RHI_PipelineState pso; + pso.name = "ocean_vertical_fft"; + pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_vertical_fft_c); + cmd_list->SetPipelineState(pso); + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_spectrum, displacement_spectrum); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); + cmd_list->Dispatch(1, displacement_spectrum->GetHeight(), 1); + + // for the lifetime of the engine, this will be read as an srv, so transition here + //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } cmd_list->EndTimeblock(); } diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index b547f764f6..f390fbc3cd 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -391,6 +391,11 @@ namespace spartan shader(Renderer_Shader::ocean_advance_spectrum_c) = make_shared(); shader(Renderer_Shader::ocean_advance_spectrum_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\advance_spectrum.hlsl", false); + shader(Renderer_Shader::ocean_horizontal_fft_c) = make_shared(); + shader(Renderer_Shader::ocean_horizontal_fft_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\horizontal_fft.hlsl", false); + + shader(Renderer_Shader::ocean_vertical_fft_c) = make_shared(); + shader(Renderer_Shader::ocean_vertical_fft_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\vertical_fft.hlsl", false); } // blur From a0d9a21b8049a8ff57ec7c4083dcd34c39f0cd41 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 11 Sep 2025 14:20:10 +0200 Subject: [PATCH 050/133] [ocean] generate displacement and slope maps --- data/shaders/ocean/generate_maps.hlsl | 43 +++++++++++++++++++ source/runtime/Rendering/Renderer.h | 1 + .../runtime/Rendering/Renderer_Definitions.h | 5 +++ source/runtime/Rendering/Renderer_Passes.cpp | 28 ++++++++++++ .../runtime/Rendering/Renderer_Resources.cpp | 7 +++ 5 files changed, 84 insertions(+) create mode 100644 data/shaders/ocean/generate_maps.hlsl diff --git a/data/shaders/ocean/generate_maps.hlsl b/data/shaders/ocean/generate_maps.hlsl new file mode 100644 index 0000000000..b2c420c7f2 --- /dev/null +++ b/data/shaders/ocean/generate_maps.hlsl @@ -0,0 +1,43 @@ +#include "../common.hlsl" + +RWTexture2D displacement_spectrum : register(u10); +RWTexture2D slope_spectrum : register(u11); +RWTexture2D displacement_map : register(u12); +RWTexture2D slope_map : register(u13); + +float4 Permute(float4 data, float3 id) +{ + return data * (1.0f - 2.0f * ((id.x + id.y) % 2)); +} + +[numthreads(8, 8, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + const float2 Lambda = float2(1.0f, 1.0f); + + float4 htildeDisplacement = Permute(displacement_spectrum[thread_id.xy], thread_id); + float4 htildeSlope = Permute(slope_spectrum[thread_id.xy], thread_id); + + float2 dxdz = htildeDisplacement.rg; + float2 dydxz = htildeDisplacement.ba; + float2 dyxdyz = htildeSlope.rg; + float2 dxxdzz = htildeSlope.ba; + + float3 displacement = float3(Lambda.x * dxdz.x, dydxz.x, Lambda.y * dxdz.y); + float2 slopes = dyxdyz.xy / (1 + abs(dxxdzz * Lambda)); + + //float jacobian = (1.0f + Lambda.x * dxxdzz.x) * (1.0f + Lambda.y * dxxdzz.y) - Lambda.x * Lambda.y * dydxz.y * dydxz.y; + //float covariance = slopes.x * slopes.y; + // + //float foam = htildeDisplacement.a; + //foam *= exp(-srt - > computeParams - > foamDecayRate); + //foam = saturate(foam); + // + //float biasedJacobian = max(0.0f, -(jacobian - srt - > computeParams - > foamBias)); + // + //if (biasedJacobian > srt - > computeParams - > foamThreshold) + // foam += srt - > computeParams - > foamAdd * biasedJacobian; + + displacement_map[thread_id.xy] = float4(displacement, 0.0f /*foam*/); + slope_map[thread_id.xy] = float4(slopes, 0.0f, 1.0f); +} diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index 314a3d1040..e16e053601 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -167,6 +167,7 @@ namespace spartan static void Pass_AdvanceSpectrum(RHI_CommandList* cmd_list); static void Pass_ApplyHorizontalFFT(RHI_CommandList* cmd_list); static void Pass_ApplyVerticalFFT(RHI_CommandList* cmd_list); + static void Pass_GenerateMaps(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 9d3e38592c..5a5235ffe1 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -140,6 +140,8 @@ namespace spartan ocean_initial_spectrum = 9, ocean_displacement_spectrum = 10, ocean_slope_spectrum = 11, + ocean_displacement_map = 12, + ocean_slope_map = 13 }; enum class Renderer_Shader : uint8_t @@ -198,6 +200,7 @@ namespace spartan ocean_advance_spectrum_c, ocean_horizontal_fft_c, ocean_vertical_fft_c, + ocean_generate_maps_c, max }; @@ -233,6 +236,8 @@ namespace spartan ocean_initial_spectrum, ocean_displacement_spectrum, ocean_slope_spectrum, + ocean_displacement_map, + ocean_slope_map, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 210400ec13..6e8d8cccd9 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -141,6 +141,7 @@ namespace spartan Pass_AdvanceSpectrum(cmd_list_graphics_present); Pass_ApplyHorizontalFFT(cmd_list_graphics_present); Pass_ApplyVerticalFFT(cmd_list_graphics_present); + Pass_GenerateMaps(cmd_list_graphics_present); } Pass_GBuffer(cmd_list_graphics_present, is_transparent); @@ -1204,6 +1205,33 @@ namespace spartan cmd_list->EndTimeblock(); } + void Renderer::Pass_GenerateMaps(RHI_CommandList* cmd_list) + { + RHI_Texture* displacement_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_spectrum); + RHI_Texture* slope_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_slope_spectrum); + + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + + cmd_list->BeginTimeblock("ocean_map_generation"); + { + RHI_PipelineState pso; + pso.name = "ocean_map_generation"; + pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_generate_maps_c); + cmd_list->SetPipelineState(pso); + + cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_spectrum, displacement_spectrum); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_map, displacement_map); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_map, slope_map); + cmd_list->Dispatch(displacement_map); + + // for the lifetime of the engine, this will be read as an srv, so transition here + //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } + void Renderer::Pass_PostProcess(RHI_CommandList* cmd_list) { // acquire render targets diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index f390fbc3cd..6cbe0be76a 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -232,6 +232,10 @@ namespace spartan render_target(Renderer_RenderTarget::ocean_displacement_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_spectrum"); render_target(Renderer_RenderTarget::ocean_slope_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_spectrum"); + + render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_map"); + + render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_map"); } // occlusion @@ -396,6 +400,9 @@ namespace spartan shader(Renderer_Shader::ocean_vertical_fft_c) = make_shared(); shader(Renderer_Shader::ocean_vertical_fft_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\vertical_fft.hlsl", false); + + shader(Renderer_Shader::ocean_generate_maps_c) = make_shared(); + shader(Renderer_Shader::ocean_generate_maps_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\generate_maps.hlsl", false); } // blur From 4bc44c36eea39a6e6f89a758a9235fb22a298fd9 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 11 Sep 2025 16:34:51 +0200 Subject: [PATCH 051/133] [ocean] wip displacement and shading --- data/shaders/g_buffer.hlsl | 3 + data/shaders/light.hlsl | 8 ++ source/runtime/Game/Game.cpp | 91 +++++++++++++------- source/runtime/Rendering/Renderer_Passes.cpp | 8 +- 4 files changed, 77 insertions(+), 33 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 098d49795a..6589060326 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -29,6 +29,8 @@ struct gbuffer float2 velocity : SV_Target3; }; +Texture2D displacement_map : register(t17); + // rotate UV around center (0.5, 0.5) by angle float2 rotate_uv(float2 uv, float angle) { @@ -99,6 +101,7 @@ static float4 sample_texture(gbuffer_vertex vertex, uint texture_index, Surface gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { + input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb; gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform); // transform world space position to clip space diff --git a/data/shaders/light.hlsl b/data/shaders/light.hlsl index 2b7f5d741e..b6daffed0c 100644 --- a/data/shaders/light.hlsl +++ b/data/shaders/light.hlsl @@ -26,6 +26,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fog.hlsl" //============================ +RWTexture2D slope_map : register(u13); + float3 subsurface_scattering(Surface surface, Light light, AngularInfo angular_info) { const float distortion = 0.3f; @@ -72,6 +74,12 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) if (early_exit_1 || early_exit_2) return; + if (surface.is_ocean()) + { + float4 slope = slope_map.Load(int3(thread_id.xy, 0)); + surface.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); + } + float3 out_diffuse = 0.0f; float3 out_specular = 0.0f; float out_shadow = 1.0f; diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index dab27115dd..e3192d2785 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -341,43 +341,70 @@ namespace spartan vector indices; geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, dimension); - // split into tiles - const uint32_t tile_count = std::max(1u, density / 6); // dynamic tile count based on density, minimum 1 - vector> tiled_vertices; - vector> tiled_indices; - vector tile_offsets; - spartan::geometry_processing::split_surface_into_tiles(vertices, indices, tile_count, tiled_vertices, tiled_indices, tile_offsets); + string name = "ocean mesh"; - for (uint32_t tile_index = 0; tile_index < static_cast(tiled_vertices.size()); tile_index++) - { - string name = "tile_" + to_string(tile_index); + // create mesh if it doesn't exist + shared_ptr mesh = meshes.emplace_back(make_shared()); + mesh->SetObjectName(name); + mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); + mesh->AddGeometry(vertices, indices, false); + mesh->CreateGpuBuffers(); - // create mesh if it doesn't exist - shared_ptr mesh = meshes.emplace_back(make_shared()); - mesh->SetObjectName(name); - mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); - mesh->AddGeometry(tiled_vertices[tile_index], tiled_indices[tile_index], false); - mesh->CreateGpuBuffers(); + // create a child entity, add a renderable, and this mesh tile to it + { + Entity* entity_tile = World::CreateEntity(); + entity_tile->SetObjectName(name); + entity_tile->SetParent(water); + //entity_tile->SetPosition(tile_offsets[tile_index]); - // create a child entity, add a renderable, and this mesh tile to it + if (Renderable* renderable = entity_tile->AddComponent()) { - Entity* entity_tile = World::CreateEntity(); - entity_tile->SetObjectName(name); - entity_tile->SetParent(water); - entity_tile->SetPosition(tile_offsets[tile_index]); - - if (Renderable* renderable = entity_tile->AddComponent()) - { - renderable->SetMesh(mesh.get()); - renderable->SetMaterial(material); - renderable->SetFlag(RenderableFlags::CastsShadows, false); - } - - // enable buoyancy - Physics* physics = entity_tile->AddComponent(); - physics->SetBodyType(BodyType::Water); + renderable->SetMesh(mesh.get()); + renderable->SetMaterial(material); + renderable->SetFlag(RenderableFlags::CastsShadows, false); } + + // enable buoyancy + Physics* physics = entity_tile->AddComponent(); + physics->SetBodyType(BodyType::Water); } + //// split into tiles + //const uint32_t tile_count = std::max(1u, density / 6); // dynamic tile count based on density, minimum 1 + //vector> tiled_vertices; + //vector> tiled_indices; + //vector tile_offsets; + //spartan::geometry_processing::split_surface_into_tiles(vertices, indices, tile_count, tiled_vertices, tiled_indices, tile_offsets); + + //for (uint32_t tile_index = 0; tile_index < static_cast(tiled_vertices.size()); tile_index++) + //{ + // string name = "tile_" + to_string(tile_index); + + // // create mesh if it doesn't exist + // shared_ptr mesh = meshes.emplace_back(make_shared()); + // mesh->SetObjectName(name); + // mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); + // mesh->AddGeometry(tiled_vertices[tile_index], tiled_indices[tile_index], false); + // mesh->CreateGpuBuffers(); + + // // create a child entity, add a renderable, and this mesh tile to it + // { + // Entity* entity_tile = World::CreateEntity(); + // entity_tile->SetObjectName(name); + // entity_tile->SetParent(water); + // entity_tile->SetPosition(tile_offsets[tile_index]); + + // if (Renderable* renderable = entity_tile->AddComponent()) + // { + // renderable->SetMesh(mesh.get()); + // renderable->SetMaterial(material); + // renderable->SetFlag(RenderableFlags::CastsShadows, false); + // } + + // // enable buoyancy + // Physics* physics = entity_tile->AddComponent(); + // physics->SetBodyType(BodyType::Water); + // } + //} } return water; @@ -1685,7 +1712,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 2); + auto water = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 1024.0f); water->SetParent(entity); diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 6e8d8cccd9..b4804987e3 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -684,7 +684,13 @@ namespace spartan cmd_list->SetCullMode(GetOption(Renderer_Option::Wireframe) ? RHI_CullMode::None : static_cast(material->GetProperty(MaterialProperty::CullMode))); cmd_list->SetBufferVertex(renderable->GetVertexBuffer(), renderable->GetInstanceBuffer()); cmd_list->SetBufferIndex(renderable->GetIndexBuffer()); - + + if (material->IsOcean()) + { + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + cmd_list->SetTexture(17, displacement_map); + } + cmd_list->DrawIndexed( renderable->GetIndexCount(draw_call.lod_index), renderable->GetIndexOffset(draw_call.lod_index), From 411fe0f5299f69eccce0bbb51c36f0dc6a845dc8 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Thu, 11 Sep 2025 19:40:46 +0200 Subject: [PATCH 052/133] [ocean] move spectrums through time and applied normals --- data/shaders/g_buffer.hlsl | 4 ++++ data/shaders/ocean/advance_spectrum.hlsl | 2 +- data/shaders/ocean/initial_spectrum.hlsl | 3 +-- source/runtime/Game/Game.cpp | 2 +- source/runtime/Rendering/Renderer_Passes.cpp | 9 +++++++++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 6589060326..7579bdca48 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -30,6 +30,7 @@ struct gbuffer }; Texture2D displacement_map : register(t17); +Texture2D slope_map : register(t18); // rotate UV around center (0.5, 0.5) by angle float2 rotate_uv(float2 uv, float angle) @@ -102,6 +103,9 @@ static float4 sample_texture(gbuffer_vertex vertex, uint texture_index, Surface gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb; + float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0); + input.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); + gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform); // transform world space position to clip space diff --git a/data/shaders/ocean/advance_spectrum.hlsl b/data/shaders/ocean/advance_spectrum.hlsl index ebb1f16804..e3067c37a7 100644 --- a/data/shaders/ocean/advance_spectrum.hlsl +++ b/data/shaders/ocean/advance_spectrum.hlsl @@ -43,7 +43,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float repeatTime = params.repeatTime; //frac(srt->time); float w_0 = 2.0f * PI / repeatTime; - float dispersion = floor(sqrt(G * kMag) / w_0) * w_0 * 0.16f; //srt - > time; + float dispersion = floor(sqrt(G * kMag) / w_0) * w_0 * buffer_frame.time; //srt - > time; float2 exponent = EulerFormula(dispersion); diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index 40283f0fbd..85f695ee1d 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -126,8 +126,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) surface.Build(thread_id.xy, resolution_out, true, false); uint seed = thread_id.x + SPECTRUM_TEX_SIZE * thread_id.y + SPECTRUM_TEX_SIZE; - //seed += srt - > frame; - seed += 0; + seed += pass_get_f2_value().x; // seed += frame_number JonswapParameters params = surface.jonswap_parameters; diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index e3192d2785..dbadc48cae 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1712,7 +1712,7 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 1024.0f); + auto water = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 256.0f); water->SetParent(entity); diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index b4804987e3..d5829f3cbb 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -689,6 +689,9 @@ namespace spartan { RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); cmd_list->SetTexture(17, displacement_map); + + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + cmd_list->SetTexture(18, slope_map); } cmd_list->DrawIndexed( @@ -1114,6 +1117,9 @@ namespace spartan pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_initial_spectrum_c); cmd_list->SetPipelineState(pso); + m_pcb_pass_cpu.set_f2_value(GetFrameNumber(), 0.0f); + cmd_list->PushConstants(m_pcb_pass_cpu); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); cmd_list->Dispatch(initial_spectrum); @@ -1156,6 +1162,9 @@ namespace spartan pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_advance_spectrum_c); cmd_list->SetPipelineState(pso); + //m_pcb_pass_cpu.set_f2_value(, 0.0f); + //cmd_list->PushConstants(m_pcb_pass_cpu); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_spectrum, displacement_spectrum); cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); From 53f9b04bb597c9d72e90a83fee4a158ec77b007f Mon Sep 17 00:00:00 2001 From: George Bolba Date: Thu, 11 Sep 2025 20:27:57 +0200 Subject: [PATCH 053/133] [ocean] removed useless code since normals are computed in the gbuffer pass --- data/shaders/light.hlsl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/data/shaders/light.hlsl b/data/shaders/light.hlsl index b6daffed0c..3d0972eb68 100644 --- a/data/shaders/light.hlsl +++ b/data/shaders/light.hlsl @@ -26,8 +26,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fog.hlsl" //============================ -RWTexture2D slope_map : register(u13); - float3 subsurface_scattering(Surface surface, Light light, AngularInfo angular_info) { const float distortion = 0.3f; @@ -73,12 +71,6 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) bool early_exit_2 = pass_is_transparent() && surface.is_opaque(); if (early_exit_1 || early_exit_2) return; - - if (surface.is_ocean()) - { - float4 slope = slope_map.Load(int3(thread_id.xy, 0)); - surface.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); - } float3 out_diffuse = 0.0f; float3 out_specular = 0.0f; From 14cda224710e137299682b2b45b44ae56e6a2459 Mon Sep 17 00:00:00 2001 From: George Bolba <93228212+Gikster007@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:26:59 +0200 Subject: [PATCH 054/133] [project] task template --- .github/ISSUE_TEMPLATE/new-task.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/new-task.md diff --git a/.github/ISSUE_TEMPLATE/new-task.md b/.github/ISSUE_TEMPLATE/new-task.md new file mode 100644 index 0000000000..c61bf28178 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-task.md @@ -0,0 +1,14 @@ +--- +name: New Task +about: Create a New Task for the project +title: '' +labels: Task +assignees: Gikster007 + +--- + +Task Description: + + +Acceptance Criteria: +- From f0385c51e55ef2390f87ad6bf825c32535b6eef5 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sat, 13 Sep 2025 16:09:33 +0200 Subject: [PATCH 055/133] [ocean] fixed sync issue --- source/runtime/Rendering/Renderer_Passes.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index d5829f3cbb..09ac86ed94 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -1122,9 +1122,6 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); cmd_list->Dispatch(initial_spectrum); - - // for the lifetime of the engine, this will be read as an srv, so transition here - //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } cmd_list->EndTimeblock(); } @@ -1162,16 +1159,10 @@ namespace spartan pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_advance_spectrum_c); cmd_list->SetPipelineState(pso); - //m_pcb_pass_cpu.set_f2_value(, 0.0f); - //cmd_list->PushConstants(m_pcb_pass_cpu); - cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_spectrum, displacement_spectrum); cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); cmd_list->Dispatch(initial_spectrum); - - // for the lifetime of the engine, this will be read as an srv, so transition here - //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } cmd_list->EndTimeblock(); } @@ -1192,8 +1183,8 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); cmd_list->Dispatch(1, displacement_spectrum->GetHeight(), 1); - // for the lifetime of the engine, this will be read as an srv, so transition here - //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + displacement_spectrum->SetLayout(RHI_Image_Layout::Attachment, cmd_list); + slope_spectrum->SetLayout(RHI_Image_Layout::Attachment, cmd_list); } cmd_list->EndTimeblock(); } @@ -1213,9 +1204,6 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_spectrum, displacement_spectrum); cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); cmd_list->Dispatch(1, displacement_spectrum->GetHeight(), 1); - - // for the lifetime of the engine, this will be read as an srv, so transition here - //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } cmd_list->EndTimeblock(); } From 4b19008c7d3d1dcc480757ee3aa3bf9b21e360c9 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 14 Sep 2025 20:23:47 +0200 Subject: [PATCH 056/133] [ocean] removed commented code --- source/runtime/Game/Game.cpp | 37 ---------------------- source/runtime/Rendering/Material.cpp | 45 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index dbadc48cae..fd2a54972a 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -368,43 +368,6 @@ namespace spartan Physics* physics = entity_tile->AddComponent(); physics->SetBodyType(BodyType::Water); } - //// split into tiles - //const uint32_t tile_count = std::max(1u, density / 6); // dynamic tile count based on density, minimum 1 - //vector> tiled_vertices; - //vector> tiled_indices; - //vector tile_offsets; - //spartan::geometry_processing::split_surface_into_tiles(vertices, indices, tile_count, tiled_vertices, tiled_indices, tile_offsets); - - //for (uint32_t tile_index = 0; tile_index < static_cast(tiled_vertices.size()); tile_index++) - //{ - // string name = "tile_" + to_string(tile_index); - - // // create mesh if it doesn't exist - // shared_ptr mesh = meshes.emplace_back(make_shared()); - // mesh->SetObjectName(name); - // mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); - // mesh->AddGeometry(tiled_vertices[tile_index], tiled_indices[tile_index], false); - // mesh->CreateGpuBuffers(); - - // // create a child entity, add a renderable, and this mesh tile to it - // { - // Entity* entity_tile = World::CreateEntity(); - // entity_tile->SetObjectName(name); - // entity_tile->SetParent(water); - // entity_tile->SetPosition(tile_offsets[tile_index]); - - // if (Renderable* renderable = entity_tile->AddComponent()) - // { - // renderable->SetMesh(mesh.get()); - // renderable->SetMaterial(material); - // renderable->SetFlag(RenderableFlags::CastsShadows, false); - // } - - // // enable buoyancy - // Physics* physics = entity_tile->AddComponent(); - // physics->SetBodyType(BodyType::Water); - // } - //} } return water; diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index 1e464c2887..57242cedb2 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -87,6 +87,7 @@ namespace spartan case MaterialProperty::WindAnimation: return "wind_animation"; case MaterialProperty::ColorVariationFromInstance: return "color_variation_from_instance"; case MaterialProperty::IsWater: return "vertex_animate_water"; + case MaterialProperty::IsOcean: return "is_ocean"; // Render settings case MaterialProperty::CullMode: return "cull_mode"; @@ -102,6 +103,34 @@ namespace spartan } } + const char* ocean_property_to_char_ptr(JonswapParameters property) + { + switch (property) + { + case spartan::JonswapParameters::Scale: return "scale"; + case spartan::JonswapParameters::SpreadBlend: return "spread_blend"; + case spartan::JonswapParameters::Swell: return "swell"; + case spartan::JonswapParameters::Gamma: return "gamma"; + case spartan::JonswapParameters::ShortWavesFade: return "short_waves_fade"; + case spartan::JonswapParameters::WindDirection: return "wind_direction"; + case spartan::JonswapParameters::Fetch: return "fetch"; + case spartan::JonswapParameters::WindSpeed: return "wind_speed"; + case spartan::JonswapParameters::RepeatTime: return "repeat_time"; + case spartan::JonswapParameters::Angle: return "angle"; + case spartan::JonswapParameters::Alpha: return "alpha"; + case spartan::JonswapParameters::PeakOmega: return "peak_omega"; + case spartan::JonswapParameters::Depth: return "depth"; + case spartan::JonswapParameters::LowCutoff: return "low_cutoff"; + case spartan::JonswapParameters::HighCutoff: return "high_cutoff"; + case spartan::JonswapParameters::Max: return "max"; + default: + { + SP_ASSERT_MSG(false, "Unknown ocean property"); + return nullptr; + } + } + } + float jonswap_alpha(float fetch, float windSpeed) { return 0.076f * pow(9.81f * fetch / windSpeed / windSpeed, -0.22f); } float jonswap_peak_frequency(float fetch, float windSpeed) { float g = 9.81f; @@ -306,6 +335,12 @@ namespace spartan const char* attribute_name = material_property_to_char_ptr(static_cast(i)); m_properties[i] = node_material.child(attribute_name).text().as_float(); } + + for (uint32_t i = 0; i < static_cast(JonswapParameters::Max); ++i) + { + const char* attribute_name = ocean_property_to_char_ptr(static_cast(i)); + m_ocean_properties[i] = node_material.child(attribute_name).text().as_float(); + } // load textures pugi::xml_node textures_node = node_material.child("textures"); @@ -353,6 +388,16 @@ namespace spartan const char* attribute_name = material_property_to_char_ptr(static_cast(i)); material_node.append_child(attribute_name).text().set(m_properties[i]); } + + // save ocean properties + if (GetProperty(MaterialProperty::IsOcean) == 1.0f) + { + for (uint32_t i = 0; i < static_cast(JonswapParameters::Max); ++i) + { + const char* attribute_name = ocean_property_to_char_ptr(static_cast(i)); + material_node.append_child(attribute_name).text().set(m_ocean_properties[i]); + } + } // save textures pugi::xml_node textures_node = material_node.append_child("textures"); From ccc2c29115185aa5b210ac85bb83d2b7729425e0 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 15 Sep 2025 09:46:52 +0200 Subject: [PATCH 057/133] [ocean] saving and loading material parameters --- source/runtime/Game/Game.cpp | 23 ++++++++++++++++++++--- source/runtime/Rendering/Material.cpp | 3 ++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index fd2a54972a..d76148ef51 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -58,7 +58,7 @@ namespace spartan Entity* default_environment = nullptr; Entity* default_light_directional = nullptr; Entity* default_metal_cube = nullptr; - Entity* default_water = nullptr; + Entity* default_ocean = nullptr; vector> meshes; namespace entities @@ -331,6 +331,8 @@ namespace spartan material->SetOceanProperty(JonswapParameters::Depth, 20.0f); material->SetOceanProperty(JonswapParameters::LowCutoff, 0.001f); material->SetOceanProperty(JonswapParameters::HighCutoff, 1000.0f); + + material->LoadFromFile(material->GetResourceFilePath()); } // geometry @@ -1675,17 +1677,32 @@ namespace spartan auto entity = World::CreateEntity(); - auto water = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 256.0f); + default_ocean = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 256.0f); - water->SetParent(entity); + default_ocean->SetParent(entity); default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } + + void on_shutdown() + { + if (!default_ocean) + return; + + Material* material = default_ocean->GetDescendantByName("ocean mesh")->GetComponent()->GetMaterial(); + if (!material) + SP_ASSERT_MSG(false, "Failed to get ocean material"); + + material->SaveToFile(material->GetResourceFilePath()); + + default_ocean = nullptr; + } } } void Game::Shutdown() { + worlds::ocean::on_shutdown(); default_floor = nullptr; default_camera = nullptr; default_environment = nullptr; diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index 57242cedb2..879e037b29 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -772,7 +772,8 @@ namespace spartan float Material::GetOceanProperty(const JonswapParameters property_type) const { - SP_ASSERT_MSG(m_properties[static_cast(MaterialProperty::IsOcean)] == 1.0f, "Only ocean materials can have ocean properties"); + if (m_properties[static_cast(MaterialProperty::IsOcean)] != 1.0f) + return 0.0f; return m_ocean_properties[static_cast(property_type)]; } From a7173621025c8645be724f5d54107f5ad3c9cdb9 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 15 Sep 2025 10:32:10 +0200 Subject: [PATCH 058/133] [ocean] fixed ocean material loading logic --- source/runtime/Game/Game.cpp | 48 +++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index f08c1f41fe..54adc6116c 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -309,30 +309,32 @@ namespace spartan material->SetObjectName("material_ocean"); material->SetResourceFilePath("ocean" + string(EXTENSION_MATERIAL)); - float windDir = 100.0f; - - material->SetColor(Color(0.0f, 150.0f / 255.0f, 130.0f / 255.0f, 150.0f / 255.0f)); - material->SetProperty(MaterialProperty::IsOcean, 1.0f); - - material->SetOceanProperty(JonswapParameters::Angle, 0.0f); //handled internally - material->SetOceanProperty(JonswapParameters::Alpha, 0.0f); // handled internally - material->SetOceanProperty(JonswapParameters::PeakOmega, 0.0f); // handled internally - - material->SetOceanProperty(JonswapParameters::Scale, 2.5f); - material->SetOceanProperty(JonswapParameters::SpreadBlend, 0.9f); - material->SetOceanProperty(JonswapParameters::Swell, 0.6f); - material->SetOceanProperty(JonswapParameters::Fetch, 10000.0f); - material->SetOceanProperty(JonswapParameters::WindDirection, windDir); - material->SetOceanProperty(JonswapParameters::WindSpeed, 100.0f); - material->SetOceanProperty(JonswapParameters::Gamma, 3.3f); - material->SetOceanProperty(JonswapParameters::ShortWavesFade, 0.0f); - material->SetOceanProperty(JonswapParameters::RepeatTime, 200.0f); - - material->SetOceanProperty(JonswapParameters::Depth, 20.0f); - material->SetOceanProperty(JonswapParameters::LowCutoff, 0.001f); - material->SetOceanProperty(JonswapParameters::HighCutoff, 1000.0f); - material->LoadFromFile(material->GetResourceFilePath()); + + // if material fails to load from file + if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) + { + material->SetColor(Color(0.0f, 150.0f / 255.0f, 130.0f / 255.0f, 150.0f / 255.0f)); + material->SetProperty(MaterialProperty::IsOcean, 1.0f); + + material->SetOceanProperty(JonswapParameters::Angle, 0.0f); //handled internally + material->SetOceanProperty(JonswapParameters::Alpha, 0.0f); // handled internally + material->SetOceanProperty(JonswapParameters::PeakOmega, 0.0f); // handled internally + + material->SetOceanProperty(JonswapParameters::Scale, 0.01f); + material->SetOceanProperty(JonswapParameters::SpreadBlend, 0.9f); + material->SetOceanProperty(JonswapParameters::Swell, 1.0f); + material->SetOceanProperty(JonswapParameters::Fetch, 10000.0f); + material->SetOceanProperty(JonswapParameters::WindDirection, 90.0f); + material->SetOceanProperty(JonswapParameters::WindSpeed, 100.0f); + material->SetOceanProperty(JonswapParameters::Gamma, 3.3f); + material->SetOceanProperty(JonswapParameters::ShortWavesFade, 0.0f); + material->SetOceanProperty(JonswapParameters::RepeatTime, 200.0f); + + material->SetOceanProperty(JonswapParameters::Depth, 20.0f); + material->SetOceanProperty(JonswapParameters::LowCutoff, 0.001f); + material->SetOceanProperty(JonswapParameters::HighCutoff, 1000.0f); + } } // geometry From 85b1406cf4753dec2bfd9980f1298217e519ee71 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 15 Sep 2025 18:12:18 +0200 Subject: [PATCH 059/133] [ocean] wip adding foam --- data/shaders/common_resources.hlsl | 9 ++- data/shaders/common_structs.hlsl | 43 +++++++------ data/shaders/light_composition.hlsl | 18 ++++++ data/shaders/ocean/advance_spectrum.hlsl | 2 +- data/shaders/ocean/generate_maps.hlsl | 36 ++++++----- data/shaders/ocean/initial_spectrum.hlsl | 2 +- source/editor/Widgets/Properties.cpp | 38 ++++++------ source/runtime/Game/Game.cpp | 39 ++++++------ source/runtime/Rendering/Material.cpp | 64 +++++++++++--------- source/runtime/Rendering/Material.h | 15 +++-- source/runtime/Rendering/Renderer.cpp | 34 ++++++----- source/runtime/Rendering/Renderer_Buffers.h | 7 ++- source/runtime/Rendering/Renderer_Passes.cpp | 3 + 13 files changed, 190 insertions(+), 120 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index a79408f722..e66199da5f 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -109,7 +109,7 @@ struct MaterialParameters float clearcoat; float clearcoat_roughness; - struct JonswapParameters + struct OceanParameters { float scale; float spreadBlend; @@ -128,7 +128,12 @@ struct MaterialParameters float depth; float lowCutoff; float highCutoff; - } jonswap_parameters; + + float foamDecayRate; + float foamBias; + float foamThreshold; + float foamAdd; + } ocean_parameters; bool has_texture_occlusion() { return (flags & (1 << 7)) != 0; } bool has_texture_roughness() { return (flags & (1 << 3)) != 0; } diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index 427fc6df72..17aaf23b5c 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -26,7 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SPARTAN_COMMON_STRUCT #define SPARTAN_COMMON_STRUCT -struct JonswapParameters +struct OceanParameters { float scale; float spreadBlend; @@ -45,6 +45,11 @@ struct JonswapParameters float depth; float lowCutoff; float highCutoff; + + float foamDecayRate; + float foamBias; + float foamThreshold; + float foamAdd; }; struct Surface @@ -75,7 +80,7 @@ struct Surface float camera_to_pixel_length; float3 diffuse_energy; - JonswapParameters jonswap_parameters; + OceanParameters ocean_parameters; // easy access to certain properties bool has_texture_height() { return flags & uint(1U << 0); } @@ -129,21 +134,25 @@ struct Surface diffuse_energy = 1.0f; // jonswap parameters - jonswap_parameters.alpha = material.jonswap_parameters.alpha; - jonswap_parameters.angle = material.jonswap_parameters.angle; - jonswap_parameters.fetch = material.jonswap_parameters.fetch; - jonswap_parameters.gamma = material.jonswap_parameters.gamma; - jonswap_parameters.peakOmega = material.jonswap_parameters.peakOmega; - jonswap_parameters.repeatTime = material.jonswap_parameters.repeatTime; - jonswap_parameters.scale = material.jonswap_parameters.scale; - jonswap_parameters.shortWavesFade = material.jonswap_parameters.shortWavesFade; - jonswap_parameters.spreadBlend = material.jonswap_parameters.spreadBlend; - jonswap_parameters.swell = material.jonswap_parameters.swell; - jonswap_parameters.windDirection = material.jonswap_parameters.windDirection; - jonswap_parameters.windSpeed = material.jonswap_parameters.windSpeed; - jonswap_parameters.depth = material.jonswap_parameters.depth; - jonswap_parameters.lowCutoff = material.jonswap_parameters.lowCutoff; - jonswap_parameters.highCutoff = material.jonswap_parameters.highCutoff; + ocean_parameters.alpha = material.ocean_parameters.alpha; + ocean_parameters.angle = material.ocean_parameters.angle; + ocean_parameters.fetch = material.ocean_parameters.fetch; + ocean_parameters.gamma = material.ocean_parameters.gamma; + ocean_parameters.peakOmega = material.ocean_parameters.peakOmega; + ocean_parameters.repeatTime = material.ocean_parameters.repeatTime; + ocean_parameters.scale = material.ocean_parameters.scale; + ocean_parameters.shortWavesFade = material.ocean_parameters.shortWavesFade; + ocean_parameters.spreadBlend = material.ocean_parameters.spreadBlend; + ocean_parameters.swell = material.ocean_parameters.swell; + ocean_parameters.windDirection = material.ocean_parameters.windDirection; + ocean_parameters.windSpeed = material.ocean_parameters.windSpeed; + ocean_parameters.depth = material.ocean_parameters.depth; + ocean_parameters.lowCutoff = material.ocean_parameters.lowCutoff; + ocean_parameters.highCutoff = material.ocean_parameters.highCutoff; + ocean_parameters.foamDecayRate = material.ocean_parameters.foamDecayRate; + ocean_parameters.foamBias = material.ocean_parameters.foamBias; + ocean_parameters.foamThreshold = material.ocean_parameters.foamThreshold; + ocean_parameters.foamAdd = material.ocean_parameters.foamAdd; // roughness is authored as perceptual roughness, as is convention roughness_alpha = roughness * roughness; diff --git a/data/shaders/light_composition.hlsl b/data/shaders/light_composition.hlsl index 914e97c764..46a368a963 100644 --- a/data/shaders/light_composition.hlsl +++ b/data/shaders/light_composition.hlsl @@ -24,6 +24,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fog.hlsl" //==================== +RWTexture2D slope_map : register(u13); + [numthreads(THREAD_GROUP_COUNT_X, THREAD_GROUP_COUNT_Y, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { @@ -76,4 +78,20 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float accumulate = (pass_is_transparent() && !surface.is_transparent()) ? 1.0f : 0.0f; // transparent surfaces will sample the background via refraction, no need to blend tex_uav[thread_id.xy] = float4(light_diffuse * surface.albedo + light_specular + light_emissive + light_atmospheric, alpha) + tex_uav[thread_id.xy] * accumulate; + + if (surface.is_ocean()) + { + //float3 foamColor = float3(1.0f, 0.0f, 0.0f); + // + //float4 ndcPos = mul(float4(surface.position, 1.0f), buffer_frame.view_projection); + // + //float2 uv = ndc_to_uv(ndcPos.xyz); + //float foam = 1.0f;//slope_map[thread_id.xy].a; + //tex_uav[thread_id.xy].rgb = lerp(tex_uav[thread_id.xy].rgb, foamColor, foam); + tex_uav[thread_id.xy].rgb = float3(0.0f, 1.0f, 0.0f); + } + else + { + tex_uav[thread_id.xy].rgb = float3(1.0f, 0.0f, 0.0f); + } } diff --git a/data/shaders/ocean/advance_spectrum.hlsl b/data/shaders/ocean/advance_spectrum.hlsl index e3067c37a7..2875e22053 100644 --- a/data/shaders/ocean/advance_spectrum.hlsl +++ b/data/shaders/ocean/advance_spectrum.hlsl @@ -39,7 +39,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) initial_spectrum.GetDimensions(resolution_out.x, resolution_out.y); Surface surface; surface.Build(thread_id.xy, resolution_out, true, false); - JonswapParameters params = surface.jonswap_parameters; + OceanParameters params = surface.ocean_parameters; float repeatTime = params.repeatTime; //frac(srt->time); float w_0 = 2.0f * PI / repeatTime; diff --git a/data/shaders/ocean/generate_maps.hlsl b/data/shaders/ocean/generate_maps.hlsl index b2c420c7f2..9cba691891 100644 --- a/data/shaders/ocean/generate_maps.hlsl +++ b/data/shaders/ocean/generate_maps.hlsl @@ -26,18 +26,26 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float3 displacement = float3(Lambda.x * dxdz.x, dydxz.x, Lambda.y * dxdz.y); float2 slopes = dyxdyz.xy / (1 + abs(dxxdzz * Lambda)); - //float jacobian = (1.0f + Lambda.x * dxxdzz.x) * (1.0f + Lambda.y * dxxdzz.y) - Lambda.x * Lambda.y * dydxz.y * dydxz.y; - //float covariance = slopes.x * slopes.y; - // - //float foam = htildeDisplacement.a; - //foam *= exp(-srt - > computeParams - > foamDecayRate); - //foam = saturate(foam); - // - //float biasedJacobian = max(0.0f, -(jacobian - srt - > computeParams - > foamBias)); - // - //if (biasedJacobian > srt - > computeParams - > foamThreshold) - // foam += srt - > computeParams - > foamAdd * biasedJacobian; - - displacement_map[thread_id.xy] = float4(displacement, 0.0f /*foam*/); - slope_map[thread_id.xy] = float4(slopes, 0.0f, 1.0f); + float jacobian = (1.0f + Lambda.x * dxxdzz.x) * (1.0f + Lambda.y * dxxdzz.y) - Lambda.x * Lambda.y * dydxz.y * dydxz.y; + float covariance = slopes.x * slopes.y; + + // create surface + float2 resolution_out; + slope_map.GetDimensions(resolution_out.x, resolution_out.y); + Surface surface; + surface.Build(thread_id.xy, resolution_out, false, false); + + OceanParameters params = surface.ocean_parameters; + + float foam = htildeDisplacement.a; + foam *= exp(-params.foamDecayRate); + foam = saturate(foam); + + float biasedJacobian = max(0.0f, -(jacobian - params.foamBias)); + + if (biasedJacobian > params.foamThreshold) + foam += params.foamAdd * biasedJacobian; + + displacement_map[thread_id.xy] = float4(displacement, 1.0f); + slope_map[thread_id.xy] = float4(slopes, 0.0f, foam); } diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index 85f695ee1d..41250059dc 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -128,7 +128,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) uint seed = thread_id.x + SPECTRUM_TEX_SIZE * thread_id.y + SPECTRUM_TEX_SIZE; seed += pass_get_f2_value().x; // seed += frame_number - JonswapParameters params = surface.jonswap_parameters; + OceanParameters params = surface.ocean_parameters; float halfN = SPECTRUM_TEX_SIZE / 2.0f; diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 5983f77556..5ad4328323 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -826,9 +826,9 @@ void Properties::ShowMaterial(Material* material) const // ocean properties if (material->GetProperty(MaterialProperty::IsOcean)) { - const auto show_jonswap_params = [this, &material](const char* name, const char* tooltip, const JonswapParameters params) + const auto show_jonswap_params = [this, &material](const char* name, const char* tooltip, const OceanParameters params) { - bool show_modifier = params != JonswapParameters::Max; + bool show_modifier = params != OceanParameters::Max; // name if (name) @@ -858,21 +858,25 @@ void Properties::ShowMaterial(Material* material) const } }; - show_jonswap_params("Alpha", "", JonswapParameters::Alpha); - show_jonswap_params("Angle", "", JonswapParameters::Angle); - show_jonswap_params("Fetch", "", JonswapParameters::Fetch); - show_jonswap_params("Gamma", "", JonswapParameters::Gamma); - show_jonswap_params("Peak Omega", "", JonswapParameters::PeakOmega); - show_jonswap_params("Repeat Time", "", JonswapParameters::RepeatTime); - show_jonswap_params("Scale", "", JonswapParameters::Scale); - show_jonswap_params("Short Waves Fade", "", JonswapParameters::ShortWavesFade); - show_jonswap_params("Spread Blend", "", JonswapParameters::SpreadBlend); - show_jonswap_params("Swell", "", JonswapParameters::Swell); - show_jonswap_params("Wind Direction", "", JonswapParameters::WindDirection); - show_jonswap_params("Wind Speed", "", JonswapParameters::WindSpeed); - show_jonswap_params("Depth", "", JonswapParameters::Depth); - show_jonswap_params("Low Cutoff", "", JonswapParameters::LowCutoff); - show_jonswap_params("High Cutoff", "", JonswapParameters::HighCutoff); + show_jonswap_params("Alpha", "", OceanParameters::Alpha); + show_jonswap_params("Angle", "", OceanParameters::Angle); + show_jonswap_params("Fetch", "", OceanParameters::Fetch); + show_jonswap_params("Gamma", "", OceanParameters::Gamma); + show_jonswap_params("Peak Omega", "", OceanParameters::PeakOmega); + show_jonswap_params("Repeat Time", "", OceanParameters::RepeatTime); + show_jonswap_params("Scale", "", OceanParameters::Scale); + show_jonswap_params("Short Waves Fade", "", OceanParameters::ShortWavesFade); + show_jonswap_params("Spread Blend", "", OceanParameters::SpreadBlend); + show_jonswap_params("Swell", "", OceanParameters::Swell); + show_jonswap_params("Wind Direction", "", OceanParameters::WindDirection); + show_jonswap_params("Wind Speed", "", OceanParameters::WindSpeed); + show_jonswap_params("Depth", "", OceanParameters::Depth); + show_jonswap_params("Low Cutoff", "", OceanParameters::LowCutoff); + show_jonswap_params("High Cutoff", "", OceanParameters::HighCutoff); + show_jonswap_params("Foam Decay Rate", "", OceanParameters::FoamDecayRate); + show_jonswap_params("Foam Bias", "", OceanParameters::FoamBias); + show_jonswap_params("Foam Threshold", "", OceanParameters::FoamThreshold); + show_jonswap_params("Foam Add", "", OceanParameters::FoamAdd); } // uv diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 54adc6116c..4473206dfa 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -317,23 +317,28 @@ namespace spartan material->SetColor(Color(0.0f, 150.0f / 255.0f, 130.0f / 255.0f, 150.0f / 255.0f)); material->SetProperty(MaterialProperty::IsOcean, 1.0f); - material->SetOceanProperty(JonswapParameters::Angle, 0.0f); //handled internally - material->SetOceanProperty(JonswapParameters::Alpha, 0.0f); // handled internally - material->SetOceanProperty(JonswapParameters::PeakOmega, 0.0f); // handled internally - - material->SetOceanProperty(JonswapParameters::Scale, 0.01f); - material->SetOceanProperty(JonswapParameters::SpreadBlend, 0.9f); - material->SetOceanProperty(JonswapParameters::Swell, 1.0f); - material->SetOceanProperty(JonswapParameters::Fetch, 10000.0f); - material->SetOceanProperty(JonswapParameters::WindDirection, 90.0f); - material->SetOceanProperty(JonswapParameters::WindSpeed, 100.0f); - material->SetOceanProperty(JonswapParameters::Gamma, 3.3f); - material->SetOceanProperty(JonswapParameters::ShortWavesFade, 0.0f); - material->SetOceanProperty(JonswapParameters::RepeatTime, 200.0f); - - material->SetOceanProperty(JonswapParameters::Depth, 20.0f); - material->SetOceanProperty(JonswapParameters::LowCutoff, 0.001f); - material->SetOceanProperty(JonswapParameters::HighCutoff, 1000.0f); + material->SetOceanProperty(OceanParameters::Angle, 0.0f); //handled internally + material->SetOceanProperty(OceanParameters::Alpha, 0.0f); // handled internally + material->SetOceanProperty(OceanParameters::PeakOmega, 0.0f); // handled internally + + material->SetOceanProperty(OceanParameters::Scale, 0.01f); + material->SetOceanProperty(OceanParameters::SpreadBlend, 0.9f); + material->SetOceanProperty(OceanParameters::Swell, 1.0f); + material->SetOceanProperty(OceanParameters::Fetch, 10000.0f); + material->SetOceanProperty(OceanParameters::WindDirection, 90.0f); + material->SetOceanProperty(OceanParameters::WindSpeed, 100.0f); + material->SetOceanProperty(OceanParameters::Gamma, 3.3f); + material->SetOceanProperty(OceanParameters::ShortWavesFade, 0.0f); + material->SetOceanProperty(OceanParameters::RepeatTime, 200.0f); + + material->SetOceanProperty(OceanParameters::Depth, 20.0f); + material->SetOceanProperty(OceanParameters::LowCutoff, 0.001f); + material->SetOceanProperty(OceanParameters::HighCutoff, 1000.0f); + + material->SetOceanProperty(OceanParameters::FoamDecayRate, 3.0f); + material->SetOceanProperty(OceanParameters::FoamThreshold, -1.8f); + material->SetOceanProperty(OceanParameters::FoamBias, 0.2f); + material->SetOceanProperty(OceanParameters::FoamAdd, 0.12f); } } diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index 09733e329b..2299ee1be0 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -103,26 +103,30 @@ namespace spartan } } - const char* ocean_property_to_char_ptr(JonswapParameters property) + const char* ocean_property_to_char_ptr(OceanParameters property) { switch (property) { - case spartan::JonswapParameters::Scale: return "scale"; - case spartan::JonswapParameters::SpreadBlend: return "spread_blend"; - case spartan::JonswapParameters::Swell: return "swell"; - case spartan::JonswapParameters::Gamma: return "gamma"; - case spartan::JonswapParameters::ShortWavesFade: return "short_waves_fade"; - case spartan::JonswapParameters::WindDirection: return "wind_direction"; - case spartan::JonswapParameters::Fetch: return "fetch"; - case spartan::JonswapParameters::WindSpeed: return "wind_speed"; - case spartan::JonswapParameters::RepeatTime: return "repeat_time"; - case spartan::JonswapParameters::Angle: return "angle"; - case spartan::JonswapParameters::Alpha: return "alpha"; - case spartan::JonswapParameters::PeakOmega: return "peak_omega"; - case spartan::JonswapParameters::Depth: return "depth"; - case spartan::JonswapParameters::LowCutoff: return "low_cutoff"; - case spartan::JonswapParameters::HighCutoff: return "high_cutoff"; - case spartan::JonswapParameters::Max: return "max"; + case spartan::OceanParameters::Scale: return "scale"; + case spartan::OceanParameters::SpreadBlend: return "spread_blend"; + case spartan::OceanParameters::Swell: return "swell"; + case spartan::OceanParameters::Gamma: return "gamma"; + case spartan::OceanParameters::ShortWavesFade: return "short_waves_fade"; + case spartan::OceanParameters::WindDirection: return "wind_direction"; + case spartan::OceanParameters::Fetch: return "fetch"; + case spartan::OceanParameters::WindSpeed: return "wind_speed"; + case spartan::OceanParameters::RepeatTime: return "repeat_time"; + case spartan::OceanParameters::Angle: return "angle"; + case spartan::OceanParameters::Alpha: return "alpha"; + case spartan::OceanParameters::PeakOmega: return "peak_omega"; + case spartan::OceanParameters::Depth: return "depth"; + case spartan::OceanParameters::LowCutoff: return "low_cutoff"; + case spartan::OceanParameters::HighCutoff: return "high_cutoff"; + case spartan::OceanParameters::FoamDecayRate: return "foam_decay_rate"; + case spartan::OceanParameters::FoamThreshold: return "foam_threshold"; + case spartan::OceanParameters::FoamAdd: return "foam_add"; + case spartan::OceanParameters::FoamBias: return "foam_bias"; + case spartan::OceanParameters::Max: return "max"; default: { SP_ASSERT_MSG(false, "Unknown ocean property"); @@ -341,9 +345,9 @@ namespace spartan m_properties[i] = node_material.child(attribute_name).text().as_float(); } - for (uint32_t i = 0; i < static_cast(JonswapParameters::Max); ++i) + for (uint32_t i = 0; i < static_cast(OceanParameters::Max); ++i) { - const char* attribute_name = ocean_property_to_char_ptr(static_cast(i)); + const char* attribute_name = ocean_property_to_char_ptr(static_cast(i)); m_ocean_properties[i] = node_material.child(attribute_name).text().as_float(); } @@ -397,9 +401,9 @@ namespace spartan // save ocean properties if (GetProperty(MaterialProperty::IsOcean) == 1.0f) { - for (uint32_t i = 0; i < static_cast(JonswapParameters::Max); ++i) + for (uint32_t i = 0; i < static_cast(OceanParameters::Max); ++i) { - const char* attribute_name = ocean_property_to_char_ptr(static_cast(i)); + const char* attribute_name = ocean_property_to_char_ptr(static_cast(i)); material_node.append_child(attribute_name).text().set(m_ocean_properties[i]); } } @@ -781,7 +785,7 @@ namespace spartan SaveToFile(GetResourceFilePath()); } - float Material::GetOceanProperty(const JonswapParameters property_type) const + float Material::GetOceanProperty(const OceanParameters property_type) const { if (m_properties[static_cast(MaterialProperty::IsOcean)] != 1.0f) return 0.0f; @@ -789,32 +793,32 @@ namespace spartan return m_ocean_properties[static_cast(property_type)]; } - void Material::SetOceanProperty(const JonswapParameters property_type, const float value) + void Material::SetOceanProperty(const OceanParameters property_type, const float value) { SP_ASSERT_MSG(m_properties[static_cast(MaterialProperty::IsOcean)] == 1.0f, "Only ocean materials can have ocean properties"); // special cases - if (property_type == JonswapParameters::Fetch || property_type == JonswapParameters::WindSpeed) + if (property_type == OceanParameters::Fetch || property_type == OceanParameters::WindSpeed) { // update fetch or windspeed m_ocean_properties[static_cast(property_type)] = value; // get fetch and windspeed - float fetch = m_ocean_properties[static_cast(JonswapParameters::Fetch)]; - float windSpeed = m_ocean_properties[static_cast(JonswapParameters::WindSpeed)]; + float fetch = m_ocean_properties[static_cast(OceanParameters::Fetch)]; + float windSpeed = m_ocean_properties[static_cast(OceanParameters::WindSpeed)]; // update alpha and peakOmega - m_ocean_properties[static_cast(JonswapParameters::Alpha)] = jonswap_alpha(fetch, windSpeed); - m_ocean_properties[static_cast(JonswapParameters::PeakOmega)] = jonswap_peak_frequency(fetch, windSpeed); + m_ocean_properties[static_cast(OceanParameters::Alpha)] = jonswap_alpha(fetch, windSpeed); + m_ocean_properties[static_cast(OceanParameters::PeakOmega)] = jonswap_peak_frequency(fetch, windSpeed); } - else if (property_type == JonswapParameters::WindDirection) + else if (property_type == OceanParameters::WindDirection) { // update wind direction m_ocean_properties[static_cast(property_type)] = value; // update angle, based on the wind direction float angle = value / 180.0f * pi; - m_ocean_properties[static_cast(JonswapParameters::Angle)] = angle; + m_ocean_properties[static_cast(OceanParameters::Angle)] = angle; } else { diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 1a6b3e9512..1b9a19be71 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -111,7 +111,7 @@ namespace spartan }; // used for ocean calculations - enum class JonswapParameters + enum class OceanParameters { Scale, // used to scale the Spectrum [1.0f, 5.0f] --> Value Range SpreadBlend, // used to blend between agitated water motion, and windDirection [0.0f, 1.0f] @@ -132,6 +132,11 @@ namespace spartan LowCutoff, HighCutoff, + FoamDecayRate, + FoamBias, + FoamThreshold, + FoamAdd, + Max }; @@ -165,8 +170,8 @@ namespace spartan // properties float GetProperty(const MaterialProperty property_type) const { return m_properties[static_cast(property_type)]; } void SetProperty(const MaterialProperty property_type, const float value); - float GetOceanProperty(const JonswapParameters property_type) const; - void SetOceanProperty(const JonswapParameters property_type, const float value); + float GetOceanProperty(const OceanParameters property_type) const; + void SetOceanProperty(const OceanParameters property_type, const float value); void SetColor(const Color& color); bool IsTransparent() const { return GetProperty(MaterialProperty::ColorA) < 1.0f; } bool IsAlphaTested(); @@ -181,12 +186,12 @@ namespace spartan void MarkSpectrumAsComputed() { m_should_compute_spectrum = false; } const std::array(MaterialProperty::Max)>& GetProperties() const { return m_properties; } - const std::array(JonswapParameters::Max)>& GetOceanProperties() const { return m_ocean_properties; } + const std::array(OceanParameters::Max)>& GetOceanProperties() const { return m_ocean_properties; } private: std::array(MaterialTextureType::Max) * slots_per_texture> m_textures; std::array(MaterialProperty::Max)> m_properties; - std::array(JonswapParameters::Max)> m_ocean_properties; + std::array(OceanParameters::Max)> m_ocean_properties; uint32_t m_index = 0; bool m_should_compute_spectrum = true; }; diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index f81dc65362..9916944341 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -861,21 +861,25 @@ namespace spartan properties[count].world_space_uv = material->GetProperty(MaterialProperty::WorldSpaceUv); // ocean - properties[count].jonswap_parameters.alpha = material->GetOceanProperty(JonswapParameters::Alpha); - properties[count].jonswap_parameters.angle = material->GetOceanProperty(JonswapParameters::Angle); - properties[count].jonswap_parameters.fetch = material->GetOceanProperty(JonswapParameters::Fetch); - properties[count].jonswap_parameters.gamma = material->GetOceanProperty(JonswapParameters::Gamma); - properties[count].jonswap_parameters.peakOmega = material->GetOceanProperty(JonswapParameters::PeakOmega); - properties[count].jonswap_parameters.repeatTime = material->GetOceanProperty(JonswapParameters::RepeatTime); - properties[count].jonswap_parameters.scale = material->GetOceanProperty(JonswapParameters::Scale); - properties[count].jonswap_parameters.shortWavesFade = material->GetOceanProperty(JonswapParameters::ShortWavesFade); - properties[count].jonswap_parameters.spreadBlend = material->GetOceanProperty(JonswapParameters::SpreadBlend); - properties[count].jonswap_parameters.swell = material->GetOceanProperty(JonswapParameters::Swell); - properties[count].jonswap_parameters.windDirection = material->GetOceanProperty(JonswapParameters::WindDirection); - properties[count].jonswap_parameters.windSpeed = material->GetOceanProperty(JonswapParameters::WindSpeed); - properties[count].jonswap_parameters.depth = material->GetOceanProperty(JonswapParameters::Depth); - properties[count].jonswap_parameters.lowCutoff = material->GetOceanProperty(JonswapParameters::LowCutoff); - properties[count].jonswap_parameters.highCutoff = material->GetOceanProperty(JonswapParameters::HighCutoff); + properties[count].jonswap_parameters.alpha = material->GetOceanProperty(OceanParameters::Alpha); + properties[count].jonswap_parameters.angle = material->GetOceanProperty(OceanParameters::Angle); + properties[count].jonswap_parameters.fetch = material->GetOceanProperty(OceanParameters::Fetch); + properties[count].jonswap_parameters.gamma = material->GetOceanProperty(OceanParameters::Gamma); + properties[count].jonswap_parameters.peakOmega = material->GetOceanProperty(OceanParameters::PeakOmega); + properties[count].jonswap_parameters.repeatTime = material->GetOceanProperty(OceanParameters::RepeatTime); + properties[count].jonswap_parameters.scale = material->GetOceanProperty(OceanParameters::Scale); + properties[count].jonswap_parameters.shortWavesFade = material->GetOceanProperty(OceanParameters::ShortWavesFade); + properties[count].jonswap_parameters.spreadBlend = material->GetOceanProperty(OceanParameters::SpreadBlend); + properties[count].jonswap_parameters.swell = material->GetOceanProperty(OceanParameters::Swell); + properties[count].jonswap_parameters.windDirection = material->GetOceanProperty(OceanParameters::WindDirection); + properties[count].jonswap_parameters.windSpeed = material->GetOceanProperty(OceanParameters::WindSpeed); + properties[count].jonswap_parameters.depth = material->GetOceanProperty(OceanParameters::Depth); + properties[count].jonswap_parameters.lowCutoff = material->GetOceanProperty(OceanParameters::LowCutoff); + properties[count].jonswap_parameters.highCutoff = material->GetOceanProperty(OceanParameters::HighCutoff); + properties[count].jonswap_parameters.foamDecayRate = material->GetOceanProperty(OceanParameters::FoamDecayRate); + properties[count].jonswap_parameters.foamBias = material->GetOceanProperty(OceanParameters::FoamBias); + properties[count].jonswap_parameters.foamThreshold = material->GetOceanProperty(OceanParameters::FoamThreshold); + properties[count].jonswap_parameters.foamAdd = material->GetOceanProperty(OceanParameters::FoamAdd); // flags properties[count].flags = material->HasTextureOfType(MaterialTextureType::Height) ? (1U << 0) : 0; diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index df95505d80..0da6dbc9f7 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -182,7 +182,7 @@ namespace spartan float clearcoat; float clearcoat_roughness; - struct JonswapParameters + struct OceanParameters { float scale; float spreadBlend; @@ -201,6 +201,11 @@ namespace spartan float depth; float lowCutoff; float highCutoff; + + float foamDecayRate; + float foamBias; + float foamThreshold; + float foamAdd; } jonswap_parameters; }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 1801a0faaf..0a901ac979 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -1002,6 +1002,7 @@ namespace spartan RHI_Texture* tex_light_diffuse = GetRenderTarget(Renderer_RenderTarget::light_diffuse); RHI_Texture* tex_light_specular = GetRenderTarget(Renderer_RenderTarget::light_specular); RHI_Texture* tex_light_volumetric = GetRenderTarget(Renderer_RenderTarget::light_volumetric); + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); cmd_list->InsertBarrierReadWrite(tex_out, RHI_BarrierType::EnsureReadThenWrite); @@ -1026,6 +1027,8 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsSrv::tex4, tex_light_specular); cmd_list->SetTexture(Renderer_BindingsSrv::tex5, tex_light_volumetric); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_map, slope_map); + // render cmd_list->Dispatch(tex_out); } From 2cba1d62f69446e1af80106bdb8d6d724f4fe094 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Tue, 16 Sep 2025 16:58:22 +0200 Subject: [PATCH 060/133] [ocean] wip added graphics pass to add foam --- data/shaders/light_composition.hlsl | 29 +++-- data/shaders/light_image_based.hlsl | 14 +++ data/shaders/ocean/foam.hlsl | 48 +++++++++ source/runtime/Rendering/Renderer.h | 1 + .../runtime/Rendering/Renderer_Definitions.h | 2 + source/runtime/Rendering/Renderer_Passes.cpp | 101 +++++++++++++++++- .../runtime/Rendering/Renderer_Resources.cpp | 7 ++ 7 files changed, 184 insertions(+), 18 deletions(-) create mode 100644 data/shaders/ocean/foam.hlsl diff --git a/data/shaders/light_composition.hlsl b/data/shaders/light_composition.hlsl index 46a368a963..03e43cd983 100644 --- a/data/shaders/light_composition.hlsl +++ b/data/shaders/light_composition.hlsl @@ -24,7 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fog.hlsl" //==================== -RWTexture2D slope_map : register(u13); +//RWTexture2D slope_map : register(u13); [numthreads(THREAD_GROUP_COUNT_X, THREAD_GROUP_COUNT_Y, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) @@ -79,19 +79,16 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float accumulate = (pass_is_transparent() && !surface.is_transparent()) ? 1.0f : 0.0f; // transparent surfaces will sample the background via refraction, no need to blend tex_uav[thread_id.xy] = float4(light_diffuse * surface.albedo + light_specular + light_emissive + light_atmospheric, alpha) + tex_uav[thread_id.xy] * accumulate; - if (surface.is_ocean()) - { - //float3 foamColor = float3(1.0f, 0.0f, 0.0f); - // - //float4 ndcPos = mul(float4(surface.position, 1.0f), buffer_frame.view_projection); - // - //float2 uv = ndc_to_uv(ndcPos.xyz); - //float foam = 1.0f;//slope_map[thread_id.xy].a; - //tex_uav[thread_id.xy].rgb = lerp(tex_uav[thread_id.xy].rgb, foamColor, foam); - tex_uav[thread_id.xy].rgb = float3(0.0f, 1.0f, 0.0f); - } - else - { - tex_uav[thread_id.xy].rgb = float3(1.0f, 0.0f, 0.0f); - } + //if (!surface.is_sky() && surface.is_ocean()) + //{ + // float3 foamColor = float3(1.0f, 1.0f, 1.0f); + // + // float4 ndcPos = mul(float4(surface.position, 1.0f), buffer_frame.view_projection); + // + // float2 uv = ndc_to_uv(ndcPos.xyz); + // float foam = 1.0f;//slope_map[uv].a; + // + // tex_uav[thread_id.xy].rgb = lerp(tex_uav[thread_id.xy].rgb, foamColor, foam); + // tex_uav[thread_id.xy].rgb = slope_map[thread_id.xy].rgb; + //} } diff --git a/data/shaders/light_image_based.hlsl b/data/shaders/light_image_based.hlsl index 15190538ee..e363d54b2d 100644 --- a/data/shaders/light_image_based.hlsl +++ b/data/shaders/light_image_based.hlsl @@ -23,6 +23,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "brdf.hlsl" //================== +Texture2D slope_map : register(t7); + float3 get_dominant_specular_direction(float3 normal, float3 reflection, float roughness) { const float smoothness = 1.0f - roughness; @@ -86,4 +88,16 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) ibl *= surface.alpha; tex_uav[thread_id.xy] += float4(ibl, 0.0f); + + // if (/*!surface.is_sky() &&*/ surface.is_ocean()) + // { + // float3 foamColor = float3(1.0f, 1.0f, 1.0f); + + // float2 uv = world_to_uv(surface.position, true); + //float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], uv, 0); + // float foam = slope.a * 300.0f; + + // tex_uav[thread_id.xy].rgb = lerp(tex_uav[thread_id.xy].rgb, foamColor, foam); + // //tex_uav[thread_id.xy].rgb = float3(uv, 0.0f); + // } } diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl new file mode 100644 index 0000000000..e05a6aa17f --- /dev/null +++ b/data/shaders/ocean/foam.hlsl @@ -0,0 +1,48 @@ +#include "../common.hlsl" + +struct VSOUT +{ + float4 pos : SV_Position; + float2 uv : TEXCOORD; +}; + +VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) +{ + //input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb; + //float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0); + //input.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); + + //gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform); + + //// transform world space position to clip space + //Surface surface; + //surface.flags = GetMaterial().flags; + //if (!surface.is_tessellated()) + //{ + // vertex = transform_to_clip_space(vertex); + //} + + //return vertex; + + VSOUT vs_out; + + float3 wpos = mul(input.position, buffer_pass.transform).xyz; + vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); + vs_out.uv = input.uv; + + return vs_out; +} + +Texture2D output : register(t0); +Texture2D slope_map : register(t1); + +//[earlydepthstencil] +float4 main_ps(VSOUT vertex) : SV_Target0 +{ + const float4 foam_color = float4(1.0f, 1.0f, 1.0f, 1.0f); + const float foam = slope_map.Sample(samplers[sampler_point_clamp], vertex.uv).a * 300.0f; + + const float4 output_sample = output.Sample(samplers[sampler_point_clamp], vertex.uv); + + return float4(lerp(output_sample.rgb, foam_color.rgb, foam), output_sample.a); +} diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index 862728353d..2e243ab17c 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -168,6 +168,7 @@ namespace spartan static void Pass_ApplyHorizontalFFT(RHI_CommandList* cmd_list); static void Pass_ApplyVerticalFFT(RHI_CommandList* cmd_list); static void Pass_GenerateMaps(RHI_CommandList* cmd_list); + static void Pass_ApplyFoam(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 5808a1da92..a69fec2971 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -203,6 +203,8 @@ namespace spartan ocean_horizontal_fft_c, ocean_vertical_fft_c, ocean_generate_maps_c, + ocean_foam_v, + ocean_foam_p, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 0a901ac979..c79862bf5f 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -153,6 +153,8 @@ namespace spartan Pass_Light_ImageBased(cmd_list_graphics_present); // ibl from skysphere and global illumination Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); // ssr + Pass_ApplyFoam(cmd_list_graphics_present); + // render -> output resolution Pass_Upscale(cmd_list_graphics_present); @@ -1002,7 +1004,7 @@ namespace spartan RHI_Texture* tex_light_diffuse = GetRenderTarget(Renderer_RenderTarget::light_diffuse); RHI_Texture* tex_light_specular = GetRenderTarget(Renderer_RenderTarget::light_specular); RHI_Texture* tex_light_volumetric = GetRenderTarget(Renderer_RenderTarget::light_volumetric); - RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + //RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); cmd_list->InsertBarrierReadWrite(tex_out, RHI_BarrierType::EnsureReadThenWrite); @@ -1027,7 +1029,7 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsSrv::tex4, tex_light_specular); cmd_list->SetTexture(Renderer_BindingsSrv::tex5, tex_light_volumetric); - cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_map, slope_map); + //cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_map, slope_map); // render cmd_list->Dispatch(tex_out); @@ -1040,6 +1042,7 @@ namespace spartan // acquire resources RHI_Shader* shader = GetShader(Renderer_Shader::light_image_based_c); RHI_Texture* tex_out = GetRenderTarget(Renderer_RenderTarget::frame_render); + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); cmd_list->BeginTimeblock("light_image_based"); { @@ -1056,6 +1059,7 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsSrv::tex, GetRenderTarget(Renderer_RenderTarget::light_shadow)); cmd_list->SetTexture(Renderer_BindingsSrv::tex2, GetRenderTarget(Renderer_RenderTarget::lut_brdf_specular)); cmd_list->SetTexture(Renderer_BindingsSrv::tex3, GetRenderTarget(Renderer_RenderTarget::skysphere)); + cmd_list->SetTexture(/*Renderer_BindingsUav::ocean_slope_map*/7, slope_map); // set pass constants m_pcb_pass_cpu.set_f3_value(static_cast(GetRenderTarget(Renderer_RenderTarget::skysphere)->GetMipCount())); @@ -1234,6 +1238,99 @@ namespace spartan // for the lifetime of the engine, this will be read as an srv, so transition here //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + + slope_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + } + cmd_list->EndTimeblock(); + } + + void Renderer::Pass_ApplyFoam(RHI_CommandList* cmd_list) + { + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + RHI_Texture* tex_depth = GetRenderTarget(Renderer_RenderTarget::gbuffer_depth); + RHI_Texture* tex_out = GetRenderTarget(Renderer_RenderTarget::frame_render); + + tex_out->SetLayout(RHI_Image_Layout::General, cmd_list); + + cmd_list->BeginTimeblock("ocean_foam"); + { + // set pipeline state + RHI_PipelineState pso; + pso.name = "ocean_foam"; + pso.shaders[RHI_Shader_Type::Vertex] = GetShader(Renderer_Shader::ocean_foam_v); + pso.shaders[RHI_Shader_Type::Pixel] = GetShader(Renderer_Shader::ocean_foam_p); + pso.blend_state = GetBlendState(Renderer_BlendState::Additive); + pso.rasterizer_state = GetRasterizerState(Renderer_RasterizerState::Solid); + pso.depth_stencil_state = GetDepthStencilState(Renderer_DepthStencilState::Off); + pso.vrs_input_texture = GetOption(Renderer_Option::VariableRateShading) ? GetRenderTarget(Renderer_RenderTarget::shading_rate) : nullptr; + pso.resolution_scale = true; + pso.render_target_color_textures[0] = tex_out; + pso.render_target_depth_texture = tex_depth; + pso.clear_color[0] = Color::standard_transparent; + cmd_list->SetPipelineState(pso); + + for (uint32_t i = 0; i < m_draw_call_count; i++) + { + const Renderer_DrawCall& draw_call = m_draw_calls[i]; + Renderable* renderable = draw_call.renderable; + Material* material = renderable->GetMaterial(); + if (!material || !material->IsOcean() || !draw_call.camera_visible) + continue; + + //// tessellation & culling + //{ + // bool is_tessellated = material->GetProperty(MaterialProperty::Tessellation) > 0.0f; + // RHI_Shader* hull = is_tessellated ? GetShader(Renderer_Shader::tessellation_h) : nullptr; + // RHI_Shader* domain = is_tessellated ? GetShader(Renderer_Shader::tessellation_d) : nullptr; + + // if (pso.shaders[RHI_Shader_Type::Hull] != hull || pso.shaders[RHI_Shader_Type::Domain] != domain) + // { + // pso.shaders[RHI_Shader_Type::Hull] = hull; + // pso.shaders[RHI_Shader_Type::Domain] = domain; + // cmd_list->SetPipelineState(pso); + // } + //} + + // pass constants + { + Entity* entity = renderable->GetEntity(); + m_pcb_pass_cpu.transform = entity->GetMatrix(); + m_pcb_pass_cpu.set_transform_previous(entity->GetMatrixPrevious()); + m_pcb_pass_cpu.set_is_transparent_and_material_index(true, material->GetIndex()); + cmd_list->PushConstants(m_pcb_pass_cpu); + + entity->SetMatrixPrevious(m_pcb_pass_cpu.transform); + } + + // draw + { + cmd_list->SetCullMode(static_cast(material->GetProperty(MaterialProperty::CullMode))); + cmd_list->SetBufferVertex(renderable->GetVertexBuffer(), renderable->GetInstanceBuffer()); + cmd_list->SetBufferIndex(renderable->GetIndexBuffer()); + + cmd_list->SetTexture(0, tex_out); + cmd_list->SetTexture(1, slope_map); + + cmd_list->DrawIndexed( + renderable->GetIndexCount(draw_call.lod_index), + renderable->GetIndexOffset(draw_call.lod_index), + renderable->GetVertexOffset(draw_call.lod_index), + renderable->HasInstancing() ? draw_call.instance_index : 0, + renderable->HasInstancing() ? draw_call.instance_count : 1 + ); + + // at this point, we don't want clear in case another render pass is implicitly started + pso.clear_depth = rhi_depth_load; + } + } + + // perform early resource transitions + //tex_color->SetLayout(RHI_Image_Layout::General, cmd_list); + //tex_normal->SetLayout(RHI_Image_Layout::General, cmd_list); + //tex_material->SetLayout(RHI_Image_Layout::General, cmd_list); + //tex_velocity->SetLayout(RHI_Image_Layout::General, cmd_list); + //tex_depth->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); // Pass_Sss() reads it as a srv + //cmd_list->InsertPendingBarrierGroup(); } cmd_list->EndTimeblock(); } diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index aa71c846ac..08fd58c24d 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -407,6 +407,13 @@ namespace spartan shader(Renderer_Shader::ocean_generate_maps_c) = make_shared(); shader(Renderer_Shader::ocean_generate_maps_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\generate_maps.hlsl", false); + + shader(Renderer_Shader::ocean_foam_v) = make_shared(); + shader(Renderer_Shader::ocean_foam_v)->Compile(RHI_Shader_Type::Vertex, shader_dir + "ocean\\foam.hlsl", async, RHI_Vertex_Type::PosUvNorTan); + + shader(Renderer_Shader::ocean_foam_p) = make_shared(); + shader(Renderer_Shader::ocean_foam_p)->Compile(RHI_Shader_Type::Pixel, shader_dir + "ocean\\foam.hlsl", async); + } // blur From aa2824abb844d4457831f67185ec599f8c0b4867 Mon Sep 17 00:00:00 2001 From: Panos Karabelas Date: Tue, 16 Sep 2025 17:33:43 +0100 Subject: [PATCH 061/133] [apply_foam] set clear color to load --- source/runtime/Rendering/Renderer_Passes.cpp | 50 +++++--------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index c79862bf5f..29b2fbeb9e 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -1248,7 +1248,7 @@ namespace spartan { RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); RHI_Texture* tex_depth = GetRenderTarget(Renderer_RenderTarget::gbuffer_depth); - RHI_Texture* tex_out = GetRenderTarget(Renderer_RenderTarget::frame_render); + RHI_Texture* tex_out = GetRenderTarget(Renderer_RenderTarget::frame_render); tex_out->SetLayout(RHI_Image_Layout::General, cmd_list); @@ -1256,17 +1256,17 @@ namespace spartan { // set pipeline state RHI_PipelineState pso; - pso.name = "ocean_foam"; + pso.name = "ocean_foam"; pso.shaders[RHI_Shader_Type::Vertex] = GetShader(Renderer_Shader::ocean_foam_v); - pso.shaders[RHI_Shader_Type::Pixel] = GetShader(Renderer_Shader::ocean_foam_p); - pso.blend_state = GetBlendState(Renderer_BlendState::Additive); - pso.rasterizer_state = GetRasterizerState(Renderer_RasterizerState::Solid); - pso.depth_stencil_state = GetDepthStencilState(Renderer_DepthStencilState::Off); - pso.vrs_input_texture = GetOption(Renderer_Option::VariableRateShading) ? GetRenderTarget(Renderer_RenderTarget::shading_rate) : nullptr; - pso.resolution_scale = true; - pso.render_target_color_textures[0] = tex_out; - pso.render_target_depth_texture = tex_depth; - pso.clear_color[0] = Color::standard_transparent; + pso.shaders[RHI_Shader_Type::Pixel] = GetShader(Renderer_Shader::ocean_foam_p); + pso.blend_state = GetBlendState(Renderer_BlendState::Additive); + pso.rasterizer_state = GetRasterizerState(Renderer_RasterizerState::Solid); + pso.depth_stencil_state = GetDepthStencilState(Renderer_DepthStencilState::Off); + pso.vrs_input_texture = GetOption(Renderer_Option::VariableRateShading) ? GetRenderTarget(Renderer_RenderTarget::shading_rate) : nullptr; + pso.resolution_scale = true; + pso.render_target_color_textures[0] = tex_out; + pso.render_target_depth_texture = tex_depth; + pso.clear_color[0] = rhi_color_load; cmd_list->SetPipelineState(pso); for (uint32_t i = 0; i < m_draw_call_count; i++) @@ -1277,29 +1277,11 @@ namespace spartan if (!material || !material->IsOcean() || !draw_call.camera_visible) continue; - //// tessellation & culling - //{ - // bool is_tessellated = material->GetProperty(MaterialProperty::Tessellation) > 0.0f; - // RHI_Shader* hull = is_tessellated ? GetShader(Renderer_Shader::tessellation_h) : nullptr; - // RHI_Shader* domain = is_tessellated ? GetShader(Renderer_Shader::tessellation_d) : nullptr; - - // if (pso.shaders[RHI_Shader_Type::Hull] != hull || pso.shaders[RHI_Shader_Type::Domain] != domain) - // { - // pso.shaders[RHI_Shader_Type::Hull] = hull; - // pso.shaders[RHI_Shader_Type::Domain] = domain; - // cmd_list->SetPipelineState(pso); - // } - //} - // pass constants { - Entity* entity = renderable->GetEntity(); - m_pcb_pass_cpu.transform = entity->GetMatrix(); - m_pcb_pass_cpu.set_transform_previous(entity->GetMatrixPrevious()); + m_pcb_pass_cpu.transform = renderable->GetEntity()->GetMatrix(); m_pcb_pass_cpu.set_is_transparent_and_material_index(true, material->GetIndex()); cmd_list->PushConstants(m_pcb_pass_cpu); - - entity->SetMatrixPrevious(m_pcb_pass_cpu.transform); } // draw @@ -1323,14 +1305,6 @@ namespace spartan pso.clear_depth = rhi_depth_load; } } - - // perform early resource transitions - //tex_color->SetLayout(RHI_Image_Layout::General, cmd_list); - //tex_normal->SetLayout(RHI_Image_Layout::General, cmd_list); - //tex_material->SetLayout(RHI_Image_Layout::General, cmd_list); - //tex_velocity->SetLayout(RHI_Image_Layout::General, cmd_list); - //tex_depth->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); // Pass_Sss() reads it as a srv - //cmd_list->InsertPendingBarrierGroup(); } cmd_list->EndTimeblock(); } From d2af0dd457811d91fb00e70955d93079bff4833e Mon Sep 17 00:00:00 2001 From: Panos Karabelas Date: Tue, 16 Sep 2025 17:53:06 +0100 Subject: [PATCH 062/133] [foam] fixed blending --- data/shaders/ocean/foam.hlsl | 30 +++----------------- source/runtime/Rendering/Renderer_Passes.cpp | 3 +- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index e05a6aa17f..ee33b77024 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -8,41 +8,19 @@ struct VSOUT VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { - //input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb; - //float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0); - //input.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); - - //gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform); - - //// transform world space position to clip space - //Surface surface; - //surface.flags = GetMaterial().flags; - //if (!surface.is_tessellated()) - //{ - // vertex = transform_to_clip_space(vertex); - //} - - //return vertex; - VSOUT vs_out; float3 wpos = mul(input.position, buffer_pass.transform).xyz; - vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); - vs_out.uv = input.uv; + vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); + vs_out.uv = input.uv; return vs_out; } -Texture2D output : register(t0); -Texture2D slope_map : register(t1); - -//[earlydepthstencil] float4 main_ps(VSOUT vertex) : SV_Target0 { const float4 foam_color = float4(1.0f, 1.0f, 1.0f, 1.0f); - const float foam = slope_map.Sample(samplers[sampler_point_clamp], vertex.uv).a * 300.0f; - - const float4 output_sample = output.Sample(samplers[sampler_point_clamp], vertex.uv); + const float foam = tex.Sample(samplers[sampler_point_clamp], vertex.uv).a * 300.0f; - return float4(lerp(output_sample.rgb, foam_color.rgb, foam), output_sample.a); + return foam_color; } diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 29b2fbeb9e..a60c53019e 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -1290,8 +1290,7 @@ namespace spartan cmd_list->SetBufferVertex(renderable->GetVertexBuffer(), renderable->GetInstanceBuffer()); cmd_list->SetBufferIndex(renderable->GetIndexBuffer()); - cmd_list->SetTexture(0, tex_out); - cmd_list->SetTexture(1, slope_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex, slope_map); cmd_list->DrawIndexed( renderable->GetIndexCount(draw_call.lod_index), From 6e20c8258d4a11a0fe1b19968fe38139b58d75f6 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Tue, 16 Sep 2025 19:52:58 +0200 Subject: [PATCH 063/133] [foam] proper foam --- data/shaders/ocean/foam.hlsl | 14 +++++++------- source/runtime/Rendering/Renderer_Passes.cpp | 7 +++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index ee33b77024..4967cb5edf 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -3,16 +3,16 @@ struct VSOUT { float4 pos : SV_Position; - float2 uv : TEXCOORD; + float2 uv : TEXCOORD; }; VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { VSOUT vs_out; - - float3 wpos = mul(input.position, buffer_pass.transform).xyz; - vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); - vs_out.uv = input.uv; + float3 displaced_pos = input.position.xyz + tex.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).xyz; + float3 wpos = mul(float4(displaced_pos, 1.0f), buffer_pass.transform).xyz; + vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); + vs_out.uv = input.uv; return vs_out; } @@ -20,7 +20,7 @@ VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) float4 main_ps(VSOUT vertex) : SV_Target0 { const float4 foam_color = float4(1.0f, 1.0f, 1.0f, 1.0f); - const float foam = tex.Sample(samplers[sampler_point_clamp], vertex.uv).a * 300.0f; + const float foam = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).a * 10.0f; - return foam_color; + return foam_color * foam; } diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index a60c53019e..757a8b3695 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -1246,10 +1246,12 @@ namespace spartan void Renderer::Pass_ApplyFoam(RHI_CommandList* cmd_list) { - RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); RHI_Texture* tex_depth = GetRenderTarget(Renderer_RenderTarget::gbuffer_depth); RHI_Texture* tex_out = GetRenderTarget(Renderer_RenderTarget::frame_render); + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + tex_out->SetLayout(RHI_Image_Layout::General, cmd_list); cmd_list->BeginTimeblock("ocean_foam"); @@ -1290,7 +1292,8 @@ namespace spartan cmd_list->SetBufferVertex(renderable->GetVertexBuffer(), renderable->GetInstanceBuffer()); cmd_list->SetBufferIndex(renderable->GetIndexBuffer()); - cmd_list->SetTexture(Renderer_BindingsSrv::tex, slope_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex, displacement_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex2, slope_map); cmd_list->DrawIndexed( renderable->GetIndexCount(draw_call.lod_index), From 4d9330dc1696f821ac80f9c4df1581794210fa85 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 10:07:25 +0200 Subject: [PATCH 064/133] [git] updated gitignore to exclude slnx file for vs2026 configs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 20c9ab421e..30d3853ba8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # User-specific files *.sln +*.slnx *.vcxproj *.vcxproj.filters *.suo From 45cfc86e97f291dc5d71a133e4ed55b510601971 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 10:28:58 +0200 Subject: [PATCH 065/133] [ocean] cleaned up shader unused comments --- data/shaders/ocean/advance_spectrum.hlsl | 4 ++-- data/shaders/ocean/initial_spectrum.hlsl | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/data/shaders/ocean/advance_spectrum.hlsl b/data/shaders/ocean/advance_spectrum.hlsl index 2875e22053..2f1f847654 100644 --- a/data/shaders/ocean/advance_spectrum.hlsl +++ b/data/shaders/ocean/advance_spectrum.hlsl @@ -41,9 +41,9 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) surface.Build(thread_id.xy, resolution_out, true, false); OceanParameters params = surface.ocean_parameters; - float repeatTime = params.repeatTime; //frac(srt->time); + float repeatTime = params.repeatTime; float w_0 = 2.0f * PI / repeatTime; - float dispersion = floor(sqrt(G * kMag) / w_0) * w_0 * buffer_frame.time; //srt - > time; + float dispersion = floor(sqrt(G * kMag) / w_0) * w_0 * buffer_frame.time; float2 exponent = EulerFormula(dispersion); diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index 41250059dc..cb7762801b 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -1,5 +1,3 @@ -//static const float PI = 3.14159265f; - #include "../common.hlsl" static const float G = 9.81f; From c451e865f2d02d975748e135a4053b2df74c5482 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 10:40:13 +0200 Subject: [PATCH 066/133] [license] added license and code inspiration credit --- data/shaders/ocean/advance_spectrum.hlsl | 24 ++++++++++++++++++++++ data/shaders/ocean/fft_common.hlsl | 24 ++++++++++++++++++++++ data/shaders/ocean/foam.hlsl | 24 ++++++++++++++++++++++ data/shaders/ocean/generate_maps.hlsl | 24 ++++++++++++++++++++++ data/shaders/ocean/horizontal_fft.hlsl | 24 ++++++++++++++++++++++ data/shaders/ocean/initial_spectrum.hlsl | 26 ++++++++++++++++++++++-- data/shaders/ocean/pack_spectrum.hlsl | 26 ++++++++++++++++++++++-- data/shaders/ocean/vertical_fft.hlsl | 24 ++++++++++++++++++++++ 8 files changed, 192 insertions(+), 4 deletions(-) diff --git a/data/shaders/ocean/advance_spectrum.hlsl b/data/shaders/ocean/advance_spectrum.hlsl index 2f1f847654..e31eb42cf2 100644 --- a/data/shaders/ocean/advance_spectrum.hlsl +++ b/data/shaders/ocean/advance_spectrum.hlsl @@ -1,3 +1,27 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + #include "../common.hlsl" static const float G = 9.81f; diff --git a/data/shaders/ocean/fft_common.hlsl b/data/shaders/ocean/fft_common.hlsl index 37cbda88c3..68524d8aae 100644 --- a/data/shaders/ocean/fft_common.hlsl +++ b/data/shaders/ocean/fft_common.hlsl @@ -1,3 +1,27 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + #ifndef SPARTAN_FFT_COMMON #define SPARTAN_FFT_COMMON diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index 4967cb5edf..1275f109cf 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -1,3 +1,27 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + #include "../common.hlsl" struct VSOUT diff --git a/data/shaders/ocean/generate_maps.hlsl b/data/shaders/ocean/generate_maps.hlsl index 9cba691891..e3a482b7fd 100644 --- a/data/shaders/ocean/generate_maps.hlsl +++ b/data/shaders/ocean/generate_maps.hlsl @@ -1,3 +1,27 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + #include "../common.hlsl" RWTexture2D displacement_spectrum : register(u10); diff --git a/data/shaders/ocean/horizontal_fft.hlsl b/data/shaders/ocean/horizontal_fft.hlsl index 4f2d9e04ae..b5674ca0d7 100644 --- a/data/shaders/ocean/horizontal_fft.hlsl +++ b/data/shaders/ocean/horizontal_fft.hlsl @@ -1,3 +1,27 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + #include "fft_common.hlsl" RWTexture2D displacement_spectrum : register(u10); diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index cb7762801b..f073540cfe 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -1,3 +1,27 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + #include "../common.hlsl" static const float G = 9.81f; @@ -6,8 +30,6 @@ static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; RWTexture2D initial_spectrum : register(u9); -// Heavily Inspired by Acerola's Implementation: -// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute float2 UniformToGaussian(float u1, float u2) { float R = sqrt(-2.0f * log(u1)); diff --git a/data/shaders/ocean/pack_spectrum.hlsl b/data/shaders/ocean/pack_spectrum.hlsl index 0a1f6a0f5e..36f98ae917 100644 --- a/data/shaders/ocean/pack_spectrum.hlsl +++ b/data/shaders/ocean/pack_spectrum.hlsl @@ -1,3 +1,27 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + #include "../common.hlsl" static const uint SPECTRUM_TEX_SIZE = 512; @@ -7,8 +31,6 @@ RWTexture2D initial_spectrum : register(u9); [numthreads(8, 8, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { - // Heavily Inspired by Acerola's Implementation: - // https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute float2 h0 = initial_spectrum[thread_id.xy].rg; float2 h0conj = initial_spectrum[uint2((SPECTRUM_TEX_SIZE - thread_id.x) % SPECTRUM_TEX_SIZE, (SPECTRUM_TEX_SIZE - thread_id.y) % SPECTRUM_TEX_SIZE)].rg; diff --git a/data/shaders/ocean/vertical_fft.hlsl b/data/shaders/ocean/vertical_fft.hlsl index eba24506c5..8cafe53034 100644 --- a/data/shaders/ocean/vertical_fft.hlsl +++ b/data/shaders/ocean/vertical_fft.hlsl @@ -1,3 +1,27 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Inspired by Acerola's Implementation: +// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute + #include "fft_common.hlsl" RWTexture2D displacement_spectrum : register(u10); From 3a8d62524fa7ec50cdd784ff64d24d4ea4730de7 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 12:08:59 +0200 Subject: [PATCH 067/133] [ocean] displacement and slope scale --- data/shaders/common_resources.hlsl | 3 ++ data/shaders/common_structs.hlsl | 43 ++++++++++++--------- data/shaders/g_buffer.hlsl | 9 +++-- data/shaders/ocean/foam.hlsl | 3 +- source/editor/Widgets/Properties.cpp | 2 + source/runtime/Game/Game.cpp | 3 ++ source/runtime/Rendering/Material.cpp | 2 + source/runtime/Rendering/Material.h | 3 ++ source/runtime/Rendering/Renderer.cpp | 40 ++++++++++--------- source/runtime/Rendering/Renderer_Buffers.h | 3 ++ 10 files changed, 69 insertions(+), 42 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index e66199da5f..40ae18b2a5 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -133,6 +133,9 @@ struct MaterialParameters float foamBias; float foamThreshold; float foamAdd; + + float displacementScale; + float slopeScale; } ocean_parameters; bool has_texture_occlusion() { return (flags & (1 << 7)) != 0; } diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index 17aaf23b5c..82c61c845e 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -50,6 +50,9 @@ struct OceanParameters float foamBias; float foamThreshold; float foamAdd; + + float displacementScale; + float slopeScale; }; struct Surface @@ -134,25 +137,27 @@ struct Surface diffuse_energy = 1.0f; // jonswap parameters - ocean_parameters.alpha = material.ocean_parameters.alpha; - ocean_parameters.angle = material.ocean_parameters.angle; - ocean_parameters.fetch = material.ocean_parameters.fetch; - ocean_parameters.gamma = material.ocean_parameters.gamma; - ocean_parameters.peakOmega = material.ocean_parameters.peakOmega; - ocean_parameters.repeatTime = material.ocean_parameters.repeatTime; - ocean_parameters.scale = material.ocean_parameters.scale; - ocean_parameters.shortWavesFade = material.ocean_parameters.shortWavesFade; - ocean_parameters.spreadBlend = material.ocean_parameters.spreadBlend; - ocean_parameters.swell = material.ocean_parameters.swell; - ocean_parameters.windDirection = material.ocean_parameters.windDirection; - ocean_parameters.windSpeed = material.ocean_parameters.windSpeed; - ocean_parameters.depth = material.ocean_parameters.depth; - ocean_parameters.lowCutoff = material.ocean_parameters.lowCutoff; - ocean_parameters.highCutoff = material.ocean_parameters.highCutoff; - ocean_parameters.foamDecayRate = material.ocean_parameters.foamDecayRate; - ocean_parameters.foamBias = material.ocean_parameters.foamBias; - ocean_parameters.foamThreshold = material.ocean_parameters.foamThreshold; - ocean_parameters.foamAdd = material.ocean_parameters.foamAdd; + ocean_parameters.alpha = material.ocean_parameters.alpha; + ocean_parameters.angle = material.ocean_parameters.angle; + ocean_parameters.fetch = material.ocean_parameters.fetch; + ocean_parameters.gamma = material.ocean_parameters.gamma; + ocean_parameters.peakOmega = material.ocean_parameters.peakOmega; + ocean_parameters.repeatTime = material.ocean_parameters.repeatTime; + ocean_parameters.scale = material.ocean_parameters.scale; + ocean_parameters.shortWavesFade = material.ocean_parameters.shortWavesFade; + ocean_parameters.spreadBlend = material.ocean_parameters.spreadBlend; + ocean_parameters.swell = material.ocean_parameters.swell; + ocean_parameters.windDirection = material.ocean_parameters.windDirection; + ocean_parameters.windSpeed = material.ocean_parameters.windSpeed; + ocean_parameters.depth = material.ocean_parameters.depth; + ocean_parameters.lowCutoff = material.ocean_parameters.lowCutoff; + ocean_parameters.highCutoff = material.ocean_parameters.highCutoff; + ocean_parameters.foamDecayRate = material.ocean_parameters.foamDecayRate; + ocean_parameters.foamBias = material.ocean_parameters.foamBias; + ocean_parameters.foamThreshold = material.ocean_parameters.foamThreshold; + ocean_parameters.foamAdd = material.ocean_parameters.foamAdd; + ocean_parameters.displacementScale = material.ocean_parameters.displacementScale; + ocean_parameters.slopeScale = material.ocean_parameters.slopeScale; // roughness is authored as perceptual roughness, as is convention roughness_alpha = roughness * roughness; diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 7579bdca48..145ad5c33e 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -102,15 +102,18 @@ static float4 sample_texture(gbuffer_vertex vertex, uint texture_index, Surface gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { - input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb; - float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0); + MaterialParameters material = GetMaterial(); + + input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; + + float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0) * material.ocean_parameters.slopeScale; input.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform); // transform world space position to clip space Surface surface; - surface.flags = GetMaterial().flags; + surface.flags = material.flags; if (!surface.is_tessellated()) { vertex = transform_to_clip_space(vertex); diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index 1275f109cf..e79fe6d140 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -33,7 +33,8 @@ struct VSOUT VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { VSOUT vs_out; - float3 displaced_pos = input.position.xyz + tex.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).xyz; + + float3 displaced_pos = input.position.xyz + tex.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).xyz * GetMaterial().ocean_parameters.displacementScale; float3 wpos = mul(float4(displaced_pos, 1.0f), buffer_pass.transform).xyz; vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); vs_out.uv = input.uv; diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 5ad4328323..cc222a5885 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -877,6 +877,8 @@ void Properties::ShowMaterial(Material* material) const show_jonswap_params("Foam Bias", "", OceanParameters::FoamBias); show_jonswap_params("Foam Threshold", "", OceanParameters::FoamThreshold); show_jonswap_params("Foam Add", "", OceanParameters::FoamAdd); + show_jonswap_params("Displacement Scale", "", OceanParameters::DisplacementScale); + show_jonswap_params("Slope Scale", "", OceanParameters::SlopeScale); } // uv diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 4473206dfa..621fcdb7eb 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -339,6 +339,9 @@ namespace spartan material->SetOceanProperty(OceanParameters::FoamThreshold, -1.8f); material->SetOceanProperty(OceanParameters::FoamBias, 0.2f); material->SetOceanProperty(OceanParameters::FoamAdd, 0.12f); + + material->SetOceanProperty(OceanParameters::DisplacementScale, 1.0f); + material->SetOceanProperty(OceanParameters::SlopeScale, 1.0f); } } diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index 2299ee1be0..b353d002b3 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -126,6 +126,8 @@ namespace spartan case spartan::OceanParameters::FoamThreshold: return "foam_threshold"; case spartan::OceanParameters::FoamAdd: return "foam_add"; case spartan::OceanParameters::FoamBias: return "foam_bias"; + case spartan::OceanParameters::DisplacementScale: return "displacement_scale"; + case spartan::OceanParameters::SlopeScale: return "slope_scale"; case spartan::OceanParameters::Max: return "max"; default: { diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 1b9a19be71..967a25a171 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -137,6 +137,9 @@ namespace spartan FoamThreshold, FoamAdd, + DisplacementScale, + SlopeScale, + Max }; diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index fb76bbc371..c9a20ad4ee 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -861,25 +861,27 @@ namespace spartan properties[count].world_space_uv = material->GetProperty(MaterialProperty::WorldSpaceUv); // ocean - properties[count].jonswap_parameters.alpha = material->GetOceanProperty(OceanParameters::Alpha); - properties[count].jonswap_parameters.angle = material->GetOceanProperty(OceanParameters::Angle); - properties[count].jonswap_parameters.fetch = material->GetOceanProperty(OceanParameters::Fetch); - properties[count].jonswap_parameters.gamma = material->GetOceanProperty(OceanParameters::Gamma); - properties[count].jonswap_parameters.peakOmega = material->GetOceanProperty(OceanParameters::PeakOmega); - properties[count].jonswap_parameters.repeatTime = material->GetOceanProperty(OceanParameters::RepeatTime); - properties[count].jonswap_parameters.scale = material->GetOceanProperty(OceanParameters::Scale); - properties[count].jonswap_parameters.shortWavesFade = material->GetOceanProperty(OceanParameters::ShortWavesFade); - properties[count].jonswap_parameters.spreadBlend = material->GetOceanProperty(OceanParameters::SpreadBlend); - properties[count].jonswap_parameters.swell = material->GetOceanProperty(OceanParameters::Swell); - properties[count].jonswap_parameters.windDirection = material->GetOceanProperty(OceanParameters::WindDirection); - properties[count].jonswap_parameters.windSpeed = material->GetOceanProperty(OceanParameters::WindSpeed); - properties[count].jonswap_parameters.depth = material->GetOceanProperty(OceanParameters::Depth); - properties[count].jonswap_parameters.lowCutoff = material->GetOceanProperty(OceanParameters::LowCutoff); - properties[count].jonswap_parameters.highCutoff = material->GetOceanProperty(OceanParameters::HighCutoff); - properties[count].jonswap_parameters.foamDecayRate = material->GetOceanProperty(OceanParameters::FoamDecayRate); - properties[count].jonswap_parameters.foamBias = material->GetOceanProperty(OceanParameters::FoamBias); - properties[count].jonswap_parameters.foamThreshold = material->GetOceanProperty(OceanParameters::FoamThreshold); - properties[count].jonswap_parameters.foamAdd = material->GetOceanProperty(OceanParameters::FoamAdd); + properties[count].jonswap_parameters.alpha = material->GetOceanProperty(OceanParameters::Alpha); + properties[count].jonswap_parameters.angle = material->GetOceanProperty(OceanParameters::Angle); + properties[count].jonswap_parameters.fetch = material->GetOceanProperty(OceanParameters::Fetch); + properties[count].jonswap_parameters.gamma = material->GetOceanProperty(OceanParameters::Gamma); + properties[count].jonswap_parameters.peakOmega = material->GetOceanProperty(OceanParameters::PeakOmega); + properties[count].jonswap_parameters.repeatTime = material->GetOceanProperty(OceanParameters::RepeatTime); + properties[count].jonswap_parameters.scale = material->GetOceanProperty(OceanParameters::Scale); + properties[count].jonswap_parameters.shortWavesFade = material->GetOceanProperty(OceanParameters::ShortWavesFade); + properties[count].jonswap_parameters.spreadBlend = material->GetOceanProperty(OceanParameters::SpreadBlend); + properties[count].jonswap_parameters.swell = material->GetOceanProperty(OceanParameters::Swell); + properties[count].jonswap_parameters.windDirection = material->GetOceanProperty(OceanParameters::WindDirection); + properties[count].jonswap_parameters.windSpeed = material->GetOceanProperty(OceanParameters::WindSpeed); + properties[count].jonswap_parameters.depth = material->GetOceanProperty(OceanParameters::Depth); + properties[count].jonswap_parameters.lowCutoff = material->GetOceanProperty(OceanParameters::LowCutoff); + properties[count].jonswap_parameters.highCutoff = material->GetOceanProperty(OceanParameters::HighCutoff); + properties[count].jonswap_parameters.foamDecayRate = material->GetOceanProperty(OceanParameters::FoamDecayRate); + properties[count].jonswap_parameters.foamBias = material->GetOceanProperty(OceanParameters::FoamBias); + properties[count].jonswap_parameters.foamThreshold = material->GetOceanProperty(OceanParameters::FoamThreshold); + properties[count].jonswap_parameters.foamAdd = material->GetOceanProperty(OceanParameters::FoamAdd); + properties[count].jonswap_parameters.displacementScale = material->GetOceanProperty(OceanParameters::DisplacementScale); + properties[count].jonswap_parameters.slopeScale = material->GetOceanProperty(OceanParameters::SlopeScale); // flags properties[count].flags = material->HasTextureOfType(MaterialTextureType::Height) ? (1U << 0) : 0; diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index 0da6dbc9f7..7279e75c57 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -206,6 +206,9 @@ namespace spartan float foamBias; float foamThreshold; float foamAdd; + + float displacementScale; + float slopeScale; } jonswap_parameters; }; From d525b1260337bc7a52701b818ba767edd7cd03b2 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 14:39:27 +0200 Subject: [PATCH 068/133] [ocean] shader cleanup --- data/shaders/common.hlsl | 1 + data/shaders/ocean/advance_spectrum.hlsl | 10 +------ data/shaders/ocean/common_ocean.hlsl | 36 ++++++++++++++++++++++++ data/shaders/ocean/fft_common.hlsl | 3 +- data/shaders/ocean/foam.hlsl | 2 +- data/shaders/ocean/generate_maps.hlsl | 7 +---- data/shaders/ocean/horizontal_fft.hlsl | 3 -- data/shaders/ocean/initial_spectrum.hlsl | 8 +----- data/shaders/ocean/pack_spectrum.hlsl | 6 +--- data/shaders/ocean/vertical_fft.hlsl | 3 -- 10 files changed, 43 insertions(+), 36 deletions(-) create mode 100644 data/shaders/ocean/common_ocean.hlsl diff --git a/data/shaders/common.hlsl b/data/shaders/common.hlsl index e1577ee197..4d80be64db 100644 --- a/data/shaders/common.hlsl +++ b/data/shaders/common.hlsl @@ -45,6 +45,7 @@ static const uint THREAD_GROUP_COUNT_X = 8; static const uint THREAD_GROUP_COUNT_Y = 8; static const uint THREAD_GROUP_COUNT = 64; static const float DEG_TO_RAD = PI / 180.0f; +static const float G = 9.81f; /*------------------------------------------------------------------------------ SATURATE diff --git a/data/shaders/ocean/advance_spectrum.hlsl b/data/shaders/ocean/advance_spectrum.hlsl index e31eb42cf2..4bbf5e14ae 100644 --- a/data/shaders/ocean/advance_spectrum.hlsl +++ b/data/shaders/ocean/advance_spectrum.hlsl @@ -22,15 +22,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Inspired by Acerola's Implementation: // https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute -#include "../common.hlsl" - -static const float G = 9.81f; -static const uint SPECTRUM_TEX_SIZE = 512; -static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; - -RWTexture2D initial_spectrum : register(u9); -RWTexture2D displacement_spectrum : register(u10); -RWTexture2D slope_spectrum : register(u11); +#include "common_ocean.hlsl" float2 ComplexMult(float2 a, float2 b) { diff --git a/data/shaders/ocean/common_ocean.hlsl b/data/shaders/ocean/common_ocean.hlsl new file mode 100644 index 0000000000..09baaaf4cc --- /dev/null +++ b/data/shaders/ocean/common_ocean.hlsl @@ -0,0 +1,36 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "../common.hlsl" + +#ifndef SPARTAN_COMMON_OCEAN +#define SPARTAN_COMMON_OCEAN + +static const uint SPECTRUM_TEX_SIZE = 512; +static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; + +RWTexture2D initial_spectrum : register(u9); +RWTexture2D displacement_spectrum : register(u10); +RWTexture2D slope_spectrum : register(u11); +RWTexture2D displacement_map : register(u12); +RWTexture2D slope_map : register(u13); + +#endif // SPARTAN_COMMON_OCEAN diff --git a/data/shaders/ocean/fft_common.hlsl b/data/shaders/ocean/fft_common.hlsl index 68524d8aae..f6a14758a0 100644 --- a/data/shaders/ocean/fft_common.hlsl +++ b/data/shaders/ocean/fft_common.hlsl @@ -25,8 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SPARTAN_FFT_COMMON #define SPARTAN_FFT_COMMON -static const uint SPECTRUM_TEX_SIZE = 512; -static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; +#include "common_ocean.hlsl" static const uint LOG_SIZE = log(SPECTRUM_TEX_SIZE) / log(2); // result of Log base 2 of SPECTRUM_TEX_SIZE diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index e79fe6d140..84a7fcfd28 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -22,7 +22,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Inspired by Acerola's Implementation: // https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute -#include "../common.hlsl" +#include "common_ocean.hlsl" struct VSOUT { diff --git a/data/shaders/ocean/generate_maps.hlsl b/data/shaders/ocean/generate_maps.hlsl index e3a482b7fd..3c03a4c9e8 100644 --- a/data/shaders/ocean/generate_maps.hlsl +++ b/data/shaders/ocean/generate_maps.hlsl @@ -22,12 +22,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Inspired by Acerola's Implementation: // https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute -#include "../common.hlsl" - -RWTexture2D displacement_spectrum : register(u10); -RWTexture2D slope_spectrum : register(u11); -RWTexture2D displacement_map : register(u12); -RWTexture2D slope_map : register(u13); +#include "common_ocean.hlsl" float4 Permute(float4 data, float3 id) { diff --git a/data/shaders/ocean/horizontal_fft.hlsl b/data/shaders/ocean/horizontal_fft.hlsl index b5674ca0d7..95123c0ac3 100644 --- a/data/shaders/ocean/horizontal_fft.hlsl +++ b/data/shaders/ocean/horizontal_fft.hlsl @@ -24,9 +24,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fft_common.hlsl" -RWTexture2D displacement_spectrum : register(u10); -RWTexture2D slope_spectrum : register(u11); - [numthreads(512, 1, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index f073540cfe..2b05cec18c 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -22,13 +22,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Inspired by Acerola's Implementation: // https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute -#include "../common.hlsl" - -static const float G = 9.81f; -static const uint SPECTRUM_TEX_SIZE = 512; -static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; - -RWTexture2D initial_spectrum : register(u9); +#include "common_ocean.hlsl" float2 UniformToGaussian(float u1, float u2) { diff --git a/data/shaders/ocean/pack_spectrum.hlsl b/data/shaders/ocean/pack_spectrum.hlsl index 36f98ae917..b187a9fd81 100644 --- a/data/shaders/ocean/pack_spectrum.hlsl +++ b/data/shaders/ocean/pack_spectrum.hlsl @@ -22,11 +22,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Inspired by Acerola's Implementation: // https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute -#include "../common.hlsl" - -static const uint SPECTRUM_TEX_SIZE = 512; - -RWTexture2D initial_spectrum : register(u9); +#include "common_ocean.hlsl" [numthreads(8, 8, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) diff --git a/data/shaders/ocean/vertical_fft.hlsl b/data/shaders/ocean/vertical_fft.hlsl index 8cafe53034..16851429aa 100644 --- a/data/shaders/ocean/vertical_fft.hlsl +++ b/data/shaders/ocean/vertical_fft.hlsl @@ -24,9 +24,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fft_common.hlsl" -RWTexture2D displacement_spectrum : register(u10); -RWTexture2D slope_spectrum : register(u11); - [numthreads(512, 1, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { From 3916afc65c51f48c369567551aa1d94fe861207c Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 14:48:33 +0200 Subject: [PATCH 069/133] [ocean] switched from push constants to per frame buffer --- data/shaders/ocean/initial_spectrum.hlsl | 4 ++-- source/runtime/Rendering/Renderer_Passes.cpp | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index 2b05cec18c..a5aa87f04c 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -140,8 +140,8 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) surface.Build(thread_id.xy, resolution_out, true, false); uint seed = thread_id.x + SPECTRUM_TEX_SIZE * thread_id.y + SPECTRUM_TEX_SIZE; - seed += pass_get_f2_value().x; // seed += frame_number - + seed += buffer_frame.frame; + OceanParameters params = surface.ocean_parameters; float halfN = SPECTRUM_TEX_SIZE / 2.0f; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 757a8b3695..0da52e7fee 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -1124,9 +1124,6 @@ namespace spartan pso.shaders[Compute] = GetShader(Renderer_Shader::ocean_initial_spectrum_c); cmd_list->SetPipelineState(pso); - m_pcb_pass_cpu.set_f2_value(GetFrameNumber(), 0.0f); - cmd_list->PushConstants(m_pcb_pass_cpu); - cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); cmd_list->Dispatch(initial_spectrum); } From 2b9fd018b68c82acdb49b0996003acdcaa06ea33 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 15:03:50 +0200 Subject: [PATCH 070/133] [ocean] increased amount of vertices for the ocean mesh --- source/runtime/Game/Game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 621fcdb7eb..0c15de62d9 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1687,7 +1687,7 @@ namespace spartan auto entity = World::CreateEntity(); - default_ocean = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 256.0f); + default_ocean = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 512); default_ocean->SetParent(entity); From ebf8299c27bcd2a68c37287f8b8d28b3ec102d24 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 15:55:01 +0200 Subject: [PATCH 071/133] [ocean] added texture size variable --- source/runtime/Rendering/Renderer_Resources.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 08fd58c24d..54fd9fe1f3 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -227,15 +227,17 @@ namespace spartan // ocean { uint32_t flags = RHI_Texture_Uav | RHI_Texture_Srv; - render_target(Renderer_RenderTarget::ocean_initial_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_initial_spectrum"); + uint32_t texture_size = 512; - render_target(Renderer_RenderTarget::ocean_displacement_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_spectrum"); + render_target(Renderer_RenderTarget::ocean_initial_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_initial_spectrum"); - render_target(Renderer_RenderTarget::ocean_slope_spectrum) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_spectrum"); + render_target(Renderer_RenderTarget::ocean_displacement_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_spectrum"); - render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_map"); + render_target(Renderer_RenderTarget::ocean_slope_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_spectrum"); - render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, 512, 512, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_map"); + render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_map"); + + render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_map"); } // occlusion From 3cc6731de56a711e0b75d277f14982ba2d91cdfc Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 17 Sep 2025 16:10:29 +0200 Subject: [PATCH 072/133] [ocean] compute fix --- data/shaders/ocean/horizontal_fft.hlsl | 2 +- data/shaders/ocean/vertical_fft.hlsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/shaders/ocean/horizontal_fft.hlsl b/data/shaders/ocean/horizontal_fft.hlsl index 95123c0ac3..f55a8c7ab1 100644 --- a/data/shaders/ocean/horizontal_fft.hlsl +++ b/data/shaders/ocean/horizontal_fft.hlsl @@ -24,7 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fft_common.hlsl" -[numthreads(512, 1, 1)] +[numthreads(SPECTRUM_TEX_SIZE, 1, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { displacement_spectrum[thread_id.xy] = FFT(thread_id.x, displacement_spectrum[thread_id.xy]); diff --git a/data/shaders/ocean/vertical_fft.hlsl b/data/shaders/ocean/vertical_fft.hlsl index 16851429aa..14fdaa1c94 100644 --- a/data/shaders/ocean/vertical_fft.hlsl +++ b/data/shaders/ocean/vertical_fft.hlsl @@ -24,7 +24,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fft_common.hlsl" -[numthreads(512, 1, 1)] +[numthreads(SPECTRUM_TEX_SIZE, 1, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { displacement_spectrum[thread_id.yx] = FFT(thread_id.x, displacement_spectrum[thread_id.yx]); From a0e00ae8153a913c352d7a64baa25fb2b83f4933 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 18 Sep 2025 15:31:00 +0200 Subject: [PATCH 073/133] [ocean] swithced directional light with a point light --- data/shaders/ocean/foam.hlsl | 2 +- source/runtime/Game/Game.cpp | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index 84a7fcfd28..7ee7370eae 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -45,7 +45,7 @@ VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) float4 main_ps(VSOUT vertex) : SV_Target0 { const float4 foam_color = float4(1.0f, 1.0f, 1.0f, 1.0f); - const float foam = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).a * 10.0f; + const float foam = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).a * 1.0f; return foam_color * foam; } diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 0c15de62d9..b217383937 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1683,7 +1683,7 @@ namespace spartan void create() { entities::camera(); - entities::sun(true); + //entities::sun(true); auto entity = World::CreateEntity(); @@ -1691,7 +1691,16 @@ namespace spartan default_ocean->SetParent(entity); - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + auto light_entity = World::CreateEntity(); + + Light* point = light_entity->AddComponent(); + point->SetLightType(LightType::Point); + point->SetRange(20.0f); + point->SetTemperature(2500.0f); + point->SetIntensity(8500.0f); + point->SetObjectName("Point Light"); + + //default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } void on_shutdown() From cf6cc87fc35dde9d3962f79411a582ff2520468b Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 22 Sep 2025 20:57:15 +0200 Subject: [PATCH 074/133] [ocean] ocean grid made of multiple tiles (wip) --- source/editor/Widgets/Properties.cpp | 4 + source/runtime/Game/Game.cpp | 119 ++++++++++++++++++++++----- source/runtime/Rendering/Material.h | 3 + 3 files changed, 107 insertions(+), 19 deletions(-) diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 5423913936..12e67834cc 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -885,6 +885,10 @@ void Properties::ShowMaterial(Material* material) const show_jonswap_params("Foam Add", "", OceanParameters::FoamAdd); show_jonswap_params("Displacement Scale", "", OceanParameters::DisplacementScale); show_jonswap_params("Slope Scale", "", OceanParameters::SlopeScale); + + int tile_count = material->GetOceanTileCount(); + ImGui::InputInt("Ocean Tiles", &tile_count); + material->SetOceanTileCount(tile_count); } // uv diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index b217383937..497a96132d 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -296,7 +296,7 @@ namespace spartan return water; } - Entity* ocean(const Vector3& position, float dimension, uint32_t density) + Entity* ocean(std::shared_ptr material, const Vector3& position, float dimension, uint32_t density, uint32_t grid_size) { // entity Entity* water = World::CreateEntity(); @@ -304,12 +304,12 @@ namespace spartan water->SetPosition(position); // material - shared_ptr material = make_shared(); { material->SetObjectName("material_ocean"); material->SetResourceFilePath("ocean" + string(EXTENSION_MATERIAL)); material->LoadFromFile(material->GetResourceFilePath()); + material->SetOceanTileCount(grid_size); // if material fails to load from file if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) @@ -363,22 +363,32 @@ namespace spartan mesh->CreateGpuBuffers(); // create a child entity, add a renderable, and this mesh tile to it + for (uint32_t row = 0; row < grid_size; row++) { - Entity* entity_tile = World::CreateEntity(); - entity_tile->SetObjectName(name); - entity_tile->SetParent(water); - //entity_tile->SetPosition(tile_offsets[tile_index]); - - if (Renderable* renderable = entity_tile->AddComponent()) + for (uint32_t col = 0; col < grid_size; col++) { - renderable->SetMesh(mesh.get()); - renderable->SetMaterial(material); - renderable->SetFlag(RenderableFlags::CastsShadows, false); - } + int tile_index = col + row * grid_size; + + string tile_name = "ocean tile_" + to_string(tile_index); + + Entity* entity_tile = World::CreateEntity(); + entity_tile->SetObjectName(tile_name); + entity_tile->SetParent(water); - // enable buoyancy - Physics* physics = entity_tile->AddComponent(); - physics->SetBodyType(BodyType::Water); + Vector3 tile_position = { col * dimension, 0.0f, row * dimension }; + entity_tile->SetPosition(tile_position); + + if (Renderable* renderable = entity_tile->AddComponent()) + { + renderable->SetMesh(mesh.get()); + renderable->SetMaterial(material); + renderable->SetFlag(RenderableFlags::CastsShadows, false); + } + + // enable buoyancy + Physics* physics = entity_tile->AddComponent(); + physics->SetBodyType(BodyType::Water); + } } } @@ -1680,6 +1690,10 @@ namespace spartan namespace ocean { + uint32_t ocean_tile_count = 2; + const float tile_size = 20.0f; + shared_ptr material = make_shared(); + void create() { entities::camera(); @@ -1687,28 +1701,86 @@ namespace spartan auto entity = World::CreateEntity(); - default_ocean = entities::ocean({ 0.0f, 0.0f, 0.0f }, 20.0f, 512); + default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }, tile_size, 512, ocean_tile_count); default_ocean->SetParent(entity); auto light_entity = World::CreateEntity(); + light_entity->SetPosition({ 10.0f, 10.0f, 10.0f }); Light* point = light_entity->AddComponent(); point->SetLightType(LightType::Point); - point->SetRange(20.0f); - point->SetTemperature(2500.0f); + point->SetRange(35.0f); + point->SetTemperature(3500.0f); point->SetIntensity(8500.0f); point->SetObjectName("Point Light"); //default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } + void tick() + { + if (!material) + return; + + uint32_t current_tile_count = material->GetOceanTileCount(); + if (current_tile_count != ocean_tile_count) + { + ocean_tile_count = current_tile_count; + auto& children = default_ocean->GetChildren(); + + for (uint32_t i = 0; i < children.size(); i++) + { + World::RemoveEntity(children[i]); + } + children.clear(); + + std::shared_ptr ocean_mesh; + + for (size_t i = 0; i < meshes.size(); i++) + { + if (meshes[i]->GetObjectName() == "ocean mesh") + ocean_mesh = meshes[i]; + } + + if (ocean_mesh.get() == nullptr) + return; + + for (uint32_t row = 0; row < current_tile_count; row++) + { + for (uint32_t col = 0; col < current_tile_count; col++) + { + int tile_index = col + row * current_tile_count; + + string tile_name = "ocean tile_" + to_string(tile_index); + + Entity* entity_tile = World::CreateEntity(); + entity_tile->SetObjectName(tile_name); + entity_tile->SetParent(default_ocean); + + Vector3 tile_position = { col * tile_size, 0.0f, row * tile_size }; + entity_tile->SetPosition(tile_position); + + if (Renderable* renderable = entity_tile->AddComponent()) + { + renderable->SetMesh(ocean_mesh.get()); + renderable->SetMaterial(material); + renderable->SetFlag(RenderableFlags::CastsShadows, false); + } + + // enable buoyancy + Physics* physics = entity_tile->AddComponent(); + physics->SetBodyType(BodyType::Water); + } + } + } + } + void on_shutdown() { if (!default_ocean) return; - Material* material = default_ocean->GetDescendantByName("ocean mesh")->GetComponent()->GetMaterial(); if (!material) SP_ASSERT_MSG(false, "Failed to get ocean material"); @@ -1736,6 +1808,11 @@ namespace spartan void Game::Tick() { + if (loaded_world == DefaultWorld::Ocean) + { + worlds::ocean::tick(); + } + if (!Engine::IsFlagSet(EngineMode::Playing)) return; @@ -1753,6 +1830,10 @@ namespace spartan { worlds::forest::tick(); } + else if (loaded_world == DefaultWorld::Ocean) + { + worlds::ocean::tick(); + } } void Game::Load(DefaultWorld default_world) diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 967a25a171..702a1134c0 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -187,6 +187,8 @@ namespace spartan uint32_t GetIndex() const { return m_index; } bool ShouldComputeSpectrum() const { return m_should_compute_spectrum; } void MarkSpectrumAsComputed() { m_should_compute_spectrum = false; } + void SetOceanTileCount(const uint32_t count) { m_ocean_tiles = count; } + uint32_t GetOceanTileCount() const { return m_ocean_tiles; } const std::array(MaterialProperty::Max)>& GetProperties() const { return m_properties; } const std::array(OceanParameters::Max)>& GetOceanProperties() const { return m_ocean_properties; } @@ -197,5 +199,6 @@ namespace spartan std::array(OceanParameters::Max)> m_ocean_properties; uint32_t m_index = 0; bool m_should_compute_spectrum = true; + uint32_t m_ocean_tiles = 1; }; } From 7f98babccd222a7c76896060da4d70dab7f5ce52 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 22 Sep 2025 21:20:18 +0200 Subject: [PATCH 075/133] [ocean] moved tick --- source/runtime/Game/Game.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 696a258cce..3e75eb4a62 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1829,7 +1829,10 @@ namespace spartan void Game::EditorTick() { - + if (loaded_world == DefaultWorld::Ocean) + { + worlds::ocean::tick(); + } } void Game::Load(DefaultWorld default_world) From 204fd673604c9cfd1ff4750ceca3261ff6559f59 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Tue, 23 Sep 2025 15:07:28 +0200 Subject: [PATCH 076/133] [ocean] added displacement and foam map visualisation per tile --- data/shaders/ocean/foam.hlsl | 11 +++++++++++ source/editor/Widgets/Properties.cpp | 8 ++++++++ source/runtime/Rendering/Material.h | 6 ++++++ source/runtime/Rendering/Renderer_Passes.cpp | 16 ++++++++++++---- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index 7ee7370eae..38358d55ef 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -33,9 +33,13 @@ struct VSOUT VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { VSOUT vs_out; + + float2 map_flags = pass_get_f2_value(); float3 displaced_pos = input.position.xyz + tex.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).xyz * GetMaterial().ocean_parameters.displacementScale; float3 wpos = mul(float4(displaced_pos, 1.0f), buffer_pass.transform).xyz; + if (any(map_flags) == 1.0f) + wpos = mul(float4(input.position.xyz, 1.0f), buffer_pass.transform).xyz; vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); vs_out.uv = input.uv; @@ -47,5 +51,12 @@ float4 main_ps(VSOUT vertex) : SV_Target0 const float4 foam_color = float4(1.0f, 1.0f, 1.0f, 1.0f); const float foam = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).a * 1.0f; + float2 map_flags = pass_get_f2_value(); + + if (map_flags.x == 1.0f) + return float4(tex.Sample(samplers[sampler_point_clamp], vertex.uv).rgb, 1.0f); + if (map_flags.y == 1.0f) + return float4(tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).rgb, 1.0f); + return foam_color * foam; } diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 12e67834cc..bc2132607e 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -889,6 +889,14 @@ void Properties::ShowMaterial(Material* material) const int tile_count = material->GetOceanTileCount(); ImGui::InputInt("Ocean Tiles", &tile_count); material->SetOceanTileCount(tile_count); + + bool show_displacement = material->GetShowDisplacement(); + ImGui::Checkbox("Show Displacement Map", &show_displacement); + material->SetShowDiplacement(show_displacement); + + bool show_slope = material->GetShowSlope(); + ImGui::Checkbox("Show Slope Map", &show_slope); + material->SetShowSlope(show_slope); } // uv diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 702a1134c0..896609e704 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -179,6 +179,10 @@ namespace spartan bool IsTransparent() const { return GetProperty(MaterialProperty::ColorA) < 1.0f; } bool IsAlphaTested(); bool IsOcean() const { return GetProperty(MaterialProperty::IsOcean) == 1.0f; } + void SetShowDiplacement(bool show) { m_show_displacement = show; } + bool GetShowDisplacement() const { return m_show_displacement; } + void SetShowSlope(bool show) { m_show_slope = show; } + bool GetShowSlope() const { return m_show_slope; } // misc void PrepareForGpu(); @@ -199,6 +203,8 @@ namespace spartan std::array(OceanParameters::Max)> m_ocean_properties; uint32_t m_index = 0; bool m_should_compute_spectrum = true; + bool m_show_displacement = false; + bool m_show_slope = false; uint32_t m_ocean_tiles = 1; }; } diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 621c171c9e..399d9c5f54 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -686,11 +686,14 @@ namespace spartan if (material->IsOcean()) { - RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); - cmd_list->SetTexture(17, displacement_map); + if (!material->GetShowDisplacement() && !material->GetShowSlope()) + { + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + cmd_list->SetTexture(17, displacement_map); - RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); - cmd_list->SetTexture(18, slope_map); + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + cmd_list->SetTexture(18, slope_map); + } } cmd_list->DrawIndexed( @@ -1277,6 +1280,11 @@ namespace spartan { m_pcb_pass_cpu.transform = renderable->GetEntity()->GetMatrix(); m_pcb_pass_cpu.set_is_transparent_and_material_index(true, material->GetIndex()); + + float a = material->GetShowDisplacement() ? 1.0f : 0.0f; + float b = material->GetShowSlope() ? 1.0f : 0.0f; + m_pcb_pass_cpu.set_f2_value(a, b); + cmd_list->PushConstants(m_pcb_pass_cpu); } From e2020cad18e46fa457b6515353c25fce11b71574 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Tue, 23 Sep 2025 17:31:42 +0200 Subject: [PATCH 077/133] [ocean] added some comments in the foam pass --- data/shaders/ocean/foam.hlsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index 38358d55ef..fcc051c0a6 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -38,7 +38,7 @@ VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) float3 displaced_pos = input.position.xyz + tex.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).xyz * GetMaterial().ocean_parameters.displacementScale; float3 wpos = mul(float4(displaced_pos, 1.0f), buffer_pass.transform).xyz; - if (any(map_flags) == 1.0f) + if (any(map_flags) == 1.0f) // If debugging displacement/slope map, then dont apply displacement map wpos = mul(float4(input.position.xyz, 1.0f), buffer_pass.transform).xyz; vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); vs_out.uv = input.uv; @@ -53,9 +53,9 @@ float4 main_ps(VSOUT vertex) : SV_Target0 float2 map_flags = pass_get_f2_value(); - if (map_flags.x == 1.0f) + if (map_flags.x == 1.0f) // Displacement Map return float4(tex.Sample(samplers[sampler_point_clamp], vertex.uv).rgb, 1.0f); - if (map_flags.y == 1.0f) + if (map_flags.y == 1.0f) // Slope Map return float4(tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).rgb, 1.0f); return foam_color * foam; From 0e3fc15a1436ed6c67fada58cf54e97bd9b4d712 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 24 Sep 2025 13:29:51 +0200 Subject: [PATCH 078/133] [ocean] added length scale parameter --- data/shaders/common_resources.hlsl | 1 + data/shaders/common_structs.hlsl | 2 + data/shaders/ocean/advance_spectrum.hlsl | 15 ++-- data/shaders/ocean/common_ocean.hlsl | 6 +- data/shaders/ocean/foam.hlsl | 76 ++++++++++++++++++++- data/shaders/ocean/initial_spectrum.hlsl | 7 +- source/editor/Widgets/Properties.cpp | 1 + source/runtime/Game/Game.cpp | 1 + source/runtime/Rendering/Material.cpp | 1 + source/runtime/Rendering/Material.h | 1 + source/runtime/Rendering/Renderer.cpp | 1 + source/runtime/Rendering/Renderer_Buffers.h | 1 + 12 files changed, 96 insertions(+), 17 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index 40ae18b2a5..8c0cca5acb 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -136,6 +136,7 @@ struct MaterialParameters float displacementScale; float slopeScale; + float lengthScale; } ocean_parameters; bool has_texture_occlusion() { return (flags & (1 << 7)) != 0; } diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index 39a345097f..35d717d454 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -53,6 +53,7 @@ struct OceanParameters float displacementScale; float slopeScale; + float lengthScale; }; struct Surface @@ -158,6 +159,7 @@ struct Surface ocean_parameters.foamAdd = material.ocean_parameters.foamAdd; ocean_parameters.displacementScale = material.ocean_parameters.displacementScale; ocean_parameters.slopeScale = material.ocean_parameters.slopeScale; + ocean_parameters.lengthScale = material.ocean_parameters.lengthScale; // roughness is authored as perceptual roughness, as is convention roughness_alpha = roughness * roughness; diff --git a/data/shaders/ocean/advance_spectrum.hlsl b/data/shaders/ocean/advance_spectrum.hlsl index 4bbf5e14ae..148585c45b 100644 --- a/data/shaders/ocean/advance_spectrum.hlsl +++ b/data/shaders/ocean/advance_spectrum.hlsl @@ -41,8 +41,14 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float2 h0 = initialSignal.rg; float2 h0conj = initialSignal.ba; + float2 resolution_out; + initial_spectrum.GetDimensions(resolution_out.x, resolution_out.y); + Surface surface; + surface.Build(thread_id.xy, resolution_out, true, false); + OceanParameters params = surface.ocean_parameters; + float halfN = SPECTRUM_TEX_SIZE / 2.0f; - float2 K = (thread_id.xy - halfN) * 2.0f * PI / LENGTH_SCALE; + float2 K = (thread_id.xy - halfN) * 2.0f * PI / params.lengthScale; float kMag = length(K); float kMagRcp = rcp(kMag); @@ -51,15 +57,10 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) kMagRcp = 1.0f; } - float2 resolution_out; - initial_spectrum.GetDimensions(resolution_out.x, resolution_out.y); - Surface surface; - surface.Build(thread_id.xy, resolution_out, true, false); - OceanParameters params = surface.ocean_parameters; float repeatTime = params.repeatTime; float w_0 = 2.0f * PI / repeatTime; - float dispersion = floor(sqrt(G * kMag) / w_0) * w_0 * buffer_frame.time; + float dispersion = Dispersion(kMag, params.depth) * buffer_frame.time; float2 exponent = EulerFormula(dispersion); diff --git a/data/shaders/ocean/common_ocean.hlsl b/data/shaders/ocean/common_ocean.hlsl index 09baaaf4cc..30ac8ff8f0 100644 --- a/data/shaders/ocean/common_ocean.hlsl +++ b/data/shaders/ocean/common_ocean.hlsl @@ -25,7 +25,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SPARTAN_COMMON_OCEAN static const uint SPECTRUM_TEX_SIZE = 512; -static const uint LENGTH_SCALE = SPECTRUM_TEX_SIZE / 8; RWTexture2D initial_spectrum : register(u9); RWTexture2D displacement_spectrum : register(u10); @@ -33,4 +32,9 @@ RWTexture2D slope_spectrum : register(u11); RWTexture2D displacement_map : register(u12); RWTexture2D slope_map : register(u13); +float Dispersion(float kMag, float depth) +{ + return sqrt(G * kMag * tanh(min(kMag * depth, 20))); +} + #endif // SPARTAN_COMMON_OCEAN diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl index fcc051c0a6..55332ffcb2 100644 --- a/data/shaders/ocean/foam.hlsl +++ b/data/shaders/ocean/foam.hlsl @@ -30,6 +30,74 @@ struct VSOUT float2 uv : TEXCOORD; }; +// hash +float hash21(float2 p) +{ + p = frac(p * float2(123.34f, 456.21f)); + p += dot(p, p + 78.233f); + return frac(p.x * p.y); +} + +// value noise +float noise2D(float2 p) +{ + float2 i = floor(p); + float2 f = frac(p); + float a = hash21(i); + float b = hash21(i + float2(1.0f, 0.0f)); + float c = hash21(i + float2(0.0f, 1.0f)); + float d = hash21(i + float2(1.0f, 1.0f)); + float2 u = f * f * (3.0f - 2.0f * f); + return lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y); +} + +// fractal Brownian motion (multiple octaves) +float fbmNoise(float2 uv) +{ + float val = 0.0f; + float amp = 0.5f; + float freq = 1.0f; + for (int i = 0; i < 5; i++) + { // 5 octaves + val += amp * noise2D(uv * freq); + freq *= 2.0f; + amp *= 0.5f; + } + return val; +} + +// worley noise (optional bubble-like pattern) +float worley(float2 p) +{ + float2 i = floor(p); + float2 f = frac(p); + float minDist = 1.0f; + for (int y = -1; y <= 1; y++) + { + for (int x = -1; x <= 1; x++) + { + float2 neighbor = float2(x, y); + float2 p = hash21(i + neighbor) * float2(1.0f, 1.0f); + float2 diff = neighbor + p - f; + float d = dot(diff, diff); + minDist = min(minDist, d); + } + } + return minDist; +} + +float computeFoamNoise(float2 uv, float time) +{ + // high frequency UV, animate over time + float2 noiseUV = uv * 32.0f + time * float2(0.1f, 0.05f); + + float fbm = fbmNoise(noiseUV); + float bubbles = 1.0f - saturate(sqrt(worley(noiseUV * 2.0f))); + + // combine fbm + bubbles for frothy look + return saturate(fbm * bubbles); +} + VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { VSOUT vs_out; @@ -49,8 +117,10 @@ VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) float4 main_ps(VSOUT vertex) : SV_Target0 { const float4 foam_color = float4(1.0f, 1.0f, 1.0f, 1.0f); - const float foam = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).a * 1.0f; - + const float foam_mask = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).a * 1.0f; + + const float foam_noise = computeFoamNoise(vertex.uv, buffer_frame.time); + float2 map_flags = pass_get_f2_value(); if (map_flags.x == 1.0f) // Displacement Map @@ -58,5 +128,5 @@ float4 main_ps(VSOUT vertex) : SV_Target0 if (map_flags.y == 1.0f) // Slope Map return float4(tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).rgb, 1.0f); - return foam_color * foam; + return float4(foam_noise.xxxx) * foam_mask; } diff --git a/data/shaders/ocean/initial_spectrum.hlsl b/data/shaders/ocean/initial_spectrum.hlsl index a5aa87f04c..bc54b738f3 100644 --- a/data/shaders/ocean/initial_spectrum.hlsl +++ b/data/shaders/ocean/initial_spectrum.hlsl @@ -32,11 +32,6 @@ float2 UniformToGaussian(float u1, float u2) return float2(R * cos(theta), R * sin(theta)); } -float Dispersion(float kMag, float depth) -{ - return sqrt(G * kMag * tanh(min(kMag * depth, 20))); -} - float DispersionDerivative(float kMag, float depth) { float th = tanh(min(kMag * depth, 20)); @@ -146,7 +141,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float halfN = SPECTRUM_TEX_SIZE / 2.0f; - float deltaK = 2.0f * PI / LENGTH_SCALE; + float deltaK = 2.0f * PI / params.lengthScale; float2 K = (thread_id.xy - halfN) * deltaK; float kLength = length(K); diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index bc2132607e..c2b803aedc 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -885,6 +885,7 @@ void Properties::ShowMaterial(Material* material) const show_jonswap_params("Foam Add", "", OceanParameters::FoamAdd); show_jonswap_params("Displacement Scale", "", OceanParameters::DisplacementScale); show_jonswap_params("Slope Scale", "", OceanParameters::SlopeScale); + show_jonswap_params("Length Scale", "", OceanParameters::LengthScale); int tile_count = material->GetOceanTileCount(); ImGui::InputInt("Ocean Tiles", &tile_count); diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 3e75eb4a62..9eaa8bf475 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -342,6 +342,7 @@ namespace spartan material->SetOceanProperty(OceanParameters::DisplacementScale, 1.0f); material->SetOceanProperty(OceanParameters::SlopeScale, 1.0f); + material->SetOceanProperty(OceanParameters::LengthScale, 64.0f); } } diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index b353d002b3..15c8a711db 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -128,6 +128,7 @@ namespace spartan case spartan::OceanParameters::FoamBias: return "foam_bias"; case spartan::OceanParameters::DisplacementScale: return "displacement_scale"; case spartan::OceanParameters::SlopeScale: return "slope_scale"; + case spartan::OceanParameters::LengthScale: return "length_scale"; case spartan::OceanParameters::Max: return "max"; default: { diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 896609e704..e5787aa7b9 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -139,6 +139,7 @@ namespace spartan DisplacementScale, SlopeScale, + LengthScale, Max }; diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index be8992ea3f..2e054611e2 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -846,6 +846,7 @@ namespace spartan properties[count].jonswap_parameters.foamAdd = material->GetOceanProperty(OceanParameters::FoamAdd); properties[count].jonswap_parameters.displacementScale = material->GetOceanProperty(OceanParameters::DisplacementScale); properties[count].jonswap_parameters.slopeScale = material->GetOceanProperty(OceanParameters::SlopeScale); + properties[count].jonswap_parameters.lengthScale = material->GetOceanProperty(OceanParameters::LengthScale); // flags properties[count].flags = material->HasTextureOfType(MaterialTextureType::Height) ? (1U << 0) : 0; diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index 7279e75c57..1bb33cf382 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -209,6 +209,7 @@ namespace spartan float displacementScale; float slopeScale; + float lengthScale; } jonswap_parameters; }; From 8b0729c26214d7b680cab16135aeffe46cf2f7ad Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 24 Sep 2025 13:34:52 +0200 Subject: [PATCH 079/133] [ocean] updated default parameters --- source/runtime/Game/Game.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 9eaa8bf475..73623fbdd3 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -321,12 +321,12 @@ namespace spartan material->SetOceanProperty(OceanParameters::Alpha, 0.0f); // handled internally material->SetOceanProperty(OceanParameters::PeakOmega, 0.0f); // handled internally - material->SetOceanProperty(OceanParameters::Scale, 0.01f); + material->SetOceanProperty(OceanParameters::Scale, 1.0f); material->SetOceanProperty(OceanParameters::SpreadBlend, 0.9f); material->SetOceanProperty(OceanParameters::Swell, 1.0f); - material->SetOceanProperty(OceanParameters::Fetch, 10000.0f); - material->SetOceanProperty(OceanParameters::WindDirection, 90.0f); - material->SetOceanProperty(OceanParameters::WindSpeed, 100.0f); + material->SetOceanProperty(OceanParameters::Fetch, 100000.0f); + material->SetOceanProperty(OceanParameters::WindDirection, 135.0f); + material->SetOceanProperty(OceanParameters::WindSpeed, 3.0f); material->SetOceanProperty(OceanParameters::Gamma, 3.3f); material->SetOceanProperty(OceanParameters::ShortWavesFade, 0.0f); material->SetOceanProperty(OceanParameters::RepeatTime, 200.0f); @@ -336,13 +336,13 @@ namespace spartan material->SetOceanProperty(OceanParameters::HighCutoff, 1000.0f); material->SetOceanProperty(OceanParameters::FoamDecayRate, 3.0f); - material->SetOceanProperty(OceanParameters::FoamThreshold, -1.8f); - material->SetOceanProperty(OceanParameters::FoamBias, 0.2f); - material->SetOceanProperty(OceanParameters::FoamAdd, 0.12f); + material->SetOceanProperty(OceanParameters::FoamThreshold, 0.0f); + material->SetOceanProperty(OceanParameters::FoamBias, 1.2f); + material->SetOceanProperty(OceanParameters::FoamAdd, 1.0f); - material->SetOceanProperty(OceanParameters::DisplacementScale, 1.0f); - material->SetOceanProperty(OceanParameters::SlopeScale, 1.0f); - material->SetOceanProperty(OceanParameters::LengthScale, 64.0f); + material->SetOceanProperty(OceanParameters::DisplacementScale, 0.8f); + material->SetOceanProperty(OceanParameters::SlopeScale, 0.8f); + material->SetOceanProperty(OceanParameters::LengthScale, 32.0f); } } From 76cf10415b7821398e6898411a8cb207489cb147 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 24 Sep 2025 14:45:12 +0200 Subject: [PATCH 080/133] [ocean] move simulation from per tile to per ocean grid --- source/runtime/Game/Game.cpp | 3 +++ source/runtime/Rendering/Renderer_Passes.cpp | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 73623fbdd3..817781f66b 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -302,6 +302,7 @@ namespace spartan Entity* water = World::CreateEntity(); water->SetObjectName("ocean"); water->SetPosition(position); + water->SetScale({ 1.0f, 1.0f, 1.0f }); // material { @@ -359,7 +360,9 @@ namespace spartan // create mesh if it doesn't exist shared_ptr mesh = meshes.emplace_back(make_shared()); mesh->SetObjectName(name); + mesh->SetRootEntity(water); mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); + mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false); mesh->AddGeometry(vertices, indices, false); mesh->CreateGpuBuffers(); diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 399d9c5f54..1d5eea56ba 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -117,6 +117,7 @@ namespace spartan bool is_transparent = true; // Ocean Passes + Material* prev_material = nullptr; for (uint32_t i = 0; i < m_draw_call_count; i++) { const Renderer_DrawCall& draw_call = m_draw_calls[i]; @@ -124,9 +125,11 @@ namespace spartan Material* material = renderable->GetMaterial(); // get ocean material - if (!material->IsOcean()) + if (!material->IsOcean() || material == prev_material) continue; + prev_material = material; + if (material->ShouldComputeSpectrum()) { SP_LOG_INFO("Computing Ocean Spectrum..."); From dee25633dd171428c31b85c335101f381f5293bd Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 24 Sep 2025 21:42:49 +0200 Subject: [PATCH 081/133] [ocean] removing tiling wip --- data/shaders/g_buffer.hlsl | 67 +++++++++++++++++++- source/runtime/Rendering/Renderer_Passes.cpp | 2 +- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 145ad5c33e..44533b01e4 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -99,13 +99,78 @@ static float4 sample_texture(gbuffer_vertex vertex, uint texture_index, Surface return color; } +static const float2 hexRatio = float2(1.0f, sqrt(3.0f)); + +float4 GetHexGridInfo(float2 uv) +{ + float4 hexIndex = round(float4(uv, uv - float2(0.5f, 1.0f)) / hexRatio.xyxy); + float4 hexCenter = float4(hexIndex.xy * hexRatio, (hexIndex.zw + 0.5f) * hexRatio); + float4 offset = uv.xyxy - hexCenter; + return dot(offset.xy, offset.xy) < dot(offset.zw, offset.zw) ? + float4(hexCenter.xy, hexIndex.xy) : + float4(hexCenter.zw, hexIndex.zw); +} + +float GetHexSDF(in float2 p) +{ + p = abs(p); + return 0.5f - max(dot(p, hexRatio * 0.5f), p.x); +} + +//xy: node pos, z: weight +float3 GetTriangleInterpNode(in float2 pos, in float freq, in int nodeIndex) +{ + float2 nodeOffsets[3] = + { + float2(0.0f, 0.0f), + float2(1.0f, 1.0f), + float2(1.0f, -1.0f) + }; + + float2 uv = pos * freq + nodeOffsets[nodeIndex] / hexRatio.xy * 0.5f; + float4 hexInfo = GetHexGridInfo(uv); + float dist = GetHexSDF(uv - hexInfo.xy) * 2.0f; + return float3(hexInfo.xy / freq, dist); +} + +float3 hash33(float3 p) +{ + p = float3(dot(p, float3(127.1f, 311.7f, 74.7f)), + dot(p, float3(269.5f, 183.3f, 246.1f)), + dot(p, float3(113.5f, 271.9f, 124.6f))); + + return frac(sin(p) * 43758.5453123f); +} + +float4 GetTextureSample(Texture2D texture, float2 pos, float freq, float2 nodePoint) +{ + const float3 hash = hash33(float3(nodePoint.xy, 0.0f)); + const float ang = hash.x * 2.0f * 3.14159265f; + + const float2x2 rotation = float2x2( + cos(ang), sin(ang), + -sin(ang), cos(ang) + ); + + const float2 uv = mul(rotation, pos * freq) + hash.yz; + + return texture.SampleLevel(samplers[sampler_point_clamp], uv, 0); +} gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { MaterialParameters material = GetMaterial(); - input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; + //input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; + float3 displacement; + for (int i = 0; i < 3; i++) + { + float3 interpNode = GetTriangleInterpNode(input.uv, 20.0f, i); + displacement += GetTextureSample(displacement_map, input.uv, 10.0f, interpNode.xy) * interpNode.z; + } + input.position.xyz += displacement; + float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0) * material.ocean_parameters.slopeScale; input.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 1d5eea56ba..6648de175f 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -155,7 +155,7 @@ namespace spartan Pass_Light_ImageBased(cmd_list_graphics_present); Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); - Pass_ApplyFoam(cmd_list_graphics_present); + //Pass_ApplyFoam(cmd_list_graphics_present); Pass_AA_Upscale(cmd_list_graphics_present); Pass_PostProcess(cmd_list_graphics_present); From 57870e83e6bbbd5b94e343dae6b5214840996a58 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 25 Sep 2025 10:27:58 +0200 Subject: [PATCH 082/133] [ocean] mip mapped displacement and slope maps --- source/runtime/Rendering/Renderer_Passes.cpp | 2 ++ source/runtime/Rendering/Renderer_Resources.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 6648de175f..bd799ffe69 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -1239,6 +1239,8 @@ namespace spartan // for the lifetime of the engine, this will be read as an srv, so transition here //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + Pass_Downscale(cmd_list, displacement_map, Renderer_DownsampleFilter::Max); + slope_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } cmd_list->EndTimeblock(); diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 54fd9fe1f3..1f9ab919a3 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -235,9 +235,9 @@ namespace spartan render_target(Renderer_RenderTarget::ocean_slope_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_spectrum"); - render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_map"); + render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_displacement_map"); - render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_map"); + render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_slope_map"); } // occlusion From 72bc076d31728aa36572fa817d114a040a5331c2 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 25 Sep 2025 16:36:59 +0200 Subject: [PATCH 083/133] [ocean] texture synthesis progress --- data/shaders/g_buffer.hlsl | 70 +---------- data/shaders/ocean/common_ocean.hlsl | 11 +- data/shaders/ocean/synthesise_maps.hlsl | 112 ++++++++++++++++++ .../runtime/Rendering/Renderer_Definitions.h | 13 +- source/runtime/Rendering/Renderer_Passes.cpp | 33 +++++- .../runtime/Rendering/Renderer_Resources.cpp | 5 + 6 files changed, 163 insertions(+), 81 deletions(-) create mode 100644 data/shaders/ocean/synthesise_maps.hlsl diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 44533b01e4..c31f241e08 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -29,7 +29,7 @@ struct gbuffer float2 velocity : SV_Target3; }; -Texture2D displacement_map : register(t17); +//Texture2D displacement_map : register(t17); Texture2D slope_map : register(t18); // rotate UV around center (0.5, 0.5) by angle @@ -99,77 +99,11 @@ static float4 sample_texture(gbuffer_vertex vertex, uint texture_index, Surface return color; } -static const float2 hexRatio = float2(1.0f, sqrt(3.0f)); - -float4 GetHexGridInfo(float2 uv) -{ - float4 hexIndex = round(float4(uv, uv - float2(0.5f, 1.0f)) / hexRatio.xyxy); - float4 hexCenter = float4(hexIndex.xy * hexRatio, (hexIndex.zw + 0.5f) * hexRatio); - float4 offset = uv.xyxy - hexCenter; - return dot(offset.xy, offset.xy) < dot(offset.zw, offset.zw) ? - float4(hexCenter.xy, hexIndex.xy) : - float4(hexCenter.zw, hexIndex.zw); -} - -float GetHexSDF(in float2 p) -{ - p = abs(p); - return 0.5f - max(dot(p, hexRatio * 0.5f), p.x); -} - -//xy: node pos, z: weight -float3 GetTriangleInterpNode(in float2 pos, in float freq, in int nodeIndex) -{ - float2 nodeOffsets[3] = - { - float2(0.0f, 0.0f), - float2(1.0f, 1.0f), - float2(1.0f, -1.0f) - }; - - float2 uv = pos * freq + nodeOffsets[nodeIndex] / hexRatio.xy * 0.5f; - float4 hexInfo = GetHexGridInfo(uv); - float dist = GetHexSDF(uv - hexInfo.xy) * 2.0f; - return float3(hexInfo.xy / freq, dist); -} - -float3 hash33(float3 p) -{ - p = float3(dot(p, float3(127.1f, 311.7f, 74.7f)), - dot(p, float3(269.5f, 183.3f, 246.1f)), - dot(p, float3(113.5f, 271.9f, 124.6f))); - - return frac(sin(p) * 43758.5453123f); -} - -float4 GetTextureSample(Texture2D texture, float2 pos, float freq, float2 nodePoint) -{ - const float3 hash = hash33(float3(nodePoint.xy, 0.0f)); - const float ang = hash.x * 2.0f * 3.14159265f; - - const float2x2 rotation = float2x2( - cos(ang), sin(ang), - -sin(ang), cos(ang) - ); - - const float2 uv = mul(rotation, pos * freq) + hash.yz; - - return texture.SampleLevel(samplers[sampler_point_clamp], uv, 0); -} - gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { MaterialParameters material = GetMaterial(); - //input.position.xyz += displacement_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; - float3 displacement; - for (int i = 0; i < 3; i++) - { - float3 interpNode = GetTriangleInterpNode(input.uv, 20.0f, i); - displacement += GetTextureSample(displacement_map, input.uv, 10.0f, interpNode.xy) * interpNode.z; - } - - input.position.xyz += displacement; + input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0) * material.ocean_parameters.slopeScale; input.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); diff --git a/data/shaders/ocean/common_ocean.hlsl b/data/shaders/ocean/common_ocean.hlsl index 30ac8ff8f0..b1d05f3f39 100644 --- a/data/shaders/ocean/common_ocean.hlsl +++ b/data/shaders/ocean/common_ocean.hlsl @@ -26,11 +26,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. static const uint SPECTRUM_TEX_SIZE = 512; -RWTexture2D initial_spectrum : register(u9); -RWTexture2D displacement_spectrum : register(u10); -RWTexture2D slope_spectrum : register(u11); -RWTexture2D displacement_map : register(u12); -RWTexture2D slope_map : register(u13); +RWTexture2D initial_spectrum : register(u9); +RWTexture2D displacement_spectrum : register(u10); +RWTexture2D slope_spectrum : register(u11); +RWTexture2D displacement_map : register(u12); +RWTexture2D slope_map : register(u13); +RWTexture2D synthesised_displacement : register(u14); float Dispersion(float kMag, float depth) { diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl new file mode 100644 index 0000000000..136fa5c614 --- /dev/null +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -0,0 +1,112 @@ +/* +Copyright(c) 2025 George Bolba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "common_ocean.hlsl" + +static const float2 hexRatio = float2(1.0f, sqrt(3.0f)); + +float4 GetHexGridInfo(float2 uv) +{ + float4 hexIndex = round(float4(uv, uv - float2(0.5f, 1.0f)) / hexRatio.xyxy); + float4 hexCenter = float4(hexIndex.xy * hexRatio, (hexIndex.zw + 0.5f) * hexRatio); + float4 offset = uv.xyxy - hexCenter; + return dot(offset.xy, offset.xy) < dot(offset.zw, offset.zw) ? + float4(hexCenter.xy, hexIndex.xy) : + float4(hexCenter.zw, hexIndex.zw); +} + +float GetHexSDF(in float2 p) +{ + p = abs(p); + return 0.5f - max(dot(p, hexRatio * 0.5f), p.x); +} + +//xy: node pos, z: weight +float3 GetTriangleInterpNode(in float2 pos, in float freq, in int nodeIndex) +{ + float2 nodeOffsets[3] = + { + float2(0.0f, 0.0f), + float2(1.0f, 1.0f), + float2(1.0f, -1.0f) + }; + + float2 uv = pos * freq + nodeOffsets[nodeIndex] / hexRatio.xy * 0.5f; + float4 hexInfo = GetHexGridInfo(uv); + float dist = GetHexSDF(uv - hexInfo.xy) * 2.0f; + return float3(hexInfo.xy / freq, dist); +} + +float3 hash33(float3 p) +{ + p = float3(dot(p, float3(127.1f, 311.7f, 74.7f)), + dot(p, float3(269.5f, 183.3f, 246.1f)), + dot(p, float3(113.5f, 271.9f, 124.6f))); + + return frac(sin(p) * 43758.5453123f); +} + +float4 GetTextureSample(Texture2D texture, float2 pos, float freq, float2 nodePoint) +{ + const float3 hash = hash33(float3(nodePoint.xy, 0.0f)); + const float ang = hash.x * 2.0f * 3.14159265f; + + const float2x2 rotation = float2x2( + cos(ang), sin(ang), + -sin(ang), cos(ang) + ); + + float2 uv = mul(pos * freq, rotation) + hash.yz; + uv = pos * freq + hash.yz; + return texture.SampleLevel(samplers[sampler_point_wrap], uv, 0); +} + +float3 PreserveVariance(float3 linearColor, float3 meanColor, float moment2) +{ + return (linearColor - meanColor) / sqrt(moment2) + meanColor; +} + +[numthreads(8, 8, 1)] +void main_cs(uint3 thread_id : SV_DispatchThreadID) +{ + const uint2 pixel_coord = thread_id.xy; + uint2 texture_size; + synthesised_displacement.GetDimensions(texture_size.x, texture_size.y); + + float2 uv = (pixel_coord + 0.5f) / texture_size; + + const float tex_freq = 0.5f; + const float tile_freq = 2.0f; + + float3 output = float3(0.0f, 0.0f, 0.0f); + float moment2 = 0.0f; + for (int i = 0; i < 3; i++) + { + float3 interp_node = GetTriangleInterpNode(uv, tile_freq, i); + // tex2 = displacement_map as SRV + output.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy) * interp_node.z; + + moment2 = interp_node.z * interp_node.z; + } + const float3 mean_color = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 9); + + synthesised_displacement[thread_id.xy] = float4(output, 1.0f); +} diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 1819bfd9cc..b4ca6210ae 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -130,11 +130,12 @@ namespace spartan visibility = 6, sb_spd = 7, tex_spd = 8, - ocean_initial_spectrum = 9, - ocean_displacement_spectrum = 10, - ocean_slope_spectrum = 11, - ocean_displacement_map = 12, - ocean_slope_map = 13 + ocean_initial_spectrum = 9, + ocean_displacement_spectrum = 10, + ocean_slope_spectrum = 11, + ocean_displacement_map = 12, + ocean_slope_map = 13, + ocean_synthesised_displacement = 14 }; enum class Renderer_Shader : uint8_t @@ -195,6 +196,7 @@ namespace spartan ocean_horizontal_fft_c, ocean_vertical_fft_c, ocean_generate_maps_c, + ocean_synthesise_maps_c, ocean_foam_v, ocean_foam_p, max @@ -236,6 +238,7 @@ namespace spartan ocean_slope_spectrum, ocean_displacement_map, ocean_slope_map, + ocean_synthesised_displacement, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index bd799ffe69..6c98755227 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -670,6 +670,33 @@ namespace spartan } } + // displacement map synthesis + if (material->IsOcean()) + { + cmd_list->BeginTimeblock("Ocean Displacement Map Synthesis"); + + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + RHI_Texture* synthesised_displacement = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); + + displacement_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list, rhi_all_mips, 10); + + RHI_PipelineState pso2; + pso2.name = "ocean_displacement_map_synthesis"; + pso2.shaders[Compute] = GetShader(Renderer_Shader::ocean_synthesise_maps_c); + cmd_list->SetPipelineState(pso2); + + cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_displacement, synthesised_displacement); + + cmd_list->Dispatch(synthesised_displacement); + + //synthesised_displacement->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + + cmd_list->EndTimeblock(); + } + + cmd_list->SetPipelineState(pso); + // pass constants { Entity* entity = renderable->GetEntity(); @@ -691,8 +718,8 @@ namespace spartan { if (!material->GetShowDisplacement() && !material->GetShowSlope()) { - RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); - cmd_list->SetTexture(17, displacement_map); + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); + cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); cmd_list->SetTexture(18, slope_map); @@ -1239,7 +1266,7 @@ namespace spartan // for the lifetime of the engine, this will be read as an srv, so transition here //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); - Pass_Downscale(cmd_list, displacement_map, Renderer_DownsampleFilter::Max); + Pass_Downscale(cmd_list, displacement_map, Renderer_DownsampleFilter::Min); slope_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 1f9ab919a3..3d82a9f9f2 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -238,6 +238,8 @@ namespace spartan render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_displacement_map"); render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_slope_map"); + + render_target(Renderer_RenderTarget::ocean_synthesised_displacement) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_synthesised_displacement_map"); } // occlusion @@ -410,6 +412,9 @@ namespace spartan shader(Renderer_Shader::ocean_generate_maps_c) = make_shared(); shader(Renderer_Shader::ocean_generate_maps_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\generate_maps.hlsl", false); + shader(Renderer_Shader::ocean_synthesise_maps_c) = make_shared(); + shader(Renderer_Shader::ocean_synthesise_maps_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\synthesise_maps.hlsl", false); + shader(Renderer_Shader::ocean_foam_v) = make_shared(); shader(Renderer_Shader::ocean_foam_v)->Compile(RHI_Shader_Type::Vertex, shader_dir + "ocean\\foam.hlsl", async, RHI_Vertex_Type::PosUvNorTan); From 4fd12cf052ea6e2cbfef835fd88f37d5e233f3cd Mon Sep 17 00:00:00 2001 From: George Bolba Date: Fri, 26 Sep 2025 15:00:26 +0200 Subject: [PATCH 084/133] [ocean] fixed variance preserving by incrementing the moment --- data/shaders/ocean/synthesise_maps.hlsl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 136fa5c614..9c4510cfd0 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -93,8 +93,8 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float2 uv = (pixel_coord + 0.5f) / texture_size; - const float tex_freq = 0.5f; - const float tile_freq = 2.0f; + const float tex_freq = 1.0f; + const float tile_freq = 1.0f; float3 output = float3(0.0f, 0.0f, 0.0f); float moment2 = 0.0f; @@ -102,11 +102,12 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) { float3 interp_node = GetTriangleInterpNode(uv, tile_freq, i); // tex2 = displacement_map as SRV - output.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy) * interp_node.z; + output.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy).rgb * interp_node.z; - moment2 = interp_node.z * interp_node.z; + moment2 += interp_node.z * interp_node.z; } - const float3 mean_color = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 9); + const float3 mean_color = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 9).rgb; - synthesised_displacement[thread_id.xy] = float4(output, 1.0f); + synthesised_displacement[thread_id.xy] = float4(PreserveVariance(output, mean_color, moment2), 1.0f); + //synthesised_displacement[thread_id.xy] = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 0); } From 934062f987f9b3f779be802376bb50296cc548fa Mon Sep 17 00:00:00 2001 From: George Bolba Date: Fri, 26 Sep 2025 15:01:53 +0200 Subject: [PATCH 085/133] [ocean] added texture synthesis shader inspiration --- data/shaders/ocean/synthesise_maps.hlsl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 9c4510cfd0..72110644f6 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -21,6 +21,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "common_ocean.hlsl" +// Inspired by https://www.shadertoy.com/view/tsVGRd + static const float2 hexRatio = float2(1.0f, sqrt(3.0f)); float4 GetHexGridInfo(float2 uv) From f6d50142a3c69cf28f3dcf36eddbca1dd16a197f Mon Sep 17 00:00:00 2001 From: George Bolba Date: Fri, 26 Sep 2025 15:05:56 +0200 Subject: [PATCH 086/133] [ocean] removed rotation in texture synthesis shader --- data/shaders/ocean/synthesise_maps.hlsl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 72110644f6..9f04557431 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -69,14 +69,8 @@ float3 hash33(float3 p) float4 GetTextureSample(Texture2D texture, float2 pos, float freq, float2 nodePoint) { const float3 hash = hash33(float3(nodePoint.xy, 0.0f)); - const float ang = hash.x * 2.0f * 3.14159265f; - - const float2x2 rotation = float2x2( - cos(ang), sin(ang), - -sin(ang), cos(ang) - ); - float2 uv = mul(pos * freq, rotation) + hash.yz; + float2 uv = pos * freq + hash.yz; uv = pos * freq + hash.yz; return texture.SampleLevel(samplers[sampler_point_wrap], uv, 0); } From 1b8996f62d9e710befb8fc653cf7db08ec32da21 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Fri, 26 Sep 2025 15:46:27 +0200 Subject: [PATCH 087/133] [ocean] apply synthesis to slope map --- data/shaders/g_buffer.hlsl | 2 +- data/shaders/ocean/common_ocean.hlsl | 1 + data/shaders/ocean/synthesise_maps.hlsl | 14 ++++++++++---- source/runtime/Rendering/Renderer_Definitions.h | 4 +++- source/runtime/Rendering/Renderer_Passes.cpp | 15 +++++++++------ source/runtime/Rendering/Renderer_Resources.cpp | 2 ++ 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index c31f241e08..f97344348b 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -105,7 +105,7 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; - float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], input.uv, 0) * material.ocean_parameters.slopeScale; + float4 slope = tex3.SampleLevel(samplers[sampler_point_clamp], input.uv, 0) * material.ocean_parameters.slopeScale; input.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform); diff --git a/data/shaders/ocean/common_ocean.hlsl b/data/shaders/ocean/common_ocean.hlsl index b1d05f3f39..eaa482a7e8 100644 --- a/data/shaders/ocean/common_ocean.hlsl +++ b/data/shaders/ocean/common_ocean.hlsl @@ -32,6 +32,7 @@ RWTexture2D slope_spectrum : register(u11); RWTexture2D displacement_map : register(u12); RWTexture2D slope_map : register(u13); RWTexture2D synthesised_displacement : register(u14); +RWTexture2D synthesised_slope : register(u15); float Dispersion(float kMag, float depth) { diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 9f04557431..f86d2cfe5b 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -92,18 +92,24 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) const float tex_freq = 1.0f; const float tile_freq = 1.0f; - float3 output = float3(0.0f, 0.0f, 0.0f); + float3 displacement = float3(0.0f, 0.0f, 0.0f); + float3 slope = float3(0.0f, 0.0f, 0.0f); float moment2 = 0.0f; for (int i = 0; i < 3; i++) { float3 interp_node = GetTriangleInterpNode(uv, tile_freq, i); // tex2 = displacement_map as SRV - output.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy).rgb * interp_node.z; + displacement.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy).rgb * interp_node.z; + slope.xyz += GetTextureSample(tex3, uv, tex_freq, interp_node.xy).rgb * interp_node.z; moment2 += interp_node.z * interp_node.z; } - const float3 mean_color = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 9).rgb; + const float3 mean_displacement = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 9).rgb; + const float3 mean_slope = tex3.SampleLevel(samplers[sampler_point_clamp], uv, 9).rgb; - synthesised_displacement[thread_id.xy] = float4(PreserveVariance(output, mean_color, moment2), 1.0f); + synthesised_displacement[thread_id.xy] = float4(PreserveVariance(displacement, mean_displacement, moment2), 1.0f); + synthesised_slope[thread_id.xy] = float4(PreserveVariance(slope, mean_slope, moment2), tex3[thread_id.xy].a); + + //synthesised_slope[thread_id.xy] = tex3.SampleLevel(samplers[sampler_point_clamp], uv, 0); //synthesised_displacement[thread_id.xy] = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 0); } diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index b4ca6210ae..c270a3d5f4 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -135,7 +135,8 @@ namespace spartan ocean_slope_spectrum = 11, ocean_displacement_map = 12, ocean_slope_map = 13, - ocean_synthesised_displacement = 14 + ocean_synthesised_displacement = 14, + ocean_synthesised_slope = 15 }; enum class Renderer_Shader : uint8_t @@ -239,6 +240,7 @@ namespace spartan ocean_displacement_map, ocean_slope_map, ocean_synthesised_displacement, + ocean_synthesised_slope, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 6c98755227..f3c288da9b 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -676,7 +676,10 @@ namespace spartan cmd_list->BeginTimeblock("Ocean Displacement Map Synthesis"); RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + RHI_Texture* synthesised_displacement = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); + RHI_Texture* synthesised_slope = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_slope); displacement_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list, rhi_all_mips, 10); @@ -686,11 +689,13 @@ namespace spartan cmd_list->SetPipelineState(pso2); cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_displacement, synthesised_displacement); + cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_slope, synthesised_slope); cmd_list->Dispatch(synthesised_displacement); - //synthesised_displacement->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + synthesised_slope->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); cmd_list->EndTimeblock(); } @@ -721,8 +726,8 @@ namespace spartan RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); - RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); - cmd_list->SetTexture(18, slope_map); + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_slope); + cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); } } @@ -1263,10 +1268,8 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_map, slope_map); cmd_list->Dispatch(displacement_map); - // for the lifetime of the engine, this will be read as an srv, so transition here - //initial_spectrum->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); - Pass_Downscale(cmd_list, displacement_map, Renderer_DownsampleFilter::Min); + Pass_Downscale(cmd_list, slope_map, Renderer_DownsampleFilter::Min); slope_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 3d82a9f9f2..71fd9fff9c 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -240,6 +240,8 @@ namespace spartan render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_slope_map"); render_target(Renderer_RenderTarget::ocean_synthesised_displacement) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_synthesised_displacement_map"); + + render_target(Renderer_RenderTarget::ocean_synthesised_slope) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_synthesised_slope_map"); } // occlusion From 5c60e7356fbccb821fde6304bc944d80aef3d309 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 29 Sep 2025 10:58:39 +0200 Subject: [PATCH 088/133] [ocean] fixed slope synthesis and brought back foam --- data/shaders/ocean/synthesise_maps.hlsl | 16 +++++++++------- source/runtime/Rendering/Renderer_Passes.cpp | 10 +++++----- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index f86d2cfe5b..27876da69f 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -80,6 +80,11 @@ float3 PreserveVariance(float3 linearColor, float3 meanColor, float moment2) return (linearColor - meanColor) / sqrt(moment2) + meanColor; } +float4 PreserveVariance(float4 linearColor, float4 meanColor, float moment2) +{ + return (linearColor - meanColor) / sqrt(moment2) + meanColor; +} + [numthreads(8, 8, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { @@ -93,23 +98,20 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) const float tile_freq = 1.0f; float3 displacement = float3(0.0f, 0.0f, 0.0f); - float3 slope = float3(0.0f, 0.0f, 0.0f); + float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); float moment2 = 0.0f; for (int i = 0; i < 3; i++) { float3 interp_node = GetTriangleInterpNode(uv, tile_freq, i); // tex2 = displacement_map as SRV displacement.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy).rgb * interp_node.z; - slope.xyz += GetTextureSample(tex3, uv, tex_freq, interp_node.xy).rgb * interp_node.z; + slope += GetTextureSample(tex3, uv, tex_freq, interp_node.xy) * interp_node.z; moment2 += interp_node.z * interp_node.z; } const float3 mean_displacement = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 9).rgb; - const float3 mean_slope = tex3.SampleLevel(samplers[sampler_point_clamp], uv, 9).rgb; + const float4 mean_slope = tex3.SampleLevel(samplers[sampler_point_clamp], uv, 9); synthesised_displacement[thread_id.xy] = float4(PreserveVariance(displacement, mean_displacement, moment2), 1.0f); - synthesised_slope[thread_id.xy] = float4(PreserveVariance(slope, mean_slope, moment2), tex3[thread_id.xy].a); - - //synthesised_slope[thread_id.xy] = tex3.SampleLevel(samplers[sampler_point_clamp], uv, 0); - //synthesised_displacement[thread_id.xy] = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 0); + synthesised_slope[thread_id.xy] = PreserveVariance(slope, mean_slope, moment2); } diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index f3c288da9b..1fee4ee71a 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -155,7 +155,7 @@ namespace spartan Pass_Light_ImageBased(cmd_list_graphics_present); Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); - //Pass_ApplyFoam(cmd_list_graphics_present); + Pass_ApplyFoam(cmd_list_graphics_present); Pass_AA_Upscale(cmd_list_graphics_present); Pass_PostProcess(cmd_list_graphics_present); @@ -1268,8 +1268,8 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_map, slope_map); cmd_list->Dispatch(displacement_map); - Pass_Downscale(cmd_list, displacement_map, Renderer_DownsampleFilter::Min); - Pass_Downscale(cmd_list, slope_map, Renderer_DownsampleFilter::Min); + Pass_Downscale(cmd_list, displacement_map, Renderer_DownsampleFilter::Average); + Pass_Downscale(cmd_list, slope_map, Renderer_DownsampleFilter::Average); slope_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); } @@ -1281,8 +1281,8 @@ namespace spartan RHI_Texture* tex_depth = GetRenderTarget(Renderer_RenderTarget::gbuffer_depth); RHI_Texture* tex_out = GetRenderTarget(Renderer_RenderTarget::frame_render); - RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); - RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_slope); tex_out->SetLayout(RHI_Image_Layout::General, cmd_list); From ef87b743891e1d7b35da2d0a47a4bd16d014edc9 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 29 Sep 2025 11:14:45 +0200 Subject: [PATCH 089/133] [ocean] updated lighting --- source/runtime/Game/Game.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 817781f66b..c3210f768a 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1710,12 +1710,12 @@ namespace spartan default_ocean->SetParent(entity); auto light_entity = World::CreateEntity(); - light_entity->SetPosition({ 10.0f, 10.0f, 10.0f }); + light_entity->SetPosition({ 10.0f, 16.0f, 10.0f }); Light* point = light_entity->AddComponent(); point->SetLightType(LightType::Point); - point->SetRange(35.0f); - point->SetTemperature(3500.0f); + point->SetRange(50.0f); + point->SetTemperature(5500.0f); point->SetIntensity(8500.0f); point->SetObjectName("Point Light"); From cbb52b421a7af788df6aee8f86c86fb1d581239c Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 29 Sep 2025 18:37:06 +0200 Subject: [PATCH 090/133] [ocean] wip no tiling between ocean tiles --- data/shaders/ocean/synthesise_maps.hlsl | 9 +++++---- source/runtime/Rendering/Renderer_Passes.cpp | 11 +++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 27876da69f..093f74e52c 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -66,9 +66,9 @@ float3 hash33(float3 p) return frac(sin(p) * 43758.5453123f); } -float4 GetTextureSample(Texture2D texture, float2 pos, float freq, float2 nodePoint) +float4 GetTextureSample(Texture2D texture, float2 pos, float freq, float2 nodePoint, float z) { - const float3 hash = hash33(float3(nodePoint.xy, 0.0f)); + const float3 hash = hash33(float3(nodePoint.xy, z)); float2 uv = pos * freq + hash.yz; uv = pos * freq + hash.yz; @@ -104,8 +104,9 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) { float3 interp_node = GetTriangleInterpNode(uv, tile_freq, i); // tex2 = displacement_map as SRV - displacement.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy).rgb * interp_node.z; - slope += GetTextureSample(tex3, uv, tex_freq, interp_node.xy) * interp_node.z; + const float ocean_tile_index = pass_get_f2_value().x; + displacement.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy, ocean_tile_index).rgb * interp_node.z; + slope += GetTextureSample(tex3, uv, tex_freq, interp_node.xy, ocean_tile_index) * interp_node.z; moment2 += interp_node.z * interp_node.z; } diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 1fee4ee71a..5c2d02f2e7 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -155,7 +155,7 @@ namespace spartan Pass_Light_ImageBased(cmd_list_graphics_present); Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); - Pass_ApplyFoam(cmd_list_graphics_present); + //Pass_ApplyFoam(cmd_list_graphics_present); Pass_AA_Upscale(cmd_list_graphics_present); Pass_PostProcess(cmd_list_graphics_present); @@ -648,6 +648,8 @@ namespace spartan pso.clear_color[3] = is_transparent_pass ? rhi_color_load : Color::standard_transparent; cmd_list->SetPipelineState(pso); + // TEMPORARY + uint32_t tile_index = 0; for (uint32_t i = 0; i < m_draw_call_count; i++) { const Renderer_DrawCall& draw_call = m_draw_calls[i]; @@ -670,9 +672,11 @@ namespace spartan } } - // displacement map synthesis + // ocean synthesis if (material->IsOcean()) { + tile_index++; + cmd_list->BeginTimeblock("Ocean Displacement Map Synthesis"); RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); @@ -693,6 +697,9 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_displacement, synthesised_displacement); cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_slope, synthesised_slope); + m_pcb_pass_cpu.set_f2_value(static_cast(tile_index), 0.0f); + cmd_list->PushConstants(m_pcb_pass_cpu); + cmd_list->Dispatch(synthesised_displacement); synthesised_slope->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); From 31b36cebd9aaa0f63f8c2a78e52683fe6a1f829e Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 29 Sep 2025 21:43:22 +0200 Subject: [PATCH 091/133] [ocean] moved ocean normals to pixel shader of gbuffer --- data/shaders/g_buffer.hlsl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index f97344348b..dc87ffd6bf 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -105,9 +105,6 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; - float4 slope = tex3.SampleLevel(samplers[sampler_point_clamp], input.uv, 0) * material.ocean_parameters.slopeScale; - input.normal = normalize(float3(-slope.x, 1.0f, -slope.y)); - gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform); // transform world space position to clip space @@ -259,6 +256,11 @@ gbuffer main_ps(gbuffer_vertex vertex) float3x3 tangent_to_world = make_tangent_to_world_matrix(vertex.normal, vertex.tangent); normal = normalize(mul(tangent_normal, tangent_to_world).xyz); } + else if (surface.is_ocean()) + { + float4 slope = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv) * material.ocean_parameters.slopeScale; + normal = normalize(float3(-slope.x, 1.0f, -slope.y)); + } // occlusion, roughness, metalness, height sample { From 011567b7e7806aa3ae3224664a74083f5d8cc7ba Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 29 Sep 2025 22:47:17 +0200 Subject: [PATCH 092/133] [ocean] dirty way of displaying synthesised displacement --- data/shaders/g_buffer.hlsl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index dc87ffd6bf..5408d1438e 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -103,7 +103,7 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI { MaterialParameters material = GetMaterial(); - input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; + //input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform); @@ -260,6 +260,8 @@ gbuffer main_ps(gbuffer_vertex vertex) { float4 slope = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv) * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); + + albedo = tex2.SampleLevel(samplers[sampler_point_clamp], vertex.uv, 0).rgba * material.ocean_parameters.displacementScale; } // occlusion, roughness, metalness, height sample From 9226791e1dec8b4a5f46582219c0722492c397c2 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Tue, 30 Sep 2025 09:39:59 +0200 Subject: [PATCH 093/133] [ocean] removed physics bodies since water physics body has been removed in last upstream update --- source/runtime/Game/Game.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 78481fe571..4c9ba1334c 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -381,8 +381,8 @@ namespace spartan } // enable buoyancy - Physics* physics = entity_tile->AddComponent(); - physics->SetBodyType(BodyType::Water); + //Physics* physics = entity_tile->AddComponent(); + //physics->SetBodyType(BodyType::Water); } } } @@ -1798,8 +1798,8 @@ namespace spartan } // enable buoyancy - Physics* physics = entity_tile->AddComponent(); - physics->SetBodyType(BodyType::Water); + //Physics* physics = entity_tile->AddComponent(); + //physics->SetBodyType(BodyType::Water); } } } From 24d4f1ef36785c3a976ca1708efc24eeecfb3456 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Tue, 30 Sep 2025 16:44:41 +0200 Subject: [PATCH 094/133] [ocean] different settings and parameters --- data/shaders/g_buffer.hlsl | 6 +-- data/shaders/ocean/synthesise_maps.hlsl | 4 +- source/editor/Widgets/Properties.cpp | 19 ++++++++- source/runtime/Game/Game.cpp | 57 +++++++++++++++++++++---- source/runtime/Rendering/Material.h | 9 ++++ 5 files changed, 80 insertions(+), 15 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 888736cfe8..4eaebfe907 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -103,7 +103,7 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI { MaterialParameters material = GetMaterial(); - //input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; + input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; float3 position_world = 0.0f; float3 position_world_previous = 0.0f; @@ -275,10 +275,10 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) } else if (surface.is_ocean()) { - float4 slope = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv) * material.ocean_parameters.slopeScale; + float4 slope = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy) * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); - albedo = tex2.SampleLevel(samplers[sampler_point_clamp], vertex.uv, 0).rgba * material.ocean_parameters.displacementScale; + //albedo = tex2.SampleLevel(samplers[sampler_point_clamp], vertex.uv_misc.xy, 0).rgba * material.ocean_parameters.displacementScale; } diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 093f74e52c..e4cfb5b453 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -94,8 +94,8 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float2 uv = (pixel_coord + 0.5f) / texture_size; - const float tex_freq = 1.0f; - const float tile_freq = 1.0f; + const float tex_freq = 10.0f; + const float tile_freq = 5.0f; float3 displacement = float3(0.0f, 0.0f, 0.0f); float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 1ab6339783..51c5eba386 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -904,7 +904,24 @@ void Properties::ShowMaterial(Material* material) const int tile_count = material->GetOceanTileCount(); ImGui::InputInt("Ocean Tiles", &tile_count); - material->SetOceanTileCount(tile_count); + if (ImGui::IsItemDeactivatedAfterEdit()) + { + material->SetOceanTileCount(tile_count); + } + + float tile_size = material->GetOceanTileSize(); + ImGui::InputFloat("Ocean Tile Size", &tile_size); + if (ImGui::IsItemDeactivatedAfterEdit()) + { + material->SetOceanTileSize(tile_size); + } + + int vertices_count = material->GetOceanVerticesCount(); + ImGui::InputInt("Ocean Vertices Count", &vertices_count); + if (ImGui::IsItemDeactivatedAfterEdit()) + { + material->SetOceanVerticesCount(vertices_count); + } bool show_displacement = material->GetShowDisplacement(); ImGui::Checkbox("Show Displacement Map", &show_displacement); diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 4c9ba1334c..4395f2b716 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -287,7 +287,7 @@ namespace spartan return water; } - Entity* ocean(std::shared_ptr material, const Vector3& position, float dimension, uint32_t density, uint32_t grid_size) + Entity* ocean(std::shared_ptr material, const Vector3& position, float tile_size, uint32_t density, uint32_t grid_size) { // entity Entity* water = World::CreateEntity(); @@ -303,6 +303,9 @@ namespace spartan material->LoadFromFile(material->GetResourceFilePath()); material->SetOceanTileCount(grid_size); + material->SetOceanTileSize(tile_size); + material->SetOceanVerticesCount(density); + // if material fails to load from file if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) { @@ -344,7 +347,7 @@ namespace spartan const uint32_t grid_points_per_dimension = density; vector vertices; vector indices; - geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, dimension); + geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, tile_size); string name = "ocean mesh"; @@ -370,7 +373,7 @@ namespace spartan entity_tile->SetObjectName(tile_name); entity_tile->SetParent(water); - Vector3 tile_position = { col * dimension, 0.0f, row * dimension }; + Vector3 tile_position = { col * tile_size, 0.0f, row * tile_size }; entity_tile->SetPosition(tile_position); if (Renderable* renderable = entity_tile->AddComponent()) @@ -1719,18 +1722,19 @@ namespace spartan namespace ocean { - uint32_t ocean_tile_count = 2; - const float tile_size = 20.0f; + uint32_t ocean_tile_count = 1; + float tile_size = 512.0f; + uint32_t vertices_count = 512; shared_ptr material = make_shared(); void create() { entities::camera(); - //entities::sun(true); + entities::sun(true); auto entity = World::CreateEntity(); - default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }, tile_size, 512, ocean_tile_count); + default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }, tile_size, vertices_count, ocean_tile_count); default_ocean->SetParent(entity); @@ -1744,7 +1748,7 @@ namespace spartan point->SetIntensity(8500.0f); point->SetObjectName("Point Light"); - //default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } void tick() @@ -1753,7 +1757,7 @@ namespace spartan return; uint32_t current_tile_count = material->GetOceanTileCount(); - if (current_tile_count != ocean_tile_count) + if (current_tile_count != ocean_tile_count || tile_size != material->GetOceanTileSize() || vertices_count != material->GetOceanVerticesCount()) { ocean_tile_count = current_tile_count; auto& children = default_ocean->GetChildren(); @@ -1775,6 +1779,41 @@ namespace spartan if (ocean_mesh.get() == nullptr) return; + // regenerate mesh + if (tile_size != material->GetOceanTileSize() || vertices_count != material->GetOceanVerticesCount()) + { + tile_size = material->GetOceanTileSize(); + vertices_count = material->GetOceanVerticesCount(); + + // generate grid + const uint32_t grid_points_per_dimension = vertices_count; + vector vertices; + vector indices; + geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, tile_size); + + string name = "ocean mesh"; + + // create mesh if it doesn't exist + ocean_mesh->Clear(); + + for (std::vector>::iterator it = meshes.begin(); it != meshes.end();) + { + std::shared_ptr m = *it; + if (m->GetObjectName() == "ocean mesh") + it = meshes.erase(it); + else; + ++it; + } + + ocean_mesh = meshes.emplace_back(make_shared()); + ocean_mesh->SetObjectName(name); + ocean_mesh->SetRootEntity(default_ocean); + ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); + ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false); + ocean_mesh->AddGeometry(vertices, indices, false); + ocean_mesh->CreateGpuBuffers(); + } + for (uint32_t row = 0; row < current_tile_count; row++) { for (uint32_t col = 0; col < current_tile_count; col++) diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index ec225da2bb..e0592aca21 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -190,10 +190,16 @@ namespace spartan uint32_t GetUsedSlotCount() const; void SetIndex(const uint32_t index) { m_index = index; } uint32_t GetIndex() const { return m_index; } + + // ocean bool ShouldComputeSpectrum() const { return m_should_compute_spectrum; } void MarkSpectrumAsComputed() { m_should_compute_spectrum = false; } void SetOceanTileCount(const uint32_t count) { m_ocean_tiles = count; } uint32_t GetOceanTileCount() const { return m_ocean_tiles; } + void SetOceanVerticesCount(const uint32_t count) { m_ocean_vertices_count = count; } + uint32_t GetOceanVerticesCount() const { return m_ocean_vertices_count; } + void SetOceanTileSize(const float size) { m_ocean_tile_size = size; } + float GetOceanTileSize() const { return m_ocean_tile_size; } const std::array(MaterialProperty::Max)>& GetProperties() const { return m_properties; } const std::array(OceanParameters::Max)>& GetOceanProperties() const { return m_ocean_properties; } @@ -204,9 +210,12 @@ namespace spartan std::array(OceanParameters::Max)> m_ocean_properties; uint32_t m_index = 0; std::mutex m_mutex; + bool m_should_compute_spectrum = true; bool m_show_displacement = false; bool m_show_slope = false; uint32_t m_ocean_tiles = 1; + uint32_t m_ocean_vertices_count = 0; + float m_ocean_tile_size = 0.0f; }; } From bede03d676068e872d94402ddab182c249d53811 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 1 Oct 2025 17:03:19 +0200 Subject: [PATCH 095/133] [ocean] texture synthesis improvements wip --- data/shaders/ocean/synthesise_maps.hlsl | 15 ++++++- source/runtime/Game/Game.cpp | 46 ++++++++++---------- source/runtime/Rendering/Renderer_Passes.cpp | 7 +-- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index e4cfb5b453..c001a444f2 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -94,8 +94,16 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float2 uv = (pixel_coord + 0.5f) / texture_size; - const float tex_freq = 10.0f; - const float tile_freq = 5.0f; + const float3 pass_values = pass_get_f3_value(); + const float2 tile_xz_pos = pass_values.xy; + const float tile_size = pass_values.z; + + const float2 tile_origin_uv_space = float2(tile_xz_pos.x / tile_size, tile_xz_pos.y / tile_size); + + uv = tile_origin_uv_space + uv * tile_size; + + const float tex_freq = 2.0f; + const float tile_freq = 1.0f; float3 displacement = float3(0.0f, 0.0f, 0.0f); float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); @@ -115,4 +123,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) synthesised_displacement[thread_id.xy] = float4(PreserveVariance(displacement, mean_displacement, moment2), 1.0f); synthesised_slope[thread_id.xy] = PreserveVariance(slope, mean_slope, moment2); + + //synthesised_displacement[thread_id.xy] = tex2[thread_id.xy]; + synthesised_slope[thread_id.xy] = tex3[thread_id.xy]; } diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 4395f2b716..302a58380c 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -309,7 +309,7 @@ namespace spartan // if material fails to load from file if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) { - material->SetColor(Color(0.0f, 150.0f / 255.0f, 130.0f / 255.0f, 150.0f / 255.0f)); + material->SetColor(Color(0.0f, 142.0f / 255.0f, 229.0f / 255.0f, 254.0f / 255.0f)); material->SetProperty(MaterialProperty::IsOcean, 1.0f); material->SetOceanProperty(OceanParameters::Angle, 0.0f); //handled internally @@ -321,7 +321,7 @@ namespace spartan material->SetOceanProperty(OceanParameters::Swell, 1.0f); material->SetOceanProperty(OceanParameters::Fetch, 100000.0f); material->SetOceanProperty(OceanParameters::WindDirection, 135.0f); - material->SetOceanProperty(OceanParameters::WindSpeed, 3.0f); + material->SetOceanProperty(OceanParameters::WindSpeed, 5.0f); material->SetOceanProperty(OceanParameters::Gamma, 3.3f); material->SetOceanProperty(OceanParameters::ShortWavesFade, 0.0f); material->SetOceanProperty(OceanParameters::RepeatTime, 200.0f); @@ -335,9 +335,9 @@ namespace spartan material->SetOceanProperty(OceanParameters::FoamBias, 1.2f); material->SetOceanProperty(OceanParameters::FoamAdd, 1.0f); - material->SetOceanProperty(OceanParameters::DisplacementScale, 0.8f); - material->SetOceanProperty(OceanParameters::SlopeScale, 0.8f); - material->SetOceanProperty(OceanParameters::LengthScale, 32.0f); + material->SetOceanProperty(OceanParameters::DisplacementScale, 1.0f); + material->SetOceanProperty(OceanParameters::SlopeScale, 0.6f); + material->SetOceanProperty(OceanParameters::LengthScale, 48.0f); } } @@ -1722,15 +1722,15 @@ namespace spartan namespace ocean { - uint32_t ocean_tile_count = 1; - float tile_size = 512.0f; + uint32_t ocean_tile_count = 2; + float tile_size = 128.0f; uint32_t vertices_count = 512; shared_ptr material = make_shared(); void create() { entities::camera(); - entities::sun(true); + //entities::sun(true); auto entity = World::CreateEntity(); @@ -1739,16 +1739,16 @@ namespace spartan default_ocean->SetParent(entity); auto light_entity = World::CreateEntity(); - light_entity->SetPosition({ 10.0f, 16.0f, 10.0f }); + light_entity->SetPosition({ 64.0f, 180.0f, 64.0f }); Light* point = light_entity->AddComponent(); point->SetLightType(LightType::Point); - point->SetRange(50.0f); - point->SetTemperature(5500.0f); + point->SetRange(400.0f); + point->SetTemperature(100000.0f); point->SetIntensity(8500.0f); point->SetObjectName("Point Light"); - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + //default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } void tick() @@ -1791,25 +1791,25 @@ namespace spartan vector indices; geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, tile_size); - string name = "ocean mesh"; + //string name = "ocean mesh"; // create mesh if it doesn't exist ocean_mesh->Clear(); - for (std::vector>::iterator it = meshes.begin(); it != meshes.end();) - { - std::shared_ptr m = *it; - if (m->GetObjectName() == "ocean mesh") - it = meshes.erase(it); - else; - ++it; - } + //for (std::vector>::iterator it = meshes.begin(); it != meshes.end();) + //{ + // std::shared_ptr m = *it; + // if (m->GetObjectName() == "ocean mesh") + // it = meshes.erase(it); + // else; + // ++it; + //} - ocean_mesh = meshes.emplace_back(make_shared()); + /*ocean_mesh = meshes.emplace_back(make_shared()); ocean_mesh->SetObjectName(name); ocean_mesh->SetRootEntity(default_ocean); ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); - ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false); + ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false);*/ ocean_mesh->AddGeometry(vertices, indices, false); ocean_mesh->CreateGpuBuffers(); } diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 8648e3e175..34ff6c114e 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -648,8 +648,7 @@ namespace spartan pso.clear_color[3] = is_transparent_pass ? rhi_color_load : Color::standard_transparent; cmd_list->SetPipelineState(pso); - // TEMPORARY - uint32_t tile_index = 0; + uint32_t tile_index = 0; // TEMPORARY for (uint32_t i = 0; i < m_draw_call_count; i++) { const Renderer_DrawCall& draw_call = m_draw_calls[i]; @@ -697,7 +696,9 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_displacement, synthesised_displacement); cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_slope, synthesised_slope); - m_pcb_pass_cpu.set_f2_value(static_cast(tile_index), 0.0f); + Vector3 tile_pos = renderable->GetEntity()->GetPosition(); + + m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); cmd_list->PushConstants(m_pcb_pass_cpu); cmd_list->Dispatch(synthesised_displacement); From fd49c55bdc5d1ca0596052367c7120c527e9a4bf Mon Sep 17 00:00:00 2001 From: George Bolba Date: Thu, 2 Oct 2025 17:12:19 +0200 Subject: [PATCH 096/133] [ocean] apply synthesis and small improvements --- data/shaders/g_buffer.hlsl | 11 ++++++++--- data/shaders/light_image_based.hlsl | 14 -------------- data/shaders/ocean/synthesise_maps.hlsl | 6 +++--- source/editor/Widgets/Properties.cpp | 4 ++++ source/runtime/Game/Game.cpp | 6 +++--- source/runtime/Rendering/Renderer_Passes.cpp | 3 +-- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 4eaebfe907..23a9f9d599 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -102,8 +102,9 @@ static float4 sample_texture(gbuffer_vertex vertex, uint texture_index, Surface gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { MaterialParameters material = GetMaterial(); - - input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; + + if (material.ocean_parameters.displacementScale > -1.0f) + input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; float3 position_world = 0.0f; float3 position_world_previous = 0.0f; @@ -278,7 +279,11 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) float4 slope = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy) * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); - //albedo = tex2.SampleLevel(samplers[sampler_point_clamp], vertex.uv_misc.xy, 0).rgba * material.ocean_parameters.displacementScale; + // display displacement map for debug purposes + if (material.ocean_parameters.displacementScale <= -1.0f) + albedo = tex2.SampleLevel(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy, 0).rgba; + else if (material.ocean_parameters.slopeScale <= -1.0f) // or display slope map + albedo = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy); } diff --git a/data/shaders/light_image_based.hlsl b/data/shaders/light_image_based.hlsl index e363d54b2d..15190538ee 100644 --- a/data/shaders/light_image_based.hlsl +++ b/data/shaders/light_image_based.hlsl @@ -23,8 +23,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "brdf.hlsl" //================== -Texture2D slope_map : register(t7); - float3 get_dominant_specular_direction(float3 normal, float3 reflection, float roughness) { const float smoothness = 1.0f - roughness; @@ -88,16 +86,4 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) ibl *= surface.alpha; tex_uav[thread_id.xy] += float4(ibl, 0.0f); - - // if (/*!surface.is_sky() &&*/ surface.is_ocean()) - // { - // float3 foamColor = float3(1.0f, 1.0f, 1.0f); - - // float2 uv = world_to_uv(surface.position, true); - //float4 slope = slope_map.SampleLevel(samplers[sampler_point_clamp], uv, 0); - // float foam = slope.a * 300.0f; - - // tex_uav[thread_id.xy].rgb = lerp(tex_uav[thread_id.xy].rgb, foamColor, foam); - // //tex_uav[thread_id.xy].rgb = float3(uv, 0.0f); - // } } diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index c001a444f2..9b57cb5e21 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -100,10 +100,10 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) const float2 tile_origin_uv_space = float2(tile_xz_pos.x / tile_size, tile_xz_pos.y / tile_size); - uv = tile_origin_uv_space + uv * tile_size; + uv = tile_origin_uv_space + uv; - const float tex_freq = 2.0f; - const float tile_freq = 1.0f; + const float tex_freq = 3.0f; + const float tile_freq = 3.0f; float3 displacement = float3(0.0f, 0.0f, 0.0f); float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 51c5eba386..485a6f0901 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -926,10 +926,14 @@ void Properties::ShowMaterial(Material* material) const bool show_displacement = material->GetShowDisplacement(); ImGui::Checkbox("Show Displacement Map", &show_displacement); material->SetShowDiplacement(show_displacement); + if (show_displacement) + material->SetOceanProperty(OceanParameters::DisplacementScale, -1.0f); bool show_slope = material->GetShowSlope(); ImGui::Checkbox("Show Slope Map", &show_slope); material->SetShowSlope(show_slope); + if (show_slope) + material->SetOceanProperty(OceanParameters::SlopeScale, -1.0f); } // uv diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 302a58380c..b043dea993 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1739,12 +1739,12 @@ namespace spartan default_ocean->SetParent(entity); auto light_entity = World::CreateEntity(); - light_entity->SetPosition({ 64.0f, 180.0f, 64.0f }); + light_entity->SetPosition({ 196.0f, 280.0f, 196.0f }); Light* point = light_entity->AddComponent(); point->SetLightType(LightType::Point); - point->SetRange(400.0f); - point->SetTemperature(100000.0f); + point->SetRange(800.0f); + point->SetTemperature(10000.0f); point->SetIntensity(8500.0f); point->SetObjectName("Point Light"); diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 34ff6c114e..9d783b5650 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -155,7 +155,7 @@ namespace spartan Pass_Light_ImageBased(cmd_list_graphics_present); Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); - //Pass_ApplyFoam(cmd_list_graphics_present); + Pass_ApplyFoam(cmd_list_graphics_present); Pass_AA_Upscale(cmd_list_graphics_present); Pass_PostProcess(cmd_list_graphics_present); @@ -1102,7 +1102,6 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsSrv::tex, GetRenderTarget(Renderer_RenderTarget::light_shadow)); cmd_list->SetTexture(Renderer_BindingsSrv::tex2, GetRenderTarget(Renderer_RenderTarget::lut_brdf_specular)); cmd_list->SetTexture(Renderer_BindingsSrv::tex3, GetRenderTarget(Renderer_RenderTarget::skysphere)); - cmd_list->SetTexture(/*Renderer_BindingsUav::ocean_slope_map*/7, slope_map); // set pass constants m_pcb_pass_cpu.set_f3_value(static_cast(GetRenderTarget(Renderer_RenderTarget::skysphere)->GetMipCount())); From 30254053bec3b6423158984cb2d74d196d938cf6 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 5 Oct 2025 10:41:37 +0200 Subject: [PATCH 097/133] [ocean] remove comments and temp disable foam --- data/shaders/light_composition.hlsl | 15 --------------- source/runtime/Rendering/Renderer_Passes.cpp | 5 +---- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/data/shaders/light_composition.hlsl b/data/shaders/light_composition.hlsl index 5766862dcf..21c0a7bbe8 100644 --- a/data/shaders/light_composition.hlsl +++ b/data/shaders/light_composition.hlsl @@ -24,8 +24,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "fog.hlsl" //==================== -//RWTexture2D slope_map : register(u13); - [numthreads(THREAD_GROUP_COUNT_X, THREAD_GROUP_COUNT_Y, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { @@ -98,17 +96,4 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float accumulate = (pass_is_transparent() && !surface.is_transparent()) ? 1.0f : 0.0f; // transparent surfaces will sample the background via refraction, no need to blend tex_uav[thread_id.xy] = float4(light_diffuse * surface.albedo + light_specular + light_emissive + light_atmospheric, alpha) + tex_uav[thread_id.xy] * accumulate; - - //if (!surface.is_sky() && surface.is_ocean()) - //{ - // float3 foamColor = float3(1.0f, 1.0f, 1.0f); - // - // float4 ndcPos = mul(float4(surface.position, 1.0f), buffer_frame.view_projection); - // - // float2 uv = ndc_to_uv(ndcPos.xyz); - // float foam = 1.0f;//slope_map[uv].a; - // - // tex_uav[thread_id.xy].rgb = lerp(tex_uav[thread_id.xy].rgb, foamColor, foam); - // tex_uav[thread_id.xy].rgb = slope_map[thread_id.xy].rgb; - //} } diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 5e9b2de48d..c1cc347bae 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -157,7 +157,7 @@ namespace spartan Pass_Light_ImageBased(cmd_list_graphics_present); Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); - Pass_ApplyFoam(cmd_list_graphics_present); + //Pass_ApplyFoam(cmd_list_graphics_present); Pass_AA_Upscale(cmd_list_graphics_present); Pass_PostProcess(cmd_list_graphics_present); @@ -1051,7 +1051,6 @@ namespace spartan RHI_Texture* tex_light_diffuse = GetRenderTarget(Renderer_RenderTarget::light_diffuse); RHI_Texture* tex_light_specular = GetRenderTarget(Renderer_RenderTarget::light_specular); RHI_Texture* tex_light_volumetric = GetRenderTarget(Renderer_RenderTarget::light_volumetric); - //RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); cmd_list->InsertBarrierReadWrite(tex_out, RHI_BarrierType::EnsureReadThenWrite); @@ -1076,8 +1075,6 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsSrv::tex4, tex_light_specular); cmd_list->SetTexture(Renderer_BindingsSrv::tex5, tex_light_volumetric); - //cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_map, slope_map); - // render cmd_list->Dispatch(tex_out, GetOption(Renderer_Option::ResolutionScale)); } From 9863ce77930202d7e881087060b6b467e0f8488d Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 5 Oct 2025 17:46:50 +0200 Subject: [PATCH 098/133] [ocean] foam is now added in the albedo gbuffer and ocean sim runs before opaque passes --- data/shaders/g_buffer.hlsl | 3 + data/shaders/ocean/foam.hlsl | 132 ------------------ source/runtime/Rendering/Renderer_Passes.cpp | 138 +++++-------------- 3 files changed, 36 insertions(+), 237 deletions(-) delete mode 100644 data/shaders/ocean/foam.hlsl diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 1dea8a7d9a..0501ec86e5 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -273,6 +273,9 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) float4 slope = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy) * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); + // apply foam (foam mask is stored in the alpha channel of slope map) + albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); + // display displacement map for debug purposes if (material.ocean_parameters.displacementScale <= -1.0f) albedo = tex2.SampleLevel(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy, 0).rgba; diff --git a/data/shaders/ocean/foam.hlsl b/data/shaders/ocean/foam.hlsl deleted file mode 100644 index 55332ffcb2..0000000000 --- a/data/shaders/ocean/foam.hlsl +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright(c) 2025 George Bolba - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions : - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -// Inspired by Acerola's Implementation: -// https://github.com/GarrettGunnell/Water/blob/main/Assets/Shaders/FFTWater.compute - -#include "common_ocean.hlsl" - -struct VSOUT -{ - float4 pos : SV_Position; - float2 uv : TEXCOORD; -}; - -// hash -float hash21(float2 p) -{ - p = frac(p * float2(123.34f, 456.21f)); - p += dot(p, p + 78.233f); - return frac(p.x * p.y); -} - -// value noise -float noise2D(float2 p) -{ - float2 i = floor(p); - float2 f = frac(p); - float a = hash21(i); - float b = hash21(i + float2(1.0f, 0.0f)); - float c = hash21(i + float2(0.0f, 1.0f)); - float d = hash21(i + float2(1.0f, 1.0f)); - float2 u = f * f * (3.0f - 2.0f * f); - return lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y); -} - -// fractal Brownian motion (multiple octaves) -float fbmNoise(float2 uv) -{ - float val = 0.0f; - float amp = 0.5f; - float freq = 1.0f; - for (int i = 0; i < 5; i++) - { // 5 octaves - val += amp * noise2D(uv * freq); - freq *= 2.0f; - amp *= 0.5f; - } - return val; -} - -// worley noise (optional bubble-like pattern) -float worley(float2 p) -{ - float2 i = floor(p); - float2 f = frac(p); - float minDist = 1.0f; - for (int y = -1; y <= 1; y++) - { - for (int x = -1; x <= 1; x++) - { - float2 neighbor = float2(x, y); - float2 p = hash21(i + neighbor) * float2(1.0f, 1.0f); - float2 diff = neighbor + p - f; - float d = dot(diff, diff); - minDist = min(minDist, d); - } - } - return minDist; -} - -float computeFoamNoise(float2 uv, float time) -{ - // high frequency UV, animate over time - float2 noiseUV = uv * 32.0f + time * float2(0.1f, 0.05f); - - float fbm = fbmNoise(noiseUV); - float bubbles = 1.0f - saturate(sqrt(worley(noiseUV * 2.0f))); - - // combine fbm + bubbles for frothy look - return saturate(fbm * bubbles); -} - -VSOUT main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) -{ - VSOUT vs_out; - - float2 map_flags = pass_get_f2_value(); - - float3 displaced_pos = input.position.xyz + tex.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).xyz * GetMaterial().ocean_parameters.displacementScale; - float3 wpos = mul(float4(displaced_pos, 1.0f), buffer_pass.transform).xyz; - if (any(map_flags) == 1.0f) // If debugging displacement/slope map, then dont apply displacement map - wpos = mul(float4(input.position.xyz, 1.0f), buffer_pass.transform).xyz; - vs_out.pos = mul(float4(wpos, 1.0f), buffer_frame.view_projection); - vs_out.uv = input.uv; - - return vs_out; -} - -float4 main_ps(VSOUT vertex) : SV_Target0 -{ - const float4 foam_color = float4(1.0f, 1.0f, 1.0f, 1.0f); - const float foam_mask = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).a * 1.0f; - - const float foam_noise = computeFoamNoise(vertex.uv, buffer_frame.time); - - float2 map_flags = pass_get_f2_value(); - - if (map_flags.x == 1.0f) // Displacement Map - return float4(tex.Sample(samplers[sampler_point_clamp], vertex.uv).rgb, 1.0f); - if (map_flags.y == 1.0f) // Slope Map - return float4(tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv).rgb, 1.0f); - - return float4(foam_noise.xxxx) * foam_mask; -} diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 28591ba0ac..0730de255c 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -99,6 +99,37 @@ namespace spartan { Pass_VariableRateShading(cmd_list_graphics_present); + // Ocean Passes + Material* prev_material = nullptr; + for (uint32_t i = 0; i < m_draw_call_count; i++) + { + const Renderer_DrawCall& draw_call = m_draw_calls[i]; + Renderable* renderable = draw_call.renderable; + Material* material = renderable->GetMaterial(); + + // get ocean material + if (!material->IsOcean() || material == prev_material) + continue; + + prev_material = material; + + if (material->ShouldComputeSpectrum()) + { + SP_LOG_INFO("Computing Ocean Spectrum..."); + Pass_ComputeInitialSpectrum(cmd_list_graphics_present); + // calculates conjugate and stores it in BA channels of the initial spectrum + Pass_PackSpectrum(cmd_list_graphics_present); + + material->MarkSpectrumAsComputed(); + } + + // computes displacement and slope maps + Pass_AdvanceSpectrum(cmd_list_graphics_present); + Pass_ApplyHorizontalFFT(cmd_list_graphics_present); + Pass_ApplyVerticalFFT(cmd_list_graphics_present); + Pass_GenerateMaps(cmd_list_graphics_present); + } + // opaques { bool is_transparent = false; @@ -118,46 +149,15 @@ namespace spartan { bool is_transparent = true; - // Ocean Passes - Material* prev_material = nullptr; - for (uint32_t i = 0; i < m_draw_call_count; i++) - { - const Renderer_DrawCall& draw_call = m_draw_calls[i]; - Renderable* renderable = draw_call.renderable; - Material* material = renderable->GetMaterial(); - - // get ocean material - if (!material->IsOcean() || material == prev_material) - continue; - - prev_material = material; - - if (material->ShouldComputeSpectrum()) - { - SP_LOG_INFO("Computing Ocean Spectrum..."); - Pass_ComputeInitialSpectrum(cmd_list_graphics_present); - // calculates conjugate and stores it in BA channels of the initial spectrum - Pass_PackSpectrum(cmd_list_graphics_present); - - material->MarkSpectrumAsComputed(); - } - - // computes displacement and slope maps - Pass_AdvanceSpectrum(cmd_list_graphics_present); - Pass_ApplyHorizontalFFT(cmd_list_graphics_present); - Pass_ApplyVerticalFFT(cmd_list_graphics_present); - Pass_GenerateMaps(cmd_list_graphics_present); - } - Pass_GBuffer(cmd_list_graphics_present, is_transparent); Pass_Light(cmd_list_graphics_present, is_transparent); Pass_Light_Composition(cmd_list_graphics_present, is_transparent); } Pass_Light_ImageBased(cmd_list_graphics_present); - Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); + //Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); - //Pass_ApplyFoam(cmd_list_graphics_present); + Pass_ApplyFoam(cmd_list_graphics_present); Pass_AA_Upscale(cmd_list_graphics_present); Pass_PostProcess(cmd_list_graphics_present); @@ -1283,78 +1283,6 @@ namespace spartan cmd_list->EndTimeblock(); } - void Renderer::Pass_ApplyFoam(RHI_CommandList* cmd_list) - { - RHI_Texture* tex_depth = GetRenderTarget(Renderer_RenderTarget::gbuffer_depth); - RHI_Texture* tex_out = GetRenderTarget(Renderer_RenderTarget::frame_render); - - RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); - RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_slope); - - tex_out->SetLayout(RHI_Image_Layout::General, cmd_list); - - cmd_list->BeginTimeblock("ocean_foam"); - { - // set pipeline state - RHI_PipelineState pso; - pso.name = "ocean_foam"; - pso.shaders[RHI_Shader_Type::Vertex] = GetShader(Renderer_Shader::ocean_foam_v); - pso.shaders[RHI_Shader_Type::Pixel] = GetShader(Renderer_Shader::ocean_foam_p); - pso.blend_state = GetBlendState(Renderer_BlendState::Additive); - pso.rasterizer_state = GetRasterizerState(Renderer_RasterizerState::Solid); - pso.depth_stencil_state = GetDepthStencilState(Renderer_DepthStencilState::Off); - pso.vrs_input_texture = GetOption(Renderer_Option::VariableRateShading) ? GetRenderTarget(Renderer_RenderTarget::shading_rate) : nullptr; - pso.resolution_scale = true; - pso.render_target_color_textures[0] = tex_out; - pso.render_target_depth_texture = tex_depth; - pso.clear_color[0] = rhi_color_load; - cmd_list->SetPipelineState(pso); - - for (uint32_t i = 0; i < m_draw_call_count; i++) - { - const Renderer_DrawCall& draw_call = m_draw_calls[i]; - Renderable* renderable = draw_call.renderable; - Material* material = renderable->GetMaterial(); - if (!material || !material->IsOcean() || !draw_call.camera_visible) - continue; - - // pass constants - { - m_pcb_pass_cpu.transform = renderable->GetEntity()->GetMatrix(); - m_pcb_pass_cpu.set_is_transparent_and_material_index(true, material->GetIndex()); - - float a = material->GetShowDisplacement() ? 1.0f : 0.0f; - float b = material->GetShowSlope() ? 1.0f : 0.0f; - m_pcb_pass_cpu.set_f2_value(a, b); - - cmd_list->PushConstants(m_pcb_pass_cpu); - } - - // draw - { - cmd_list->SetCullMode(static_cast(material->GetProperty(MaterialProperty::CullMode))); - cmd_list->SetBufferVertex(renderable->GetVertexBuffer(), renderable->GetInstanceBuffer()); - cmd_list->SetBufferIndex(renderable->GetIndexBuffer()); - - cmd_list->SetTexture(Renderer_BindingsSrv::tex, displacement_map); - cmd_list->SetTexture(Renderer_BindingsSrv::tex2, slope_map); - - cmd_list->DrawIndexed( - renderable->GetIndexCount(draw_call.lod_index), - renderable->GetIndexOffset(draw_call.lod_index), - renderable->GetVertexOffset(draw_call.lod_index), - renderable->HasInstancing() ? draw_call.instance_index : 0, - renderable->HasInstancing() ? draw_call.instance_count : 1 - ); - - // at this point, we don't want clear in case another render pass is implicitly started - pso.clear_depth = rhi_depth_load; - } - } - } - cmd_list->EndTimeblock(); - } - void Renderer::Pass_PostProcess(RHI_CommandList* cmd_list) { // acquire render targets From acfead6f5773eef507d51e5762cc8b4bda4d6452 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 5 Oct 2025 18:08:47 +0200 Subject: [PATCH 099/133] [ocean] improved settings all around and fixed foam pass removal remnants --- data/shaders/ocean/synthesise_maps.hlsl | 6 +++--- source/runtime/Game/Game.cpp | 14 +++++++------- source/runtime/Rendering/Renderer.h | 1 - source/runtime/Rendering/Renderer_Definitions.h | 2 -- source/runtime/Rendering/Renderer_Passes.cpp | 2 -- source/runtime/Rendering/Renderer_Resources.cpp | 7 ------- 6 files changed, 10 insertions(+), 22 deletions(-) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 9b57cb5e21..4d045d0466 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -102,8 +102,8 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) uv = tile_origin_uv_space + uv; - const float tex_freq = 3.0f; - const float tile_freq = 3.0f; + const float tex_freq = 1.0f; + const float tile_freq = 2.0f; float3 displacement = float3(0.0f, 0.0f, 0.0f); float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); @@ -125,5 +125,5 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) synthesised_slope[thread_id.xy] = PreserveVariance(slope, mean_slope, moment2); //synthesised_displacement[thread_id.xy] = tex2[thread_id.xy]; - synthesised_slope[thread_id.xy] = tex3[thread_id.xy]; + //synthesised_slope[thread_id.xy] = tex3[thread_id.xy]; } diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 2df3f13b86..1d8067687b 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -319,9 +319,9 @@ namespace spartan material->SetOceanProperty(OceanParameters::Scale, 1.0f); material->SetOceanProperty(OceanParameters::SpreadBlend, 0.9f); material->SetOceanProperty(OceanParameters::Swell, 1.0f); - material->SetOceanProperty(OceanParameters::Fetch, 100000.0f); + material->SetOceanProperty(OceanParameters::Fetch, 1280000.0f); material->SetOceanProperty(OceanParameters::WindDirection, 135.0f); - material->SetOceanProperty(OceanParameters::WindSpeed, 5.0f); + material->SetOceanProperty(OceanParameters::WindSpeed, 2.8f); material->SetOceanProperty(OceanParameters::Gamma, 3.3f); material->SetOceanProperty(OceanParameters::ShortWavesFade, 0.0f); material->SetOceanProperty(OceanParameters::RepeatTime, 200.0f); @@ -331,13 +331,13 @@ namespace spartan material->SetOceanProperty(OceanParameters::HighCutoff, 1000.0f); material->SetOceanProperty(OceanParameters::FoamDecayRate, 3.0f); - material->SetOceanProperty(OceanParameters::FoamThreshold, 0.0f); + material->SetOceanProperty(OceanParameters::FoamThreshold, 0.5f); material->SetOceanProperty(OceanParameters::FoamBias, 1.2f); material->SetOceanProperty(OceanParameters::FoamAdd, 1.0f); material->SetOceanProperty(OceanParameters::DisplacementScale, 1.0f); - material->SetOceanProperty(OceanParameters::SlopeScale, 0.6f); - material->SetOceanProperty(OceanParameters::LengthScale, 48.0f); + material->SetOceanProperty(OceanParameters::SlopeScale, 1.0f); + material->SetOceanProperty(OceanParameters::LengthScale, 128.0f); } } @@ -1730,7 +1730,7 @@ namespace spartan void create() { entities::camera(); - //entities::sun(true); + entities::sun(true); auto entity = World::CreateEntity(); @@ -1748,7 +1748,7 @@ namespace spartan point->SetIntensity(8500.0f); point->SetObjectName("Point Light"); - //default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } void tick() diff --git a/source/runtime/Rendering/Renderer.h b/source/runtime/Rendering/Renderer.h index 1e766ebc03..8b87a3f492 100644 --- a/source/runtime/Rendering/Renderer.h +++ b/source/runtime/Rendering/Renderer.h @@ -168,7 +168,6 @@ namespace spartan static void Pass_ApplyHorizontalFFT(RHI_CommandList* cmd_list); static void Pass_ApplyVerticalFFT(RHI_CommandList* cmd_list); static void Pass_GenerateMaps(RHI_CommandList* cmd_list); - static void Pass_ApplyFoam(RHI_CommandList* cmd_list); // passes - debug/editor static void Pass_Grid(RHI_CommandList* cmd_list, RHI_Texture* tex_out); static void Pass_Lines(RHI_CommandList* cmd_list, RHI_Texture* tex_out); diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 6c22871ba4..7cd90f3a48 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -198,8 +198,6 @@ namespace spartan ocean_vertical_fft_c, ocean_generate_maps_c, ocean_synthesise_maps_c, - ocean_foam_v, - ocean_foam_p, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 0730de255c..673b47a525 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -156,8 +156,6 @@ namespace spartan Pass_Light_ImageBased(cmd_list_graphics_present); //Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); - - Pass_ApplyFoam(cmd_list_graphics_present); Pass_AA_Upscale(cmd_list_graphics_present); Pass_PostProcess(cmd_list_graphics_present); diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 033cfbcd11..8c3d1a9bfa 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -414,13 +414,6 @@ namespace spartan shader(Renderer_Shader::ocean_synthesise_maps_c) = make_shared(); shader(Renderer_Shader::ocean_synthesise_maps_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\synthesise_maps.hlsl", false); - - shader(Renderer_Shader::ocean_foam_v) = make_shared(); - shader(Renderer_Shader::ocean_foam_v)->Compile(RHI_Shader_Type::Vertex, shader_dir + "ocean\\foam.hlsl", async, RHI_Vertex_Type::PosUvNorTan); - - shader(Renderer_Shader::ocean_foam_p) = make_shared(); - shader(Renderer_Shader::ocean_foam_p)->Compile(RHI_Shader_Type::Pixel, shader_dir + "ocean\\foam.hlsl", async); - } // blur From 9b3e91d9acdd56a410909f282902becd2aaa53f3 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 5 Oct 2025 21:20:05 +0200 Subject: [PATCH 100/133] [ocean] disabled point light --- source/runtime/Game/Game.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 4aa4a10b35..5e8fcdce9a 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1738,7 +1738,7 @@ namespace spartan default_ocean->SetParent(entity); - auto light_entity = World::CreateEntity(); + /*auto light_entity = World::CreateEntity(); light_entity->SetPosition({ 196.0f, 280.0f, 196.0f }); Light* point = light_entity->AddComponent(); @@ -1747,6 +1747,7 @@ namespace spartan point->SetTemperature(10000.0f); point->SetIntensity(8500.0f); point->SetObjectName("Point Light"); + */ default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); } From 310c0c638fc870f5819641e45d446aa85514355f Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 5 Oct 2025 22:02:42 +0200 Subject: [PATCH 101/133] [ocean] cleaned up synthesis shader --- data/shaders/ocean/synthesise_maps.hlsl | 79 ++++++++++++------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 4d045d0466..bda2695f94 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -23,38 +23,36 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // Inspired by https://www.shadertoy.com/view/tsVGRd -static const float2 hexRatio = float2(1.0f, sqrt(3.0f)); +static const float2 hex_ratio = float2(1.0f, sqrt(3.0f)); -float4 GetHexGridInfo(float2 uv) +float4 get_hex_grid_info(float2 uv) { - float4 hexIndex = round(float4(uv, uv - float2(0.5f, 1.0f)) / hexRatio.xyxy); - float4 hexCenter = float4(hexIndex.xy * hexRatio, (hexIndex.zw + 0.5f) * hexRatio); - float4 offset = uv.xyxy - hexCenter; - return dot(offset.xy, offset.xy) < dot(offset.zw, offset.zw) ? - float4(hexCenter.xy, hexIndex.xy) : - float4(hexCenter.zw, hexIndex.zw); + const float4 hex_index = round(float4(uv, uv - float2(0.5f, 1.0f)) / hex_ratio.xyxy); + const float4 hex_center = float4(hex_index.xy * hex_ratio, (hex_index.zw + 0.5f) * hex_ratio); + const float4 offset = uv.xyxy - hex_center; + return dot(offset.xy, offset.xy) < dot(offset.zw, offset.zw) ? float4(hex_center.xy, hex_index.xy) : float4(hex_center.zw, hex_index.zw); } -float GetHexSDF(in float2 p) +float get_hex_sdf(in float2 p) { p = abs(p); - return 0.5f - max(dot(p, hexRatio * 0.5f), p.x); + return 0.5f - max(dot(p, hex_ratio * 0.5f), p.x); } //xy: node pos, z: weight -float3 GetTriangleInterpNode(in float2 pos, in float freq, in int nodeIndex) +float3 get_triangle_interp_node(in float2 pos, in float freq, in int node_index) { - float2 nodeOffsets[3] = + const float2 node_offsets[3] = { float2(0.0f, 0.0f), float2(1.0f, 1.0f), float2(1.0f, -1.0f) }; - float2 uv = pos * freq + nodeOffsets[nodeIndex] / hexRatio.xy * 0.5f; - float4 hexInfo = GetHexGridInfo(uv); - float dist = GetHexSDF(uv - hexInfo.xy) * 2.0f; - return float3(hexInfo.xy / freq, dist); + const float2 uv = pos * freq + node_offsets[node_index] / hex_ratio.xy * 0.5f; + const float4 hex_info = get_hex_grid_info(uv); + const float dist = get_hex_sdf(uv - hex_info.xy) * 2.0f; + return float3(hex_info.xy / freq, dist); } float3 hash33(float3 p) @@ -66,23 +64,34 @@ float3 hash33(float3 p) return frac(sin(p) * 43758.5453123f); } -float4 GetTextureSample(Texture2D texture, float2 pos, float freq, float2 nodePoint, float z) +float4 get_texture_sample(Texture2D texture, float2 pos, float freq, float2 node_point) { - const float3 hash = hash33(float3(nodePoint.xy, z)); + const float3 hash = hash33(float3(node_point.xy, 0.0f)); - float2 uv = pos * freq + hash.yz; - uv = pos * freq + hash.yz; + const float2 uv = pos * freq + hash.yz; return texture.SampleLevel(samplers[sampler_point_wrap], uv, 0); } -float3 PreserveVariance(float3 linearColor, float3 meanColor, float moment2) +void preserve_variance(out float4 linear_color, float4 mean_color, float moment2) { - return (linearColor - meanColor) / sqrt(moment2) + meanColor; + linear_color = (linear_color - mean_color) / sqrt(moment2) + mean_color; } -float4 PreserveVariance(float4 linearColor, float4 meanColor, float moment2) +void synthesize(Texture2D example, out float4 output, float2 uv, float tex_freq, float tile_freq) { - return (linearColor - meanColor) / sqrt(moment2) + meanColor; + float moment2 = 0.0f; + + for (int i = 0; i < 3; i++) + { + const float3 interp_node = get_triangle_interp_node(uv, tile_freq, i); + output += get_texture_sample(example, uv, tex_freq, interp_node.xy) * interp_node.z; + + moment2 += interp_node.z * interp_node.z; + } + // assumes example is a mip mapped 512x512 texture and samples lowest mip (1x1) + const float4 mean_example = example.SampleLevel(samplers[sampler_point_clamp], uv, 9); + + preserve_variance(output, mean_example, moment2); } [numthreads(8, 8, 1)] @@ -105,25 +114,15 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) const float tex_freq = 1.0f; const float tile_freq = 2.0f; - float3 displacement = float3(0.0f, 0.0f, 0.0f); + float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); + synthesize(tex2, displacement, uv, tex_freq, tile_freq); + float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); - float moment2 = 0.0f; - for (int i = 0; i < 3; i++) - { - float3 interp_node = GetTriangleInterpNode(uv, tile_freq, i); - // tex2 = displacement_map as SRV - const float ocean_tile_index = pass_get_f2_value().x; - displacement.xyz += GetTextureSample(tex2, uv, tex_freq, interp_node.xy, ocean_tile_index).rgb * interp_node.z; - slope += GetTextureSample(tex3, uv, tex_freq, interp_node.xy, ocean_tile_index) * interp_node.z; + synthesize(tex3, slope, uv, tex_freq, tile_freq); - moment2 += interp_node.z * interp_node.z; - } - const float3 mean_displacement = tex2.SampleLevel(samplers[sampler_point_clamp], uv, 9).rgb; - const float4 mean_slope = tex3.SampleLevel(samplers[sampler_point_clamp], uv, 9); + synthesised_displacement[thread_id.xy] = displacement; + synthesised_slope[thread_id.xy] = slope; - synthesised_displacement[thread_id.xy] = float4(PreserveVariance(displacement, mean_displacement, moment2), 1.0f); - synthesised_slope[thread_id.xy] = PreserveVariance(slope, mean_slope, moment2); - //synthesised_displacement[thread_id.xy] = tex2[thread_id.xy]; //synthesised_slope[thread_id.xy] = tex3[thread_id.xy]; } From 951bc5b7c7ff40d8cfd9a43def8842e0610feec8 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 5 Oct 2025 22:04:20 +0200 Subject: [PATCH 102/133] [ocean] removed unncessary in keyword --- data/shaders/ocean/synthesise_maps.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index bda2695f94..40ce49ab97 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -33,14 +33,14 @@ float4 get_hex_grid_info(float2 uv) return dot(offset.xy, offset.xy) < dot(offset.zw, offset.zw) ? float4(hex_center.xy, hex_index.xy) : float4(hex_center.zw, hex_index.zw); } -float get_hex_sdf(in float2 p) +float get_hex_sdf(float2 p) { p = abs(p); return 0.5f - max(dot(p, hex_ratio * 0.5f), p.x); } //xy: node pos, z: weight -float3 get_triangle_interp_node(in float2 pos, in float freq, in int node_index) +float3 get_triangle_interp_node(float2 pos, float freq, int node_index) { const float2 node_offsets[3] = { From 9066b9a56b1a43dc4f08e180849e2aeae088e3d7 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sun, 5 Oct 2025 22:07:19 +0200 Subject: [PATCH 103/133] [ocean] safety initialisation --- data/shaders/ocean/synthesise_maps.hlsl | 1 + 1 file changed, 1 insertion(+) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 40ce49ab97..fe99f57d26 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -79,6 +79,7 @@ void preserve_variance(out float4 linear_color, float4 mean_color, float moment2 void synthesize(Texture2D example, out float4 output, float2 uv, float tex_freq, float tile_freq) { + output = float4(0.0f, 0.0f, 0.0f, 0.0f); // init to 0.0f for safety reasons float moment2 = 0.0f; for (int i = 0; i < 3; i++) From cee32faef2cba47c686edd454e6bf842e720f3d8 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 6 Oct 2025 11:17:49 +0200 Subject: [PATCH 104/133] [ocean] moved texture synthesis to gbuffer pass --- data/shaders/g_buffer.hlsl | 31 +++++++-- data/shaders/ocean/synthesise_maps.hlsl | 53 ++++++++------- .../runtime/Rendering/Renderer_Definitions.h | 3 - source/runtime/Rendering/Renderer_Passes.cpp | 68 ++++++++++--------- .../runtime/Rendering/Renderer_Resources.cpp | 7 -- 5 files changed, 92 insertions(+), 70 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 0501ec86e5..8ea9a72755 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -19,6 +19,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //= INCLUDES ========= #include "common.hlsl" +#include "ocean/synthesise_maps.hlsl" //==================== struct gbuffer @@ -29,9 +30,6 @@ struct gbuffer float2 velocity : SV_Target3; }; -//Texture2D displacement_map : register(t17); -Texture2D slope_map : register(t18); - // rotate UV around center (0.5, 0.5) by angle float2 rotate_uv(float2 uv, float angle) { @@ -104,7 +102,19 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI MaterialParameters material = GetMaterial(); if (material.ocean_parameters.displacementScale > -1.0f) - input.position.xyz += tex2.SampleLevel(samplers[sampler_point_clamp], input.uv, 0).rgb * material.ocean_parameters.displacementScale; + { + const float3 pass_values = pass_get_f3_value(); + const float2 tile_xz_pos = pass_values.xy; + const float tile_size = pass_values.z; + const float2 world_space_tile_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); + + const float tex_freq = 1.0f; + const float tile_freq = 2.0f; + + float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); + synthesize(tex2, displacement, world_space_tile_uv, tex_freq, tile_freq); + input.position.xyz += displacement * material.ocean_parameters.displacementScale; + } float3 position_world = 0.0f; float3 position_world_previous = 0.0f; @@ -270,7 +280,18 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) } else if (surface.is_ocean()) { - float4 slope = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy) * material.ocean_parameters.slopeScale; + const float3 pass_values = pass_get_f3_value(); + const float2 tile_xz_pos = pass_values.xy; + const float tile_size = pass_values.z; + const float2 world_space_tile_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, tile_xz_pos, tile_size); + + const float tex_freq = 1.0f; + const float tile_freq = 2.0f; + + float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); + synthesize(tex3, slope, world_space_tile_uv, tex_freq, tile_freq); + + slope = slope * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); // apply foam (foam mask is stored in the alpha channel of slope map) diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index fe99f57d26..b34c18c6b4 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -69,7 +69,7 @@ float4 get_texture_sample(Texture2D texture, float2 pos, float freq, float2 node const float3 hash = hash33(float3(node_point.xy, 0.0f)); const float2 uv = pos * freq + hash.yz; - return texture.SampleLevel(samplers[sampler_point_wrap], uv, 0); + return texture.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); } void preserve_variance(out float4 linear_color, float4 mean_color, float moment2) @@ -95,35 +95,42 @@ void synthesize(Texture2D example, out float4 output, float2 uv, float tex_freq, preserve_variance(output, mean_example, moment2); } -[numthreads(8, 8, 1)] -void main_cs(uint3 thread_id : SV_DispatchThreadID) +float2 ocean_get_world_space_uvs(float2 uv, float2 tile_xz_pos, float tile_size) { - const uint2 pixel_coord = thread_id.xy; - uint2 texture_size; - synthesised_displacement.GetDimensions(texture_size.x, texture_size.y); + const float2 tile_origin_uv_space = float2(tile_xz_pos.x / tile_size, tile_xz_pos.y / tile_size); + + return tile_origin_uv_space + uv; +} + +//[numthreads(8, 8, 1)] +//void main_cs(uint3 thread_id : SV_DispatchThreadID) +//{ +// const uint2 pixel_coord = thread_id.xy; +// uint2 texture_size; +// synthesised_displacement.GetDimensions(texture_size.x, texture_size.y); - float2 uv = (pixel_coord + 0.5f) / texture_size; +// float2 uv = (pixel_coord + 0.5f) / texture_size; - const float3 pass_values = pass_get_f3_value(); - const float2 tile_xz_pos = pass_values.xy; - const float tile_size = pass_values.z; +// const float3 pass_values = pass_get_f3_value(); +// const float2 tile_xz_pos = pass_values.xy; +// const float tile_size = pass_values.z; - const float2 tile_origin_uv_space = float2(tile_xz_pos.x / tile_size, tile_xz_pos.y / tile_size); +// const float2 tile_origin_uv_space = float2(tile_xz_pos.x / tile_size, tile_xz_pos.y / tile_size); - uv = tile_origin_uv_space + uv; +// uv = tile_origin_uv_space + uv; - const float tex_freq = 1.0f; - const float tile_freq = 2.0f; +// const float tex_freq = 1.0f; +// const float tile_freq = 2.0f; - float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, uv, tex_freq, tile_freq); +// float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); +// synthesize(tex2, displacement, uv, tex_freq, tile_freq); - float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex3, slope, uv, tex_freq, tile_freq); +// float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); +// synthesize(tex3, slope, uv, tex_freq, tile_freq); - synthesised_displacement[thread_id.xy] = displacement; - synthesised_slope[thread_id.xy] = slope; +// synthesised_displacement[thread_id.xy] = displacement; +// synthesised_slope[thread_id.xy] = slope; - //synthesised_displacement[thread_id.xy] = tex2[thread_id.xy]; - //synthesised_slope[thread_id.xy] = tex3[thread_id.xy]; -} +// //synthesised_displacement[thread_id.xy] = tex2[thread_id.xy]; +// //synthesised_slope[thread_id.xy] = tex3[thread_id.xy]; +//} diff --git a/source/runtime/Rendering/Renderer_Definitions.h b/source/runtime/Rendering/Renderer_Definitions.h index 7cd90f3a48..4d42f7d86f 100644 --- a/source/runtime/Rendering/Renderer_Definitions.h +++ b/source/runtime/Rendering/Renderer_Definitions.h @@ -197,7 +197,6 @@ namespace spartan ocean_horizontal_fft_c, ocean_vertical_fft_c, ocean_generate_maps_c, - ocean_synthesise_maps_c, max }; @@ -237,8 +236,6 @@ namespace spartan ocean_slope_spectrum, ocean_displacement_map, ocean_slope_map, - ocean_synthesised_displacement, - ocean_synthesised_slope, max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 673b47a525..6cea068421 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -673,42 +673,42 @@ namespace spartan } } - // ocean synthesis - if (material->IsOcean()) - { - tile_index++; + //// ocean synthesis + //if (material->IsOcean()) + //{ + // tile_index++; - cmd_list->BeginTimeblock("Ocean Displacement Map Synthesis"); + // cmd_list->BeginTimeblock("Ocean Displacement Map Synthesis"); - RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); - RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + // RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + // RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); - RHI_Texture* synthesised_displacement = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); - RHI_Texture* synthesised_slope = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_slope); + // RHI_Texture* synthesised_displacement = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); + // RHI_Texture* synthesised_slope = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_slope); - displacement_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list, rhi_all_mips, 10); + // displacement_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list, rhi_all_mips, 10); - RHI_PipelineState pso2; - pso2.name = "ocean_displacement_map_synthesis"; - pso2.shaders[Compute] = GetShader(Renderer_Shader::ocean_synthesise_maps_c); - cmd_list->SetPipelineState(pso2); + // RHI_PipelineState pso2; + // pso2.name = "ocean_displacement_map_synthesis"; + // pso2.shaders[Compute] = GetShader(Renderer_Shader::ocean_synthesise_maps_c); + // cmd_list->SetPipelineState(pso2); - cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); - cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); - cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_displacement, synthesised_displacement); - cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_slope, synthesised_slope); + // cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); + // cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); + // cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_displacement, synthesised_displacement); + // cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_slope, synthesised_slope); - Vector3 tile_pos = renderable->GetEntity()->GetPosition(); + // Vector3 tile_pos = renderable->GetEntity()->GetPosition(); - m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); - cmd_list->PushConstants(m_pcb_pass_cpu); + // m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); + // cmd_list->PushConstants(m_pcb_pass_cpu); - cmd_list->Dispatch(synthesised_displacement); + // cmd_list->Dispatch(synthesised_displacement); - synthesised_slope->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); + // synthesised_slope->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); - cmd_list->EndTimeblock(); - } + // cmd_list->EndTimeblock(); + //} cmd_list->SetPipelineState(pso); @@ -718,6 +718,13 @@ namespace spartan m_pcb_pass_cpu.transform = entity->GetMatrix(); m_pcb_pass_cpu.set_transform_previous(entity->GetMatrixPrevious()); m_pcb_pass_cpu.set_is_transparent_and_material_index(is_transparent_pass, material->GetIndex()); + + if (material->IsOcean()) + { + const Vector3 tile_pos = entity->GetPosition(); + m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); + } + cmd_list->PushConstants(m_pcb_pass_cpu); entity->SetMatrixPrevious(m_pcb_pass_cpu.transform); @@ -731,14 +738,11 @@ namespace spartan if (material->IsOcean()) { - if (!material->GetShowDisplacement() && !material->GetShowSlope()) - { - RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); - cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); - RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_slope); - cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); - } + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); } cmd_list->DrawIndexed( diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 807fd9dee2..dce0677965 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -239,10 +239,6 @@ namespace spartan render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_displacement_map"); render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_slope_map"); - - render_target(Renderer_RenderTarget::ocean_synthesised_displacement) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_synthesised_displacement_map"); - - render_target(Renderer_RenderTarget::ocean_synthesised_slope) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_synthesised_slope_map"); } // occlusion @@ -412,9 +408,6 @@ namespace spartan shader(Renderer_Shader::ocean_generate_maps_c) = make_shared(); shader(Renderer_Shader::ocean_generate_maps_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\generate_maps.hlsl", false); - - shader(Renderer_Shader::ocean_synthesise_maps_c) = make_shared(); - shader(Renderer_Shader::ocean_synthesise_maps_c)->Compile(RHI_Shader_Type::Compute, shader_dir + "ocean\\synthesise_maps.hlsl", false); } // blur From 07386567185a71cd3982d9be1cdd06f1717e4f29 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 6 Oct 2025 15:53:40 +0200 Subject: [PATCH 105/133] [ocean] accumulating foam --- data/shaders/g_buffer.hlsl | 2 +- data/shaders/ocean/common_ocean.hlsl | 62 +++++++++++++++++++++++++++ data/shaders/ocean/generate_maps.hlsl | 2 +- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 8ea9a72755..3094cc7f2e 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -295,6 +295,7 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) normal = normalize(float3(-slope.x, 1.0f, -slope.y)); // apply foam (foam mask is stored in the alpha channel of slope map) + //const float foam_noise = compute_foam_noise(vertex.uv_misc.xy, buffer_frame.time); albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); // display displacement map for debug purposes @@ -303,7 +304,6 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) else if (material.ocean_parameters.slopeScale <= -1.0f) // or display slope map albedo = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy); } - // apply curved normals for grass blades if (surface.is_grass_blade()) diff --git a/data/shaders/ocean/common_ocean.hlsl b/data/shaders/ocean/common_ocean.hlsl index eaa482a7e8..08500e1f51 100644 --- a/data/shaders/ocean/common_ocean.hlsl +++ b/data/shaders/ocean/common_ocean.hlsl @@ -39,4 +39,66 @@ float Dispersion(float kMag, float depth) return sqrt(G * kMag * tanh(min(kMag * depth, 20))); } +float hash21(float2 p) +{ + p = frac(p * float2(123.34f, 456.21f)); + p += dot(p, p + 78.233f); + return frac(p.x * p.y); +} + +float noise_2d(float2 p) +{ + float2 i = floor(p); + float2 f = frac(p); + float a = hash21(i); + float b = hash21(i + float2(1.0f, 0.0f)); + float c = hash21(i + float2(0.0f, 1.0f)); + float d = hash21(i + float2(1.0f, 1.0f)); + float2 u = f * f * (3.0f - 2.0f * f); + return lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y); +} + +float fbm_noise(float2 uv) +{ + float val = 0.0f; + float amp = 0.5f; + float freq = 1.0f; + for (int i = 0; i < 5; i++) + { + val += amp * noise_2d(uv * freq); + freq *= 2.0f; + amp *= 0.5f; + } + return val; +} + +float worley(float2 p) +{ + float2 i = floor(p); + float2 f = frac(p); + float min_dist = 1.0f; + for (int y = -1; y <= 1; y++) + { + for (int x = -1; x <= 1; x++) + { + float2 neighbor = float2(x, y); + float2 p = hash21(i + neighbor) * float2(1.0f, 1.0f); + float2 diff = neighbor + p - f; + float d = dot(diff, diff); + min_dist = min(min_dist, d); + } + } + return min_dist; +} + +float compute_foam_noise(float2 uv, float time) +{ + float2 noise_uv = uv * 32.0f + time * float2(0.1f, 0.05f); + + float fbm = fbm_noise(noise_uv); + float bubbles = 1.0f - saturate(sqrt(worley(noise_uv * 2.0f))); + + return saturate(fbm * bubbles); +} + #endif // SPARTAN_COMMON_OCEAN diff --git a/data/shaders/ocean/generate_maps.hlsl b/data/shaders/ocean/generate_maps.hlsl index 3c03a4c9e8..7b8b37435c 100644 --- a/data/shaders/ocean/generate_maps.hlsl +++ b/data/shaders/ocean/generate_maps.hlsl @@ -56,7 +56,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) OceanParameters params = surface.ocean_parameters; - float foam = htildeDisplacement.a; + float foam = slope_map[thread_id.xy].a; foam *= exp(-params.foamDecayRate); foam = saturate(foam); From 533696fdba970b2cf4ca8847edddd01361d33a84 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 6 Oct 2025 19:47:08 +0200 Subject: [PATCH 106/133] [ocean] added debug views for displacement and slope maps --- data/shaders/g_buffer.hlsl | 28 +++++++++-- source/editor/Widgets/Properties.cpp | 8 +-- source/runtime/Rendering/Material.h | 13 +++-- source/runtime/Rendering/Renderer_Passes.cpp | 51 ++++++-------------- 4 files changed, 49 insertions(+), 51 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 3094cc7f2e..1b7e580bda 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -298,11 +298,29 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) //const float foam_noise = compute_foam_noise(vertex.uv_misc.xy, buffer_frame.time); albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); - // display displacement map for debug purposes - if (material.ocean_parameters.displacementScale <= -1.0f) - albedo = tex2.SampleLevel(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy, 0).rgba; - else if (material.ocean_parameters.slopeScale <= -1.0f) // or display slope map - albedo = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy); + const float2 debug_values = pass_get_f2_value(); + + if (debug_values.x == 1.0f) // displacement + { + if (debug_values.y == 1.0f) // show synthesised version + { + // first we must synthesise again since we dont have access + // to the synthesised displacement (it's calculated in the vertex stage) + float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); + synthesize(tex2, displacement, world_space_tile_uv, tex_freq, tile_freq); + + albedo = displacement; + } + else // show original displacement + albedo = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgba; + } + else if (debug_values.x == 2.0f) // slope + { + if (debug_values.y == 1.0f) // show synthesised version + albedo = float4(slope.rgb, 1.0f); + else // show original slope + albedo = float4(tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgb, 1.0f); + } } // apply curved normals for grass blades diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 485a6f0901..85ea144105 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -926,14 +926,14 @@ void Properties::ShowMaterial(Material* material) const bool show_displacement = material->GetShowDisplacement(); ImGui::Checkbox("Show Displacement Map", &show_displacement); material->SetShowDiplacement(show_displacement); - if (show_displacement) - material->SetOceanProperty(OceanParameters::DisplacementScale, -1.0f); bool show_slope = material->GetShowSlope(); ImGui::Checkbox("Show Slope Map", &show_slope); material->SetShowSlope(show_slope); - if (show_slope) - material->SetOceanProperty(OceanParameters::SlopeScale, -1.0f); + + bool show_synthesised = material->GetShowSynthesised(); + ImGui::Checkbox("Show Synthesised Version", &show_synthesised); + material->SetShowSynthesised(show_synthesised); } // uv diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index e0592aca21..238fd459b7 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -179,11 +179,6 @@ namespace spartan void SetColor(const Color& color); bool IsTransparent() const { return GetProperty(MaterialProperty::ColorA) < 1.0f; } bool IsAlphaTested(); - bool IsOcean() const { return GetProperty(MaterialProperty::IsOcean) == 1.0f; } - void SetShowDiplacement(bool show) { m_show_displacement = show; } - bool GetShowDisplacement() const { return m_show_displacement; } - void SetShowSlope(bool show) { m_show_slope = show; } - bool GetShowSlope() const { return m_show_slope; } // misc void PrepareForGpu(); @@ -200,6 +195,13 @@ namespace spartan uint32_t GetOceanVerticesCount() const { return m_ocean_vertices_count; } void SetOceanTileSize(const float size) { m_ocean_tile_size = size; } float GetOceanTileSize() const { return m_ocean_tile_size; } + bool IsOcean() const { return GetProperty(MaterialProperty::IsOcean) == 1.0f; } + void SetShowDiplacement(bool show) { m_show_displacement = show; } + bool GetShowDisplacement() const { return m_show_displacement; } + void SetShowSlope(bool show) { m_show_slope = show; } + bool GetShowSlope() const { return m_show_slope; } + void SetShowSynthesised(bool show) { m_show_synthesised = show; } + bool GetShowSynthesised() const { return m_show_synthesised; } const std::array(MaterialProperty::Max)>& GetProperties() const { return m_properties; } const std::array(OceanParameters::Max)>& GetOceanProperties() const { return m_ocean_properties; } @@ -214,6 +216,7 @@ namespace spartan bool m_should_compute_spectrum = true; bool m_show_displacement = false; bool m_show_slope = false; + bool m_show_synthesised = false; uint32_t m_ocean_tiles = 1; uint32_t m_ocean_vertices_count = 0; float m_ocean_tile_size = 0.0f; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 6cea068421..25769aaf75 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -673,43 +673,6 @@ namespace spartan } } - //// ocean synthesis - //if (material->IsOcean()) - //{ - // tile_index++; - - // cmd_list->BeginTimeblock("Ocean Displacement Map Synthesis"); - - // RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); - // RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); - - // RHI_Texture* synthesised_displacement = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_displacement); - // RHI_Texture* synthesised_slope = GetRenderTarget(Renderer_RenderTarget::ocean_synthesised_slope); - - // displacement_map->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list, rhi_all_mips, 10); - - // RHI_PipelineState pso2; - // pso2.name = "ocean_displacement_map_synthesis"; - // pso2.shaders[Compute] = GetShader(Renderer_Shader::ocean_synthesise_maps_c); - // cmd_list->SetPipelineState(pso2); - - // cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); - // cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); - // cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_displacement, synthesised_displacement); - // cmd_list->SetTexture(Renderer_BindingsUav::ocean_synthesised_slope, synthesised_slope); - - // Vector3 tile_pos = renderable->GetEntity()->GetPosition(); - - // m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); - // cmd_list->PushConstants(m_pcb_pass_cpu); - - // cmd_list->Dispatch(synthesised_displacement); - - // synthesised_slope->SetLayout(RHI_Image_Layout::Shader_Read, cmd_list); - - // cmd_list->EndTimeblock(); - //} - cmd_list->SetPipelineState(pso); // pass constants @@ -723,6 +686,20 @@ namespace spartan { const Vector3 tile_pos = entity->GetPosition(); m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); + + float debug_maps = 0.0f; // 0.0f = none, 1.0f = displacement, 2.0f = slope + if (material->GetShowDisplacement()) + debug_maps = 1.0f; + else if (material->GetShowSlope()) + debug_maps = 2.0f; + + // 0.0f for original displacement/slope + // 1.0f for synthesised displacement/slope + float synthesised_flag = 0.0f; + if (material->GetShowSynthesised()) + synthesised_flag = 1.0f; + + m_pcb_pass_cpu.set_f2_value(debug_maps, synthesised_flag); } cmd_list->PushConstants(m_pcb_pass_cpu); From 2bc421b171ffa2ce3e80375eec0ad0c272012bd0 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 6 Oct 2025 23:51:42 +0200 Subject: [PATCH 107/133] [ocean] moving ocean with the camera --- source/runtime/Game/Game.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 5e8fcdce9a..9daf3394d4 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1722,7 +1722,7 @@ namespace spartan namespace ocean { - uint32_t ocean_tile_count = 2; + uint32_t ocean_tile_count = 6; float tile_size = 128.0f; uint32_t vertices_count = 512; shared_ptr material = make_shared(); @@ -1843,6 +1843,14 @@ namespace spartan } } } + + Vector3 camera_pos = default_camera->GetPosition(); + + //Vector3 ocean_pos = default_ocean->GetPosition(); + //Vector3 new_ocean_pos = ocean_pos; + //new_ocean_pos.x = camera_pos.x; + //new_ocean_pos.z = camera_pos.z; + //default_ocean->SetPosition(new_ocean_pos); } void on_shutdown() From 09f841a86b61e1d2ac45d3722a729c2476c6bfc8 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Tue, 7 Oct 2025 16:29:09 +0200 Subject: [PATCH 108/133] [ocean] basic flow map functionality --- data/shaders/auto_exposure.hlsl | 6 +-- data/shaders/g_buffer.hlsl | 24 ++++++++++-- data/shaders/ocean/synthesise_maps.hlsl | 40 ++++++++++++++++++++ source/editor/Widgets/Properties.cpp | 1 + source/runtime/Game/Game.cpp | 1 + source/runtime/Rendering/Material.h | 1 + source/runtime/Rendering/Renderer_Passes.cpp | 4 +- 7 files changed, 70 insertions(+), 7 deletions(-) diff --git a/data/shaders/auto_exposure.hlsl b/data/shaders/auto_exposure.hlsl index 77be2e5057..53f9e94173 100644 --- a/data/shaders/auto_exposure.hlsl +++ b/data/shaders/auto_exposure.hlsl @@ -32,15 +32,15 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) uint mip_index = mip_count - 1; tex.GetDimensions(mip_index, w, h, mip_count); float lum_sum = 0.0; - for (uint y = 0; y < h; y++) + for (uint y = 0; y < 16; y++) { - for (uint x = 0; x < w; x++) + for (uint x = 0; x < 16; x++) { float3 col = tex.Load(int3(x, y, mip_index)).rgb; lum_sum += dot(col, float3(0.2126, 0.7152, 0.0722)); } } - float lum = lum_sum / float(w * h); + float lum = lum_sum / (16.0 * 16.0); // read previous exposure float prev = tex2.Load(int3(0, 0, 0)).r; diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 1b7e580bda..d772830422 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -112,7 +112,13 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI const float tile_freq = 2.0f; float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, world_space_tile_uv, tex_freq, tile_freq); + float2 flow_dir = normalize(tex4.SampleLevel(samplers[sampler_point_clamp], world_space_tile_uv / float2(6.0f, 6.0f), 0).xy * 2 - 1); + //flow_dir = (1.0f, 1.0f); + float2 flow_uv = world_space_tile_uv / float2(6.0f, 6.0f); + + flow_dir = float2(1.0f, 1.0f); + flow_dir = normalize(flow_dir); + synthesize_with_flow(tex2, displacement, flow_dir, material.ocean_parameters.windDirection, world_space_tile_uv, tex_freq, tile_freq); input.position.xyz += displacement * material.ocean_parameters.displacementScale; } @@ -289,7 +295,13 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) const float tile_freq = 2.0f; float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex3, slope, world_space_tile_uv, tex_freq, tile_freq); + float2 flow_dir = (1.0f, 1.0f); + float2 flow_uv = world_space_tile_uv / float2(6.0f, 6.0f); + + flow_dir = float2(1.0f, 1.0f); + flow_dir = normalize(flow_dir); + //synthesize(tex3, slope, world_space_tile_uv, tex_freq, tile_freq); + synthesize_with_flow(tex3, slope, flow_dir, material.ocean_parameters.windDirection, world_space_tile_uv, tex_freq, tile_freq); slope = slope * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); @@ -307,7 +319,10 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) // first we must synthesise again since we dont have access // to the synthesised displacement (it's calculated in the vertex stage) float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, world_space_tile_uv, tex_freq, tile_freq); + float2 flow_dir = normalize(tex4.SampleLevel(samplers[sampler_point_clamp], world_space_tile_uv / float2(6.0f, 6.0f), 0).xy * 2 - 1); + flow_dir = float2(1.0f, 1.0f); + flow_dir = normalize(flow_dir); + synthesize_with_flow(tex2, displacement, flow_dir, material.ocean_parameters.windDirection, world_space_tile_uv, tex_freq, tile_freq); albedo = displacement; } @@ -321,6 +336,9 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) else // show original slope albedo = float4(tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgb, 1.0f); } + + //albedo = tex4.Sample(samplers[sampler_anisotropic_wrap], world_space_tile_uv / float2(6.0f, 6.0f)).rgba; + //albedo = float4(flow_dir, 0.0f, 1.0f); } // apply curved normals for grass blades diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index b34c18c6b4..f383ab7a06 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -95,6 +95,46 @@ void synthesize(Texture2D example, out float4 output, float2 uv, float tex_freq, preserve_variance(output, mean_example, moment2); } +void synthesize_with_flow(Texture2D example, out float4 output, float2 flow_dir, float wind_dir_deg, float2 uv, float tex_freq, float tile_freq) +{ + // convert wind dir from degrees to a 2d vector + const float wind_dir_rad = radians(wind_dir_deg); + const float2 wind_dir = float2(cos(wind_dir_rad), sin(wind_dir_rad)); + + const float theta = atan2(flow_dir.y, flow_dir.x) - atan2(wind_dir.y, wind_dir.x); + const float cosT = cos(theta); + const float sinT = sin(theta); + + // Rotation matrices + const float2x2 R2 = float2x2(cosT, -sinT, sinT, cosT); + const float4x4 R4 = float4x4( + cosT, 0, sinT, 0, + 0, 1, 0, 0, + -sinT, 0, cosT, 0, + 0, 0, 0, 1); + + output = float4(0.0f, 0.0f, 0.0f, 0.0f); // init to 0.0f for safety reasons + float moment2 = 0.0f; + + for (int i = 0; i < 3; i++) + { + const float3 interp_node = get_triangle_interp_node(uv, tile_freq, i); + + const float2 rotated_pos = mul(R2, (interp_node.xy - uv)) + uv; + + const float4 sample = get_texture_sample(example, rotated_pos, tex_freq, interp_node.xy); + + const float4 rotated_sample = mul(R4, sample); + + output += rotated_sample * interp_node.z; + moment2 += interp_node.z * interp_node.z; + } + // assumes example is a mip mapped 512x512 texture and samples lowest mip (1x1) + const float4 mean_example = example.SampleLevel(samplers[sampler_point_clamp], uv, 9); + + preserve_variance(output, mean_example, moment2); +} + float2 ocean_get_world_space_uvs(float2 uv, float2 tile_xz_pos, float tile_size) { const float2 tile_origin_uv_space = float2(tile_xz_pos.x / tile_size, tile_xz_pos.y / tile_size); diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 85ea144105..1109e1a06f 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -836,6 +836,7 @@ void Properties::ShowMaterial(Material* material) const show_property("Occlusion", "Amount of light loss, can be complementary to SSAO", MaterialTextureType::Occlusion, MaterialProperty::Max); show_property("Emission", "Light emission from the surface, works nice with bloom", MaterialTextureType::Emission, MaterialProperty::Max); show_property("Alpha mask", "Discards pixels", MaterialTextureType::AlphaMask, MaterialProperty::Max); + show_property("Flowmap", "Stores Flow Data", MaterialTextureType::Flowmap, MaterialProperty::Max); show_property("Clearcoat", "Extra white specular layer on top of others", MaterialTextureType::Max, MaterialProperty::Clearcoat); show_property("Clearcoat roughness", "Roughness of clearcoat specular", MaterialTextureType::Max, MaterialProperty::Clearcoat_Roughness); show_property("Anisotropic", "Amount of anisotropy for specular reflection", MaterialTextureType::Max, MaterialProperty::Anisotropic); diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 9daf3394d4..268d0e81f0 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -305,6 +305,7 @@ namespace spartan material->SetOceanTileSize(tile_size); material->SetOceanVerticesCount(density); + material->SetTexture(MaterialTextureType::Flowmap, "project\\materials\\water\\flowmap.png"); // if material fails to load from file if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 238fd459b7..3420ef113c 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -43,6 +43,7 @@ namespace spartan Height, // packed a AlphaMask, // packed into color a Packed, // occlusion, roughness, metalness, height + Flowmap, Max }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 25769aaf75..3654c5170a 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -650,7 +650,6 @@ namespace spartan pso.clear_color[3] = is_transparent_pass ? rhi_color_load : Color::standard_transparent; cmd_list->SetPipelineState(pso); - uint32_t tile_index = 0; // TEMPORARY for (uint32_t i = 0; i < m_draw_call_count; i++) { const Renderer_DrawCall& draw_call = m_draw_calls[i]; @@ -720,6 +719,9 @@ namespace spartan RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); + + RHI_Texture* flowmap = material->GetTexture(MaterialTextureType::Flowmap); + cmd_list->SetTexture(Renderer_BindingsSrv::tex4, flowmap); } cmd_list->DrawIndexed( From c6a1030d76b54e7daacb5286ef00c99bb44e2db4 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 8 Oct 2025 12:08:23 +0200 Subject: [PATCH 109/133] [ocean] removed flow dir for now and moved debug settings to material property --- data/shaders/common_resources.hlsl | 4 ++ data/shaders/common_structs.hlsl | 7 +++ data/shaders/g_buffer.hlsl | 46 +++++--------------- data/shaders/ocean/synthesise_maps.hlsl | 10 ++++- source/editor/Widgets/Properties.cpp | 12 ++--- source/runtime/Rendering/Material.cpp | 3 ++ source/runtime/Rendering/Material.h | 13 ++---- source/runtime/Rendering/Renderer.cpp | 3 ++ source/runtime/Rendering/Renderer_Buffers.h | 4 ++ source/runtime/Rendering/Renderer_Passes.cpp | 14 ------ 10 files changed, 51 insertions(+), 65 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index 8c0cca5acb..aa78a36409 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -137,6 +137,10 @@ struct MaterialParameters float displacementScale; float slopeScale; float lengthScale; + + float debugDisplacement; + float debugSlope; + float debugSynthesised; } ocean_parameters; bool has_texture_occlusion() { return (flags & (1 << 7)) != 0; } diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index 35d717d454..11fefb4133 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -54,6 +54,10 @@ struct OceanParameters float displacementScale; float slopeScale; float lengthScale; + + float debugDisplacement; + float debugSlope; + float debugSynthesised; }; struct Surface @@ -160,6 +164,9 @@ struct Surface ocean_parameters.displacementScale = material.ocean_parameters.displacementScale; ocean_parameters.slopeScale = material.ocean_parameters.slopeScale; ocean_parameters.lengthScale = material.ocean_parameters.lengthScale; + ocean_parameters.debugDisplacement = material.ocean_parameters.debugDisplacement; + ocean_parameters.debugSlope = material.ocean_parameters.debugSlope; + ocean_parameters.debugSynthesised = material.ocean_parameters.debugSynthesised; // roughness is authored as perceptual roughness, as is convention roughness_alpha = roughness * roughness; diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index d772830422..1e09574b8f 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -107,18 +107,10 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI const float2 tile_xz_pos = pass_values.xy; const float tile_size = pass_values.z; const float2 world_space_tile_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); - - const float tex_freq = 1.0f; - const float tile_freq = 2.0f; float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - float2 flow_dir = normalize(tex4.SampleLevel(samplers[sampler_point_clamp], world_space_tile_uv / float2(6.0f, 6.0f), 0).xy * 2 - 1); - //flow_dir = (1.0f, 1.0f); - float2 flow_uv = world_space_tile_uv / float2(6.0f, 6.0f); - - flow_dir = float2(1.0f, 1.0f); - flow_dir = normalize(flow_dir); - synthesize_with_flow(tex2, displacement, flow_dir, material.ocean_parameters.windDirection, world_space_tile_uv, tex_freq, tile_freq); + synthesize(tex2, displacement, world_space_tile_uv); + input.position.xyz += displacement * material.ocean_parameters.displacementScale; } @@ -290,55 +282,41 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) const float2 tile_xz_pos = pass_values.xy; const float tile_size = pass_values.z; const float2 world_space_tile_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, tile_xz_pos, tile_size); - - const float tex_freq = 1.0f; - const float tile_freq = 2.0f; float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); - float2 flow_dir = (1.0f, 1.0f); - float2 flow_uv = world_space_tile_uv / float2(6.0f, 6.0f); - - flow_dir = float2(1.0f, 1.0f); - flow_dir = normalize(flow_dir); - //synthesize(tex3, slope, world_space_tile_uv, tex_freq, tile_freq); - synthesize_with_flow(tex3, slope, flow_dir, material.ocean_parameters.windDirection, world_space_tile_uv, tex_freq, tile_freq); + synthesize(tex3, slope, world_space_tile_uv); slope = slope * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); // apply foam (foam mask is stored in the alpha channel of slope map) //const float foam_noise = compute_foam_noise(vertex.uv_misc.xy, buffer_frame.time); - albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); - - const float2 debug_values = pass_get_f2_value(); + //albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); - if (debug_values.x == 1.0f) // displacement + if (material.ocean_parameters.debugDisplacement == 1.0f) // displacement { - if (debug_values.y == 1.0f) // show synthesised version + if (material.ocean_parameters.debugSynthesised == 1.0f) // show synthesised version { // first we must synthesise again since we dont have access // to the synthesised displacement (it's calculated in the vertex stage) float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - float2 flow_dir = normalize(tex4.SampleLevel(samplers[sampler_point_clamp], world_space_tile_uv / float2(6.0f, 6.0f), 0).xy * 2 - 1); - flow_dir = float2(1.0f, 1.0f); - flow_dir = normalize(flow_dir); - synthesize_with_flow(tex2, displacement, flow_dir, material.ocean_parameters.windDirection, world_space_tile_uv, tex_freq, tile_freq); + synthesize(tex2, displacement, world_space_tile_uv); albedo = displacement; } else // show original displacement albedo = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgba; } - else if (debug_values.x == 2.0f) // slope + else if (material.ocean_parameters.debugSlope == 1.0f) // slope { - if (debug_values.y == 1.0f) // show synthesised version - albedo = float4(slope.rgb, 1.0f); + if (material.ocean_parameters.debugSynthesised == 1.0f) // show synthesised version + albedo = slope; else // show original slope - albedo = float4(tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgb, 1.0f); + albedo = tex3.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy); } //albedo = tex4.Sample(samplers[sampler_anisotropic_wrap], world_space_tile_uv / float2(6.0f, 6.0f)).rgba; - //albedo = float4(flow_dir, 0.0f, 1.0f); + //albedo = float4(flow_dir * 0.5f + 0.5f, 0.0f, 1.0f); } // apply curved normals for grass blades diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index f383ab7a06..bdd6d4f299 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -77,8 +77,11 @@ void preserve_variance(out float4 linear_color, float4 mean_color, float moment2 linear_color = (linear_color - mean_color) / sqrt(moment2) + mean_color; } -void synthesize(Texture2D example, out float4 output, float2 uv, float tex_freq, float tile_freq) +void synthesize(Texture2D example, out float4 output, float2 uv) { + const float tex_freq = 1.0f; + const float tile_freq = 2.0f; + output = float4(0.0f, 0.0f, 0.0f, 0.0f); // init to 0.0f for safety reasons float moment2 = 0.0f; @@ -95,8 +98,11 @@ void synthesize(Texture2D example, out float4 output, float2 uv, float tex_freq, preserve_variance(output, mean_example, moment2); } -void synthesize_with_flow(Texture2D example, out float4 output, float2 flow_dir, float wind_dir_deg, float2 uv, float tex_freq, float tile_freq) +void synthesize_with_flow(Texture2D example, out float4 output, float2 flow_dir, float wind_dir_deg, float2 uv) { + const float tex_freq = 1.0f; + const float tile_freq = 2.0f; + // convert wind dir from degrees to a 2d vector const float wind_dir_rad = radians(wind_dir_deg); const float2 wind_dir = float2(cos(wind_dir_rad), sin(wind_dir_rad)); diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 1109e1a06f..d51fe93dd9 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -924,17 +924,17 @@ void Properties::ShowMaterial(Material* material) const material->SetOceanVerticesCount(vertices_count); } - bool show_displacement = material->GetShowDisplacement(); + bool show_displacement = material->GetOceanProperty(OceanParameters::DebugDisplacement) == 1.0f ? true : false; ImGui::Checkbox("Show Displacement Map", &show_displacement); - material->SetShowDiplacement(show_displacement); + material->SetOceanProperty(OceanParameters::DebugDisplacement, show_displacement ? 1.0f : 0.0f); - bool show_slope = material->GetShowSlope(); + bool show_slope = material->GetOceanProperty(OceanParameters::DebugSlope) == 1.0f ? true : false; ImGui::Checkbox("Show Slope Map", &show_slope); - material->SetShowSlope(show_slope); + material->SetOceanProperty(OceanParameters::DebugSlope, show_slope ? 1.0f : 0.0f); - bool show_synthesised = material->GetShowSynthesised(); + bool show_synthesised = material->GetOceanProperty(OceanParameters::DebugSynthesised) == 1.0f ? true : false; ImGui::Checkbox("Show Synthesised Version", &show_synthesised); - material->SetShowSynthesised(show_synthesised); + material->SetOceanProperty(OceanParameters::DebugSynthesised, show_synthesised ? 1.0f : 0.0f); } // uv diff --git a/source/runtime/Rendering/Material.cpp b/source/runtime/Rendering/Material.cpp index af0430db93..bf8cf63bf1 100644 --- a/source/runtime/Rendering/Material.cpp +++ b/source/runtime/Rendering/Material.cpp @@ -129,6 +129,9 @@ namespace spartan case spartan::OceanParameters::DisplacementScale: return "displacement_scale"; case spartan::OceanParameters::SlopeScale: return "slope_scale"; case spartan::OceanParameters::LengthScale: return "length_scale"; + case spartan::OceanParameters::DebugDisplacement: return "debug_displacement"; + case spartan::OceanParameters::DebugSlope: return "debug_slope"; + case spartan::OceanParameters::DebugSynthesised: return "debug_synthesised"; case spartan::OceanParameters::Max: return "max"; default: { diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 3420ef113c..008bc5e483 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -142,6 +142,10 @@ namespace spartan SlopeScale, LengthScale, + DebugDisplacement, + DebugSlope, + DebugSynthesised, + Max }; @@ -197,12 +201,6 @@ namespace spartan void SetOceanTileSize(const float size) { m_ocean_tile_size = size; } float GetOceanTileSize() const { return m_ocean_tile_size; } bool IsOcean() const { return GetProperty(MaterialProperty::IsOcean) == 1.0f; } - void SetShowDiplacement(bool show) { m_show_displacement = show; } - bool GetShowDisplacement() const { return m_show_displacement; } - void SetShowSlope(bool show) { m_show_slope = show; } - bool GetShowSlope() const { return m_show_slope; } - void SetShowSynthesised(bool show) { m_show_synthesised = show; } - bool GetShowSynthesised() const { return m_show_synthesised; } const std::array(MaterialProperty::Max)>& GetProperties() const { return m_properties; } const std::array(OceanParameters::Max)>& GetOceanProperties() const { return m_ocean_properties; } @@ -215,9 +213,6 @@ namespace spartan std::mutex m_mutex; bool m_should_compute_spectrum = true; - bool m_show_displacement = false; - bool m_show_slope = false; - bool m_show_synthesised = false; uint32_t m_ocean_tiles = 1; uint32_t m_ocean_vertices_count = 0; float m_ocean_tile_size = 0.0f; diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index 08e687b26f..d5ec7f2611 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -854,6 +854,9 @@ namespace spartan properties[count].jonswap_parameters.displacementScale = material->GetOceanProperty(OceanParameters::DisplacementScale); properties[count].jonswap_parameters.slopeScale = material->GetOceanProperty(OceanParameters::SlopeScale); properties[count].jonswap_parameters.lengthScale = material->GetOceanProperty(OceanParameters::LengthScale); + properties[count].jonswap_parameters.debugDisplacement = material->GetOceanProperty(OceanParameters::DebugDisplacement); + properties[count].jonswap_parameters.debugSlope = material->GetOceanProperty(OceanParameters::DebugSlope); + properties[count].jonswap_parameters.debugSynthesised = material->GetOceanProperty(OceanParameters::DebugSynthesised); // flags properties[count].flags = material->HasTextureOfType(MaterialTextureType::Height) ? (1U << 0) : 0; diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index 1bb33cf382..39d62cbbfe 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -210,6 +210,10 @@ namespace spartan float displacementScale; float slopeScale; float lengthScale; + + float debugDisplacement; + float debugSlope; + float debugSynthesised; } jonswap_parameters; }; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 3654c5170a..b5d55d190f 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -685,20 +685,6 @@ namespace spartan { const Vector3 tile_pos = entity->GetPosition(); m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); - - float debug_maps = 0.0f; // 0.0f = none, 1.0f = displacement, 2.0f = slope - if (material->GetShowDisplacement()) - debug_maps = 1.0f; - else if (material->GetShowSlope()) - debug_maps = 2.0f; - - // 0.0f for original displacement/slope - // 1.0f for synthesised displacement/slope - float synthesised_flag = 0.0f; - if (material->GetShowSynthesised()) - synthesised_flag = 1.0f; - - m_pcb_pass_cpu.set_f2_value(debug_maps, synthesised_flag); } cmd_list->PushConstants(m_pcb_pass_cpu); From ccdf2abc3deacf0756c4ece0b473b31bafeee6a8 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 8 Oct 2025 14:31:20 +0200 Subject: [PATCH 110/133] [ocean] re-enabled foam --- data/shaders/g_buffer.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 1e09574b8f..acf02d655b 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -291,7 +291,7 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) // apply foam (foam mask is stored in the alpha channel of slope map) //const float foam_noise = compute_foam_noise(vertex.uv_misc.xy, buffer_frame.time); - //albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); + albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); if (material.ocean_parameters.debugDisplacement == 1.0f) // displacement { From 49db853fd0832f90127ec9c0d2ee6ff1bba07090 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 8 Oct 2025 17:44:25 +0200 Subject: [PATCH 111/133] [ocean] improved flow synthesis implementation --- data/shaders/g_buffer.hlsl | 11 +++-- data/shaders/ocean/synthesise_maps.hlsl | 66 ++++++------------------- 2 files changed, 22 insertions(+), 55 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index acf02d655b..d1922d65b1 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -109,7 +109,8 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI const float2 world_space_tile_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, world_space_tile_uv); + //synthesize(tex2, displacement, world_space_tile_uv); + synthesize_with_flow(tex2, displacement, material.ocean_parameters.windDirection, world_space_tile_uv); input.position.xyz += displacement * material.ocean_parameters.displacementScale; } @@ -284,7 +285,8 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) const float2 world_space_tile_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, tile_xz_pos, tile_size); float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex3, slope, world_space_tile_uv); + //synthesize(tex3, slope, world_space_tile_uv); + synthesize_with_flow(tex3, slope, material.ocean_parameters.windDirection, world_space_tile_uv); slope = slope * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); @@ -300,8 +302,9 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) // first we must synthesise again since we dont have access // to the synthesised displacement (it's calculated in the vertex stage) float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, world_space_tile_uv); - + //synthesize(tex2, displacement, world_space_tile_uv); + synthesize_with_flow(tex2, displacement, material.ocean_parameters.windDirection, world_space_tile_uv); + albedo = displacement; } else // show original displacement diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index bdd6d4f299..ff155200f0 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -98,7 +98,7 @@ void synthesize(Texture2D example, out float4 output, float2 uv) preserve_variance(output, mean_example, moment2); } -void synthesize_with_flow(Texture2D example, out float4 output, float2 flow_dir, float wind_dir_deg, float2 uv) +void synthesize_with_flow(Texture2D example, out float4 output, float wind_dir_deg, float2 uv) { const float tex_freq = 1.0f; const float tile_freq = 2.0f; @@ -106,18 +106,6 @@ void synthesize_with_flow(Texture2D example, out float4 output, float2 flow_dir, // convert wind dir from degrees to a 2d vector const float wind_dir_rad = radians(wind_dir_deg); const float2 wind_dir = float2(cos(wind_dir_rad), sin(wind_dir_rad)); - - const float theta = atan2(flow_dir.y, flow_dir.x) - atan2(wind_dir.y, wind_dir.x); - const float cosT = cos(theta); - const float sinT = sin(theta); - - // Rotation matrices - const float2x2 R2 = float2x2(cosT, -sinT, sinT, cosT); - const float4x4 R4 = float4x4( - cosT, 0, sinT, 0, - 0, 1, 0, 0, - -sinT, 0, cosT, 0, - 0, 0, 0, 1); output = float4(0.0f, 0.0f, 0.0f, 0.0f); // init to 0.0f for safety reasons float moment2 = 0.0f; @@ -126,13 +114,22 @@ void synthesize_with_flow(Texture2D example, out float4 output, float2 flow_dir, { const float3 interp_node = get_triangle_interp_node(uv, tile_freq, i); - const float2 rotated_pos = mul(R2, (interp_node.xy - uv)) + uv; + float2 to_center = interp_node.xy - float2(3.0f, 3.0f); + float2 flow_dir = normalize(float2(-to_center.y, to_center.x)); + const float theta = atan2(flow_dir.y, flow_dir.x) - atan2(wind_dir.y, wind_dir.x); + const float cosT = cos(theta); + const float sinT = sin(theta); + const float2x2 R2 = float2x2(cosT, -sinT, sinT, cosT); + const float3x3 R3 = float3x3(cosT, 0, sinT, + 0, 1, 0, + -sinT, 0, cosT); + const float2 rotated_pos = interp_node.xy + mul(R2, uv - interp_node.xy); - const float4 sample = get_texture_sample(example, rotated_pos, tex_freq, interp_node.xy); - - const float4 rotated_sample = mul(R4, sample); + float4 sample = get_texture_sample(example, rotated_pos, tex_freq, interp_node.xy); - output += rotated_sample * interp_node.z; + sample.xyz = mul(R3, sample.xyz); + + output += sample * interp_node.z; moment2 += interp_node.z * interp_node.z; } // assumes example is a mip mapped 512x512 texture and samples lowest mip (1x1) @@ -147,36 +144,3 @@ float2 ocean_get_world_space_uvs(float2 uv, float2 tile_xz_pos, float tile_size) return tile_origin_uv_space + uv; } - -//[numthreads(8, 8, 1)] -//void main_cs(uint3 thread_id : SV_DispatchThreadID) -//{ -// const uint2 pixel_coord = thread_id.xy; -// uint2 texture_size; -// synthesised_displacement.GetDimensions(texture_size.x, texture_size.y); - -// float2 uv = (pixel_coord + 0.5f) / texture_size; - -// const float3 pass_values = pass_get_f3_value(); -// const float2 tile_xz_pos = pass_values.xy; -// const float tile_size = pass_values.z; - -// const float2 tile_origin_uv_space = float2(tile_xz_pos.x / tile_size, tile_xz_pos.y / tile_size); - -// uv = tile_origin_uv_space + uv; - -// const float tex_freq = 1.0f; -// const float tile_freq = 2.0f; - -// float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); -// synthesize(tex2, displacement, uv, tex_freq, tile_freq); - -// float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); -// synthesize(tex3, slope, uv, tex_freq, tile_freq); - -// synthesised_displacement[thread_id.xy] = displacement; -// synthesised_slope[thread_id.xy] = slope; - -// //synthesised_displacement[thread_id.xy] = tex2[thread_id.xy]; -// //synthesised_slope[thread_id.xy] = tex3[thread_id.xy]; -//} From 47a801c510248eb14ebc685d73f19e01b9717e7c Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sat, 11 Oct 2025 23:51:37 +0200 Subject: [PATCH 112/133] [ocean] got latest from upstream --- data/shaders/auto_exposure.hlsl | 6 +++--- data/shaders/common_structs.hlsl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/shaders/auto_exposure.hlsl b/data/shaders/auto_exposure.hlsl index d200682a18..fc22626c34 100644 --- a/data/shaders/auto_exposure.hlsl +++ b/data/shaders/auto_exposure.hlsl @@ -58,7 +58,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) // min/max exposure in EV const float min_ev = -0.2f; // dark - const float max_ev = 1.5; // bright + const float max_ev = 1.5; // bright // convert to linear float min_exposure = exp2(min_ev); @@ -69,8 +69,8 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) // exponential adaptation float adaptation_speed = pass_get_f3_value().x; - float tau = 1.0 / max(adaptation_speed, 0.001); - float exposure = prev + (desired_exposure - prev) * (1.0 - exp(-tau * buffer_frame.delta_time)); + float tau = 1.0 / max(adaptation_speed, 0.001); + float exposure = prev + (desired_exposure - prev) * (1.0 - exp(-tau * buffer_frame.delta_time)); // write output tex_uav[uint2(0, 0)] = float4(exposure, exposure, exposure, 1.0); diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index b0218a5709..5e78b7ecfc 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -106,7 +106,7 @@ struct Surface bool is_flower() { return flags & uint(1U << 12); } bool is_water() { return flags & uint(1U << 13); } bool is_tessellated() { return flags & uint(1U << 14); } - bool is_ocean() { return flags & uint(1U << 15); } + bool is_ocean() { return flags & uint(1U << 16); } bool is_sky() { return alpha == 0.0f; } bool is_opaque() { return alpha == 1.0f; } bool is_transparent() { return alpha > 0.0f && alpha < 1.0f; } From bee18075e573d7aedaeec1aa88b2208c35febe5d Mon Sep 17 00:00:00 2001 From: George Bolba Date: Sat, 11 Oct 2025 23:58:59 +0200 Subject: [PATCH 113/133] [ocean] check if surface is ocean before applying vertex displacement --- data/shaders/g_buffer.hlsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 93022627bf..c9f078d203 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -100,8 +100,10 @@ static float4 sample_texture(gbuffer_vertex vertex, uint texture_index, Surface gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { MaterialParameters material = GetMaterial(); + Surface surface; + surface.flags = material.flags; - if (material.ocean_parameters.displacementScale > -1.0f) + if (surface.is_ocean() && material.ocean_parameters.displacementScale > -1.0f) { const float3 pass_values = pass_get_f3_value(); const float2 tile_xz_pos = pass_values.xy; @@ -120,8 +122,6 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform, position_world, position_world_previous); // transform world space position to clip space - Surface surface; - surface.flags = material.flags; if (!surface.is_tessellated()) { vertex = transform_to_clip_space(vertex, position_world, position_world_previous); From 852a8acd80c71f4cbd14e7fee45e9b15438c8a11 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Tue, 14 Oct 2025 13:58:43 +0200 Subject: [PATCH 114/133] [ocean] depth prepass fix --- data/shaders/depth_prepass.hlsl | 19 +++++++++++++++++++ source/runtime/Game/Game.cpp | 19 +++++++++++++++---- source/runtime/Rendering/Renderer_Passes.cpp | 16 +++++++++++++++- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/data/shaders/depth_prepass.hlsl b/data/shaders/depth_prepass.hlsl index 359ca8a19d..bb409927b6 100644 --- a/data/shaders/depth_prepass.hlsl +++ b/data/shaders/depth_prepass.hlsl @@ -21,11 +21,30 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //= INCLUDES ========= #include "common.hlsl" +#include "ocean/synthesise_maps.hlsl" //==================== gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceID) { gbuffer_vertex vertex; + + MaterialParameters material = GetMaterial(); + Surface surface; + surface.flags = material.flags; + + if (surface.is_ocean()) + { + const float3 pass_values = pass_get_f3_value2(); + const float2 tile_xz_pos = pass_values.xy; + const float tile_size = pass_values.z; + const float2 world_space_tile_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); + + float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); + //synthesize(tex2, displacement, world_space_tile_uv); + synthesize_with_flow(tex2, displacement, material.ocean_parameters.windDirection, world_space_tile_uv); + + input.position.xyz += displacement * material.ocean_parameters.displacementScale; + } // transform to world space float3 position_world = 0.0f; diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 99443798c3..6418c79891 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -845,16 +845,21 @@ namespace spartan namespace forest { + uint32_t ocean_tile_count = 1; + float tile_size = 128.0f; + uint32_t vertices_count = 512; + shared_ptr ocean_material = make_shared(); + void create() { // tweak without exceeding a vram usage of 8 GB (that is until streaming is implemented) const float render_distance_trees = 2'000.0f; const float render_distance_foliage = 500.0f; const float shadow_distance = 150.0f; // beyond that, screen space shadows are enough - const float per_triangle_density_grass_blade = 15.0f; - const float per_triangle_density_flower = 0.2f; - const float per_triangle_density_tree = 0.004f; - const float per_triangle_density_rock = 0.001f; + const float per_triangle_density_grass_blade = 0.0f; + const float per_triangle_density_flower = 0.0f; + const float per_triangle_density_tree = 0.000f; + const float per_triangle_density_rock = 0.000f; // sun/lighting/mood entities::sun(true); @@ -870,6 +875,7 @@ namespace spartan // create default_terrain = World::CreateEntity(); default_terrain->SetObjectName("terrain"); + default_ocean = entities::ocean(ocean_material, { 0.0f, 0.0f, 0.0f }, tile_size, vertices_count, ocean_tile_count); // sound { @@ -964,6 +970,11 @@ namespace spartan } } + // TEMP - ocean + //RHI_Texture* height_map = terrain->GetHeightMapFinal(); + default_ocean = entities::ocean(ocean_material, { 0.0f, 0.0f, 0.0f }, tile_size, vertices_count, ocean_tile_count); + //ocean_material->SetTexture(MaterialTextureType::Height, height_map); + // water const float dimension = 8000; // meters const uint32_t density = 64; // geometric diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 0e37d2b379..1e53407ee2 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -578,6 +578,13 @@ namespace spartan bool is_tessellated = material->GetProperty(MaterialProperty::Tessellation) > 0.0f; bool has_color_texture = material->HasTextureOfType(MaterialTextureType::Color); m_pcb_pass_cpu.set_f3_value(is_tessellated ? 1.0f : 0.0f, has_color_texture ? 1.0f : 0.0f, static_cast(i)); + + if (material->IsOcean()) + { + const Vector3 tile_pos = renderable->GetEntity()->GetPosition(); + m_pcb_pass_cpu.set_f3_value2(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); + } + m_pcb_pass_cpu.set_is_transparent_and_material_index(false, material->GetIndex()); m_pcb_pass_cpu.transform = renderable->GetEntity()->GetMatrix(); cmd_list->PushConstants(m_pcb_pass_cpu); @@ -591,6 +598,12 @@ namespace spartan cmd_list->SetBufferVertex(renderable->GetVertexBuffer(), renderable->GetInstanceBuffer()); cmd_list->SetBufferIndex(renderable->GetIndexBuffer()); + if (material->IsOcean()) + { + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); + } + cmd_list->DrawIndexed( renderable->GetIndexCount(draw_call.lod_index), renderable->GetIndexOffset(draw_call.lod_index), @@ -706,7 +719,8 @@ namespace spartan RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); - RHI_Texture* flowmap = material->GetTexture(MaterialTextureType::Flowmap); + //RHI_Texture* flowmap = material->GetTexture(MaterialTextureType::Flowmap); + RHI_Texture* flowmap = material->GetTexture(MaterialTextureType::Height); cmd_list->SetTexture(Renderer_BindingsSrv::tex4, flowmap); } From 7d4123200e88a215ca0ef0d3611cc09b473b92c8 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Thu, 16 Oct 2025 23:31:14 +0200 Subject: [PATCH 115/133] [ocean] attempt at generating flowmap --- data/shaders/g_buffer.hlsl | 12 +++- source/runtime/Game/Game.cpp | 71 +++++++++++++++++++- source/runtime/Rendering/Material.h | 4 +- source/runtime/Rendering/Renderer_Passes.cpp | 12 ++-- 4 files changed, 86 insertions(+), 13 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index b5c31ce7de..8628833e84 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -1,4 +1,4 @@ -/* +/* Copyright(c) 2015-2025 Panos Karabelas Permission is hereby granted, free of charge, to any person obtaining a copy @@ -113,14 +113,19 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex2, displacement, world_space_tile_uv); synthesize_with_flow(tex2, displacement, material.ocean_parameters.windDirection, world_space_tile_uv); + + const float2 world_pos = mul(input.position, buffer_pass.transform).xz; + + const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); - input.position.xyz += displacement * material.ocean_parameters.displacementScale; + input.position.y += tex4.SampleLevel(samplers[sampler_point_wrap], uv, 0); + input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; } float3 position_world = 0.0f; float3 position_world_previous = 0.0f; gbuffer_vertex vertex = transform_to_world_space(input, instance_id, buffer_pass.transform, position_world, position_world_previous); - + // transform world space position to clip space if (!surface.is_tessellated()) { @@ -370,6 +375,7 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) //albedo = tex4.Sample(samplers[sampler_anisotropic_wrap], world_space_tile_uv / float2(6.0f, 6.0f)).rgba; //albedo = float4(flow_dir * 0.5f + 0.5f, 0.0f, 1.0f); + albedo = tex5.Sample(samplers[sampler_point_clamp], vertex.uv_misc.xy); } // apply curved normals for foliage diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index ed86a5002d..4623752c59 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -304,6 +304,7 @@ namespace spartan material->SetOceanTileSize(tile_size); material->SetOceanVerticesCount(density); + material->MarkSpectrumAsComputed(false); material->SetTexture(MaterialTextureType::Flowmap, "project\\materials\\water\\flowmap.png"); // if material fails to load from file @@ -848,6 +849,7 @@ namespace spartan float tile_size = 128.0f; uint32_t vertices_count = 512; shared_ptr ocean_material = make_shared(); + shared_ptr flow_map; void create() { @@ -970,9 +972,72 @@ namespace spartan } // TEMP - ocean - //RHI_Texture* height_map = terrain->GetHeightMapFinal(); - default_ocean = entities::ocean(ocean_material, { 0.0f, 0.0f, 0.0f }, tile_size, vertices_count, ocean_tile_count); - //ocean_material->SetTexture(MaterialTextureType::Height, height_map); + RHI_Texture* height_map = terrain->GetHeightMapFinal(); + //default_ocean = entities::ocean(ocean_material, { 0.0f, 0.0f, 0.0f }, tile_size, vertices_count, ocean_tile_count); + ocean_material->SetTexture(MaterialTextureType::Height, height_map); + // generate flowmap + { + std::vector height_data = height_map->GetMip(0, 0).bytes; + SP_ASSERT(height_data.size() > 0); + + const uint32_t tex_width = height_map->GetWidth(); + const uint32_t tex_height = height_map->GetHeight(); + + std::vector flow_data(tex_width * tex_height); + + // compute flow directions + for (uint32_t y = 1; y < tex_height - 1; y++) + { + for (uint32_t x = 1; x < tex_width - 1; x++) + { + // Sample neighbors + const float hL = (float)height_data[y * tex_width + (x - 1)]; + const float hR = (float)height_data[y * tex_width + (x + 1)]; + const float hU = (float)height_data[(y + 1) * tex_width + x]; + const float hD = (float)height_data[(y - 1) * tex_width + x]; + + // Compute gradient + const float dhdx = (hR - hL) * 0.5f; + const float dhdy = (hU - hD) * 0.5f; + + // Flow direction = -gradient (downhill) + Vector2 flow = { -dhdx, -dhdy }; + + const float len = std::sqrt(flow.x * flow.x + flow.y * flow.y); + if (len > 0.0001f) + { + flow.x /= len; + flow.y /= len; + } + + // Encode to [0,1] + const float fx = flow.x * 0.5f + 0.5f; + const float fy = flow.y * 0.5f + 0.5f; + + flow_data[x + y * tex_width] = Vector2(fx * 255.0f, fy * 255.0f); + } + } + + vector data(1); + auto& slice = data[0]; + slice.mips.resize(1); + auto& mip_bytes = slice.mips[0].bytes; + mip_bytes.resize(tex_width * tex_height * sizeof(Vector2)); + + auto copy_data = [&flow_data, &mip_bytes](uint32_t start, uint32_t end) + { + for (uint32_t i = start; i < end; i++) + { + memcpy(mip_bytes.data() + i * sizeof(Vector2), &flow_data[i], sizeof(Vector2)); + } + }; + ThreadPool::ParallelLoop(copy_data, tex_width * tex_height); + + flow_map = std::make_shared(RHI_Texture_Type::Type2D, tex_width, tex_height, 1, 1, + RHI_Format::R8G8_Unorm, RHI_Texture_Srv, "terrain_flowmap", data); + + ocean_material->SetTexture(MaterialTextureType::Flowmap, flow_map); + } // water const float dimension = 8000; // meters diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 19ae2fa0ef..3c51d598d8 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -194,7 +194,7 @@ namespace spartan // ocean bool ShouldComputeSpectrum() const { return m_should_compute_spectrum; } - void MarkSpectrumAsComputed() { m_should_compute_spectrum = false; } + void MarkSpectrumAsComputed(const bool flag) { m_should_compute_spectrum = !flag; } void SetOceanTileCount(const uint32_t count) { m_ocean_tiles = count; } uint32_t GetOceanTileCount() const { return m_ocean_tiles; } void SetOceanVerticesCount(const uint32_t count) { m_ocean_vertices_count = count; } @@ -213,7 +213,7 @@ namespace spartan uint32_t m_index = 0; std::mutex m_mutex; - bool m_should_compute_spectrum = true; + bool m_should_compute_spectrum = false; uint32_t m_ocean_tiles = 1; uint32_t m_ocean_vertices_count = 0; float m_ocean_tile_size = 0.0f; diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 7c3d3ef6d3..5fb735237a 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -121,7 +121,7 @@ namespace spartan // calculates conjugate and stores it in BA channels of the initial spectrum Pass_PackSpectrum(cmd_list_graphics_present); - material->MarkSpectrumAsComputed(); + material->MarkSpectrumAsComputed(true); } // computes displacement and slope maps @@ -156,7 +156,7 @@ namespace spartan } Pass_Light_ImageBased(cmd_list_graphics_present); - //Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); + Pass_TransparencyReflectionRefraction(cmd_list_graphics_present); Pass_AA_Upscale(cmd_list_graphics_present); Pass_PostProcess(cmd_list_graphics_present); @@ -720,9 +720,11 @@ namespace spartan RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); - //RHI_Texture* flowmap = material->GetTexture(MaterialTextureType::Flowmap); - RHI_Texture* flowmap = material->GetTexture(MaterialTextureType::Height); - cmd_list->SetTexture(Renderer_BindingsSrv::tex4, flowmap); + RHI_Texture* heightmap = material->GetTexture(MaterialTextureType::Height); + cmd_list->SetTexture(Renderer_BindingsSrv::tex4, heightmap); + + RHI_Texture* flowmap = material->GetTexture(MaterialTextureType::Flowmap); + cmd_list->SetTexture(Renderer_BindingsSrv::tex5, flowmap); } cmd_list->DrawIndexed( From eeac753f73525521a0f3307bc8974490d86ce06b Mon Sep 17 00:00:00 2001 From: George Bolba Date: Fri, 17 Oct 2025 01:07:00 +0200 Subject: [PATCH 116/133] [ocean] correct flowmap generation --- data/shaders/g_buffer.hlsl | 2 +- source/runtime/Game/Game.cpp | 74 +++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 8628833e84..f3b47bbfb2 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -375,7 +375,7 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) //albedo = tex4.Sample(samplers[sampler_anisotropic_wrap], world_space_tile_uv / float2(6.0f, 6.0f)).rgba; //albedo = float4(flow_dir * 0.5f + 0.5f, 0.0f, 1.0f); - albedo = tex5.Sample(samplers[sampler_point_clamp], vertex.uv_misc.xy); + //albedo = tex5.Sample(samplers[sampler_point_clamp], vertex.uv_misc.xy); } // apply curved normals for foliage diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 4623752c59..2ac3295ccd 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -977,44 +977,57 @@ namespace spartan ocean_material->SetTexture(MaterialTextureType::Height, height_map); // generate flowmap { - std::vector height_data = height_map->GetMip(0, 0).bytes; - SP_ASSERT(height_data.size() > 0); + const float* height_data = reinterpret_cast(height_map->GetMip(0, 0).bytes.data()); const uint32_t tex_width = height_map->GetWidth(); const uint32_t tex_height = height_map->GetHeight(); + SP_ASSERT(height_map->GetMip(0, 0).bytes.size() == tex_width * tex_height * sizeof(float)); + std::vector flow_data(tex_width * tex_height); - // compute flow directions - for (uint32_t y = 1; y < tex_height - 1; y++) + for (uint32_t y = 0; y < tex_height; y++) { - for (uint32_t x = 1; x < tex_width - 1; x++) + for (uint32_t x = 0; x < tex_width; x++) { - // Sample neighbors - const float hL = (float)height_data[y * tex_width + (x - 1)]; - const float hR = (float)height_data[y * tex_width + (x + 1)]; - const float hU = (float)height_data[(y + 1) * tex_width + x]; - const float hD = (float)height_data[(y - 1) * tex_width + x]; + float gx = 0.0f; + float gy = 0.0f; + int samples = 0; - // Compute gradient - const float dhdx = (hR - hL) * 0.5f; - const float dhdy = (hU - hD) * 0.5f; + const int kernelRadius = 2; // try 2–4 for smoother flow + for (int ky = -kernelRadius; ky <= kernelRadius; ky++) + { + for (int kx = -kernelRadius; kx <= kernelRadius; kx++) + { + uint32_t ix = std::clamp(x + kx, 0, tex_width - 1); + uint32_t iy = std::clamp(y + ky, 0, tex_height - 1); + uint32_t ixL = std::clamp(ix - 1, 0, tex_width - 1); + uint32_t ixR = std::clamp(ix + 1, 0, tex_width - 1); + uint32_t iyU = std::clamp(iy + 1, 0, tex_height - 1); + uint32_t iyD = std::clamp(iy - 1, 0, tex_height - 1); + + float hL = height_data[iy * tex_width + ixL]; + float hR = height_data[iy * tex_width + ixR]; + float hU = height_data[iyU * tex_width + ix]; + float hD = height_data[iyD * tex_width + ix]; + + gx += (hR - hL); + gy += (hD - hU); + samples++; + } + } - // Flow direction = -gradient (downhill) - Vector2 flow = { -dhdx, -dhdy }; + gx /= (samples * 2.0f); + gy /= (samples * 2.0f); - const float len = std::sqrt(flow.x * flow.x + flow.y * flow.y); + Vector2 flow = { -gx, -gy }; + float len = std::sqrt(flow.x * flow.x + flow.y * flow.y); if (len > 0.0001f) - { - flow.x /= len; - flow.y /= len; - } + flow /= len; // Encode to [0,1] - const float fx = flow.x * 0.5f + 0.5f; - const float fy = flow.y * 0.5f + 0.5f; - - flow_data[x + y * tex_width] = Vector2(fx * 255.0f, fy * 255.0f); + flow_data[y * tex_width + x] = Vector2(flow.x * 0.5f + 0.5f, flow.y * 0.5f + 0.5f); + //flow_data[y * tex_width + x] = Vector2(1.0f, 0.0f); } } @@ -1022,13 +1035,22 @@ namespace spartan auto& slice = data[0]; slice.mips.resize(1); auto& mip_bytes = slice.mips[0].bytes; - mip_bytes.resize(tex_width * tex_height * sizeof(Vector2)); + mip_bytes.resize(tex_width * tex_height * 2); // 2 bytes per pixel for R8G8_Unorm auto copy_data = [&flow_data, &mip_bytes](uint32_t start, uint32_t end) { for (uint32_t i = start; i < end; i++) { - memcpy(mip_bytes.data() + i * sizeof(Vector2), &flow_data[i], sizeof(Vector2)); + const Vector2& f = flow_data[i]; + //float fx = (f.x * 0.5f) + 0.5f; + //float fy = (f.y * 0.5f) + 0.5f; + // clamp to [0,1] + float fx = std::clamp(f.x, 0.0f, 1.0f); + float fy = std::clamp(f.y, 0.0f, 1.0f); + uint8_t r = static_cast(std::round(fx * 255.0f)); + uint8_t g = static_cast(std::round(fy * 255.0f)); + mip_bytes[i * 2 + 0] = static_cast(r); + mip_bytes[i * 2 + 1] = static_cast(g); } }; ThreadPool::ParallelLoop(copy_data, tex_width * tex_height); From 2ac1ee13adf33830b11c948f46e9c66f10c187a2 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Fri, 17 Oct 2025 17:36:45 +0200 Subject: [PATCH 117/133] [ocean] apply world flow map to water --- data/shaders/depth_prepass.hlsl | 4 ++-- data/shaders/g_buffer.hlsl | 16 +++++++-------- data/shaders/ocean/synthesise_maps.hlsl | 26 ++++++++++++++++++------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/data/shaders/depth_prepass.hlsl b/data/shaders/depth_prepass.hlsl index bb409927b6..e38b76d374 100644 --- a/data/shaders/depth_prepass.hlsl +++ b/data/shaders/depth_prepass.hlsl @@ -37,11 +37,11 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI const float3 pass_values = pass_get_f3_value2(); const float2 tile_xz_pos = pass_values.xy; const float tile_size = pass_values.z; - const float2 world_space_tile_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); + const float2 tile_local_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, material.ocean_parameters.windDirection, world_space_tile_uv); + synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); input.position.xyz += displacement * material.ocean_parameters.displacementScale; } diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index f3b47bbfb2..dbda391fba 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -108,15 +108,15 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI const float3 pass_values = pass_get_f3_value(); const float2 tile_xz_pos = pass_values.xy; const float tile_size = pass_values.z; - const float2 world_space_tile_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); + const float2 tile_local_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); - float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, material.ocean_parameters.windDirection, world_space_tile_uv); - const float2 world_pos = mul(input.position, buffer_pass.transform).xz; const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); + + float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); + //synthesize(tex2, displacement, world_space_tile_uv); + synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); input.position.y += tex4.SampleLevel(samplers[sampler_point_wrap], uv, 0); input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; @@ -337,11 +337,11 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) const float3 pass_values = pass_get_f3_value(); const float2 tile_xz_pos = pass_values.xy; const float tile_size = pass_values.z; - const float2 world_space_tile_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, tile_xz_pos, tile_size); + const float2 tile_local_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, tile_xz_pos, tile_size); float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex3, slope, world_space_tile_uv); - synthesize_with_flow(tex3, slope, material.ocean_parameters.windDirection, world_space_tile_uv); + synthesize_with_flow(tex3, slope, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); slope = slope * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); @@ -358,7 +358,7 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) // to the synthesised displacement (it's calculated in the vertex stage) float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, material.ocean_parameters.windDirection, world_space_tile_uv); + synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv, true); albedo = displacement; } diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index ff155200f0..26ca0a0b38 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -98,7 +98,7 @@ void synthesize(Texture2D example, out float4 output, float2 uv) preserve_variance(output, mean_example, moment2); } -void synthesize_with_flow(Texture2D example, out float4 output, float wind_dir_deg, float2 uv) +void synthesize_with_flow(Texture2D example, out float4 output, Texture2D flowmap, float2 tile_xz_pos, float wind_dir_deg, float2 tile_local_uv, bool debug_flow = false) { const float tex_freq = 1.0f; const float tile_freq = 2.0f; @@ -109,21 +109,30 @@ void synthesize_with_flow(Texture2D example, out float4 output, float wind_dir_d output = float4(0.0f, 0.0f, 0.0f, 0.0f); // init to 0.0f for safety reasons float moment2 = 0.0f; + float2 output_flow = float2(0.0f, 0.0f); for (int i = 0; i < 3; i++) { - const float3 interp_node = get_triangle_interp_node(uv, tile_freq, i); + const float3 interp_node = get_triangle_interp_node(tile_local_uv, tile_freq, i); + + //float2 to_center = interp_node.xy - float2(3.0f, 3.0f); + //float2 flow_dir = normalize(float2(-to_center.y, to_center.x)); + + float2 node_world_pos = (tile_xz_pos - 0.5f * 128.0f) + interp_node.xy * 128.0f; + float2 node_world_uv = (node_world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); - float2 to_center = interp_node.xy - float2(3.0f, 3.0f); - float2 flow_dir = normalize(float2(-to_center.y, to_center.x)); - const float theta = atan2(flow_dir.y, flow_dir.x) - atan2(wind_dir.y, wind_dir.x); + float2 flow_dir = flowmap.SampleLevel(samplers[sampler_point_wrap], node_world_uv, 0).rg; + flow_dir = normalize(flow_dir * 2.0f - 1.0f) * interp_node.z; + output_flow += flow_dir; + + const float theta = atan2(flow_dir.y, flow_dir.x); //- atan2(wind_dir.y, wind_dir.x); const float cosT = cos(theta); const float sinT = sin(theta); const float2x2 R2 = float2x2(cosT, -sinT, sinT, cosT); const float3x3 R3 = float3x3(cosT, 0, sinT, 0, 1, 0, -sinT, 0, cosT); - const float2 rotated_pos = interp_node.xy + mul(R2, uv - interp_node.xy); + const float2 rotated_pos = interp_node.xy + mul(R2, tile_local_uv - interp_node.xy); float4 sample = get_texture_sample(example, rotated_pos, tex_freq, interp_node.xy); @@ -133,9 +142,12 @@ void synthesize_with_flow(Texture2D example, out float4 output, float wind_dir_d moment2 += interp_node.z * interp_node.z; } // assumes example is a mip mapped 512x512 texture and samples lowest mip (1x1) - const float4 mean_example = example.SampleLevel(samplers[sampler_point_clamp], uv, 9); + const float4 mean_example = example.SampleLevel(samplers[sampler_point_clamp], tile_local_uv, 9); preserve_variance(output, mean_example, moment2); + + if (debug_flow) + output = float4(output_flow * 0.5f + 0.5f, 0.0f, 0.0f); } float2 ocean_get_world_space_uvs(float2 uv, float2 tile_xz_pos, float tile_size) From 403932c18da5b217bc2266111bca20ad701d5e60 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Fri, 17 Oct 2025 17:49:23 +0200 Subject: [PATCH 118/133] [ocean] improved sampling of heightmap --- data/shaders/g_buffer.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index dbda391fba..4795068d45 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -118,7 +118,7 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI //synthesize(tex2, displacement, world_space_tile_uv); synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); - input.position.y += tex4.SampleLevel(samplers[sampler_point_wrap], uv, 0); + input.position.y += tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; } From 003b897a683bc07f5b5247c6b7d9abf9c0499c85 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Tue, 21 Oct 2025 00:39:48 +0200 Subject: [PATCH 119/133] [ocean] trying out flowmap improvements --- data/shaders/common_vertex_processing.hlsl | 1 + data/shaders/g_buffer.hlsl | 15 ++++++++++++++- data/shaders/ocean/synthesise_maps.hlsl | 5 +++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/data/shaders/common_vertex_processing.hlsl b/data/shaders/common_vertex_processing.hlsl index abdbc169dc..715d5cb956 100644 --- a/data/shaders/common_vertex_processing.hlsl +++ b/data/shaders/common_vertex_processing.hlsl @@ -120,6 +120,7 @@ struct gbuffer_vertex float3 tangent : TANGENT_WORLD; float4 uv_misc : TEXCOORD; // xy = uv, z = height_percent, w = instance_id - packed together to reduced the interpolators (shader registers) the gpu needs to track float width_percent : TEXCOORD2; // temp, will remove + float2 tile_position : POS_TILE; }; float3 extract_position(matrix transform) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 4795068d45..b45c8ef753 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -103,6 +103,8 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI Surface surface; surface.flags = material.flags; + float2 pos = float2(0.0f, 0.0f); + if (surface.is_ocean() && material.ocean_parameters.displacementScale > -1.0f) { const float3 pass_values = pass_get_f3_value(); @@ -120,6 +122,8 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI input.position.y += tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; + + pos = world_pos; } float3 position_world = 0.0f; @@ -132,6 +136,8 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI vertex = transform_to_clip_space(vertex, position_world, position_world_previous); } + vertex.tile_position = pos; + return vertex; } @@ -358,9 +364,16 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) // to the synthesised displacement (it's calculated in the vertex stage) float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv, true); + synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); albedo = displacement; + + { + const float2 world_pos = vertex.tile_position; + const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); + float4 flow = float4(tex5.Sample(samplers[sampler_point_wrap], uv).rg, 0.0f, 1.0f); + albedo = flow; + } } else // show original displacement albedo = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgba; diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 26ca0a0b38..51d2383a10 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -122,10 +122,11 @@ void synthesize_with_flow(Texture2D example, out float4 output, Texture2D flowma float2 node_world_uv = (node_world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); float2 flow_dir = flowmap.SampleLevel(samplers[sampler_point_wrap], node_world_uv, 0).rg; - flow_dir = normalize(flow_dir * 2.0f - 1.0f) * interp_node.z; + flow_dir = normalize(flow_dir * 2.0f - 1.0f); + flow_dir = float2(flow_dir.x, flow_dir.y); //* interp_node.z; output_flow += flow_dir; - const float theta = atan2(flow_dir.y, flow_dir.x); //- atan2(wind_dir.y, wind_dir.x); + const float theta = atan2(flow_dir.y, flow_dir.x) - atan2(wind_dir.y, wind_dir.x); const float cosT = cos(theta); const float sinT = sin(theta); const float2x2 R2 = float2x2(cosT, -sinT, sinT, cosT); From b5fb8754c7712e0c30bb48225e6453014a74c1b3 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Wed, 22 Oct 2025 20:51:17 +0200 Subject: [PATCH 120/133] [ocean] debugging flow --- data/shaders/g_buffer.hlsl | 23 ++++++++++++----------- data/shaders/ocean/synthesise_maps.hlsl | 16 ++++++++-------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index b45c8ef753..12e21ad767 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -118,7 +118,7 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); + synthesize_with_flow(tex2, displacement, tex5, world_pos, material.ocean_parameters.windDirection, tile_local_uv); input.position.y += tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; @@ -347,7 +347,7 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex3, slope, world_space_tile_uv); - synthesize_with_flow(tex3, slope, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); + synthesize_with_flow(tex3, slope, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv); slope = slope * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, 1.0f, -slope.y)); @@ -364,16 +364,17 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) // to the synthesised displacement (it's calculated in the vertex stage) float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); + synthesize_with_flow(tex2, displacement, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv, true); - albedo = displacement; - - { - const float2 world_pos = vertex.tile_position; - const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); - float4 flow = float4(tex5.Sample(samplers[sampler_point_wrap], uv).rg, 0.0f, 1.0f); - albedo = flow; - } + normal = displacement; + + //{ + // const float2 world_pos = vertex.tile_position; + // const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); + // float4 flow = float4(tex5.Sample(samplers[sampler_point_wrap], uv).rg, 0.0f, 1.0f); + // albedo = float4(uv, 0.0f, 1.0f); + // albedo = flow; + //} } else // show original displacement albedo = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgba; diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 51d2383a10..238743805c 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -105,7 +105,7 @@ void synthesize_with_flow(Texture2D example, out float4 output, Texture2D flowma // convert wind dir from degrees to a 2d vector const float wind_dir_rad = radians(wind_dir_deg); - const float2 wind_dir = float2(cos(wind_dir_rad), sin(wind_dir_rad)); + const float2 wind_dir = float2(cos(wind_dir_rad), sin(wind_dir_rad)) * float2(-1.0f, -1.0f); output = float4(0.0f, 0.0f, 0.0f, 0.0f); // init to 0.0f for safety reasons float moment2 = 0.0f; @@ -115,15 +115,12 @@ void synthesize_with_flow(Texture2D example, out float4 output, Texture2D flowma { const float3 interp_node = get_triangle_interp_node(tile_local_uv, tile_freq, i); - //float2 to_center = interp_node.xy - float2(3.0f, 3.0f); - //float2 flow_dir = normalize(float2(-to_center.y, to_center.x)); - - float2 node_world_pos = (tile_xz_pos - 0.5f * 128.0f) + interp_node.xy * 128.0f; + float2 node_world_pos = tile_xz_pos; float2 node_world_uv = (node_world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); - float2 flow_dir = flowmap.SampleLevel(samplers[sampler_point_wrap], node_world_uv, 0).rg; + float2 flow_dir = flowmap.SampleLevel(samplers[sampler_bilinear_wrap], node_world_uv, 0).rg; flow_dir = normalize(flow_dir * 2.0f - 1.0f); - flow_dir = float2(flow_dir.x, flow_dir.y); //* interp_node.z; + flow_dir = float2(flow_dir.x, flow_dir.y) * interp_node.z; output_flow += flow_dir; const float theta = atan2(flow_dir.y, flow_dir.x) - atan2(wind_dir.y, wind_dir.x); @@ -148,7 +145,10 @@ void synthesize_with_flow(Texture2D example, out float4 output, Texture2D flowma preserve_variance(output, mean_example, moment2); if (debug_flow) - output = float4(output_flow * 0.5f + 0.5f, 0.0f, 0.0f); + { + //output = float4(output_flow * 0.5f + 0.5f, 0.0f, 1.0f); + output = float4(output_flow, 0.0f, 1.0f); + } } float2 ocean_get_world_space_uvs(float2 uv, float2 tile_xz_pos, float tile_size) From 73e2d9891c4c417456399e83896eac060d72fe44 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 27 Oct 2025 20:54:23 +0100 Subject: [PATCH 121/133] [ocean] better flowmap generation for lakes --- source/runtime/Game/Game.cpp | 263 ++++++++++++++++++++++++++++++++--- 1 file changed, 244 insertions(+), 19 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 2ac3295ccd..0112903580 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -851,6 +851,211 @@ namespace spartan shared_ptr ocean_material = make_shared(); shared_ptr flow_map; + inline int idx(int x, int y, int w) { return y * w + x; } + + void GenerateLakeOutwardFlow( + const float* height_data, + uint32_t tex_width, + uint32_t tex_height, + float waterLevel, // e.g. 0.0f + std::vector& out_flow_data, // output, size must be tex_width*tex_height + int blurRadius = 3, // optional smoothing radius (3..8) + float center_strength = 1.0f // scale of outward strength + ) + { + const int W = (int)tex_width; + const int H = (int)tex_height; + const int N = W * H; + out_flow_data.assign(N, Vector2(0.5f, 0.5f)); + + // 1) lake mask and shore detection + std::vector isLake(N, 0); + std::vector isShore(N, 0); + + for (int y = 0; y < H; ++y) + { + for (int x = 0; x < W; ++x) + { + int i = idx(x, y, W); + if (height_data[i] <= waterLevel) isLake[i] = 1; + } + } + + // find shore pixels: lake pixel adjacent to any non-lake (4-neighbour) + auto inBounds = [&](int x, int y) { return x >= 0 && x < W && y >= 0 && y < H; }; + for (int y = 0; y < H; ++y) + { + for (int x = 0; x < W; ++x) + { + int i = idx(x, y, W); + if (!isLake[i]) continue; + bool shore = false; + const int nx[4] = { 1,-1,0,0 }; + const int ny[4] = { 0,0,1,-1 }; + for (int k = 0; k < 4; ++k) + { + int sx = x + nx[k], sy = y + ny[k]; + if (!inBounds(sx, sy) || !isLake[idx(sx, sy, W)]) { shore = true; break; } + } + if (shore) isShore[i] = 1; + } + } + + // 2) multi-source BFS from shore pixels + // store nearest shore coords and distance (in pixels) + const int INF = 1 << 30; + std::vector dist(N, INF); + std::vector nearestX(N, -1), nearestY(N, -1); + std::deque q; + + // push all shore pixels as index seeds + for (int y = 0; y < H; ++y) + { + for (int x = 0; x < W; ++x) + { + int i = idx(x, y, W); + if (isShore[i]) + { + dist[i] = 0; + nearestX[i] = x; + nearestY[i] = y; + q.push_back(i); + } + } + } + + // if no shore pixels (rare), bail out + if (q.empty()) { + // fallback: set small wind or zero flow + for (int i = 0; i < N; i++) out_flow_data[i] = Vector2(0.5f, 0.5f); + return; + } + + const int nbrX[4] = { 1,-1,0,0 }; + const int nbrY[4] = { 0,0,1,-1 }; + + while (!q.empty()) + { + int cur = q.front(); q.pop_front(); + int cx = cur % W; + int cy = cur / W; + int cd = dist[cur]; + + for (int k = 0; k < 4; ++k) + { + int nxp = cx + nbrX[k]; + int nyp = cy + nbrY[k]; + if (!inBounds(nxp, nyp)) continue; + int ni = idx(nxp, nyp, W); + if (!isLake[ni]) continue; // only propagate inside lakes + + if (dist[ni] > cd + 1) + { + dist[ni] = cd + 1; + nearestX[ni] = nearestX[cur]; + nearestY[ni] = nearestY[cur]; + q.push_back(ni); + } + } + } + + // 3) build outward flow: nearest shore vector -> direction to shore + // also compute max distance for normalization + int maxDist = 0; + for (int i = 0; i < N; i++) if (isLake[i] && dist[i] < INF) maxDist = std::max(maxDist, dist[i]); + + if (maxDist == 0) maxDist = 1; + + for (int y = 0; y < H; ++y) + { + for (int x = 0; x < W; ++x) + { + int i = idx(x, y, W); + if (!isLake[i]) + { + // encode 0 flow on land (or whatever you prefer) + out_flow_data[i] = Vector2(0.5f, 0.5f); + continue; + } + + int sx = nearestX[i]; + int sy = nearestY[i]; + if (sx < 0) + { + // no nearest shore found (shouldn't happen) => tiny noise/wind + out_flow_data[i] = Vector2(0.5f, 0.5f); + continue; + } + + // vector from pixel -> shore + float vx = (float)sx - (float)x; + float vy = (float)sy - (float)y; + float d = std::sqrt(vx * vx + vy * vy); + if (d < 1e-6f) { + // on the shore pixel: zero magnitude + out_flow_data[i] = Vector2(0.5f, 0.5f); + } + else { + // normalized direction towards shore (points outward) + float nxv = vx / d; + float nyv = vy / d; + + // optional magnitude: stronger near center (far from shore) + float mag = (float)dist[i] / (float)maxDist; // 0..1 (0 at shore, 1 at farthest) + mag = pow(mag, 0.8f) * center_strength; // tweak exponent for shape + + // combine direction and magnitude (we'll store unit direction only; magnitude can be separate channel) + float ux = nxv * mag; + float uy = nyv * mag; + + // store as signed normalized vector in [-1,1] then encode to [0,1] + float ex = ux * 0.5f + 0.5f; + float ey = uy * 0.5f + 0.5f; + out_flow_data[i] = Vector2(ex, ey); + } + } + } + + // 4) optional: blur/smooth the flow vectors (box blur or Gaussian) + if (blurRadius > 0) + { + std::vector temp = out_flow_data; + for (int y = 0; y < H; ++y) + { + for (int x = 0; x < W; ++x) + { + int i = idx(x, y, W); + if (!isLake[i]) continue; + float sx = 0.0f, sy = 0.0f; int cnt = 0; + for (int oy = -blurRadius; oy <= blurRadius; ++oy) + { + for (int ox = -blurRadius; ox <= blurRadius; ++ox) + { + int nxp = std::clamp(x + ox, 0, W - 1); + int nyp = std::clamp(y + oy, 0, H - 1); + int ni = idx(nxp, nyp, W); + if (!isLake[ni]) continue; + sx += (temp[ni].x - 0.5f) * 2.0f; // decode back -1..1 + sy += (temp[ni].y - 0.5f) * 2.0f; + cnt++; + } + } + if (cnt > 0) { + sx /= (float)cnt; + sy /= (float)cnt; + float l = std::sqrt(sx * sx + sy * sy); + if (l > 1e-6f) { sx /= l; sy /= l; } + // reapply magnitude based on dist (optional) + float mag = (float)dist[i] / (float)maxDist; + float ux = sx * mag; + float uy = sy * mag; + out_flow_data[i] = Vector2(ux * 0.5f + 0.5f, uy * 0.5f + 0.5f); + } + } + } + } + } + void create() { // tweak without exceeding a vram usage of 8 GB (that is until streaming is implemented) @@ -985,7 +1190,15 @@ namespace spartan SP_ASSERT(height_map->GetMip(0, 0).bytes.size() == tex_width * tex_height * sizeof(float)); std::vector flow_data(tex_width * tex_height); + std::vector lake_flow(tex_width * tex_height); + + const float waterLevel = 0.0f; // threshold for "lake" height + const int kernelRadius = 8; // used for slope-based flow smoothing + // --- 1) Generate lake outward flow field --- + GenerateLakeOutwardFlow(height_data, tex_width, tex_height, waterLevel, lake_flow, 4, 1.0f); + + // --- 2) Generate slope-based (river) flow field --- for (uint32_t y = 0; y < tex_height; y++) { for (uint32_t x = 0; x < tex_width; x++) @@ -994,7 +1207,6 @@ namespace spartan float gy = 0.0f; int samples = 0; - const int kernelRadius = 2; // try 2–4 for smoother flow for (int ky = -kernelRadius; ky <= kernelRadius; ky++) { for (int kx = -kernelRadius; kx <= kernelRadius; kx++) @@ -1021,16 +1233,24 @@ namespace spartan gy /= (samples * 2.0f); Vector2 flow = { -gx, -gy }; + + // --- 3) Replace flow with lake pattern where below waterLevel --- + if (height_data[y * tex_width + x] <= waterLevel) + { + flow_data[y * tex_width + x] = lake_flow[y * tex_width + x]; + continue; + } + float len = std::sqrt(flow.x * flow.x + flow.y * flow.y); if (len > 0.0001f) flow /= len; - // Encode to [0,1] + // Encode slope flow into [0,1] flow_data[y * tex_width + x] = Vector2(flow.x * 0.5f + 0.5f, flow.y * 0.5f + 0.5f); - //flow_data[y * tex_width + x] = Vector2(1.0f, 0.0f); } } + // --- 4) Encode into R8G8_UNORM texture --- vector data(1); auto& slice = data[0]; slice.mips.resize(1); @@ -1038,25 +1258,30 @@ namespace spartan mip_bytes.resize(tex_width * tex_height * 2); // 2 bytes per pixel for R8G8_Unorm auto copy_data = [&flow_data, &mip_bytes](uint32_t start, uint32_t end) - { - for (uint32_t i = start; i < end; i++) { - const Vector2& f = flow_data[i]; - //float fx = (f.x * 0.5f) + 0.5f; - //float fy = (f.y * 0.5f) + 0.5f; - // clamp to [0,1] - float fx = std::clamp(f.x, 0.0f, 1.0f); - float fy = std::clamp(f.y, 0.0f, 1.0f); - uint8_t r = static_cast(std::round(fx * 255.0f)); - uint8_t g = static_cast(std::round(fy * 255.0f)); - mip_bytes[i * 2 + 0] = static_cast(r); - mip_bytes[i * 2 + 1] = static_cast(g); - } - }; + for (uint32_t i = start; i < end; i++) + { + const Vector2& f = flow_data[i]; + float fx = std::clamp(f.x, 0.0f, 1.0f); + float fy = std::clamp(f.y, 0.0f, 1.0f); + uint8_t r = static_cast(std::round(fx * 255.0f)); + uint8_t g = static_cast(std::round(fy * 255.0f)); + mip_bytes[i * 2 + 0] = static_cast(r); + mip_bytes[i * 2 + 1] = static_cast(g); + } + }; + ThreadPool::ParallelLoop(copy_data, tex_width * tex_height); - flow_map = std::make_shared(RHI_Texture_Type::Type2D, tex_width, tex_height, 1, 1, - RHI_Format::R8G8_Unorm, RHI_Texture_Srv, "terrain_flowmap", data); + // --- 5) Upload to GPU --- + flow_map = std::make_shared( + RHI_Texture_Type::Type2D, + tex_width, tex_height, 1, 1, + RHI_Format::R8G8_Unorm, + RHI_Texture_Srv, + "terrain_flowmap", + data + ); ocean_material->SetTexture(MaterialTextureType::Flowmap, flow_map); } From 891b1bf714729305bf0d69b3f4cccb9446fce5ef Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 27 Oct 2025 20:54:58 +0100 Subject: [PATCH 122/133] [ocean] more robust height offset in vertex shader --- data/shaders/g_buffer.hlsl | 6 ++++-- data/shaders/ocean/synthesise_maps.hlsl | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 12e21ad767..1b6bdd96e5 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -119,8 +119,10 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); //synthesize(tex2, displacement, world_space_tile_uv); synthesize_with_flow(tex2, displacement, tex5, world_pos, material.ocean_parameters.windDirection, tile_local_uv); + + const float height = tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); - input.position.y += tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); + input.position.y += height <= 0.0f ? 0.0f : height + 3.0f; input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; pos = world_pos; @@ -350,7 +352,7 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) synthesize_with_flow(tex3, slope, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv); slope = slope * material.ocean_parameters.slopeScale; - normal = normalize(float3(-slope.x, 1.0f, -slope.y)); + normal = normalize(float3(-slope.x, vertex.normal.y, -slope.y)); // apply foam (foam mask is stored in the alpha channel of slope map) //const float foam_noise = compute_foam_noise(vertex.uv_misc.xy, buffer_frame.time); diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 238743805c..7aa89dae38 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -122,6 +122,9 @@ void synthesize_with_flow(Texture2D example, out float4 output, Texture2D flowma flow_dir = normalize(flow_dir * 2.0f - 1.0f); flow_dir = float2(flow_dir.x, flow_dir.y) * interp_node.z; output_flow += flow_dir; + + //float2 to_center = interp_node.xy - float2(3.0f, 3.0f); + //float2 flow_dir = normalize(float2(to_center.y, to_center.x)); const float theta = atan2(flow_dir.y, flow_dir.x) - atan2(wind_dir.y, wind_dir.x); const float cosT = cos(theta); From ca27fa8dad2d7745d8a6716a073fa20ffa4c07dd Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 26 Jan 2026 12:17:58 +0100 Subject: [PATCH 123/133] [ocean] fixed merge errors --- data/shaders/common_vertex_processing.hlsl | 2 +- source/runtime/Game/Game.cpp | 631 ++++++++++----------- 2 files changed, 308 insertions(+), 325 deletions(-) diff --git a/data/shaders/common_vertex_processing.hlsl b/data/shaders/common_vertex_processing.hlsl index 8ee502e2b6..7c8ab8f519 100644 --- a/data/shaders/common_vertex_processing.hlsl +++ b/data/shaders/common_vertex_processing.hlsl @@ -50,7 +50,7 @@ struct gbuffer_vertex float3 tangent : TANGENT_WORLD; float4 uv_misc : TEXCOORD; // xy = uv, z = height_percent, w = instance_id - packed together to reduced the interpolators (shader registers) the gpu needs to track float width_percent : TEXCOORD2; // temp, will remove - //float2 tile_position : POS_TILE; + float2 tile_position : POS_TILE; }; float4x4 compose_instance_transform(min16float instance_position_x, min16float instance_position_y, min16float instance_position_z, uint instance_normal_oct, uint instance_yaw, uint instance_scale) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 4f982dbb37..87b0015c1a 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -60,6 +60,7 @@ namespace spartan namespace subway { void create(); } namespace minecraft { void create(); } namespace basic { void create(); } + namespace ocean { void create(); void tick(); } } //=========================================================== @@ -97,6 +98,7 @@ namespace spartan worlds::subway::create, worlds::minecraft::create, worlds::basic::create, + worlds::ocean::create, }; constexpr tick_fn world_tick[] = @@ -109,6 +111,7 @@ namespace spartan nullptr, nullptr, nullptr, + worlds::ocean::tick, }; static_assert(size(world_create) == static_cast(DefaultWorld::Max), "world_create out of sync with DefaultWorld enum"); @@ -2072,12 +2075,12 @@ namespace spartan ocean_material->SetTexture(MaterialTextureType::Height, height_map); // generate flowmap { - const float* height_data = reinterpret_cast(height_map->GetMip(0, 0).bytes.data()); + const float* height_data = reinterpret_cast(height_map->GetMip(0, 0)->bytes.data()); const uint32_t tex_width = height_map->GetWidth(); const uint32_t tex_height = height_map->GetHeight(); - SP_ASSERT(height_map->GetMip(0, 0).bytes.size() == tex_width * tex_height * sizeof(float)); + SP_ASSERT(height_map->GetMip(0, 0)->bytes.size() == tex_width * tex_height * sizeof(float)); std::vector flow_data(tex_width * tex_height); std::vector lake_flow(tex_width * tex_height); @@ -3035,397 +3038,395 @@ namespace spartan entities::material_ball(Vector3::Zero); } } - } - //==================================================================================== - //== CAR PLAYGROUND ================================================================== - namespace car_playground - { - // helper to create a cube obstacle with physics - // mass = 0 means static, mass > 0 means dynamic with that mass in kg - void create_cube(const string& name, const Vector3& position, const Vector3& euler_angles, const Vector3& scale, float mass = 0.0f) + //== CAR PLAYGROUND ================================================================== + namespace car_playground { - Entity* entity = World::CreateEntity(); - entity->SetObjectName(name); - entity->SetPosition(position); - entity->SetRotation(Quaternion::FromEulerAngles(euler_angles.x, euler_angles.y, euler_angles.z)); - entity->SetScale(scale); - - Renderable* renderable = entity->AddComponent(); - renderable->SetMesh(MeshType::Cube); - renderable->SetDefaultMaterial(); - - Physics* physics_body = entity->AddComponent(); - physics_body->SetBodyType(BodyType::Box); - physics_body->SetStatic(mass == 0.0f); - physics_body->SetMass(mass); - } + // helper to create a cube obstacle with physics + // mass = 0 means static, mass > 0 means dynamic with that mass in kg + void create_cube(const string& name, const Vector3& position, const Vector3& euler_angles, const Vector3& scale, float mass = 0.0f) + { + Entity* entity = World::CreateEntity(); + entity->SetObjectName(name); + entity->SetPosition(position); + entity->SetRotation(Quaternion::FromEulerAngles(euler_angles.x, euler_angles.y, euler_angles.z)); + entity->SetScale(scale); - void create() - { - entities::camera(false, Vector3(0.0f, 8.0f, -25.0f), Vector3(15.0f, 0.0f, 0.0f)); - entities::sun(LightPreset::dusk, true); - entities::floor(); + Renderable* renderable = entity->AddComponent(); + renderable->SetMesh(MeshType::Cube); + renderable->SetDefaultMaterial(); - // create drivable car with telemetry - car::Config car_config; - car_config.position = Vector3(0.0f, 0.5f, 0.0f); - car_config.drivable = true; - car_config.show_telemetry = true; - car_config.camera_follows = true; - car::create(car_config); + Physics* physics_body = entity->AddComponent(); + physics_body->SetBodyType(BodyType::Box); + physics_body->SetStatic(mass == 0.0f); + physics_body->SetMass(mass); + } - //================================================================================== - // zone 1: main jump ramp area (in front of spawn) - //================================================================================== + void create() + { + entities::camera(false, Vector3(0.0f, 8.0f, -25.0f), Vector3(15.0f, 0.0f, 0.0f)); + entities::sun(LightPreset::dusk, true); + entities::floor(); - // gentle starter ramp - create_cube("ramp_starter", Vector3(12.0f, 0.3f, 0.0f), Vector3(0.0f, 0.0f, 8.0f), Vector3(8.0f, 0.6f, 6.0f)); + // create drivable car with telemetry + car::Config car_config; + car_config.position = Vector3(0.0f, 0.5f, 0.0f); + car_config.drivable = true; + car_config.show_telemetry = true; + car_config.camera_follows = true; + car::create(car_config); - // main jump ramp - steep for big air - create_cube("ramp_jump_main", Vector3(28.0f, 1.2f, 0.0f), Vector3(0.0f, 0.0f, 18.0f), Vector3(10.0f, 0.8f, 7.0f)); + //================================================================================== + // zone 1: main jump ramp area (in front of spawn) + //================================================================================== - // landing ramp - downward slope for smooth landings - create_cube("ramp_landing", Vector3(50.0f, 0.5f, 0.0f), Vector3(0.0f, 0.0f, -12.0f), Vector3(12.0f, 0.6f, 7.0f)); + // gentle starter ramp + create_cube("ramp_starter", Vector3(12.0f, 0.3f, 0.0f), Vector3(0.0f, 0.0f, 8.0f), Vector3(8.0f, 0.6f, 6.0f)); - //================================================================================== - // zone 2: suspension test track (to the right of spawn) - //================================================================================== + // main jump ramp - steep for big air + create_cube("ramp_jump_main", Vector3(28.0f, 1.2f, 0.0f), Vector3(0.0f, 0.0f, 18.0f), Vector3(10.0f, 0.8f, 7.0f)); - // speed bumps - series of small bumps to test suspension - for (int i = 0; i < 8; i++) - { - float x_offset = i * 4.0f; - create_cube("speed_bump_" + to_string(i), Vector3(15.0f + x_offset, 0.15f, 20.0f), Vector3::Zero, Vector3(1.5f, 0.3f, 5.0f)); - } + // landing ramp - downward slope for smooth landings + create_cube("ramp_landing", Vector3(50.0f, 0.5f, 0.0f), Vector3(0.0f, 0.0f, -12.0f), Vector3(12.0f, 0.6f, 7.0f)); - // rumble strips - alternating small ridges - for (int i = 0; i < 12; i++) - { - float x_offset = i * 2.5f; - float height = (i % 2 == 0) ? 0.1f : 0.18f; - create_cube("rumble_" + to_string(i), Vector3(15.0f + x_offset, height * 0.5f, 30.0f), Vector3::Zero, Vector3(1.0f, height, 4.0f)); - } + //================================================================================== + // zone 2: suspension test track (to the right of spawn) + //================================================================================== - // pothole simulation - dips created by raised edges - create_cube("pothole_edge_1", Vector3(60.0f, 0.08f, 20.0f), Vector3::Zero, Vector3(0.8f, 0.16f, 6.0f)); - create_cube("pothole_edge_2", Vector3(66.0f, 0.08f, 20.0f), Vector3::Zero, Vector3(0.8f, 0.16f, 6.0f)); + // speed bumps - series of small bumps to test suspension + for (int i = 0; i < 8; i++) + { + float x_offset = i * 4.0f; + create_cube("speed_bump_" + to_string(i), Vector3(15.0f + x_offset, 0.15f, 20.0f), Vector3::Zero, Vector3(1.5f, 0.3f, 5.0f)); + } - //================================================================================== - // zone 3: stunt ramps and half-pipe (to the left of spawn) - //================================================================================== + // rumble strips - alternating small ridges + for (int i = 0; i < 12; i++) + { + float x_offset = i * 2.5f; + float height = (i % 2 == 0) ? 0.1f : 0.18f; + create_cube("rumble_" + to_string(i), Vector3(15.0f + x_offset, height * 0.5f, 30.0f), Vector3::Zero, Vector3(1.0f, height, 4.0f)); + } - // half-pipe left wall - create_cube("halfpipe_left", Vector3(-25.0f, 2.0f, 0.0f), Vector3(0.0f, 0.0f, 35.0f), Vector3(8.0f, 0.5f, 20.0f)); + // pothole simulation - dips created by raised edges + create_cube("pothole_edge_1", Vector3(60.0f, 0.08f, 20.0f), Vector3::Zero, Vector3(0.8f, 0.16f, 6.0f)); + create_cube("pothole_edge_2", Vector3(66.0f, 0.08f, 20.0f), Vector3::Zero, Vector3(0.8f, 0.16f, 6.0f)); - // half-pipe right wall - create_cube("halfpipe_right", Vector3(-25.0f, 2.0f, 15.0f), Vector3(0.0f, 0.0f, -35.0f), Vector3(8.0f, 0.5f, 20.0f)); + //================================================================================== + // zone 3: stunt ramps and half-pipe (to the left of spawn) + //================================================================================== - // half-pipe back wall (for u-turns) - create_cube("halfpipe_back", Vector3(-38.0f, 1.5f, 7.5f), Vector3(25.0f, 0.0f, 0.0f), Vector3(6.0f, 0.5f, 18.0f)); + // half-pipe left wall + create_cube("halfpipe_left", Vector3(-25.0f, 2.0f, 0.0f), Vector3(0.0f, 0.0f, 35.0f), Vector3(8.0f, 0.5f, 20.0f)); - // kicker ramp - small but steep for tricks - create_cube("kicker_ramp", Vector3(-10.0f, 0.6f, -15.0f), Vector3(0.0f, 0.0f, 25.0f), Vector3(4.0f, 0.5f, 4.0f)); + // half-pipe right wall + create_cube("halfpipe_right", Vector3(-25.0f, 2.0f, 15.0f), Vector3(0.0f, 0.0f, -35.0f), Vector3(8.0f, 0.5f, 20.0f)); - // side ramp for barrel rolls - create_cube("barrel_roll_ramp", Vector3(-15.0f, 0.8f, -25.0f), Vector3(30.0f, 45.0f, 15.0f), Vector3(5.0f, 0.4f, 3.0f)); + // half-pipe back wall (for u-turns) + create_cube("halfpipe_back", Vector3(-38.0f, 1.5f, 7.5f), Vector3(25.0f, 0.0f, 0.0f), Vector3(6.0f, 0.5f, 18.0f)); - //================================================================================== - // zone 4: slalom course (behind spawn) - //================================================================================== + // kicker ramp - small but steep for tricks + create_cube("kicker_ramp", Vector3(-10.0f, 0.6f, -15.0f), Vector3(0.0f, 0.0f, 25.0f), Vector3(4.0f, 0.5f, 4.0f)); - // slalom pylons - alternating obstacles (25 kg like plastic barriers) - for (int i = 0; i < 6; i++) - { - float z_offset = -20.0f - (i * 12.0f); - float x_offset = (i % 2 == 0) ? 5.0f : -5.0f; - create_cube("slalom_pylon_" + to_string(i), Vector3(x_offset, 1.0f, z_offset), Vector3::Zero, Vector3(1.5f, 2.0f, 1.5f), 25.0f); - } + // side ramp for barrel rolls + create_cube("barrel_roll_ramp", Vector3(-15.0f, 0.8f, -25.0f), Vector3(30.0f, 45.0f, 15.0f), Vector3(5.0f, 0.4f, 3.0f)); - // slalom finish gate pillars - dynamic so they can be knocked over - create_cube("gate_left", Vector3(-6.0f, 2.0f, -95.0f), Vector3::Zero, Vector3(1.0f, 4.0f, 1.0f), 30.0f); - create_cube("gate_right", Vector3(6.0f, 2.0f, -95.0f), Vector3::Zero, Vector3(1.0f, 4.0f, 1.0f), 30.0f); + //================================================================================== + // zone 4: slalom course (behind spawn) + //================================================================================== - //================================================================================== - // zone 5: banked turn circuit (far right area) - //================================================================================== + // slalom pylons - alternating obstacles (25 kg like plastic barriers) + for (int i = 0; i < 6; i++) + { + float z_offset = -20.0f - (i * 12.0f); + float x_offset = (i % 2 == 0) ? 5.0f : -5.0f; + create_cube("slalom_pylon_" + to_string(i), Vector3(x_offset, 1.0f, z_offset), Vector3::Zero, Vector3(1.5f, 2.0f, 1.5f), 25.0f); + } - // banked turn - outside wall - create_cube("bank_outer", Vector3(80.0f, 1.5f, 0.0f), Vector3(0.0f, 30.0f, -25.0f), Vector3(20.0f, 0.6f, 8.0f)); + // slalom finish gate pillars - dynamic so they can be knocked over + create_cube("gate_left", Vector3(-6.0f, 2.0f, -95.0f), Vector3::Zero, Vector3(1.0f, 4.0f, 1.0f), 30.0f); + create_cube("gate_right", Vector3(6.0f, 2.0f, -95.0f), Vector3::Zero, Vector3(1.0f, 4.0f, 1.0f), 30.0f); - // banked turn - inside wall - create_cube("bank_inner", Vector3(75.0f, 0.8f, 8.0f), Vector3(0.0f, 30.0f, -15.0f), Vector3(15.0f, 0.4f, 6.0f)); + //================================================================================== + // zone 5: banked turn circuit (far right area) + //================================================================================== - // exit ramp from banked turn - create_cube("bank_exit_ramp", Vector3(95.0f, 0.4f, -10.0f), Vector3(0.0f, 60.0f, 10.0f), Vector3(8.0f, 0.5f, 5.0f)); + // banked turn - outside wall + create_cube("bank_outer", Vector3(80.0f, 1.5f, 0.0f), Vector3(0.0f, 30.0f, -25.0f), Vector3(20.0f, 0.6f, 8.0f)); - //================================================================================== - // zone 6: obstacle course (scattered dynamic objects) - //================================================================================== + // banked turn - inside wall + create_cube("bank_inner", Vector3(75.0f, 0.8f, 8.0f), Vector3(0.0f, 30.0f, -15.0f), Vector3(15.0f, 0.4f, 6.0f)); - // stack of crates to crash through (20 kg wooden crates) - // add small gaps (1.55 spacing for 1.5 size) to prevent interpenetration explosions - for (int row = 0; row < 3; row++) - { - for (int col = 0; col < 3; col++) + // exit ramp from banked turn + create_cube("bank_exit_ramp", Vector3(95.0f, 0.4f, -10.0f), Vector3(0.0f, 60.0f, 10.0f), Vector3(8.0f, 0.5f, 5.0f)); + + //================================================================================== + // zone 6: obstacle course (scattered dynamic objects) + //================================================================================== + + // stack of crates to crash through (20 kg wooden crates) + // add small gaps (1.55 spacing for 1.5 size) to prevent interpenetration explosions + for (int row = 0; row < 3; row++) { - float y_pos = 0.76f + (row * 1.55f); - float x_pos = 35.0f + (col * 1.65f); - create_cube("crate_stack_" + to_string(row) + "_" + to_string(col), Vector3(x_pos, y_pos, -30.0f), Vector3::Zero, Vector3(1.5f, 1.5f, 1.5f), 20.0f); + for (int col = 0; col < 3; col++) + { + float y_pos = 0.76f + (row * 1.55f); + float x_pos = 35.0f + (col * 1.65f); + create_cube("crate_stack_" + to_string(row) + "_" + to_string(col), Vector3(x_pos, y_pos, -30.0f), Vector3::Zero, Vector3(1.5f, 1.5f, 1.5f), 20.0f); + } } - } - // barrel wall (15 kg empty barrels) - // add gaps to prevent interpenetration - for (int i = 0; i < 5; i++) - { - float x_pos = 50.0f + (i * 2.2f); - create_cube("barrel_" + to_string(i), Vector3(x_pos, 0.85f, -45.0f), Vector3(90.0f, 0.0f, 0.0f), Vector3(1.2f, 1.6f, 1.2f), 15.0f); - } + // barrel wall (15 kg empty barrels) + // add gaps to prevent interpenetration + for (int i = 0; i < 5; i++) + { + float x_pos = 50.0f + (i * 2.2f); + create_cube("barrel_" + to_string(i), Vector3(x_pos, 0.85f, -45.0f), Vector3(90.0f, 0.0f, 0.0f), Vector3(1.2f, 1.6f, 1.2f), 15.0f); + } - // pyramid of boxes (15 kg cardboard boxes) - // add small gaps to prevent interpenetration explosions - int pyramid_base = 4; - for (int level = 0; level < pyramid_base; level++) - { - int boxes_in_level = pyramid_base - level; - float y_pos = 0.62f + (level * 1.25f); - float start_x = 70.0f - (boxes_in_level * 0.65f); - for (int b = 0; b < boxes_in_level; b++) + // pyramid of boxes (15 kg cardboard boxes) + // add small gaps to prevent interpenetration explosions + int pyramid_base = 4; + for (int level = 0; level < pyramid_base; level++) { - create_cube("pyramid_" + to_string(level) + "_" + to_string(b), Vector3(start_x + (b * 1.35f), y_pos, -60.0f), Vector3::Zero, Vector3(1.2f, 1.2f, 1.2f), 15.0f); + int boxes_in_level = pyramid_base - level; + float y_pos = 0.62f + (level * 1.25f); + float start_x = 70.0f - (boxes_in_level * 0.65f); + for (int b = 0; b < boxes_in_level; b++) + { + create_cube("pyramid_" + to_string(level) + "_" + to_string(b), Vector3(start_x + (b * 1.35f), y_pos, -60.0f), Vector3::Zero, Vector3(1.2f, 1.2f, 1.2f), 15.0f); + } } - } - //================================================================================== - // zone 7: wavy terrain (far left) - //================================================================================== + //================================================================================== + // zone 7: wavy terrain (far left) + //================================================================================== - // series of sine-wave like bumps - for (int i = 0; i < 10; i++) - { - float z_pos = -40.0f + (i * 6.0f); - float height = 0.3f + 0.3f * sin(i * 0.8f); - float angle = 8.0f * sin(i * 0.5f); - create_cube("wave_" + to_string(i), Vector3(-50.0f, height, z_pos), Vector3(angle, 0.0f, 0.0f), Vector3(8.0f, 0.4f, 4.0f)); - } + // series of sine-wave like bumps + for (int i = 0; i < 10; i++) + { + float z_pos = -40.0f + (i * 6.0f); + float height = 0.3f + 0.3f * sin(i * 0.8f); + float angle = 8.0f * sin(i * 0.5f); + create_cube("wave_" + to_string(i), Vector3(-50.0f, height, z_pos), Vector3(angle, 0.0f, 0.0f), Vector3(8.0f, 0.4f, 4.0f)); + } - //================================================================================== - // zone 8: stunt park center piece - mega ramp - //================================================================================== + //================================================================================== + // zone 8: stunt park center piece - mega ramp + //================================================================================== - // approach ramp - create_cube("mega_approach", Vector3(-70.0f, 1.0f, -30.0f), Vector3(0.0f, 0.0f, 12.0f), Vector3(15.0f, 0.6f, 10.0f)); + // approach ramp + create_cube("mega_approach", Vector3(-70.0f, 1.0f, -30.0f), Vector3(0.0f, 0.0f, 12.0f), Vector3(15.0f, 0.6f, 10.0f)); - // main mega ramp - create_cube("mega_ramp", Vector3(-90.0f, 4.0f, -30.0f), Vector3(0.0f, 0.0f, 30.0f), Vector3(12.0f, 0.8f, 10.0f)); + // main mega ramp + create_cube("mega_ramp", Vector3(-90.0f, 4.0f, -30.0f), Vector3(0.0f, 0.0f, 30.0f), Vector3(12.0f, 0.8f, 10.0f)); - // mega ramp platform top - create_cube("mega_platform", Vector3(-105.0f, 7.5f, -30.0f), Vector3::Zero, Vector3(8.0f, 0.5f, 10.0f)); + // mega ramp platform top + create_cube("mega_platform", Vector3(-105.0f, 7.5f, -30.0f), Vector3::Zero, Vector3(8.0f, 0.5f, 10.0f)); - // drop ramp on other side - create_cube("mega_drop", Vector3(-118.0f, 4.0f, -30.0f), Vector3(0.0f, 0.0f, -35.0f), Vector3(10.0f, 0.8f, 10.0f)); + // drop ramp on other side + create_cube("mega_drop", Vector3(-118.0f, 4.0f, -30.0f), Vector3(0.0f, 0.0f, -35.0f), Vector3(10.0f, 0.8f, 10.0f)); - //================================================================================== - // zone 9: figure-8 crossover - //================================================================================== + //================================================================================== + // zone 9: figure-8 crossover + //================================================================================== - // elevated crossing ramp 1 - create_cube("cross_ramp_up_1", Vector3(0.0f, 1.0f, 50.0f), Vector3(0.0f, 45.0f, 15.0f), Vector3(12.0f, 0.5f, 6.0f)); + // elevated crossing ramp 1 + create_cube("cross_ramp_up_1", Vector3(0.0f, 1.0f, 50.0f), Vector3(0.0f, 45.0f, 15.0f), Vector3(12.0f, 0.5f, 6.0f)); - // elevated bridge section - create_cube("cross_bridge", Vector3(8.0f, 2.5f, 58.0f), Vector3(0.0f, 45.0f, 0.0f), Vector3(10.0f, 0.4f, 6.0f)); + // elevated bridge section + create_cube("cross_bridge", Vector3(8.0f, 2.5f, 58.0f), Vector3(0.0f, 45.0f, 0.0f), Vector3(10.0f, 0.4f, 6.0f)); - // elevated crossing ramp 2 - create_cube("cross_ramp_down_1", Vector3(16.0f, 1.0f, 66.0f), Vector3(0.0f, 45.0f, -15.0f), Vector3(12.0f, 0.5f, 6.0f)); + // elevated crossing ramp 2 + create_cube("cross_ramp_down_1", Vector3(16.0f, 1.0f, 66.0f), Vector3(0.0f, 45.0f, -15.0f), Vector3(12.0f, 0.5f, 6.0f)); - // lower path goes underneath - create_cube("under_path_guide_left", Vector3(-2.0f, 0.4f, 62.0f), Vector3(0.0f, -45.0f, 0.0f), Vector3(0.5f, 0.8f, 15.0f)); - create_cube("under_path_guide_right", Vector3(10.0f, 0.4f, 50.0f), Vector3(0.0f, -45.0f, 0.0f), Vector3(0.5f, 0.8f, 15.0f)); + // lower path goes underneath + create_cube("under_path_guide_left", Vector3(-2.0f, 0.4f, 62.0f), Vector3(0.0f, -45.0f, 0.0f), Vector3(0.5f, 0.8f, 15.0f)); + create_cube("under_path_guide_right", Vector3(10.0f, 0.4f, 50.0f), Vector3(0.0f, -45.0f, 0.0f), Vector3(0.5f, 0.8f, 15.0f)); - //================================================================================== - // zone 10: parking challenge (precision driving) - //================================================================================== + //================================================================================== + // zone 10: parking challenge (precision driving) + //================================================================================== - // tight parking spots with pillars - for (int i = 0; i < 4; i++) - { - float z_pos = 80.0f + (i * 8.0f); - create_cube("parking_left_" + to_string(i), Vector3(-8.0f, 0.5f, z_pos), Vector3::Zero, Vector3(0.3f, 1.0f, 0.3f), 5.0f); - create_cube("parking_right_" + to_string(i), Vector3(8.0f, 0.5f, z_pos), Vector3::Zero, Vector3(0.3f, 1.0f, 0.3f), 5.0f); - } + // tight parking spots with pillars + for (int i = 0; i < 4; i++) + { + float z_pos = 80.0f + (i * 8.0f); + create_cube("parking_left_" + to_string(i), Vector3(-8.0f, 0.5f, z_pos), Vector3::Zero, Vector3(0.3f, 1.0f, 0.3f), 5.0f); + create_cube("parking_right_" + to_string(i), Vector3(8.0f, 0.5f, z_pos), Vector3::Zero, Vector3(0.3f, 1.0f, 0.3f), 5.0f); + } - // parking lot boundary walls - create_cube("parking_wall_back", Vector3(0.0f, 0.5f, 115.0f), Vector3::Zero, Vector3(20.0f, 1.0f, 0.5f)); - create_cube("parking_wall_left", Vector3(-10.0f, 0.5f, 97.0f), Vector3::Zero, Vector3(0.5f, 1.0f, 38.0f)); - create_cube("parking_wall_right", Vector3(10.0f, 0.5f, 97.0f), Vector3::Zero, Vector3(0.5f, 1.0f, 38.0f)); + // parking lot boundary walls + create_cube("parking_wall_back", Vector3(0.0f, 0.5f, 115.0f), Vector3::Zero, Vector3(20.0f, 1.0f, 0.5f)); + create_cube("parking_wall_left", Vector3(-10.0f, 0.5f, 97.0f), Vector3::Zero, Vector3(0.5f, 1.0f, 38.0f)); + create_cube("parking_wall_right", Vector3(10.0f, 0.5f, 97.0f), Vector3::Zero, Vector3(0.5f, 1.0f, 38.0f)); - //================================================================================== - // decorative boundary markers - //================================================================================== + //================================================================================== + // decorative boundary markers + //================================================================================== - // corner markers for the playground area - create_cube("marker_ne", Vector3(120.0f, 1.5f, 120.0f), Vector3::Zero, Vector3(2.0f, 3.0f, 2.0f)); - create_cube("marker_nw", Vector3(-130.0f, 1.5f, 120.0f), Vector3::Zero, Vector3(2.0f, 3.0f, 2.0f)); - create_cube("marker_se", Vector3(120.0f, 1.5f, -100.0f), Vector3::Zero, Vector3(2.0f, 3.0f, 2.0f)); - create_cube("marker_sw", Vector3(-130.0f, 1.5f, -100.0f), Vector3::Zero, Vector3(2.0f, 3.0f, 2.0f)); + // corner markers for the playground area + create_cube("marker_ne", Vector3(120.0f, 1.5f, 120.0f), Vector3::Zero, Vector3(2.0f, 3.0f, 2.0f)); + create_cube("marker_nw", Vector3(-130.0f, 1.5f, 120.0f), Vector3::Zero, Vector3(2.0f, 3.0f, 2.0f)); + create_cube("marker_se", Vector3(120.0f, 1.5f, -100.0f), Vector3::Zero, Vector3(2.0f, 3.0f, 2.0f)); + create_cube("marker_sw", Vector3(-130.0f, 1.5f, -100.0f), Vector3::Zero, Vector3(2.0f, 3.0f, 2.0f)); - // make room for the telemetry display - ConsoleRegistry::Get().SetValueFromString("r.performance_metrics", "0"); + // make room for the telemetry display + ConsoleRegistry::Get().SetValueFromString("r.performance_metrics", "0"); + } } - } - //==================================================================================== - } - //======================================================================================== - - - namespace ocean - { - uint32_t ocean_tile_count = 6; - float tile_size = 128.0f; - uint32_t vertices_count = 512; - shared_ptr material = make_shared(); + //==================================================================================== - void create() + //== Ocean =========================================================================== + namespace ocean { - entities::camera(); - entities::sun(true); + uint32_t ocean_tile_count = 6; + float tile_size = 128.0f; + uint32_t vertices_count = 512; + shared_ptr material = make_shared(); - auto entity = World::CreateEntity(); + void create() + { + entities::camera(false); + entities::sun(LightPreset::day, true); - default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }, tile_size, vertices_count, ocean_tile_count); + auto entity = World::CreateEntity(); - default_ocean->SetParent(entity); + default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }, tile_size, vertices_count, ocean_tile_count); - /*auto light_entity = World::CreateEntity(); - light_entity->SetPosition({ 196.0f, 280.0f, 196.0f }); + default_ocean->SetParent(entity); - Light* point = light_entity->AddComponent(); - point->SetLightType(LightType::Point); - point->SetRange(800.0f); - point->SetTemperature(10000.0f); - point->SetIntensity(8500.0f); - point->SetObjectName("Point Light"); - */ + /*auto light_entity = World::CreateEntity(); + light_entity->SetPosition({ 196.0f, 280.0f, 196.0f }); - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } + Light* point = light_entity->AddComponent(); + point->SetLightType(LightType::Point); + point->SetRange(800.0f); + point->SetTemperature(10000.0f); + point->SetIntensity(8500.0f); + point->SetObjectName("Point Light"); + */ - void tick() - { - if (!material) - return; + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } - uint32_t current_tile_count = material->GetOceanTileCount(); - if (current_tile_count != ocean_tile_count || tile_size != material->GetOceanTileSize() || vertices_count != material->GetOceanVerticesCount()) + void tick() { - ocean_tile_count = current_tile_count; - auto& children = default_ocean->GetChildren(); - - for (uint32_t i = 0; i < children.size(); i++) - { - World::RemoveEntity(children[i]); - } - children.clear(); - - std::shared_ptr ocean_mesh; - - for (size_t i = 0; i < meshes.size(); i++) - { - if (meshes[i]->GetObjectName() == "ocean mesh") - ocean_mesh = meshes[i]; - } - - if (ocean_mesh.get() == nullptr) + if (!material) return; - // regenerate mesh - if (tile_size != material->GetOceanTileSize() || vertices_count != material->GetOceanVerticesCount()) + uint32_t current_tile_count = material->GetOceanTileCount(); + if (current_tile_count != ocean_tile_count || tile_size != material->GetOceanTileSize() || vertices_count != material->GetOceanVerticesCount()) { - tile_size = material->GetOceanTileSize(); - vertices_count = material->GetOceanVerticesCount(); + ocean_tile_count = current_tile_count; + auto& children = default_ocean->GetChildren(); - // generate grid - const uint32_t grid_points_per_dimension = vertices_count; - vector vertices; - vector indices; - geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, tile_size); + for (uint32_t i = 0; i < children.size(); i++) + { + World::RemoveEntity(children[i]); + } + children.clear(); - //string name = "ocean mesh"; + std::shared_ptr ocean_mesh; - // create mesh if it doesn't exist - ocean_mesh->Clear(); + for (size_t i = 0; i < meshes.size(); i++) + { + if (meshes[i]->GetObjectName() == "ocean mesh") + ocean_mesh = meshes[i]; + } - //for (std::vector>::iterator it = meshes.begin(); it != meshes.end();) - //{ - // std::shared_ptr m = *it; - // if (m->GetObjectName() == "ocean mesh") - // it = meshes.erase(it); - // else; - // ++it; - //} + if (ocean_mesh.get() == nullptr) + return; - /*ocean_mesh = meshes.emplace_back(make_shared()); - ocean_mesh->SetObjectName(name); - ocean_mesh->SetRootEntity(default_ocean); - ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); - ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false);*/ - ocean_mesh->AddGeometry(vertices, indices, false); - ocean_mesh->CreateGpuBuffers(); - } + // regenerate mesh + if (tile_size != material->GetOceanTileSize() || vertices_count != material->GetOceanVerticesCount()) + { + tile_size = material->GetOceanTileSize(); + vertices_count = material->GetOceanVerticesCount(); + + // generate grid + const uint32_t grid_points_per_dimension = vertices_count; + vector vertices; + vector indices; + geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, tile_size); + + //string name = "ocean mesh"; + + // create mesh if it doesn't exist + ocean_mesh->Clear(); + + //for (std::vector>::iterator it = meshes.begin(); it != meshes.end();) + //{ + // std::shared_ptr m = *it; + // if (m->GetObjectName() == "ocean mesh") + // it = meshes.erase(it); + // else; + // ++it; + //} + + /*ocean_mesh = meshes.emplace_back(make_shared()); + ocean_mesh->SetObjectName(name); + ocean_mesh->SetRootEntity(default_ocean); + ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); + ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false);*/ + ocean_mesh->AddGeometry(vertices, indices, false); + ocean_mesh->CreateGpuBuffers(); + } - for (uint32_t row = 0; row < current_tile_count; row++) - { - for (uint32_t col = 0; col < current_tile_count; col++) + for (uint32_t row = 0; row < current_tile_count; row++) { - int tile_index = col + row * current_tile_count; + for (uint32_t col = 0; col < current_tile_count; col++) + { + int tile_index = col + row * current_tile_count; - string tile_name = "ocean tile_" + to_string(tile_index); + string tile_name = "ocean tile_" + to_string(tile_index); - Entity* entity_tile = World::CreateEntity(); - entity_tile->SetObjectName(tile_name); - entity_tile->SetParent(default_ocean); + Entity* entity_tile = World::CreateEntity(); + entity_tile->SetObjectName(tile_name); + entity_tile->SetParent(default_ocean); - Vector3 tile_position = { col * tile_size, 0.0f, row * tile_size }; - entity_tile->SetPosition(tile_position); + Vector3 tile_position = { col * tile_size, 0.0f, row * tile_size }; + entity_tile->SetPosition(tile_position); - if (Renderable* renderable = entity_tile->AddComponent()) - { - renderable->SetMesh(ocean_mesh.get()); - renderable->SetMaterial(material); - renderable->SetFlag(RenderableFlags::CastsShadows, false); - } + if (Renderable* renderable = entity_tile->AddComponent()) + { + renderable->SetMesh(ocean_mesh.get()); + renderable->SetMaterial(material); + renderable->SetFlag(RenderableFlags::CastsShadows, false); + } - // enable buoyancy - //Physics* physics = entity_tile->AddComponent(); - //physics->SetBodyType(BodyType::Water); + // enable buoyancy + //Physics* physics = entity_tile->AddComponent(); + //physics->SetBodyType(BodyType::Water); + } } } - } - Vector3 camera_pos = default_camera->GetPosition(); + Vector3 camera_pos = default_camera->GetPosition(); - //Vector3 ocean_pos = default_ocean->GetPosition(); - //Vector3 new_ocean_pos = ocean_pos; - //new_ocean_pos.x = camera_pos.x; - //new_ocean_pos.z = camera_pos.z; - //default_ocean->SetPosition(new_ocean_pos); - } + //Vector3 ocean_pos = default_ocean->GetPosition(); + //Vector3 new_ocean_pos = ocean_pos; + //new_ocean_pos.x = camera_pos.x; + //new_ocean_pos.z = camera_pos.z; + //default_ocean->SetPosition(new_ocean_pos); + } - void on_shutdown() - { - if (!default_ocean) - return; + void shutdown() + { + if (!default_ocean) + return; - if (!material) - SP_ASSERT_MSG(false, "Failed to get ocean material"); + if (!material) + SP_ASSERT_MSG(false, "Failed to get ocean material"); - material->SaveToFile(material->GetResourceFilePath()); + material->SaveToFile(material->GetResourceFilePath()); - default_ocean = nullptr; + default_ocean = nullptr; + } } + //======================================================================================== } - } + //==================================================================================== //= PUBLIC API =========================================================================== void Game::Shutdown() @@ -3441,29 +3442,11 @@ namespace spartan // reset world-specific state worlds::showroom::texture_brand_logo = nullptr; - worlds::ocean::on_shutdown(); + worlds::ocean::shutdown(); car::shutdown(); meshes.clear(); } - void Game::Tick() - { - car::tick(); - - if (loaded_world == DefaultWorld::LiminalSpace) - { - worlds::liminal_space::tick(); - } - else if (loaded_world == DefaultWorld::Showroom) - { - worlds::showroom::tick(); - } - else if (loaded_world == DefaultWorld::Forest) - { - worlds::forest::tick(); - } - } - void Game::Tick() { // car tick (always) From 60de43820adeaa8a44c6f0578c28913994c36cc9 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 26 Jan 2026 15:22:56 +0100 Subject: [PATCH 124/133] [ocean] fixed wrong world entries --- source/editor/GeneralWindows.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/editor/GeneralWindows.cpp b/source/editor/GeneralWindows.cpp index 6bdb27e788..edc8b26c67 100644 --- a/source/editor/GeneralWindows.cpp +++ b/source/editor/GeneralWindows.cpp @@ -541,14 +541,15 @@ namespace const WorldEntry worlds[] = { - { "Car Showroom", "Showcase world for YouTubers/Press. Does not use experimental tech", "Complete" , "Light", 4000 }, - { "Open World Forest", "256 million of Ghost of Tsushima grass blades", "Prototype", "Very demanding", 5400 }, - { "Liminal Space", "Shifts your frequency to a nearby reality", "Prototype", "Light", 2048 }, - { "Sponza 4K", "High-resolution textures & meshes", "Complete" , "Demanding", 5000 }, - { "Subway", "GI test. No lights, only emissive textures", "Complete" , "Moderate", 5000 }, - { "Minecraft", "Blocky aesthetic", "Complete" , "Light", 4000 }, - { "Basic", "Light, camera, floor", "Complete" , "Light", 4000 }, - { "Water", "Light, camera, ocean", "Prototype", "Light", 4000 }, + { "Car Showroom", "Showcase world for YouTubers/Press. Does not use experimental tech", "Complete" , "Light", 2100 }, + { "Car Playground", "Highly realistic vehicle physics with proper tire slip, thermals, aero, LSD, multi ray tire, and speed dependent steering geometry.", "Prototype", "Light", 2100 }, + { "Open World Forest", "256 million of Ghost of Tsushima grass blades", "Prototype", "Very demanding", 5600 }, + { "Liminal Space", "Shifts your frequency to a nearby reality", "Prototype", "Light", 2100 }, + { "Sponza 4K", "High-resolution textures & meshes", "Complete" , "Demanding", 2600 }, + { "Subway", "GI test. No lights, only emissive textures", "Prototype", "Moderate", 2600 }, + { "Minecraft", "Blocky aesthetic", "Complete" , "Light", 2100 }, + { "Basic", "Light, camera, floor", "Complete" , "Light", 2100 }, + { "Water", "Light, camera, ocean", "Prototype", "Light", 2100 }, }; int world_index = 0; From 73bfe3a099e040ccc00a3006ec7634e6348eeabb Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 26 Jan 2026 19:46:52 +0100 Subject: [PATCH 125/133] [ocean] debugging materials issue --- source/runtime/Game/Game.cpp | 4 ++-- source/runtime/Rendering/Renderer_Passes.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 87b0015c1a..9bfb88e6f2 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -366,13 +366,13 @@ namespace spartan material->SetObjectName("material_ocean"); material->SetResourceFilePath("ocean" + string(EXTENSION_MATERIAL)); - material->LoadFromFile(material->GetResourceFilePath()); + //material->LoadFromFile(material->GetResourceFilePath()); material->SetOceanTileCount(grid_size); material->SetOceanTileSize(tile_size); material->SetOceanVerticesCount(density); material->MarkSpectrumAsComputed(false); - material->SetTexture(MaterialTextureType::Flowmap, "project\\materials\\water\\flowmap.png"); + //material->SetTexture(MaterialTextureType::Flowmap, "project\\materials\\water\\flowmap.png"); // if material fails to load from file if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index df27ca47ef..a612ec6afd 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -194,15 +194,15 @@ namespace spartan prev_material = material; - if (material->ShouldComputeSpectrum()) - { - SP_LOG_INFO("Computing Ocean Spectrum..."); + //if (material->ShouldComputeSpectrum()) + //{ + //SP_LOG_INFO("Computing Ocean Spectrum..."); Pass_ComputeInitialSpectrum(cmd_list_graphics_present); // calculates conjugate and stores it in BA channels of the initial spectrum Pass_PackSpectrum(cmd_list_graphics_present); material->MarkSpectrumAsComputed(true); - } + //} // computes displacement and slope maps Pass_AdvanceSpectrum(cmd_list_graphics_present); From 50863ddc702c4bc07c6c3dac525ab2712e8c3f7c Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Wed, 18 Feb 2026 22:26:02 +0100 Subject: [PATCH 126/133] [ocean] wip working ocean world --- data/shaders/common_resources.hlsl | 4 +- data/shaders/depth_prepass.hlsl | 28 ++++-- data/shaders/g_buffer.hlsl | 30 ++++--- data/shaders/indirect_cull.hlsl | 2 +- source/runtime/Game/Game.cpp | 2 +- source/runtime/Rendering/Renderer.cpp | 6 +- source/runtime/Rendering/Renderer_Buffers.h | 4 +- source/runtime/Rendering/Renderer_Passes.cpp | 92 +++++++++++++++++--- 8 files changed, 128 insertions(+), 40 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index d2a98b9576..97ab4668b3 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -302,7 +302,9 @@ struct DrawData uint material_index; uint is_transparent; uint aabb_index; - uint padding; + float tile_size; + float2 tile_xz_pos; + float2 padding; }; // bindless draw data - per-draw transforms, material indices, etc. diff --git a/data/shaders/depth_prepass.hlsl b/data/shaders/depth_prepass.hlsl index 04aac9113c..aab0baee0a 100644 --- a/data/shaders/depth_prepass.hlsl +++ b/data/shaders/depth_prepass.hlsl @@ -38,19 +38,30 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI MaterialParameters material = GetMaterial(); Surface surface; surface.flags = material.flags; + + float2 pos = float2(0.0f, 0.0f); - if (surface.is_ocean()) + if (surface.is_ocean() && material.ocean_parameters.displacementScale > -1.0f) { - const float3 pass_values = pass_get_f3_value2(); - const float2 tile_xz_pos = pass_values.xy; - const float tile_size = pass_values.z; - const float2 tile_local_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); + //const float3 pass_values = pass_get_f3_value2(); + //const float2 tile_xz_pos = pass_values.xy; + //const float tile_size = pass_values.z; + const float2 tile_local_uv = ocean_get_world_space_uvs(input.uv, _draw.tile_xz_pos, _draw.tile_size); + + const float2 world_pos = mul(input.position, _draw.transform).xz; + const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); + float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, tex5, tile_xz_pos, material.ocean_parameters.windDirection, tile_local_uv); + synthesize(tex2, displacement, tile_local_uv); + //synthesize_with_flow(tex2, displacement, tex5, world_pos, material.ocean_parameters.windDirection, tile_local_uv); + + const float height = tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); - input.position.xyz += displacement * material.ocean_parameters.displacementScale; + input.position.y += height <= 0.0f ? 0.0f : height + 3.0f; + input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; + + pos = world_pos; } // transform to world space @@ -58,6 +69,7 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI float3 position_world_previous = 0.0f; gbuffer_vertex vertex = transform_to_world_space(input, instance_id, _draw.transform, position_world, position_world_previous); vertex.material_index = _draw.material_index; + vertex.tile_position = pos; return transform_to_clip_space(vertex, position_world, position_world_previous); } diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 16c9469d29..d9e3d957a8 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -131,18 +131,18 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI if (surface.is_ocean() && material.ocean_parameters.displacementScale > -1.0f) { - const float3 pass_values = pass_get_f3_value(); - const float2 tile_xz_pos = pass_values.xy; - const float tile_size = pass_values.z; - const float2 tile_local_uv = ocean_get_world_space_uvs(input.uv, tile_xz_pos, tile_size); + //const float3 pass_values = pass_get_f3_value(); + //const float2 tile_xz_pos = pass_values.xy; + //const float tile_size = pass_values.z; + const float2 tile_local_uv = ocean_get_world_space_uvs(input.uv, _draw.tile_xz_pos, _draw.tile_size); const float2 world_pos = mul(input.position, _draw.transform).xz; const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, tex5, world_pos, material.ocean_parameters.windDirection, tile_local_uv); + synthesize(tex2, displacement, tile_local_uv); + //synthesize_with_flow(tex2, displacement, tex5, world_pos, material.ocean_parameters.windDirection, tile_local_uv); const float height = tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); @@ -300,14 +300,15 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) } else if (surface.is_ocean()) { - const float3 pass_values = pass_get_f3_value(); - const float2 tile_xz_pos = pass_values.xy; - const float tile_size = pass_values.z; - const float2 tile_local_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, tile_xz_pos, tile_size); + //const float3 pass_values = pass_get_f3_value(); + //const float2 tile_xz_pos = pass_values.xy; + //const float tile_size = pass_values.z; + + const float2 tile_local_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, _draw.tile_xz_pos, _draw.tile_size); float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); - //synthesize(tex3, slope, world_space_tile_uv); - synthesize_with_flow(tex3, slope, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv); + synthesize(tex3, slope, tile_local_uv); + //synthesize_with_flow(tex3, slope, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv); slope = slope * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, vertex.normal.y, -slope.y)); @@ -323,8 +324,8 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) // first we must synthesise again since we dont have access // to the synthesised displacement (it's calculated in the vertex stage) float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - //synthesize(tex2, displacement, world_space_tile_uv); - synthesize_with_flow(tex2, displacement, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv, true); + synthesize(tex2, displacement, tile_local_uv); + //synthesize_with_flow(tex2, displacement, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv, true); normal = displacement; @@ -392,6 +393,7 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) gbuffer g_buffer; g_buffer.albedo = albedo; g_buffer.normal = float4(normal, pass_get_material_index()); + g_buffer.normal = float4(_draw.tile_xz_pos.x, _draw.tile_xz_pos.y, _draw.tile_size, pass_get_material_index()); g_buffer.material = float4(roughness, metalness, emission, occlusion); g_buffer.velocity = velocity; return g_buffer; diff --git a/data/shaders/indirect_cull.hlsl b/data/shaders/indirect_cull.hlsl index 5b405764bf..c7dce2c34e 100644 --- a/data/shaders/indirect_cull.hlsl +++ b/data/shaders/indirect_cull.hlsl @@ -143,7 +143,7 @@ void main_cs(uint3 dispatch_thread_id : SV_DispatchThreadID) // atomically allocate a slot in the output buffers uint output_index; InterlockedAdd(indirect_draw_count[0], 1, output_index); - + // compact the draw arguments and per-draw data indirect_draw_args_out[output_index] = indirect_draw_args[draw_index]; indirect_draw_data_out[output_index] = indirect_draw_data[draw_index]; diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 3dd72a0563..5e1e67784d 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -374,7 +374,7 @@ namespace spartan // if material fails to load from file if (material->GetProperty(MaterialProperty::IsOcean) != 1.0f) { - material->SetColor(Color(0.0f, 142.0f / 255.0f, 229.0f / 255.0f, 254.0f / 255.0f)); + material->SetColor(Color(0.0f, 142.0f / 255.0f, 229.0f / 255.0f, 255.0f / 255.0f)); material->SetProperty(MaterialProperty::IsOcean, 1.0f); material->SetOceanProperty(OceanParameters::Angle, 0.0f); //handled internally diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index fa07ad36a3..263d7c56af 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -987,7 +987,8 @@ namespace spartan bool is_instanced = draw_call.instance_count > 1; bool is_alpha_tested = material->IsAlphaTested(); bool is_non_standard_cull = static_cast(material->GetProperty(MaterialProperty::CullMode)) != RHI_CullMode::Back; - return is_tessellated || is_instanced || is_alpha_tested || is_non_standard_cull; + //bool is_ocean = material->IsOcean(); // TEMP: for now, ocean is cpu driven. Might wanna move it to gpu driven + return is_tessellated || is_instanced || is_alpha_tested || is_non_standard_cull; //|| is_ocean; } void Renderer::SetCommonTextures(RHI_CommandList* cmd_list) @@ -1501,11 +1502,14 @@ namespace spartan // aabb_index points past the prepass aabbs in the shared buffer Sb_DrawData& data = m_indirect_draw_data[idx]; Entity* entity = renderable->GetEntity(); + math::Vector3 ent_pos = entity->GetPosition(); data.transform = entity->GetMatrix(); data.transform_previous = entity->GetMatrixPrevious(); data.material_index = material->GetIndex(); data.is_transparent = 0; data.aabb_index = m_draw_calls_prepass_count + idx; + data.tile_size = material->GetOceanTileSize(); + data.tile_xz_pos = { ent_pos.x, ent_pos.z }; data.padding = 0; } } diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index fc938950fa..7f6577c773 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -244,7 +244,9 @@ namespace spartan uint32_t material_index = 0; // index into the bindless material parameters array uint32_t is_transparent = 0; // transparency flag uint32_t aabb_index = 0; // index into the aabb buffer for culling - uint32_t padding = 0; + float tile_size = 0.0f; // used for the ocean synthesis + math::Vector2 tile_xz_pos; // used for the ocean synthesis + math::Vector2 padding; }; // gpu particle (matches hlsl Particle struct, 64 bytes) diff --git a/source/runtime/Rendering/Renderer_Passes.cpp b/source/runtime/Rendering/Renderer_Passes.cpp index 2d39972301..6941c7a1df 100644 --- a/source/runtime/Rendering/Renderer_Passes.cpp +++ b/source/runtime/Rendering/Renderer_Passes.cpp @@ -168,15 +168,15 @@ namespace spartan prev_material = material; - //if (material->ShouldComputeSpectrum()) - //{ + if (material->ShouldComputeSpectrum()) + { //SP_LOG_INFO("Computing Ocean Spectrum..."); Pass_ComputeInitialSpectrum(cmd_list_graphics_present); // calculates conjugate and stores it in BA channels of the initial spectrum Pass_PackSpectrum(cmd_list_graphics_present); material->MarkSpectrumAsComputed(true); - //} + } // computes displacement and slope maps Pass_AdvanceSpectrum(cmd_list_graphics_present); @@ -613,6 +613,31 @@ namespace spartan // and prior cpu-driven draws may have changed the dynamic cull mode state cmd_list->SetCullMode(RHI_CullMode::Back); + for (uint32_t i = 0; i < m_draw_call_count; i++) + { + const Renderer_DrawCall& draw_call = m_draw_calls[i]; + Renderable* renderable = draw_call.renderable; + Material* material = renderable->GetMaterial(); + if (!material || !draw_call.camera_visible) + continue; + + Entity* entity = renderable->GetEntity(); + + //if (material->IsOcean()) + //{ + // const Vector3 tile_pos = renderable->GetEntity()->GetPosition(); + // m_pcb_pass_cpu.set_f3_value2(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); + //} + + cmd_list->PushConstants(m_pcb_pass_cpu); + + if (material->IsOcean()) + { + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); + } + } + cmd_list->DrawIndexedIndirectCount( GetBuffer(Renderer_Buffer::IndirectDrawArgsOut), 0, @@ -675,11 +700,11 @@ namespace spartan m_pcb_pass_cpu.material_index = material->GetIndex(); m_pcb_pass_cpu.set_f3_value(0.0f, has_color_texture ? 1.0f : 0.0f, static_cast(i)); - if (material->IsOcean()) - { - const Vector3 tile_pos = renderable->GetEntity()->GetPosition(); - m_pcb_pass_cpu.set_f3_value2(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); - } + //if (material->IsOcean()) + //{ + // const Vector3 tile_pos = renderable->GetEntity()->GetPosition(); + // m_pcb_pass_cpu.set_f3_value2(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); + //} cmd_list->PushConstants(m_pcb_pass_cpu); } @@ -770,6 +795,40 @@ namespace spartan // and prior cpu-driven draws may have changed the dynamic cull mode state cmd_list->SetCullMode(RHI_CullMode::Back); + for (uint32_t i = 0; i < m_draw_call_count; i++) + { + const Renderer_DrawCall& draw_call = m_draw_calls[i]; + Renderable* renderable = draw_call.renderable; + Material* material = renderable->GetMaterial(); + if (!material || !draw_call.camera_visible) + continue; + + Entity* entity = renderable->GetEntity(); + + //if (material->IsOcean()) + //{ + // const Vector3 tile_pos = entity->GetPosition(); + // m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); + //} + + cmd_list->PushConstants(m_pcb_pass_cpu); + + if (material->IsOcean()) + { + RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex2, displacement_map); + + RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + cmd_list->SetTexture(Renderer_BindingsSrv::tex3, slope_map); + + RHI_Texture* heightmap = material->GetTexture(MaterialTextureType::Height); + cmd_list->SetTexture(Renderer_BindingsSrv::tex4, heightmap); + + RHI_Texture* flowmap = material->GetTexture(MaterialTextureType::Flowmap); + cmd_list->SetTexture(Renderer_BindingsSrv::tex5, flowmap); + } + } + // single indirect draw call replaces the entire opaque draw loop cmd_list->DrawIndexedIndirectCount( GetBuffer(Renderer_Buffer::IndirectDrawArgsOut), // compacted indirect args @@ -860,11 +919,11 @@ namespace spartan m_pcb_pass_cpu.is_transparent = is_transparent_pass ? 1 : 0; m_pcb_pass_cpu.material_index = material->GetIndex(); - if (material->IsOcean()) - { - const Vector3 tile_pos = entity->GetPosition(); - m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); - } + //if (material->IsOcean()) + //{ + // const Vector3 tile_pos = entity->GetPosition(); + // m_pcb_pass_cpu.set_f3_value(tile_pos.x, tile_pos.z, material->GetOceanTileSize()); + //} cmd_list->PushConstants(m_pcb_pass_cpu); @@ -1923,6 +1982,7 @@ namespace spartan void Renderer::Pass_ComputeInitialSpectrum(RHI_CommandList* cmd_list) { RHI_Texture* initial_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_initial_spectrum); + RHI_Texture* tex_normal = GetRenderTarget(Renderer_RenderTarget::gbuffer_normal); cmd_list->BeginTimeblock("ocean_intial_spectrum"); { @@ -1932,6 +1992,7 @@ namespace spartan cmd_list->SetPipelineState(pso); cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); + cmd_list->SetTexture(Renderer_BindingsSrv::gbuffer_normal, tex_normal); cmd_list->Dispatch(initial_spectrum); } cmd_list->EndTimeblock(); @@ -1962,6 +2023,7 @@ namespace spartan RHI_Texture* initial_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_initial_spectrum); RHI_Texture* displacement_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_spectrum); RHI_Texture* slope_spectrum = GetRenderTarget(Renderer_RenderTarget::ocean_slope_spectrum); + RHI_Texture* tex_normal = GetRenderTarget(Renderer_RenderTarget::gbuffer_normal); cmd_list->BeginTimeblock("ocean_advance_spectrum"); { @@ -1973,6 +2035,7 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_initial_spectrum, initial_spectrum); cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_spectrum, displacement_spectrum); cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); + cmd_list->SetTexture(Renderer_BindingsSrv::gbuffer_normal, tex_normal); cmd_list->Dispatch(initial_spectrum); } cmd_list->EndTimeblock(); @@ -2027,6 +2090,8 @@ namespace spartan RHI_Texture* displacement_map = GetRenderTarget(Renderer_RenderTarget::ocean_displacement_map); RHI_Texture* slope_map = GetRenderTarget(Renderer_RenderTarget::ocean_slope_map); + RHI_Texture* tex_normal = GetRenderTarget(Renderer_RenderTarget::gbuffer_normal); + cmd_list->BeginTimeblock("ocean_map_generation"); { RHI_PipelineState pso; @@ -2038,6 +2103,7 @@ namespace spartan cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_spectrum, slope_spectrum); cmd_list->SetTexture(Renderer_BindingsUav::ocean_displacement_map, displacement_map); cmd_list->SetTexture(Renderer_BindingsUav::ocean_slope_map, slope_map); + cmd_list->SetTexture(Renderer_BindingsSrv::gbuffer_normal, tex_normal); cmd_list->Dispatch(displacement_map); Pass_Downscale(cmd_list, displacement_map, Renderer_DownsampleFilter::Average); From 5adffa8805c22db24c8050ba65035e95cd164440 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Thu, 19 Feb 2026 17:39:24 +0100 Subject: [PATCH 127/133] [ocean] gbuffer fixes and improved foam --- data/shaders/common_vertex_processing.hlsl | 4 +++- data/shaders/g_buffer.hlsl | 10 ++++++---- data/shaders/ocean/synthesise_maps.hlsl | 15 ++++++++++++--- source/runtime/Game/Game.cpp | 2 +- source/runtime/Rendering/Renderer_Resources.cpp | 15 ++++++++------- 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/data/shaders/common_vertex_processing.hlsl b/data/shaders/common_vertex_processing.hlsl index 6f0cd5eb4b..11a045dc6a 100644 --- a/data/shaders/common_vertex_processing.hlsl +++ b/data/shaders/common_vertex_processing.hlsl @@ -51,7 +51,9 @@ struct gbuffer_vertex float4 uv_misc : TEXCOORD; // xy = uv, z = height_percent, w = instance_id - packed together to reduced the interpolators (shader registers) the gpu needs to track float width_percent : TEXCOORD2; // temp, will remove nointerpolation uint material_index : TEXCOORD3; // for indirect draws, material index passed from vs - float2 tile_position : POS_TILE; + float2 tile_position : TILE_POS; + float2 tile_xz_pos : TILE_XZ_POS; + float tile_size : TILE_SIZE; }; float4x4 compose_instance_transform(min16float instance_position_x, min16float instance_position_y, min16float instance_position_z, uint instance_normal_oct, uint instance_yaw, uint instance_scale) diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index d9e3d957a8..751102dc0c 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -157,6 +157,8 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI gbuffer_vertex vertex = transform_to_world_space(input, instance_id, _draw.transform, position_world, position_world_previous); vertex.material_index = _draw.material_index; vertex.tile_position = pos; + vertex.tile_xz_pos = _draw.tile_xz_pos; + vertex.tile_size = _draw.tile_size; return transform_to_clip_space(vertex, position_world, position_world_previous); } @@ -304,18 +306,19 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) //const float2 tile_xz_pos = pass_values.xy; //const float tile_size = pass_values.z; - const float2 tile_local_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, _draw.tile_xz_pos, _draw.tile_size); + const float2 tile_local_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, vertex.tile_xz_pos, vertex.tile_size); float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex3, slope, tile_local_uv); + synthesize(tex3, slope, tile_local_uv, true); //synthesize_with_flow(tex3, slope, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv); - slope = slope * material.ocean_parameters.slopeScale; + slope.rgb = slope.rgb * material.ocean_parameters.slopeScale; normal = normalize(float3(-slope.x, vertex.normal.y, -slope.y)); // apply foam (foam mask is stored in the alpha channel of slope map) //const float foam_noise = compute_foam_noise(vertex.uv_misc.xy, buffer_frame.time); albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); + roughness = lerp(roughness, 1.0f, slope.a); if (material.ocean_parameters.debugDisplacement == 1.0f) // displacement { @@ -393,7 +396,6 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) gbuffer g_buffer; g_buffer.albedo = albedo; g_buffer.normal = float4(normal, pass_get_material_index()); - g_buffer.normal = float4(_draw.tile_xz_pos.x, _draw.tile_xz_pos.y, _draw.tile_size, pass_get_material_index()); g_buffer.material = float4(roughness, metalness, emission, occlusion); g_buffer.velocity = velocity; return g_buffer; diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 7aa89dae38..58cbc4207e 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -77,7 +77,7 @@ void preserve_variance(out float4 linear_color, float4 mean_color, float moment2 linear_color = (linear_color - mean_color) / sqrt(moment2) + mean_color; } -void synthesize(Texture2D example, out float4 output, float2 uv) +void synthesize(Texture2D example, out float4 output, float2 uv, bool preserve_foam = false) { const float tex_freq = 1.0f; const float tile_freq = 2.0f; @@ -94,8 +94,17 @@ void synthesize(Texture2D example, out float4 output, float2 uv) } // assumes example is a mip mapped 512x512 texture and samples lowest mip (1x1) const float4 mean_example = example.SampleLevel(samplers[sampler_point_clamp], uv, 9); - - preserve_variance(output, mean_example, moment2); + + if (preserve_foam) + { + const float foam = saturate(output.a); + preserve_variance(output, mean_example, moment2); + output.a = foam; + } + else + { + preserve_variance(output, mean_example, moment2); + } } void synthesize_with_flow(Texture2D example, out float4 output, Texture2D flowmap, float2 tile_xz_pos, float wind_dir_deg, float2 tile_local_uv, bool debug_flow = false) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 5e1e67784d..8e3877fdb1 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -363,7 +363,7 @@ namespace spartan material->SetObjectName("material_ocean"); material->SetResourceFilePath("ocean" + string(EXTENSION_MATERIAL)); - //material->LoadFromFile(material->GetResourceFilePath()); + material->LoadFromFile(material->GetResourceFilePath()); material->SetOceanTileCount(grid_size); material->SetOceanTileSize(tile_size); diff --git a/source/runtime/Rendering/Renderer_Resources.cpp b/source/runtime/Rendering/Renderer_Resources.cpp index 3f3f1789fc..129a00ba74 100644 --- a/source/runtime/Rendering/Renderer_Resources.cpp +++ b/source/runtime/Rendering/Renderer_Resources.cpp @@ -360,18 +360,19 @@ namespace spartan // ocean { - uint32_t flags = RHI_Texture_Uav | RHI_Texture_Srv; - uint32_t texture_size = 512; + const uint32_t flags = RHI_Texture_Uav | RHI_Texture_Srv; + const uint32_t texture_size = 512; + const RHI_Format texture_format = RHI_Format::R16G16B16A16_Float; - render_target(Renderer_RenderTarget::ocean_initial_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_initial_spectrum"); + render_target(Renderer_RenderTarget::ocean_initial_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, texture_format, flags, "ocean_initial_spectrum"); - render_target(Renderer_RenderTarget::ocean_displacement_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_displacement_spectrum"); + render_target(Renderer_RenderTarget::ocean_displacement_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, texture_format, flags, "ocean_displacement_spectrum"); - render_target(Renderer_RenderTarget::ocean_slope_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, RHI_Format::R16G16B16A16_Float, flags, "ocean_slope_spectrum"); + render_target(Renderer_RenderTarget::ocean_slope_spectrum) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 1, texture_format, flags, "ocean_slope_spectrum"); - render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_displacement_map"); + render_target(Renderer_RenderTarget::ocean_displacement_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, texture_format, flags | RHI_Texture_PerMipViews, "ocean_displacement_map"); - render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, RHI_Format::R16G16B16A16_Float, flags | RHI_Texture_PerMipViews, "ocean_slope_map"); + render_target(Renderer_RenderTarget::ocean_slope_map) = make_shared(RHI_Texture_Type::Type2D, texture_size, texture_size, 1, 10, texture_format, flags | RHI_Texture_PerMipViews, "ocean_slope_map"); } // occlusion From 07372bcdc1b6d9a47c4cc38e1d74ad1ff0a7b1c5 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Sat, 21 Feb 2026 20:58:47 +0100 Subject: [PATCH 128/133] [ocean] clipmap implementation wip --- data/shaders/common_resources.hlsl | 5 +- data/shaders/common_vertex_processing.hlsl | 4 +- data/shaders/depth_prepass.hlsl | 38 ++- data/shaders/g_buffer.hlsl | 71 ++---- source/editor/Widgets/Properties.cpp | 21 -- source/runtime/Core/Debugging.h | 2 +- source/runtime/Game/Game.cpp | 244 +++++++------------ source/runtime/Geometry/GeometryGeneration.h | 33 +++ source/runtime/Rendering/Material.h | 13 +- source/runtime/Rendering/Renderer.cpp | 5 +- source/runtime/Rendering/Renderer_Buffers.h | 7 +- source/runtime/World/Components/Renderable.h | 10 + 12 files changed, 180 insertions(+), 273 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index 97ab4668b3..c7827b9710 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -303,8 +303,9 @@ struct DrawData uint is_transparent; uint aabb_index; float tile_size; - float2 tile_xz_pos; - float2 padding; + float2 tile_world_pos; + uint tile_res; + float padding; }; // bindless draw data - per-draw transforms, material indices, etc. diff --git a/data/shaders/common_vertex_processing.hlsl b/data/shaders/common_vertex_processing.hlsl index 11a045dc6a..81c3b9bbd4 100644 --- a/data/shaders/common_vertex_processing.hlsl +++ b/data/shaders/common_vertex_processing.hlsl @@ -51,9 +51,7 @@ struct gbuffer_vertex float4 uv_misc : TEXCOORD; // xy = uv, z = height_percent, w = instance_id - packed together to reduced the interpolators (shader registers) the gpu needs to track float width_percent : TEXCOORD2; // temp, will remove nointerpolation uint material_index : TEXCOORD3; // for indirect draws, material index passed from vs - float2 tile_position : TILE_POS; - float2 tile_xz_pos : TILE_XZ_POS; - float tile_size : TILE_SIZE; + float2 synth_uv : TEXCOORD4; }; float4x4 compose_instance_transform(min16float instance_position_x, min16float instance_position_y, min16float instance_position_z, uint instance_normal_oct, uint instance_yaw, uint instance_scale) diff --git a/data/shaders/depth_prepass.hlsl b/data/shaders/depth_prepass.hlsl index aab0baee0a..aeae7b1f0e 100644 --- a/data/shaders/depth_prepass.hlsl +++ b/data/shaders/depth_prepass.hlsl @@ -39,37 +39,27 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI Surface surface; surface.flags = material.flags; - float2 pos = float2(0.0f, 0.0f); + // transform to world space + float3 position_world = 0.0f; + float3 position_world_previous = 0.0f; + gbuffer_vertex vertex = transform_to_world_space(input, instance_id, _draw.transform, position_world, position_world_previous); if (surface.is_ocean() && material.ocean_parameters.displacementScale > -1.0f) { - //const float3 pass_values = pass_get_f3_value2(); - //const float2 tile_xz_pos = pass_values.xy; - //const float tile_size = pass_values.z; - const float2 tile_local_uv = ocean_get_world_space_uvs(input.uv, _draw.tile_xz_pos, _draw.tile_size); - - const float2 world_pos = mul(input.position, _draw.transform).xz; - - const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); + // 1. Snap to this LOD's vertex grid to prevent swimming + const float vertex_spacing = _draw.tile_size / _draw.tile_res; + position_world.xz = round(position_world.xz / vertex_spacing) * vertex_spacing; + // 2. Synthesize displacement + const float2 disp_uv = position_world.xz / material.ocean_parameters.lengthScale; float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, tile_local_uv); - //synthesize_with_flow(tex2, displacement, tex5, world_pos, material.ocean_parameters.windDirection, tile_local_uv); - - const float height = tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); - - input.position.y += height <= 0.0f ? 0.0f : height + 3.0f; - input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; + synthesize(tex2, displacement, disp_uv); - pos = world_pos; + // 3. Apply displacement + position_world.xyz += displacement.xyz; + position_world_previous = position_world; } - - // transform to world space - float3 position_world = 0.0f; - float3 position_world_previous = 0.0f; - gbuffer_vertex vertex = transform_to_world_space(input, instance_id, _draw.transform, position_world, position_world_previous); - vertex.material_index = _draw.material_index; - vertex.tile_position = pos; + vertex.material_index = _draw.material_index; return transform_to_clip_space(vertex, position_world, position_world_previous); } diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 751102dc0c..5d9c2c2946 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -127,38 +127,29 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI Surface surface; surface.flags = material.flags; - float2 pos = float2(0.0f, 0.0f); + // transform to world space + float3 position_world = 0.0f; + float3 position_world_previous = 0.0f; + gbuffer_vertex vertex = transform_to_world_space(input, instance_id, _draw.transform, position_world, position_world_previous); if (surface.is_ocean() && material.ocean_parameters.displacementScale > -1.0f) { - //const float3 pass_values = pass_get_f3_value(); - //const float2 tile_xz_pos = pass_values.xy; - //const float tile_size = pass_values.z; - const float2 tile_local_uv = ocean_get_world_space_uvs(input.uv, _draw.tile_xz_pos, _draw.tile_size); - - const float2 world_pos = mul(input.position, _draw.transform).xz; - - const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); + // 1. Snap to this LOD's vertex grid to prevent swimming + const float vertex_spacing = _draw.tile_size / _draw.tile_res; + position_world.xz = round(position_world.xz / vertex_spacing) * vertex_spacing; + // 2. Synthesize displacement + const float2 synth_uv = position_world.xz / material.ocean_parameters.lengthScale; float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, tile_local_uv); - //synthesize_with_flow(tex2, displacement, tex5, world_pos, material.ocean_parameters.windDirection, tile_local_uv); + synthesize(tex2, displacement, synth_uv); - const float height = tex4.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); - - input.position.y += height <= 0.0f ? 0.0f : height + 3.0f; - input.position.xyz += displacement.xyz * material.ocean_parameters.displacementScale; + // 3. Apply displacement + position_world.xyz += displacement.xyz; + position_world_previous = position_world; - pos = world_pos; + vertex.synth_uv = synth_uv; } - - float3 position_world = 0.0f; - float3 position_world_previous = 0.0f; - gbuffer_vertex vertex = transform_to_world_space(input, instance_id, _draw.transform, position_world, position_world_previous); - vertex.material_index = _draw.material_index; - vertex.tile_position = pos; - vertex.tile_xz_pos = _draw.tile_xz_pos; - vertex.tile_size = _draw.tile_size; + vertex.material_index = _draw.material_index; return transform_to_clip_space(vertex, position_world, position_world_previous); } @@ -302,21 +293,14 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) } else if (surface.is_ocean()) { - //const float3 pass_values = pass_get_f3_value(); - //const float2 tile_xz_pos = pass_values.xy; - //const float tile_size = pass_values.z; - - const float2 tile_local_uv = ocean_get_world_space_uvs(vertex.uv_misc.xy, vertex.tile_xz_pos, vertex.tile_size); - float4 slope = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex3, slope, tile_local_uv, true); - //synthesize_with_flow(tex3, slope, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv); - + synthesize(tex3, slope, vertex.synth_uv, true); + slope.rgb = slope.rgb * material.ocean_parameters.slopeScale; + normal = normalize(float3(-slope.x, vertex.normal.y, -slope.y)); - + // apply foam (foam mask is stored in the alpha channel of slope map) - //const float foam_noise = compute_foam_noise(vertex.uv_misc.xy, buffer_frame.time); albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); roughness = lerp(roughness, 1.0f, slope.a); @@ -324,21 +308,10 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) { if (material.ocean_parameters.debugSynthesised == 1.0f) // show synthesised version { - // first we must synthesise again since we dont have access - // to the synthesised displacement (it's calculated in the vertex stage) float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, tile_local_uv); - //synthesize_with_flow(tex2, displacement, tex5, vertex.tile_position, material.ocean_parameters.windDirection, tile_local_uv, true); + synthesize(tex2, displacement, vertex.synth_uv); - normal = displacement; - - //{ - // const float2 world_pos = vertex.tile_position; - // const float2 uv = (world_pos - (-3069.0f)) / (3069.0f - (-3069.0f)); - // float4 flow = float4(tex5.Sample(samplers[sampler_point_wrap], uv).rg, 0.0f, 1.0f); - // albedo = float4(uv, 0.0f, 1.0f); - // albedo = flow; - //} + albedo = displacement; } else // show original displacement albedo = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgba; @@ -354,6 +327,8 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) //albedo = tex4.Sample(samplers[sampler_anisotropic_wrap], world_space_tile_uv / float2(6.0f, 6.0f)).rgba; //albedo = float4(flow_dir * 0.5f + 0.5f, 0.0f, 1.0f); //albedo = tex5.Sample(samplers[sampler_point_clamp], vertex.uv_misc.xy); + + //albedo = tex2.Sample(samplers[sampler_trilinear_clamp], vertex.uv_misc.xy).rgba; } // foliage curved normals diff --git a/source/editor/Widgets/Properties.cpp b/source/editor/Widgets/Properties.cpp index 106a918f59..4f24798404 100644 --- a/source/editor/Widgets/Properties.cpp +++ b/source/editor/Widgets/Properties.cpp @@ -1621,27 +1621,6 @@ void Properties::ShowMaterial(Material* material) const show_jonswap_params("Slope Scale", "", OceanParameters::SlopeScale); show_jonswap_params("Length Scale", "", OceanParameters::LengthScale); - int tile_count = material->GetOceanTileCount(); - ImGui::InputInt("Ocean Tiles", &tile_count); - if (ImGui::IsItemDeactivatedAfterEdit()) - { - material->SetOceanTileCount(tile_count); - } - - float tile_size = material->GetOceanTileSize(); - ImGui::InputFloat("Ocean Tile Size", &tile_size); - if (ImGui::IsItemDeactivatedAfterEdit()) - { - material->SetOceanTileSize(tile_size); - } - - int vertices_count = material->GetOceanVerticesCount(); - ImGui::InputInt("Ocean Vertices Count", &vertices_count); - if (ImGui::IsItemDeactivatedAfterEdit()) - { - material->SetOceanVerticesCount(vertices_count); - } - bool show_displacement = material->GetOceanProperty(OceanParameters::DebugDisplacement) == 1.0f ? true : false; ImGui::Checkbox("Show Displacement Map", &show_displacement); material->SetOceanProperty(OceanParameters::DebugDisplacement, show_displacement ? 1.0f : 0.0f); diff --git a/source/runtime/Core/Debugging.h b/source/runtime/Core/Debugging.h index 7f348c29c5..cbbd65cda3 100644 --- a/source/runtime/Core/Debugging.h +++ b/source/runtime/Core/Debugging.h @@ -40,7 +40,7 @@ namespace spartan inline static bool m_gpu_assisted_validation_enabled = false; // performs gpu-based validation with substantial cpu and gpu performance impact inline static bool m_logging_to_file_enabled = false; // writes diagnostic logs to disk, causes high cpu overhead due to file I/O operations inline static bool m_breadcrumbs_enabled = false; // tracks gpu crash information in breadcrumbs.txt, minimal overhead (amd gpus only) - crashes in debug mode - outputs unreliable data in release mode - issue reported to amd - inline static bool m_renderdoc_enabled = true; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping + inline static bool m_renderdoc_enabled = false; // integrates RenderDoc graphics debugging, introduces high cpu overhead from api wrapping inline static bool m_gpu_marking_enabled = true; // enables gpu resource marking with negligible performance cost inline static bool m_gpu_timing_enabled = true; // enables gpu performance timing with negligible performance cost inline static bool m_shader_optimization_enabled = true; // controls shader optimization, disabling has significant performance impact diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 8e3877fdb1..2fa69cbc52 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -350,7 +350,7 @@ namespace spartan return water; } - Entity* ocean(std::shared_ptr material, const Vector3& position, float tile_size, uint32_t density, uint32_t grid_size) + Entity* ocean(std::shared_ptr material, const Vector3& position) { // entity Entity* water = World::CreateEntity(); @@ -364,10 +364,7 @@ namespace spartan material->SetResourceFilePath("ocean" + string(EXTENSION_MATERIAL)); material->LoadFromFile(material->GetResourceFilePath()); - material->SetOceanTileCount(grid_size); - material->SetOceanTileSize(tile_size); - material->SetOceanVerticesCount(density); material->MarkSpectrumAsComputed(false); //material->SetTexture(MaterialTextureType::Flowmap, "project\\materials\\water\\flowmap.png"); @@ -405,56 +402,6 @@ namespace spartan material->SetOceanProperty(OceanParameters::LengthScale, 128.0f); } } - - // geometry - { - // generate grid - const uint32_t grid_points_per_dimension = density; - vector vertices; - vector indices; - geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, tile_size); - - string name = "ocean mesh"; - - // create mesh if it doesn't exist - shared_ptr mesh = meshes.emplace_back(make_shared()); - mesh->SetObjectName(name); - mesh->SetRootEntity(water); - mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); - mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false); - mesh->AddGeometry(vertices, indices, false); - mesh->CreateGpuBuffers(); - - // create a child entity, add a renderable, and this mesh tile to it - for (uint32_t row = 0; row < grid_size; row++) - { - for (uint32_t col = 0; col < grid_size; col++) - { - int tile_index = col + row * grid_size; - - string tile_name = "ocean tile_" + to_string(tile_index); - - Entity* entity_tile = World::CreateEntity(); - entity_tile->SetObjectName(tile_name); - entity_tile->SetParent(water); - - Vector3 tile_position = { col * tile_size, 0.0f, row * tile_size }; - entity_tile->SetPosition(tile_position); - - if (Renderable* renderable = entity_tile->AddComponent()) - { - renderable->SetMesh(mesh.get()); - renderable->SetMaterial(material); - renderable->SetFlag(RenderableFlags::CastsShadows, false); - } - - // enable buoyancy - //Physics* physics = entity_tile->AddComponent(); - //physics->SetBodyType(BodyType::Water); - } - } - } - return water; } } @@ -1650,135 +1597,112 @@ namespace spartan //== Ocean =========================================================================== namespace ocean { - uint32_t ocean_tile_count = 6; - float tile_size = 128.0f; - uint32_t vertices_count = 512; shared_ptr material = make_shared(); - void create() + // Clipmap + std::vector tile_vertices {}; + std::vector tile_indices {}; + struct TileInstance { - entities::camera(false); - entities::sun(LightPreset::day, true); - - auto entity = World::CreateEntity(); - - default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }, tile_size, vertices_count, ocean_tile_count); - - default_ocean->SetParent(entity); - - /*auto light_entity = World::CreateEntity(); - light_entity->SetPosition({ 196.0f, 280.0f, 196.0f }); - - Light* point = light_entity->AddComponent(); - point->SetLightType(LightType::Point); - point->SetRange(800.0f); - point->SetTemperature(10000.0f); - point->SetIntensity(8500.0f); - point->SetObjectName("Point Light"); - */ - - default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); - } - - void tick() + Vector2 world_offset; // world-space XZ origin of this tile + float tile_scale; // world-space size of this tile + uint32_t lod_level; // which LOD level (for transition blending) + }; + std::vector instances{}; + uint32_t tile_resolution = 128; // vertices per tile side (one tile = res^2 vertices) + float base_tile_size = 32.0f; // world-space units for LOD0 tile + uint32_t lod_levels = 6; + + static void build_clipmap(Vector3 camera_pos) { - if (!material) - return; + instances.clear(); + + // Snap once, using the finest level's grid spacing. + // Since every coarser level's spacing is a power-of-two multiple + // of this, the center sits on every level's grid. + float finest_snap = base_tile_size / tile_resolution * 2.0f; + Vector2 snapped_center = { + floor(camera_pos.x / finest_snap) * finest_snap, + floor(camera_pos.z / finest_snap) * finest_snap + }; - uint32_t current_tile_count = material->GetOceanTileCount(); - if (current_tile_count != ocean_tile_count || tile_size != material->GetOceanTileSize() || vertices_count != material->GetOceanVerticesCount()) + for (uint32_t lod = 0; lod < lod_levels; lod++) { - ocean_tile_count = current_tile_count; - auto& children = default_ocean->GetChildren(); + float tile_size = base_tile_size * (float)(1 << lod); - for (uint32_t i = 0; i < children.size(); i++) + for (int tz = -2; tz < 2; tz++) { - World::RemoveEntity(children[i]); + for (int tx = -2; tx < 2; tx++) + { + // Skip center tiles (except for lod 0 ofc) + if (lod > 0 && tx >= -1 && tx <= 0 && tz >= -1 && tz <= 0) + continue; + + TileInstance inst {}; + inst.world_offset = { + snapped_center.x + tx * tile_size, + snapped_center.y + tz * tile_size + }; + inst.tile_scale = tile_size; + inst.lod_level = lod; + instances.push_back(inst); + } } - children.clear(); + } + } - std::shared_ptr ocean_mesh; + void create() + { + entities::camera(false); + entities::sun(LightPreset::day, true); - for (size_t i = 0; i < meshes.size(); i++) - { - if (meshes[i]->GetObjectName() == "ocean mesh") - ocean_mesh = meshes[i]; - } + auto entity = World::CreateEntity(); - if (ocean_mesh.get() == nullptr) - return; + default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }); - // regenerate mesh - if (tile_size != material->GetOceanTileSize() || vertices_count != material->GetOceanVerticesCount()) - { - tile_size = material->GetOceanTileSize(); - vertices_count = material->GetOceanVerticesCount(); - - // generate grid - const uint32_t grid_points_per_dimension = vertices_count; - vector vertices; - vector indices; - geometry_generation::generate_grid(&vertices, &indices, grid_points_per_dimension, tile_size); - - //string name = "ocean mesh"; - - // create mesh if it doesn't exist - ocean_mesh->Clear(); - - //for (std::vector>::iterator it = meshes.begin(); it != meshes.end();) - //{ - // std::shared_ptr m = *it; - // if (m->GetObjectName() == "ocean mesh") - // it = meshes.erase(it); - // else; - // ++it; - //} - - /*ocean_mesh = meshes.emplace_back(make_shared()); - ocean_mesh->SetObjectName(name); - ocean_mesh->SetRootEntity(default_ocean); - ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); - ocean_mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false);*/ - ocean_mesh->AddGeometry(vertices, indices, false); - ocean_mesh->CreateGpuBuffers(); - } + geometry_generation::generate_tile(&tile_vertices, &tile_indices, tile_resolution); - for (uint32_t row = 0; row < current_tile_count; row++) - { - for (uint32_t col = 0; col < current_tile_count; col++) - { - int tile_index = col + row * current_tile_count; + // create mesh if it doesn't exist + shared_ptr mesh = meshes.emplace_back(make_shared()); + mesh->SetObjectName("Clipmap Tile Test"); + mesh->SetRootEntity(default_ocean); + mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); + mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false); + mesh->AddGeometry(tile_vertices, tile_indices, false); + mesh->CreateGpuBuffers(); - string tile_name = "ocean tile_" + to_string(tile_index); + build_clipmap(default_camera->GetPosition()); - Entity* entity_tile = World::CreateEntity(); - entity_tile->SetObjectName(tile_name); - entity_tile->SetParent(default_ocean); + for (const auto& tile_inst : instances) + { + Entity* entity_tile = World::CreateEntity(); + entity_tile->SetParent(default_ocean); - Vector3 tile_position = { col * tile_size, 0.0f, row * tile_size }; - entity_tile->SetPosition(tile_position); + const Vector3 tile_position = { tile_inst.world_offset.x, 0.0f, tile_inst.world_offset.y }; + entity_tile->SetPosition(tile_position); + entity_tile->SetScale({ tile_inst.tile_scale, 1.0f, tile_inst.tile_scale }); - if (Renderable* renderable = entity_tile->AddComponent()) - { - renderable->SetMesh(ocean_mesh.get()); - renderable->SetMaterial(material); - renderable->SetFlag(RenderableFlags::CastsShadows, false); - } + material->SetClipmapTileRes(tile_resolution); - // enable buoyancy - //Physics* physics = entity_tile->AddComponent(); - //physics->SetBodyType(BodyType::Water); - } + if (Renderable* renderable = entity_tile->AddComponent()) + { + renderable->SetMesh(mesh.get()); + renderable->SetMaterial(material); + renderable->SetFlag(RenderableFlags::CastsShadows, false); + renderable->SetOceanClipmapTileScale(tile_inst.tile_scale); + renderable->SetOceanClipmapTilePos(tile_inst.world_offset); } } - Vector3 camera_pos = default_camera->GetPosition(); + default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); + } + + void tick() + { + if (!material) + return; - //Vector3 ocean_pos = default_ocean->GetPosition(); - //Vector3 new_ocean_pos = ocean_pos; - //new_ocean_pos.x = camera_pos.x; - //new_ocean_pos.z = camera_pos.z; - //default_ocean->SetPosition(new_ocean_pos); + Vector3 camera_pos = default_camera->GetPosition(); } void shutdown() diff --git a/source/runtime/Geometry/GeometryGeneration.h b/source/runtime/Geometry/GeometryGeneration.h index a38d3a93fe..25d8694973 100644 --- a/source/runtime/Geometry/GeometryGeneration.h +++ b/source/runtime/Geometry/GeometryGeneration.h @@ -110,6 +110,39 @@ namespace spartan::geometry_generation indices->emplace_back(1); } + static void generate_tile(std::vector* vertices, std::vector* indices, uint32_t resolution) + { + using namespace math; + + for (uint32_t z = 0; z <= resolution; z++) + { + for (uint32_t x = 0; x <= resolution; x++) + { + // Normalized [0,1] coordinates within the tile + const Vector3 pos = Vector3((float)x / resolution, 0.0f, (float)z / resolution ); + const Vector2 uv = Vector2((float)x / resolution, (float)z / resolution); + const Vector3 normal = Vector3(0, 1, 0); + const Vector3 tangent = Vector3(0, 0, 0); + + vertices->emplace_back(pos, uv, normal, tangent); + } + } + + for (uint32_t z = 0; z < resolution; z++) + { + for (uint32_t x = 0; x < resolution; x++) + { + const uint32_t i = z * (resolution + 1u) + x; + indices->emplace_back(i); + indices->emplace_back(i + resolution + 1u); + indices->emplace_back(i + 1u); + indices->emplace_back(i + 1u); + indices->emplace_back(i + resolution + 1u); + indices->emplace_back(i + resolution + 2u); + } + } + } + static void generate_grid(std::vector* vertices, std::vector* indices, uint32_t grid_points_per_dimension, float extent) { using namespace math; diff --git a/source/runtime/Rendering/Material.h b/source/runtime/Rendering/Material.h index 6764de5cc1..0165e140a6 100644 --- a/source/runtime/Rendering/Material.h +++ b/source/runtime/Rendering/Material.h @@ -196,12 +196,8 @@ namespace spartan // ocean bool ShouldComputeSpectrum() const { return m_should_compute_spectrum; } void MarkSpectrumAsComputed(const bool flag) { m_should_compute_spectrum = !flag; } - void SetOceanTileCount(const uint32_t count) { m_ocean_tiles = count; } - uint32_t GetOceanTileCount() const { return m_ocean_tiles; } - void SetOceanVerticesCount(const uint32_t count) { m_ocean_vertices_count = count; } - uint32_t GetOceanVerticesCount() const { return m_ocean_vertices_count; } - void SetOceanTileSize(const float size) { m_ocean_tile_size = size; } - float GetOceanTileSize() const { return m_ocean_tile_size; } + uint32_t GetClipmapTileRes() const { return m_clipmap_tile_res; } + void SetClipmapTileRes(const uint32_t res) { m_clipmap_tile_res = res; } bool IsOcean() const { return GetProperty(MaterialProperty::IsOcean) == 1.0f; } const std::array(MaterialProperty::Max)>& GetProperties() const { return m_properties; } @@ -218,9 +214,8 @@ namespace spartan bool m_needs_repack = true; // starts true so first PrepareForGpu() packs textures std::mutex m_mutex; + // ocean bool m_should_compute_spectrum = false; - uint32_t m_ocean_tiles = 1; - uint32_t m_ocean_vertices_count = 0; - float m_ocean_tile_size = 0.0f; + uint32_t m_clipmap_tile_res = 0; }; } diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index 263d7c56af..b88ab0c9ec 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -1508,8 +1508,9 @@ namespace spartan data.material_index = material->GetIndex(); data.is_transparent = 0; data.aabb_index = m_draw_calls_prepass_count + idx; - data.tile_size = material->GetOceanTileSize(); - data.tile_xz_pos = { ent_pos.x, ent_pos.z }; + data.tile_size = renderable->GetOceanClipmapTileScale(); + data.tile_world_pos = renderable->GetOceanClipmapTilePos(); + data.tile_res = material->GetClipmapTileRes(); data.padding = 0; } } diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index 7f6577c773..670b396eb5 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -244,9 +244,10 @@ namespace spartan uint32_t material_index = 0; // index into the bindless material parameters array uint32_t is_transparent = 0; // transparency flag uint32_t aabb_index = 0; // index into the aabb buffer for culling - float tile_size = 0.0f; // used for the ocean synthesis - math::Vector2 tile_xz_pos; // used for the ocean synthesis - math::Vector2 padding; + float tile_size = 0.0f; // used for ocean clipmap + math::Vector2 tile_world_pos; // used for ocean clipmap + uint32_t tile_res = 0; // used for ocean clipmap + uint32_t padding; }; // gpu particle (matches hlsl Particle struct, 64 bytes) diff --git a/source/runtime/World/Components/Renderable.h b/source/runtime/World/Components/Renderable.h index 6e93b5b877..cb5c7b762e 100644 --- a/source/runtime/World/Components/Renderable.h +++ b/source/runtime/World/Components/Renderable.h @@ -76,6 +76,12 @@ namespace spartan const math::BoundingBox& GetBoundingBox() const { return m_bounding_box; } const math::BoundingBox& GetBoundingBoxMesh() const { return m_bounding_box_mesh; } + // ocean clipmap + void SetOceanClipmapTilePos(const math::Vector2 pos) { m_tile_world_pos = pos; } + math::Vector2 GetOceanClipmapTilePos() const { return m_tile_world_pos; } + void SetOceanClipmapTileScale(const float scale) { m_tile_size = scale; } + float GetOceanClipmapTileScale() const { return m_tile_size; } + // material void SetMaterial(const std::shared_ptr& material); void SetMaterial(const std::string& file_path); @@ -124,6 +130,10 @@ namespace spartan math::BoundingBox m_bounding_box_mesh = math::BoundingBox::Unit; math::BoundingBox m_bounding_box = math::BoundingBox::Unit; + // ocean clipmap + math::Vector2 m_tile_world_pos; + float m_tile_size; + // material bool m_material_default = false; Material* m_material = nullptr; From 853d0a3882d9b0fdc13b9a498f07fff8c146563b Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Sun, 22 Feb 2026 15:52:49 +0100 Subject: [PATCH 129/133] [ocean] remove physics component on camera and runtime clipmap update --- source/runtime/Game/Game.cpp | 37 +++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 2fa69cbc52..c1cef95eac 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1608,10 +1608,11 @@ namespace spartan float tile_scale; // world-space size of this tile uint32_t lod_level; // which LOD level (for transition blending) }; - std::vector instances{}; - uint32_t tile_resolution = 128; // vertices per tile side (one tile = res^2 vertices) + std::vector instances {}; + std::vector tile_entities {}; + uint32_t tile_resolution = 128; // vertices per tile side (one tile = res^2 vertices) float base_tile_size = 32.0f; // world-space units for LOD0 tile - uint32_t lod_levels = 6; + uint32_t lod_levels = 2; static void build_clipmap(Vector3 camera_pos) { @@ -1656,7 +1657,7 @@ namespace spartan entities::camera(false); entities::sun(LightPreset::day, true); - auto entity = World::CreateEntity(); + default_camera->RemoveComponent(); default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }); @@ -1692,6 +1693,8 @@ namespace spartan renderable->SetOceanClipmapTileScale(tile_inst.tile_scale); renderable->SetOceanClipmapTilePos(tile_inst.world_offset); } + + tile_entities.emplace_back(entity_tile); } default_light_directional->GetComponent()->SetFlag(LightFlags::ShadowsScreenSpace, false); @@ -1702,7 +1705,23 @@ namespace spartan if (!material) return; - Vector3 camera_pos = default_camera->GetPosition(); + const Vector3 camera_pos = default_camera->GetPosition(); + build_clipmap(camera_pos); + + for (size_t i = 0; i < instances.size(); i++) + { + const auto& inst = instances[i]; + Entity* entity = tile_entities[i]; + + entity->SetPosition({ inst.world_offset.x, 0.0f, inst.world_offset.y }); + entity->SetScale({ inst.tile_scale, 1.0f, inst.tile_scale }); + + if (Renderable* renderable = entity->GetComponent()) + { + renderable->SetOceanClipmapTileScale(inst.tile_scale); + renderable->SetOceanClipmapTilePos(inst.world_offset); + } + } } void shutdown() @@ -1745,10 +1764,10 @@ namespace spartan void Game::Tick() { // ocean-specific tick - if (loaded_world == DefaultWorld::Ocean) - { - worlds::ocean::tick(); - } + //if (loaded_world == DefaultWorld::Ocean) + //{ + // worlds::ocean::tick(); + //} // world-specific tick if (loaded_world != DefaultWorld::Max) From 693d896afcea6ccbb55fb14010035c9f14980ef6 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Sun, 22 Feb 2026 19:21:13 +0100 Subject: [PATCH 130/133] [ocean] working clipmap implementation --- data/shaders/common_resources.hlsl | 3 ++- data/shaders/depth_prepass.hlsl | 26 ++++++++++++++++---- data/shaders/g_buffer.hlsl | 24 +++++++++++++++--- data/shaders/ocean/synthesise_maps.hlsl | 2 +- source/runtime/Game/Game.cpp | 20 +++++++-------- source/runtime/Rendering/Renderer.cpp | 5 ++-- source/runtime/Rendering/Renderer_Buffers.h | 3 ++- source/runtime/World/Components/Renderable.h | 3 +++ 8 files changed, 62 insertions(+), 24 deletions(-) diff --git a/data/shaders/common_resources.hlsl b/data/shaders/common_resources.hlsl index 5bddae5fa1..7c9deeae0e 100644 --- a/data/shaders/common_resources.hlsl +++ b/data/shaders/common_resources.hlsl @@ -304,8 +304,9 @@ struct DrawData uint aabb_index; float tile_size; float2 tile_world_pos; + float2 tile_snap_center; uint tile_res; - float padding; + float3 padding; }; // bindless draw data - per-draw transforms, material indices, etc. diff --git a/data/shaders/depth_prepass.hlsl b/data/shaders/depth_prepass.hlsl index 442a7649ef..5442f34aaf 100644 --- a/data/shaders/depth_prepass.hlsl +++ b/data/shaders/depth_prepass.hlsl @@ -46,16 +46,32 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI if (surface.is_ocean() && material.ocean_parameters.displacementScale > -1.0f) { - // 1. Snap to this LOD's vertex grid to prevent swimming + // Snap to this LOD's vertex grid to prevent swimming const float vertex_spacing = _draw.tile_size / _draw.tile_res; position_world.xz = round(position_world.xz / vertex_spacing) * vertex_spacing; - // 2. Synthesize displacement - const float2 disp_uv = position_world.xz / material.ocean_parameters.lengthScale; + // Epsilon expansion to close gaps from floating point precision + float2 tile_center = _draw.tile_world_pos + _draw.tile_size * 0.5; + float epsilon = vertex_spacing * 0.1f; + float scale = 1.0f + epsilon / (_draw.tile_size * 0.5f); + position_world.xz = tile_center + (position_world.xz - tile_center) * scale; + + // Morph toward coarser grid near LOD boundaries + float2 from_center = abs(position_world.xz - _draw.tile_snap_center); + float half_extent = _draw.tile_size * 2.0f; + float edge = max(from_center.x, from_center.y) / half_extent; + float morph = smoothstep(0.7f, 1.0f, edge); + + float coarse_spacing = vertex_spacing * 2.0f; + float2 coarse_xz = round(position_world.xz / coarse_spacing) * coarse_spacing; + position_world.xz = lerp(position_world.xz, coarse_xz, morph); + + // Synthesize + float2 synth_uv = position_world.xz / material.ocean_parameters.lengthScale; float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); - synthesize(tex2, displacement, disp_uv); + synthesize(tex2, displacement, synth_uv); - // 3. Apply displacement + // Apply displacement position_world.xyz += displacement.xyz; position_world_previous = position_world; } diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 5d9c2c2946..3249dc88c8 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -134,16 +134,32 @@ gbuffer_vertex main_vs(Vertex_PosUvNorTan input, uint instance_id : SV_InstanceI if (surface.is_ocean() && material.ocean_parameters.displacementScale > -1.0f) { - // 1. Snap to this LOD's vertex grid to prevent swimming + // Snap to this LOD's vertex grid to prevent swimming const float vertex_spacing = _draw.tile_size / _draw.tile_res; position_world.xz = round(position_world.xz / vertex_spacing) * vertex_spacing; - // 2. Synthesize displacement - const float2 synth_uv = position_world.xz / material.ocean_parameters.lengthScale; + // Epsilon expansion to close gaps from floating point precision + float2 tile_center = _draw.tile_world_pos + _draw.tile_size * 0.5; + float epsilon = vertex_spacing * 0.1f; + float scale = 1.0f + epsilon / (_draw.tile_size * 0.5f); + position_world.xz = tile_center + (position_world.xz - tile_center) * scale; + + // Morph toward coarser grid near LOD boundaries + float2 from_center = abs(position_world.xz - _draw.tile_snap_center); + float half_extent = _draw.tile_size * 2.0f; + float edge = max(from_center.x, from_center.y) / half_extent; + float morph = smoothstep(0.7f, 1.0f, edge); + + float coarse_spacing = vertex_spacing * 2.0f; + float2 coarse_xz = round(position_world.xz / coarse_spacing) * coarse_spacing; + position_world.xz = lerp(position_world.xz, coarse_xz, morph); + + // Synthesize + float2 synth_uv = position_world.xz / material.ocean_parameters.lengthScale; float4 displacement = float4(0.0f, 0.0f, 0.0f, 0.0f); synthesize(tex2, displacement, synth_uv); - // 3. Apply displacement + // Apply displacement position_world.xyz += displacement.xyz; position_world_previous = position_world; diff --git a/data/shaders/ocean/synthesise_maps.hlsl b/data/shaders/ocean/synthesise_maps.hlsl index 58cbc4207e..22bf9bd90c 100644 --- a/data/shaders/ocean/synthesise_maps.hlsl +++ b/data/shaders/ocean/synthesise_maps.hlsl @@ -69,7 +69,7 @@ float4 get_texture_sample(Texture2D texture, float2 pos, float freq, float2 node const float3 hash = hash33(float3(node_point.xy, 0.0f)); const float2 uv = pos * freq + hash.yz; - return texture.SampleLevel(samplers[sampler_anisotropic_wrap], uv, 0); + return texture.SampleLevel(samplers[sampler_bilinear_wrap], uv, 0); } void preserve_variance(out float4 linear_color, float4 mean_color, float moment2) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index c1cef95eac..5fd57a31e4 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1604,23 +1604,22 @@ namespace spartan std::vector tile_indices {}; struct TileInstance { - Vector2 world_offset; // world-space XZ origin of this tile - float tile_scale; // world-space size of this tile - uint32_t lod_level; // which LOD level (for transition blending) + Vector2 world_offset; // world-space XZ origin of this tile + Vector2 snapped_center; // used for per lod level snapping and vertex morphing + float tile_scale; // world-space size of this tile + uint32_t lod_level; // which LOD level (for transition blending) }; std::vector instances {}; std::vector tile_entities {}; uint32_t tile_resolution = 128; // vertices per tile side (one tile = res^2 vertices) float base_tile_size = 32.0f; // world-space units for LOD0 tile - uint32_t lod_levels = 2; + uint32_t lod_levels = 6; static void build_clipmap(Vector3 camera_pos) { instances.clear(); - // Snap once, using the finest level's grid spacing. - // Since every coarser level's spacing is a power-of-two multiple - // of this, the center sits on every level's grid. + // Single snap from finest level - guarantees alignment across all LODs float finest_snap = base_tile_size / tile_resolution * 2.0f; Vector2 snapped_center = { floor(camera_pos.x / finest_snap) * finest_snap, @@ -1635,17 +1634,17 @@ namespace spartan { for (int tx = -2; tx < 2; tx++) { - // Skip center tiles (except for lod 0 ofc) if (lod > 0 && tx >= -1 && tx <= 0 && tz >= -1 && tz <= 0) continue; - TileInstance inst {}; + TileInstance inst{}; inst.world_offset = { snapped_center.x + tx * tile_size, snapped_center.y + tz * tile_size }; inst.tile_scale = tile_size; inst.lod_level = lod; + inst.snapped_center = snapped_center; instances.push_back(inst); } } @@ -1705,7 +1704,8 @@ namespace spartan if (!material) return; - const Vector3 camera_pos = default_camera->GetPosition(); + Camera* camera = default_camera->GetChildByIndex(0)->GetComponent(); + const Vector3 camera_pos = camera->GetEntity()->GetPosition(); build_clipmap(camera_pos); for (size_t i = 0; i < instances.size(); i++) diff --git a/source/runtime/Rendering/Renderer.cpp b/source/runtime/Rendering/Renderer.cpp index 6d5c063a44..163c7634b6 100644 --- a/source/runtime/Rendering/Renderer.cpp +++ b/source/runtime/Rendering/Renderer.cpp @@ -816,7 +816,7 @@ namespace spartan entry.material_index = material_index; entry.is_transparent = is_transparent; entry.aabb_index = 0; - entry.padding = 0; + entry.padding = math::Vector3::Zero; // write directly to the mapped gpu buffer RHI_Buffer* buffer = GetBuffer(Renderer_Buffer::DrawData); @@ -1267,8 +1267,9 @@ namespace spartan data.aabb_index = m_draw_calls_prepass_count + idx; data.tile_size = renderable->GetOceanClipmapTileScale(); data.tile_world_pos = renderable->GetOceanClipmapTilePos(); + data.tile_snap_center = renderable->GetOceanClipmapTileSnapCenter(); data.tile_res = material->GetClipmapTileRes(); - data.padding = 0; + data.padding = math::Vector3::Zero; } } diff --git a/source/runtime/Rendering/Renderer_Buffers.h b/source/runtime/Rendering/Renderer_Buffers.h index 670b396eb5..e819aafa8f 100644 --- a/source/runtime/Rendering/Renderer_Buffers.h +++ b/source/runtime/Rendering/Renderer_Buffers.h @@ -246,8 +246,9 @@ namespace spartan uint32_t aabb_index = 0; // index into the aabb buffer for culling float tile_size = 0.0f; // used for ocean clipmap math::Vector2 tile_world_pos; // used for ocean clipmap + math::Vector2 tile_snap_center; // used for ocean clipmap uint32_t tile_res = 0; // used for ocean clipmap - uint32_t padding; + math::Vector3 padding; }; // gpu particle (matches hlsl Particle struct, 64 bytes) diff --git a/source/runtime/World/Components/Renderable.h b/source/runtime/World/Components/Renderable.h index cb5c7b762e..f4ee084027 100644 --- a/source/runtime/World/Components/Renderable.h +++ b/source/runtime/World/Components/Renderable.h @@ -79,6 +79,8 @@ namespace spartan // ocean clipmap void SetOceanClipmapTilePos(const math::Vector2 pos) { m_tile_world_pos = pos; } math::Vector2 GetOceanClipmapTilePos() const { return m_tile_world_pos; } + void SetOceanClipmapTileSnapCenter(const math::Vector2 snap) { m_tile_snap_center = snap; } + math::Vector2 GetOceanClipmapTileSnapCenter() const { return m_tile_snap_center; } void SetOceanClipmapTileScale(const float scale) { m_tile_size = scale; } float GetOceanClipmapTileScale() const { return m_tile_size; } @@ -132,6 +134,7 @@ namespace spartan // ocean clipmap math::Vector2 m_tile_world_pos; + math::Vector2 m_tile_snap_center; float m_tile_size; // material From 309d29ffc430b54be9d8d7793167b0c104234df9 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Tue, 24 Feb 2026 21:59:31 +0100 Subject: [PATCH 131/133] [ocean] skirt geometry for highest lod tiles --- source/runtime/Game/Game.cpp | 110 ++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 20 deletions(-) diff --git a/source/runtime/Game/Game.cpp b/source/runtime/Game/Game.cpp index 5fd57a31e4..2529cdb810 100644 --- a/source/runtime/Game/Game.cpp +++ b/source/runtime/Game/Game.cpp @@ -1599,36 +1599,61 @@ namespace spartan { shared_ptr material = make_shared(); - // Clipmap - std::vector tile_vertices {}; - std::vector tile_indices {}; + // Edge flag bitmask + namespace EdgeFlags + { + constexpr uint32_t None = 0; + constexpr uint32_t Left = 1 << 0; // tx == -2 + constexpr uint32_t Right = 1 << 1; // tx == 1 + constexpr uint32_t Back = 1 << 2; // tz == -2 + constexpr uint32_t Front = 1 << 3; // tz == 1 + }; + struct TileInstance { Vector2 world_offset; // world-space XZ origin of this tile Vector2 snapped_center; // used for per lod level snapping and vertex morphing float tile_scale; // world-space size of this tile uint32_t lod_level; // which LOD level (for transition blending) + uint32_t edge = EdgeFlags::None; // used when generating skirt geometry }; std::vector instances {}; std::vector tile_entities {}; uint32_t tile_resolution = 128; // vertices per tile side (one tile = res^2 vertices) float base_tile_size = 32.0f; // world-space units for LOD0 tile - uint32_t lod_levels = 6; + uint32_t lod_levels = 4; + + static void apply_skirt(std::vector* vertices, uint32_t resolution, uint32_t edge_flags, float skirt = 100000.0f) + { + for (uint32_t z = 0; z <= resolution; z++) + { + for (uint32_t x = 0; x <= resolution; x++) + { + auto& v = (*vertices)[z * (resolution + 1) + x]; + + if (x == 0 && (edge_flags & EdgeFlags::Left)) v.pos[0] -= skirt; + if (x == resolution && (edge_flags & EdgeFlags::Right)) v.pos[0] += skirt; + if (z == 0 && (edge_flags & EdgeFlags::Back)) v.pos[2] -= skirt; + if (z == resolution && (edge_flags & EdgeFlags::Front)) v.pos[2] += skirt; + } + } + } static void build_clipmap(Vector3 camera_pos) { instances.clear(); // Single snap from finest level - guarantees alignment across all LODs - float finest_snap = base_tile_size / tile_resolution * 2.0f; - Vector2 snapped_center = { + const float finest_snap = base_tile_size / tile_resolution * 2.0f; + const Vector2 snapped_center = { floor(camera_pos.x / finest_snap) * finest_snap, floor(camera_pos.z / finest_snap) * finest_snap }; for (uint32_t lod = 0; lod < lod_levels; lod++) { - float tile_size = base_tile_size * (float)(1 << lod); + const float tile_size = base_tile_size * (float)(1 << lod); + const bool is_outermost = (lod == lod_levels - 1); for (int tz = -2; tz < 2; tz++) { @@ -1645,6 +1670,15 @@ namespace spartan inst.tile_scale = tile_size; inst.lod_level = lod; inst.snapped_center = snapped_center; + + inst.edge = EdgeFlags::None; + if (is_outermost) + { + if (tx == -2) inst.edge |= EdgeFlags::Left; + if (tx == 1) inst.edge |= EdgeFlags::Right; + if (tz == -2) inst.edge |= EdgeFlags::Back; + if (tz == 1) inst.edge |= EdgeFlags::Front; + } instances.push_back(inst); } } @@ -1660,37 +1694,71 @@ namespace spartan default_ocean = entities::ocean(material, { 0.0f, 0.0f, 0.0f }); - geometry_generation::generate_tile(&tile_vertices, &tile_indices, tile_resolution); + //geometry_generation::generate_tile(&tile_vertices, &tile_indices, tile_resolution); + // Generate normal tile + std::vector normal_verts{}; + std::vector normal_indices{}; + geometry_generation::generate_tile(&normal_verts, &normal_indices, tile_resolution); - // create mesh if it doesn't exist - shared_ptr mesh = meshes.emplace_back(make_shared()); - mesh->SetObjectName("Clipmap Tile Test"); - mesh->SetRootEntity(default_ocean); - mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); - mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false); - mesh->AddGeometry(tile_vertices, tile_indices, false); - mesh->CreateGpuBuffers(); + auto make_mesh = [&](const std::string& name, + std::vector& verts, + std::vector& indices) -> shared_ptr + { + auto mesh = meshes.emplace_back(make_shared()); + mesh->SetObjectName(name); + mesh->SetRootEntity(default_ocean); + mesh->SetFlag(static_cast(MeshFlags::PostProcessOptimize), false); + mesh->SetFlag(static_cast(MeshFlags::PostProcessNormalizeScale), false); + mesh->AddGeometry(verts, indices, false); + mesh->CreateGpuBuffers(); + return mesh; + }; + auto make_skirt_mesh = [&](uint32_t flags) -> shared_ptr + { + auto verts = normal_verts; + apply_skirt(&verts, tile_resolution, flags); + return make_mesh("Clipmap Skirt Tile", verts, normal_indices); + }; + + // Create meshes + shared_ptr mesh_normal = make_mesh("Clipmap Tile", normal_verts, normal_indices); + + std::unordered_map> skirt_meshes; + skirt_meshes[EdgeFlags::Left] = make_skirt_mesh(EdgeFlags::Left); + skirt_meshes[EdgeFlags::Right] = make_skirt_mesh(EdgeFlags::Right); + skirt_meshes[EdgeFlags::Back] = make_skirt_mesh(EdgeFlags::Back); + skirt_meshes[EdgeFlags::Front] = make_skirt_mesh(EdgeFlags::Front); + skirt_meshes[EdgeFlags::Left | EdgeFlags::Back] = make_skirt_mesh(EdgeFlags::Left | EdgeFlags::Back); + skirt_meshes[EdgeFlags::Left | EdgeFlags::Front] = make_skirt_mesh(EdgeFlags::Left | EdgeFlags::Front); + skirt_meshes[EdgeFlags::Right | EdgeFlags::Back] = make_skirt_mesh(EdgeFlags::Right | EdgeFlags::Back); + skirt_meshes[EdgeFlags::Right | EdgeFlags::Front] = make_skirt_mesh(EdgeFlags::Right | EdgeFlags::Front); + + // Build initial clipmap and create entities build_clipmap(default_camera->GetPosition()); for (const auto& tile_inst : instances) { Entity* entity_tile = World::CreateEntity(); entity_tile->SetParent(default_ocean); - - const Vector3 tile_position = { tile_inst.world_offset.x, 0.0f, tile_inst.world_offset.y }; - entity_tile->SetPosition(tile_position); + entity_tile->SetPosition({ tile_inst.world_offset.x, 0.0f, tile_inst.world_offset.y }); entity_tile->SetScale({ tile_inst.tile_scale, 1.0f, tile_inst.tile_scale }); + Mesh* mesh = (tile_inst.edge != EdgeFlags::None) + ? skirt_meshes[tile_inst.edge].get() + : mesh_normal.get(); + material->SetClipmapTileRes(tile_resolution); if (Renderable* renderable = entity_tile->AddComponent()) { - renderable->SetMesh(mesh.get()); + renderable->SetMesh(mesh); renderable->SetMaterial(material); renderable->SetFlag(RenderableFlags::CastsShadows, false); renderable->SetOceanClipmapTileScale(tile_inst.tile_scale); renderable->SetOceanClipmapTilePos(tile_inst.world_offset); + renderable->SetOceanClipmapTileSnapCenter(tile_inst.snapped_center); + //renderable->SetOceanClipmapLodLevel(tile_inst.lod_level); } tile_entities.emplace_back(entity_tile); @@ -1706,6 +1774,7 @@ namespace spartan Camera* camera = default_camera->GetChildByIndex(0)->GetComponent(); const Vector3 camera_pos = camera->GetEntity()->GetPosition(); + build_clipmap(camera_pos); for (size_t i = 0; i < instances.size(); i++) @@ -1720,6 +1789,7 @@ namespace spartan { renderable->SetOceanClipmapTileScale(inst.tile_scale); renderable->SetOceanClipmapTilePos(inst.world_offset); + renderable->SetOceanClipmapTileSnapCenter(inst.snapped_center); } } } From bdd6d9dc270eb605807fe3c4388ff3706d47f899 Mon Sep 17 00:00:00 2001 From: George Bolba Date: Mon, 2 Mar 2026 22:09:45 +0100 Subject: [PATCH 132/133] [ocean] improved shading --- data/shaders/brdf.hlsl | 5 ++++- data/shaders/common_structs.hlsl | 2 +- data/shaders/light.hlsl | 33 +++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/data/shaders/brdf.hlsl b/data/shaders/brdf.hlsl index 4a87b37185..c826b44c32 100644 --- a/data/shaders/brdf.hlsl +++ b/data/shaders/brdf.hlsl @@ -56,7 +56,10 @@ float3 Diffuse_Burley(float3 diffuse_color, float roughness, float n_dot_v, floa float3 BRDF_Diffuse(Surface surface, AngularInfo angular_info) { - return Diffuse_Burley(surface.albedo, surface.roughness, angular_info.n_dot_v, angular_info.n_dot_l, angular_info.v_dot_h); + if (surface.is_ocean()) + return Diffuse_Burley(surface.albedo, surface.roughness, angular_info.n_dot_v, angular_info.n_dot_l, angular_info.v_dot_h); + else + return float3(0.0f, 0.0f, 0.0f); // ocean diffuse comes from scattering, not albedo } /*------------------------------------------------------------------------------ diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index ea80dc2672..fa19191d28 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -133,7 +133,7 @@ struct Surface roughness = sample_material.r; metallic = sample_material.g; emissive = sample_material.b; - F0 = lerp(0.04f, albedo, metallic); + F0 = flags & uint(1U << 16) ? 0.02f : lerp(0.04f, albedo, metallic); anisotropic = material.anisotropic; anisotropic_rotation = material.anisotropic_rotation; clearcoat = material.clearcoat; diff --git a/data/shaders/light.hlsl b/data/shaders/light.hlsl index 378fceb52a..75f6a95261 100644 --- a/data/shaders/light.hlsl +++ b/data/shaders/light.hlsl @@ -124,6 +124,29 @@ float3 subsurface_scattering(Surface surface, Light light, AngularInfo angular_i return light_radiance * sss_term * thickness_modulation * sss_strength * sss_color; } +// Ocean subsurface scattering +float3 ocean_subsurface(Surface surface, Light light, AngularInfo angular_info) +{ + // Subsurface scattering approximation + // Light that transmits through the wave crests + float3 scatter_color = float3(0.0f, 0.05f, 0.1f); // deep ocean scatter + + // View-dependent scattering: stronger when looking toward the light + // through thin wave crests + float v_dot_l = max(0.0f, dot(surface.camera_to_pixel, light.forward)); + float sss_intensity = pow(saturate(v_dot_l), 4.0f) * 0.5f; + + // Height-dependent: thin wave crests scatter more + // Use the displacement Y to approximate wave height + float wave_height = surface.position.y; + sss_intensity *= wave_height; + + // Fresnel modulation: light that doesn't reflect enters the volume + float fresnel_transmit = 1.0f - F_Schlick(surface.F0, 1.0f, angular_info.n_dot_v); + + return scatter_color * sss_intensity * fresnel_transmit * light.radiance; +} + [numthreads(THREAD_GROUP_COUNT_X, THREAD_GROUP_COUNT_Y, 1)] void main_cs(uint3 thread_id : SV_DispatchThreadID) { @@ -207,7 +230,7 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) { L_specular_sum += BRDF_Specular_Anisotropic(surface, angular_info); } - else + else if (!surface.is_ocean()) { L_specular_sum += BRDF_Specular_Isotropic(surface, angular_info); } @@ -229,6 +252,14 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) { L_subsurface += subsurface_scattering(surface, light, angular_info); } + + // ocean specific specular and subsurface + if (surface.is_ocean()) + { + L_specular_sum = BRDF_Specular_Isotropic(surface, angular_info); + + L_subsurface = ocean_subsurface(surface, light, angular_info); + } } // compute diffuse brdf term From 5a144cd0e0229f99057f008a3118833370fdbef1 Mon Sep 17 00:00:00 2001 From: Gikster007 Date: Wed, 11 Mar 2026 23:28:26 +0100 Subject: [PATCH 133/133] [ocean] wip lighting --- data/shaders/brdf.hlsl | 14 +++- data/shaders/common_structs.hlsl | 20 +++++- data/shaders/g_buffer.hlsl | 7 +- data/shaders/light.hlsl | 98 +++++++++++++++------------ data/shaders/ocean/generate_maps.hlsl | 3 +- 5 files changed, 95 insertions(+), 47 deletions(-) diff --git a/data/shaders/brdf.hlsl b/data/shaders/brdf.hlsl index c826b44c32..6e9265e8c6 100644 --- a/data/shaders/brdf.hlsl +++ b/data/shaders/brdf.hlsl @@ -41,6 +41,12 @@ float F_Schlick(float f0, float f90, float v_dot_h) return f0 + (f90 - f0) * pow(1.0 - v_dot_h, 5.0); } +// Inspired by Atlas talk @ GDC19 - https://youtu.be/Dqld965-Vv0?t=1590 +float F_Ocean(float n_dot_v, float roughness, float f0) +{ + return lerp(pow(1.0f - n_dot_v, 5.0f * exp(-2.69f * roughness)) / (1.0f + 22.7f * pow(roughness, 1.5f)), 1.0f, f0); +} + /*------------------------------------------------------------------------------ DIFFUSE ------------------------------------------------------------------------------*/ @@ -56,7 +62,7 @@ float3 Diffuse_Burley(float3 diffuse_color, float roughness, float n_dot_v, floa float3 BRDF_Diffuse(Surface surface, AngularInfo angular_info) { - if (surface.is_ocean()) + if (!surface.is_ocean()) return Diffuse_Burley(surface.albedo, surface.roughness, angular_info.n_dot_v, angular_info.n_dot_l, angular_info.v_dot_h); else return float3(0.0f, 0.0f, 0.0f); // ocean diffuse comes from scattering, not albedo @@ -119,6 +125,12 @@ float V_Neubelt(float n_dot_v, float n_dot_l) return saturate_16(1.0 / (4.0 * (n_dot_l + n_dot_v - n_dot_l * n_dot_v))); } +// single-direction smith G1 for ocean SSS masking, takes alpha^2 +float V1_SmithGGX(float n_dot_l, float a2) +{ + return 2.0f * n_dot_l / (n_dot_l + sqrt(n_dot_l * n_dot_l * (1.0f - a2) + a2) + FLT_MIN); +} + /*------------------------------------------------------------------------------ ENERGY ------------------------------------------------------------------------------*/ diff --git a/data/shaders/common_structs.hlsl b/data/shaders/common_structs.hlsl index fa19191d28..ff19846601 100644 --- a/data/shaders/common_structs.hlsl +++ b/data/shaders/common_structs.hlsl @@ -89,6 +89,8 @@ struct Surface float3 diffuse_energy; OceanParameters ocean_parameters; + float wave_height; + float foam_mask; // easy access to certain properties bool has_texture_height() { return flags & uint(1U << 0); } @@ -168,7 +170,23 @@ struct Surface ocean_parameters.debugDisplacement = material.ocean_parameters.debugDisplacement; ocean_parameters.debugSlope = material.ocean_parameters.debugSlope; ocean_parameters.debugSynthesised = material.ocean_parameters.debugSynthesised; - + // if we are an ocean, wave height is stored in the metallnes and emissive channels of the material gbuffer + // unpack the wave height and reset the metallic and emissive to 0.0f (expected values for an ocean surface) + // additionally, we store the foam mask in the occlusion channel of the material gbuffer as well + if (flags & uint(1U << 16)) + { + // Unpacking + uint high = (uint)(metallic * 255.0f + 0.5f); + uint low = (uint)(emissive * 255.0f + 0.5f); + wave_height = f16tof32((high << 8u) | low); + metallic = 0.0f; + emissive = 0.0f; + + foam_mask = sample_material.a; + occlusion = 0.0f; + } + + // roughness is authored as perceptual roughness, as is convention roughness_alpha = roughness * roughness; diff --git a/data/shaders/g_buffer.hlsl b/data/shaders/g_buffer.hlsl index 3249dc88c8..f432b9ec53 100644 --- a/data/shaders/g_buffer.hlsl +++ b/data/shaders/g_buffer.hlsl @@ -318,8 +318,13 @@ gbuffer main_ps(gbuffer_vertex vertex, bool is_front_face : SV_IsFrontFace) // apply foam (foam mask is stored in the alpha channel of slope map) albedo.rgb = lerp(albedo.rgb, float3(1.0f, 1.0f, 1.0f), slope.a); - roughness = lerp(roughness, 1.0f, slope.a); + // store fp16 wave height in metallnes and emission channels of material gbuffer + uint half_val = f32tof16(position_world.y); + metalness = (half_val >> 8u) / 255.0f; + emission = (half_val & 0xFFu) / 255.0f; + occlusion = slope.a; // store foam mask in occlusion channel + if (material.ocean_parameters.debugDisplacement == 1.0f) // displacement { if (material.ocean_parameters.debugSynthesised == 1.0f) // show synthesised version diff --git a/data/shaders/light.hlsl b/data/shaders/light.hlsl index 75f6a95261..b747655e41 100644 --- a/data/shaders/light.hlsl +++ b/data/shaders/light.hlsl @@ -125,26 +125,36 @@ float3 subsurface_scattering(Surface surface, Light light, AngularInfo angular_i } // Ocean subsurface scattering +static const float3 SSS_MODIFIER = float3(0.9f, 1.15f, 0.85f); float3 ocean_subsurface(Surface surface, Light light, AngularInfo angular_info) { - // Subsurface scattering approximation - // Light that transmits through the wave crests - float3 scatter_color = float3(0.0f, 0.05f, 0.1f); // deep ocean scatter - - // View-dependent scattering: stronger when looking toward the light - // through thin wave crests - float v_dot_l = max(0.0f, dot(surface.camera_to_pixel, light.forward)); - float sss_intensity = pow(saturate(v_dot_l), 4.0f) * 0.5f; - - // Height-dependent: thin wave crests scatter more - // Use the displacement Y to approximate wave height - float wave_height = surface.position.y; - sss_intensity *= wave_height; - - // Fresnel modulation: light that doesn't reflect enters the volume - float fresnel_transmit = 1.0f - F_Schlick(surface.F0, 1.0f, angular_info.n_dot_v); - - return scatter_color * sss_intensity * fresnel_transmit * light.radiance; + float a = D_GGX_Alpha(surface.roughness); + float a2 = a * a; + + float n_dot_l = angular_info.n_dot_l; + float n_dot_v = angular_info.n_dot_v; + float fresnel = F_Ocean(n_dot_v, surface.roughness, surface.F0.x); + + // masking for SSS attenuation + float v1_light = V1_SmithGGX(n_dot_l, a2); + + // view-to-light alignment + float v_dot_l = max(dot(-surface.camera_to_pixel, light.forward), 0.0f); + float l_dot_n = dot(light.forward, surface.normal); + + // forward scattering through wave crests + float sss_height = max(0.0f, surface.wave_height + 2.5f) + * pow(v_dot_l, 4.0f) + * pow(0.5f - 0.5f * l_dot_n, 3.0f); + + // near-field ambient scattering + float sss_near = 0.5f * pow(n_dot_v, 2.0f); + + // lambertian fallback + float lambertian = 0.5f * n_dot_l; + + // combine + return lerp((sss_height + sss_near) * SSS_MODIFIER / (1.0f + v1_light) + lambertian, float3(1.0f, 1.0f, 1.0f), surface.foam_mask) * (1.0f - fresnel) * light.radiance; } [numthreads(THREAD_GROUP_COUNT_X, THREAD_GROUP_COUNT_Y, 1)] @@ -225,40 +235,42 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) // compute specular brdf lobes { - // main specular lobe (anisotropic or isotropic) - if (surface.anisotropic > 0.0f) + // ocean specific specular and subsurface + if (surface.is_ocean()) { - L_specular_sum += BRDF_Specular_Anisotropic(surface, angular_info); + L_specular_sum = BRDF_Specular_Isotropic(surface, angular_info); + + L_subsurface = ocean_subsurface(surface, light, angular_info); } - else if (!surface.is_ocean()) + else { - L_specular_sum += BRDF_Specular_Isotropic(surface, angular_info); - } + // main specular lobe (anisotropic or isotropic) + if (surface.anisotropic > 0.0f) + { + L_specular_sum += BRDF_Specular_Anisotropic(surface, angular_info); + } + else if (!surface.is_ocean()) + { + L_specular_sum += BRDF_Specular_Isotropic(surface, angular_info); + } // clearcoat layer (secondary specular) - if (surface.clearcoat > 0.0f) - { - L_specular_sum += BRDF_Specular_Clearcoat(surface, angular_info); - } + if (surface.clearcoat > 0.0f) + { + L_specular_sum += BRDF_Specular_Clearcoat(surface, angular_info); + } // sheen layer (cloth-like materials) - if (surface.sheen > 0.0f) - { - L_specular_sum += BRDF_Specular_Sheen(surface, angular_info); - } + if (surface.sheen > 0.0f) + { + L_specular_sum += BRDF_Specular_Sheen(surface, angular_info); + } // subsurface scattering (translucent materials) - if (surface.subsurface_scattering > 0.0f) - { - L_subsurface += subsurface_scattering(surface, light, angular_info); - } - - // ocean specific specular and subsurface - if (surface.is_ocean()) - { - L_specular_sum = BRDF_Specular_Isotropic(surface, angular_info); - - L_subsurface = ocean_subsurface(surface, light, angular_info); + if (surface.subsurface_scattering > 0.0f) + { + L_subsurface += subsurface_scattering(surface, light, angular_info); + } } } diff --git a/data/shaders/ocean/generate_maps.hlsl b/data/shaders/ocean/generate_maps.hlsl index 7b8b37435c..bbc8ea4289 100644 --- a/data/shaders/ocean/generate_maps.hlsl +++ b/data/shaders/ocean/generate_maps.hlsl @@ -58,12 +58,13 @@ void main_cs(uint3 thread_id : SV_DispatchThreadID) float foam = slope_map[thread_id.xy].a; foam *= exp(-params.foamDecayRate); - foam = saturate(foam); float biasedJacobian = max(0.0f, -(jacobian - params.foamBias)); if (biasedJacobian > params.foamThreshold) foam += params.foamAdd * biasedJacobian; + + foam = saturate(foam); displacement_map[thread_id.xy] = float4(displacement, 1.0f); slope_map[thread_id.xy] = float4(slopes, 0.0f, foam);