diff --git a/Editor/GraphicsWindow.cpp b/Editor/GraphicsWindow.cpp index b3c9732cc..2b387ad35 100644 --- a/Editor/GraphicsWindow.cpp +++ b/Editor/GraphicsWindow.cpp @@ -259,85 +259,70 @@ void GraphicsWindow::Create(EditorComponent* _editor) }); AddWidget(&ddgiZ); - voxelRadianceCheckBox.Create("Voxel GI: "); - voxelRadianceCheckBox.SetTooltip("Toggle voxel Global Illumination computation."); - voxelRadianceCheckBox.SetPos(XMFLOAT2(x, y += step)); - voxelRadianceCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); - voxelRadianceCheckBox.OnClick([](wi::gui::EventArgs args) { - wi::renderer::SetVoxelRadianceEnabled(args.bValue); + vxgiCheckBox.Create("VXGI: "); + vxgiCheckBox.SetTooltip("Toggle Voxel Global Illumination."); + vxgiCheckBox.SetPos(XMFLOAT2(x, y += step)); + vxgiCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); + vxgiCheckBox.OnClick([](wi::gui::EventArgs args) { + wi::renderer::SetVXGIEnabled(args.bValue); }); - voxelRadianceCheckBox.SetCheck(wi::renderer::GetVoxelRadianceEnabled()); - AddWidget(&voxelRadianceCheckBox); + vxgiCheckBox.SetCheck(wi::renderer::GetVXGIEnabled()); + AddWidget(&vxgiCheckBox); - voxelRadianceDebugCheckBox.Create("DEBUG: "); - voxelRadianceDebugCheckBox.SetTooltip("Toggle Voxel GI visualization."); - voxelRadianceDebugCheckBox.SetPos(XMFLOAT2(x + wid + 1, y)); - voxelRadianceDebugCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); - voxelRadianceDebugCheckBox.OnClick([](wi::gui::EventArgs args) { - wi::renderer::SetToDrawVoxelHelper(args.bValue); + vxgiDebugCombo.Create(""); + vxgiDebugCombo.SetTooltip("Toggle VXGI visualization."); + vxgiDebugCombo.SetPos(XMFLOAT2(x + wid + 1, y)); + vxgiDebugCombo.SetSize(XMFLOAT2(80, itemheight)); + vxgiDebugCombo.AddItem("No debug", 0); + vxgiDebugCombo.AddItem("Clipmaps", VXGI_CLIPMAP_COUNT); + for (uint32_t i = 0; i < VXGI_CLIPMAP_COUNT; ++i) + { + vxgiDebugCombo.AddItem("Clipmap " + std::to_string(i), i); + } + vxgiDebugCombo.OnSelect([](wi::gui::EventArgs args) { + wi::renderer::SetToDrawVoxelHelper(args.iValue != 0, (int)args.userdata); }); - voxelRadianceDebugCheckBox.SetCheck(wi::renderer::GetToDrawVoxelHelper()); - AddWidget(&voxelRadianceDebugCheckBox); + AddWidget(&vxgiDebugCombo); - voxelRadianceSecondaryBounceCheckBox.Create("Voxel GI 2nd Bounce: "); - voxelRadianceSecondaryBounceCheckBox.SetTooltip("Toggle secondary light bounce computation for Voxel GI."); - voxelRadianceSecondaryBounceCheckBox.SetPos(XMFLOAT2(x, y += step)); - voxelRadianceSecondaryBounceCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); - voxelRadianceSecondaryBounceCheckBox.OnClick([](wi::gui::EventArgs args) { - wi::renderer::SetVoxelRadianceSecondaryBounceEnabled(args.bValue); + vxgiReflectionsCheckBox.Create("VXGI Reflections: "); + vxgiReflectionsCheckBox.SetTooltip("Toggle specular reflections computation for VXGI."); + vxgiReflectionsCheckBox.SetPos(XMFLOAT2(x + wid + 1, y)); + vxgiReflectionsCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); + vxgiReflectionsCheckBox.OnClick([](wi::gui::EventArgs args) { + wi::renderer::SetVXGIReflectionsEnabled(args.bValue); }); - voxelRadianceSecondaryBounceCheckBox.SetCheck(wi::renderer::GetVoxelRadianceSecondaryBounceEnabled()); - AddWidget(&voxelRadianceSecondaryBounceCheckBox); + vxgiReflectionsCheckBox.SetCheck(wi::renderer::GetVXGIReflectionsEnabled()); + AddWidget(&vxgiReflectionsCheckBox); - voxelRadianceReflectionsCheckBox.Create("Reflections: "); - voxelRadianceReflectionsCheckBox.SetTooltip("Toggle specular reflections computation for Voxel GI."); - voxelRadianceReflectionsCheckBox.SetPos(XMFLOAT2(x + wid + 1, y)); - voxelRadianceReflectionsCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); - voxelRadianceReflectionsCheckBox.OnClick([](wi::gui::EventArgs args) { - wi::renderer::SetVoxelRadianceReflectionsEnabled(args.bValue); + vxgiVoxelSizeSlider.Create(0.125f, 0.5f, 1, 7, "VXGI Voxel Size: "); + vxgiVoxelSizeSlider.SetTooltip("Adjust the voxel size for VXGI calculations."); + vxgiVoxelSizeSlider.SetSize(XMFLOAT2(wid, itemheight)); + vxgiVoxelSizeSlider.SetPos(XMFLOAT2(x, y += step)); + vxgiVoxelSizeSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + scene.vxgi.clipmaps[0].voxelsize = args.fValue; }); - voxelRadianceReflectionsCheckBox.SetCheck(wi::renderer::GetVoxelRadianceReflectionsEnabled()); - AddWidget(&voxelRadianceReflectionsCheckBox); + AddWidget(&vxgiVoxelSizeSlider); - voxelRadianceVoxelSizeSlider.Create(0.25, 2, 1, 7, "Voxel GI Voxel Size: "); - voxelRadianceVoxelSizeSlider.SetTooltip("Adjust the voxel size for Voxel GI calculations."); - voxelRadianceVoxelSizeSlider.SetSize(XMFLOAT2(wid, itemheight)); - voxelRadianceVoxelSizeSlider.SetPos(XMFLOAT2(x, y += step)); - voxelRadianceVoxelSizeSlider.SetValue(wi::renderer::GetVoxelRadianceVoxelSize()); - voxelRadianceVoxelSizeSlider.OnSlide([&](wi::gui::EventArgs args) { - wi::renderer::SetVoxelRadianceVoxelSize(args.fValue); + vxgiRayStepSizeSlider.Create(0.5f, 4.0f, 1.0f, 10000, "VXGI Ray Step: "); + vxgiRayStepSizeSlider.SetTooltip("Adjust the precision of ray marching for [reflection] cone tracing step. Lower values = more precision but slower performance."); + vxgiRayStepSizeSlider.SetSize(XMFLOAT2(wid, itemheight)); + vxgiRayStepSizeSlider.SetPos(XMFLOAT2(x, y += step)); + vxgiRayStepSizeSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + scene.vxgi.rayStepSize = args.fValue; }); - AddWidget(&voxelRadianceVoxelSizeSlider); + AddWidget(&vxgiRayStepSizeSlider); - voxelRadianceConeTracingSlider.Create(1, 16, 8, 15, "Voxel GI NumCones: "); - voxelRadianceConeTracingSlider.SetTooltip("Adjust the number of cones sampled in the radiance gathering phase."); - voxelRadianceConeTracingSlider.SetSize(XMFLOAT2(wid, itemheight)); - voxelRadianceConeTracingSlider.SetPos(XMFLOAT2(x, y += step)); - voxelRadianceConeTracingSlider.SetValue((float)wi::renderer::GetVoxelRadianceNumCones()); - voxelRadianceConeTracingSlider.OnSlide([&](wi::gui::EventArgs args) { - wi::renderer::SetVoxelRadianceNumCones(args.iValue); + vxgiMaxDistanceSlider.Create(0, 100, 10, 10000, "VXGI Distance: "); + vxgiMaxDistanceSlider.SetTooltip("Adjust max raymarching distance for VXGI."); + vxgiMaxDistanceSlider.SetSize(XMFLOAT2(wid, itemheight)); + vxgiMaxDistanceSlider.SetPos(XMFLOAT2(x, y += step)); + vxgiMaxDistanceSlider.OnSlide([&](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + scene.vxgi.maxDistance = args.fValue; }); - AddWidget(&voxelRadianceConeTracingSlider); - - voxelRadianceRayStepSizeSlider.Create(0.5f, 2.0f, 0.5f, 10000, "Voxel GI Ray Step: "); - voxelRadianceRayStepSizeSlider.SetTooltip("Adjust the precision of ray marching for cone tracing step. Lower values = more precision but slower performance."); - voxelRadianceRayStepSizeSlider.SetSize(XMFLOAT2(wid, itemheight)); - voxelRadianceRayStepSizeSlider.SetPos(XMFLOAT2(x, y += step)); - voxelRadianceRayStepSizeSlider.SetValue(wi::renderer::GetVoxelRadianceRayStepSize()); - voxelRadianceRayStepSizeSlider.OnSlide([&](wi::gui::EventArgs args) { - wi::renderer::SetVoxelRadianceRayStepSize(args.fValue); - }); - AddWidget(&voxelRadianceRayStepSizeSlider); - - voxelRadianceMaxDistanceSlider.Create(0, 100, 10, 10000, "Voxel GI Distance: "); - voxelRadianceMaxDistanceSlider.SetTooltip("Adjust max raymarching distance for voxel GI."); - voxelRadianceMaxDistanceSlider.SetSize(XMFLOAT2(wid, itemheight)); - voxelRadianceMaxDistanceSlider.SetPos(XMFLOAT2(x, y += step)); - voxelRadianceMaxDistanceSlider.SetValue(wi::renderer::GetVoxelRadianceMaxDistance()); - voxelRadianceMaxDistanceSlider.OnSlide([&](wi::gui::EventArgs args) { - wi::renderer::SetVoxelRadianceMaxDistance(args.fValue); - }); - AddWidget(&voxelRadianceMaxDistanceSlider); + AddWidget(&vxgiMaxDistanceSlider); variableRateShadingClassificationCheckBox.Create("VRS Classification: "); variableRateShadingClassificationCheckBox.SetTooltip("Enable classification of variable rate shading on the screen. Less important parts will be shaded with lesser resolution.\nRequires Tier2 support for variable shading rate"); @@ -1678,14 +1663,12 @@ void GraphicsWindow::ResizeLayout() ddgiRayCountSlider.SetVisible(false); ddgiBlendSpeedSlider.SetVisible(false); ddgiSmoothBackfaceSlider.SetVisible(false); - voxelRadianceDebugCheckBox.SetVisible(false); - voxelRadianceCheckBox.SetVisible(false); - voxelRadianceSecondaryBounceCheckBox.SetVisible(false); - voxelRadianceReflectionsCheckBox.SetVisible(false); - voxelRadianceVoxelSizeSlider.SetVisible(false); - voxelRadianceConeTracingSlider.SetVisible(false); - voxelRadianceRayStepSizeSlider.SetVisible(false); - voxelRadianceMaxDistanceSlider.SetVisible(false); + vxgiDebugCombo.SetVisible(false); + vxgiCheckBox.SetVisible(false); + vxgiReflectionsCheckBox.SetVisible(false); + vxgiVoxelSizeSlider.SetVisible(false); + vxgiRayStepSizeSlider.SetVisible(false); + vxgiMaxDistanceSlider.SetVisible(false); } else { @@ -1705,14 +1688,15 @@ void GraphicsWindow::ResizeLayout() ddgiBlendSpeedSlider.SetVisible(true); ddgiSmoothBackfaceSlider.SetVisible(true); ddgiSmoothBackfaceSlider.SetValue(editor->GetCurrentScene().ddgi.smooth_backface); - voxelRadianceDebugCheckBox.SetVisible(true); - voxelRadianceCheckBox.SetVisible(true); - voxelRadianceSecondaryBounceCheckBox.SetVisible(true); - voxelRadianceReflectionsCheckBox.SetVisible(true); - voxelRadianceVoxelSizeSlider.SetVisible(true); - voxelRadianceConeTracingSlider.SetVisible(true); - voxelRadianceRayStepSizeSlider.SetVisible(true); - voxelRadianceMaxDistanceSlider.SetVisible(true); + vxgiDebugCombo.SetVisible(true); + vxgiCheckBox.SetVisible(true); + vxgiReflectionsCheckBox.SetVisible(true); + vxgiVoxelSizeSlider.SetVisible(true); + vxgiVoxelSizeSlider.SetValue(editor->GetCurrentScene().vxgi.clipmaps[0].voxelsize); + vxgiRayStepSizeSlider.SetVisible(true); + vxgiRayStepSizeSlider.SetValue(editor->GetCurrentScene().vxgi.rayStepSize); + vxgiMaxDistanceSlider.SetVisible(true); + vxgiMaxDistanceSlider.SetValue(editor->GetCurrentScene().vxgi.maxDistance); add(GIBoostSlider); @@ -1734,14 +1718,12 @@ void GraphicsWindow::ResizeLayout() y += jump; - add_right(voxelRadianceCheckBox); - add_right(voxelRadianceDebugCheckBox); - add_right(voxelRadianceSecondaryBounceCheckBox); - add_right(voxelRadianceReflectionsCheckBox); - add(voxelRadianceVoxelSizeSlider); - add(voxelRadianceConeTracingSlider); - add(voxelRadianceRayStepSizeSlider); - add(voxelRadianceMaxDistanceSlider); + add_right(vxgiDebugCombo); + vxgiCheckBox.SetPos(XMFLOAT2(vxgiDebugCombo.GetPos().x - vxgiCheckBox.GetSize().x - padding, vxgiDebugCombo.GetPos().y)); + add_right(vxgiReflectionsCheckBox); + add(vxgiVoxelSizeSlider); + add(vxgiRayStepSizeSlider); + add(vxgiMaxDistanceSlider); } diff --git a/Editor/GraphicsWindow.h b/Editor/GraphicsWindow.h index 56b24b83f..15e04de80 100644 --- a/Editor/GraphicsWindow.h +++ b/Editor/GraphicsWindow.h @@ -29,14 +29,12 @@ public: wi::gui::Slider ddgiRayCountSlider; wi::gui::Slider ddgiBlendSpeedSlider; wi::gui::Slider ddgiSmoothBackfaceSlider; - wi::gui::CheckBox voxelRadianceCheckBox; - wi::gui::CheckBox voxelRadianceDebugCheckBox; - wi::gui::CheckBox voxelRadianceSecondaryBounceCheckBox; - wi::gui::CheckBox voxelRadianceReflectionsCheckBox; - wi::gui::Slider voxelRadianceVoxelSizeSlider; - wi::gui::Slider voxelRadianceConeTracingSlider; - wi::gui::Slider voxelRadianceRayStepSizeSlider; - wi::gui::Slider voxelRadianceMaxDistanceSlider; + wi::gui::CheckBox vxgiCheckBox; + wi::gui::ComboBox vxgiDebugCombo; + wi::gui::CheckBox vxgiReflectionsCheckBox; + wi::gui::Slider vxgiVoxelSizeSlider; + wi::gui::Slider vxgiRayStepSizeSlider; + wi::gui::Slider vxgiMaxDistanceSlider; wi::gui::Slider speedMultiplierSlider; wi::gui::CheckBox transparentShadowsCheckBox; wi::gui::ComboBox shadowTypeComboBox; diff --git a/Example_ImGui_Docking/Example_ImGui_Docking.cpp b/Example_ImGui_Docking/Example_ImGui_Docking.cpp index 146655568..2b9f7e9a2 100644 --- a/Example_ImGui_Docking/Example_ImGui_Docking.cpp +++ b/Example_ImGui_Docking/Example_ImGui_Docking.cpp @@ -1068,7 +1068,7 @@ void Example_ImGuiRenderer::Update(float dt) static bool bVoxelGIEnabled = false; if (ImGui::Checkbox("Voxel GI", &bVoxelGIEnabled)) { - wi::renderer::SetVoxelRadianceEnabled(bVoxelGIEnabled); + wi::renderer::SetVXGIEnabled(bVoxelGIEnabled); } diff --git a/WickedEngine/offlineshadercompiler.cpp b/WickedEngine/offlineshadercompiler.cpp index 33b2f3a6a..5ac1cddc5 100644 --- a/WickedEngine/offlineshadercompiler.cpp +++ b/WickedEngine/offlineshadercompiler.cpp @@ -38,9 +38,11 @@ wi::vector shaders = { {"depthoffield_upsampleCS", wi::graphics::ShaderStage::CS}, {"depthoffield_tileMaxCOC_verticalCS", wi::graphics::ShaderStage::CS}, {"depthoffield_tileMaxCOC_horizontalCS", wi::graphics::ShaderStage::CS}, - {"voxelRadianceSecondaryBounceCS", wi::graphics::ShaderStage::CS}, - {"voxelSceneCopyClearCS", wi::graphics::ShaderStage::CS}, - {"voxelSceneCopyClearCS_TemporalSmoothing", wi::graphics::ShaderStage::CS }, + {"vxgi_offsetprevCS", wi::graphics::ShaderStage::CS}, + {"vxgi_temporalCS", wi::graphics::ShaderStage::CS}, + {"vxgi_sdf_jumpfloodCS", wi::graphics::ShaderStage::CS}, + {"vxgi_resolve_diffuseCS", wi::graphics::ShaderStage::CS}, + {"vxgi_resolve_specularCS", wi::graphics::ShaderStage::CS}, {"upsample_bilateral_float1CS", wi::graphics::ShaderStage::CS}, {"upsample_bilateral_float4CS", wi::graphics::ShaderStage::CS}, {"upsample_bilateral_unorm1CS", wi::graphics::ShaderStage::CS}, diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index e3b48da77..75d07abfd 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -2,6 +2,7 @@ #define WI_SHADERINTEROP_RENDERER_H #include "ShaderInterop.h" #include "ShaderInterop_Weather.h" +#include "ShaderInterop_VXGI.h" struct ShaderScene { @@ -257,6 +258,27 @@ struct ShaderTextureSlot return tex.SampleLevel(sam, uv, lod); } + float4 SampleBias(in SamplerState sam, in float4 uvsets, in float bias) + { + Texture2D tex = GetTexture(); + float2 uv = GetUVSet() == 0 ? uvsets.xy : uvsets.zw; + +#ifndef DISABLE_SVT + [branch] + if (sparse_residencymap_descriptor >= 0) + { + Texture2D residency_map = bindless_textures_uint[UniformTextureSlot(sparse_residencymap_descriptor)]; + float2 virtual_tile_count; + residency_map.GetDimensions(virtual_tile_count.x, virtual_tile_count.y); + float2 virtual_image_dim = virtual_tile_count * SVT_TILE_SIZE; + float virtual_lod = get_lod(virtual_image_dim, ddx(uv), ddy(uv)); + return SampleVirtual(tex, sam, uv, residency_map, virtual_tile_count, virtual_image_dim, virtual_lod + bias); + } +#endif // DISABLE_SVT + + return tex.SampleBias(sam, uv, bias); + } + float4 SampleGrad(in SamplerState sam, in float4 uvsets, in float4 uvsets_dx, in float4 uvsets_dy) { Texture2D tex = GetTexture(); @@ -810,9 +832,8 @@ static const int impostorCaptureAngles = 36; // These option bits can be read from options constant buffer value: static const uint OPTION_BIT_TEMPORALAA_ENABLED = 1 << 0; static const uint OPTION_BIT_TRANSPARENTSHADOWS_ENABLED = 1 << 1; -static const uint OPTION_BIT_VOXELGI_ENABLED = 1 << 2; -static const uint OPTION_BIT_VOXELGI_REFLECTIONS_ENABLED = 1 << 3; -static const uint OPTION_BIT_VOXELGI_RETARGETTED = 1 << 4; +static const uint OPTION_BIT_VXGI_ENABLED = 1 << 2; +static const uint OPTION_BIT_VXGI_REFLECTIONS_ENABLED = 1 << 3; static const uint OPTION_BIT_REALISTIC_SKY = 1 << 6; static const uint OPTION_BIT_HEIGHT_FOG = 1 << 7; static const uint OPTION_BIT_RAYTRACED_SHADOWS = 1 << 8; @@ -848,19 +869,6 @@ struct FrameCB int texture_volumetricclouds_shadow_index; float2 padding0; - float3 voxelradiance_center; // center of the voxel grid in world space units - float voxelradiance_max_distance; // maximum raymarch distance for voxel GI in world-space - - float voxelradiance_size; // voxel half-extent in world space units - float voxelradiance_size_rcp; // 1.0 / voxel-half extent - uint voxelradiance_resolution; // voxel grid resolution - float voxelradiance_resolution_rcp; // 1.0 / voxel grid resolution - - uint voxelradiance_mipcount; // voxel grid mipmap count - uint voxelradiance_numcones; // number of diffuse cones to trace - float voxelradiance_numcones_rcp; // 1.0 / number of diffuse cones to trace - float voxelradiance_stepsize; // raymarch step size in voxel space units - uint envprobe_mipcount; float envprobe_mipcount_rcp; uint lightarray_offset; // indexing into entity array @@ -876,22 +884,24 @@ struct FrameCB uint temporalaa_samplerotation; float blue_noise_phase; - int padding1; int texture_random64x64_index; int texture_bluenoise_index; int texture_sheenlut_index; - int texture_skyviewlut_index; + int texture_transmittancelut_index; int texture_multiscatteringlut_index; int texture_skyluminancelut_index; - - int texture_voxelgi_index; int buffer_entityarray_index; + int buffer_entitymatrixarray_index; float gi_boost; + float padding1; + float padding2; ShaderScene scene; + + VXGI vxgi; }; struct CameraCB @@ -968,6 +978,11 @@ struct CameraCB int texture_rtshadow_index; int texture_surfelgi_index; int texture_depth_index_prev; + + int texture_vxgi_diffuse_index; + int texture_vxgi_specular_index; + int padding0; + int padding1; }; diff --git a/WickedEngine/shaders/ShaderInterop_VXGI.h b/WickedEngine/shaders/ShaderInterop_VXGI.h new file mode 100644 index 000000000..9313ad3a5 --- /dev/null +++ b/WickedEngine/shaders/ShaderInterop_VXGI.h @@ -0,0 +1,130 @@ +#ifndef WI_SHADERINTEROP_VXGI_H +#define WI_SHADERINTEROP_VXGI_H +#include "ShaderInterop.h" +#include "ShaderInterop_Renderer.h" + +// If enabled, geometry shader will be used to voxelize, and axis will be selected by geometry shader +// If disabled, vertex shader with instance replication will be used for each axis +#define VOXELIZATION_GEOMETRY_SHADER_ENABLED + +// Number of clipmaps, each doubling in size: +static const uint VXGI_CLIPMAP_COUNT = 6; +// Upsampling amount for diffuse GI resolve pass: +static const uint VXGI_DIFFUSE_UPSAMPLING = 4; +// Upsampling amount for specular GI resolve pass: +static const uint VXGI_SPECULAR_UPSAMPLING = 2; + +struct VoxelClipMap +{ + float3 center; // center of clipmap volume in world space + float voxelSize; // half-extent of one voxel +}; + +struct VXGI +{ + uint resolution; // voxel grid resolution + float resolution_rcp; // 1.0 / voxel grid resolution + float stepsize; // raymarch step size in voxel space units + float max_distance; // maximum raymarch distance for voxel GI in world-space + + int texture_radiance; + int texture_sdf; + int padding0; + int padding1; + + VoxelClipMap clipmaps[VXGI_CLIPMAP_COUNT]; + +#ifndef __cplusplus + float3 world_to_clipmap(in float3 P, in VoxelClipMap clipmap) + { + float3 diff = (P - clipmap.center) * resolution_rcp / clipmap.voxelSize; + float3 uvw = diff * float3(0.5f, -0.5f, 0.5f) + 0.5f; + return uvw; + } + float3 clipmap_to_world(in float3 uvw, in VoxelClipMap clipmap) + { + float3 P = uvw * 2 - 1; + P.y *= -1; + P *= clipmap.voxelSize; + P *= resolution; + P += clipmap.center; + return P; + } +#endif // __cplusplus +}; + +struct VoxelizerCB +{ + int3 offsetfromPrevFrame; + int clipmap_index; +}; +CONSTANTBUFFER(g_xVoxelizer, VoxelizerCB, CBSLOT_RENDERER_VOXELIZER); + + +// Cones from: https://github.com/compix/VoxelConeTracingGI/blob/master/assets/shaders/voxelConeTracing/finalLightingPass.frag + +//#define USE_32_CONES +#ifdef USE_32_CONES +// 32 Cones for higher quality (16 on average per hemisphere) +static const int DIFFUSE_CONE_COUNT = 32; +static const float DIFFUSE_CONE_APERTURE = 0.628319f; + +static const float3 DIFFUSE_CONE_DIRECTIONS[32] = { + float3(0.898904f, 0.435512f, 0.0479745f), + float3(0.898904f, -0.435512f, -0.0479745f), + float3(0.898904f, 0.0479745f, -0.435512f), + float3(0.898904f, -0.0479745f, 0.435512f), + float3(-0.898904f, 0.435512f, -0.0479745f), + float3(-0.898904f, -0.435512f, 0.0479745f), + float3(-0.898904f, 0.0479745f, 0.435512f), + float3(-0.898904f, -0.0479745f, -0.435512f), + float3(0.0479745f, 0.898904f, 0.435512f), + float3(-0.0479745f, 0.898904f, -0.435512f), + float3(-0.435512f, 0.898904f, 0.0479745f), + float3(0.435512f, 0.898904f, -0.0479745f), + float3(-0.0479745f, -0.898904f, 0.435512f), + float3(0.0479745f, -0.898904f, -0.435512f), + float3(0.435512f, -0.898904f, 0.0479745f), + float3(-0.435512f, -0.898904f, -0.0479745f), + float3(0.435512f, 0.0479745f, 0.898904f), + float3(-0.435512f, -0.0479745f, 0.898904f), + float3(0.0479745f, -0.435512f, 0.898904f), + float3(-0.0479745f, 0.435512f, 0.898904f), + float3(0.435512f, -0.0479745f, -0.898904f), + float3(-0.435512f, 0.0479745f, -0.898904f), + float3(0.0479745f, 0.435512f, -0.898904f), + float3(-0.0479745f, -0.435512f, -0.898904f), + float3(0.57735f, 0.57735f, 0.57735f), + float3(0.57735f, 0.57735f, -0.57735f), + float3(0.57735f, -0.57735f, 0.57735f), + float3(0.57735f, -0.57735f, -0.57735f), + float3(-0.57735f, 0.57735f, 0.57735f), + float3(-0.57735f, 0.57735f, -0.57735f), + float3(-0.57735f, -0.57735f, 0.57735f), + float3(-0.57735f, -0.57735f, -0.57735f) +}; +#else // 16 cones for lower quality (8 on average per hemisphere) +static const int DIFFUSE_CONE_COUNT = 16; +static const float DIFFUSE_CONE_APERTURE = 0.872665f; + +static const float3 DIFFUSE_CONE_DIRECTIONS[16] = { + float3(0.57735f, 0.57735f, 0.57735f), + float3(0.57735f, -0.57735f, -0.57735f), + float3(-0.57735f, 0.57735f, -0.57735f), + float3(-0.57735f, -0.57735f, 0.57735f), + float3(-0.903007f, -0.182696f, -0.388844f), + float3(-0.903007f, 0.182696f, 0.388844f), + float3(0.903007f, -0.182696f, 0.388844f), + float3(0.903007f, 0.182696f, -0.388844f), + float3(-0.388844f, -0.903007f, -0.182696f), + float3(0.388844f, -0.903007f, 0.182696f), + float3(0.388844f, 0.903007f, -0.182696f), + float3(-0.388844f, 0.903007f, 0.182696f), + float3(-0.182696f, -0.388844f, -0.903007f), + float3(0.182696f, 0.388844f, -0.903007f), + float3(-0.182696f, 0.388844f, 0.903007f), + float3(0.182696f, -0.388844f, 0.903007f) +}; +#endif + +#endif // WI_SHADERINTEROP_VXGI_H diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems b/WickedEngine/shaders/Shaders_SOURCE.vcxitems index 387ce1c87..5b64f9e60 100644 --- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems +++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems @@ -1098,6 +1098,18 @@ Vertex Vertex + + Compute + 4.0 + + + Compute + 4.0 + + + Compute + 4.0 + Compute 4.0 @@ -2608,7 +2620,7 @@ Pixel Pixel - + Compute Compute Compute @@ -2618,7 +2630,7 @@ Compute Compute - + Compute Compute Compute @@ -2628,16 +2640,6 @@ Compute Compute - - Compute - Compute - Compute - Compute - Compute - Compute - Compute - Compute - Vertex Vertex @@ -2708,6 +2710,7 @@ + \ No newline at end of file diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters index 358f34153..0ba323ef5 100644 --- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters +++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters @@ -200,10 +200,10 @@ CS - + CS - + CS @@ -707,9 +707,6 @@ HS - - CS - CS @@ -1061,6 +1058,15 @@ ffx-fsr2 + + CS + + + CS + + + CS + @@ -1180,5 +1186,8 @@ interop + + interop + \ No newline at end of file diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli index 5346aab57..60f282578 100644 --- a/WickedEngine/shaders/globals.hlsli +++ b/WickedEngine/shaders/globals.hlsli @@ -200,7 +200,6 @@ struct PrimitiveID #define texture_transmittancelut bindless_textures[GetFrame().texture_transmittancelut_index] #define texture_multiscatteringlut bindless_textures[GetFrame().texture_multiscatteringlut_index] #define texture_skyluminancelut bindless_textures[GetFrame().texture_skyluminancelut_index] -#define texture_voxelgi bindless_textures3D[GetFrame().texture_voxelgi_index] #define scene_acceleration_structure bindless_accelerationstructures[GetScene().TLAS] #define texture_depth bindless_textures_float[GetCamera().texture_depth_index] @@ -247,9 +246,9 @@ inline float4 attribute_at_bary(in float4 a0, in float4 a1, in float4 a2, in flo } inline bool is_saturated(float a) { return a == saturate(a); } -inline bool is_saturated(float2 a) { return is_saturated(a.x) && is_saturated(a.y); } -inline bool is_saturated(float3 a) { return is_saturated(a.x) && is_saturated(a.y) && is_saturated(a.z); } -inline bool is_saturated(float4 a) { return is_saturated(a.x) && is_saturated(a.y) && is_saturated(a.z) && is_saturated(a.w); } +inline bool is_saturated(float2 a) { return all(a == saturate(a)); } +inline bool is_saturated(float3 a) { return all(a == saturate(a)); } +inline bool is_saturated(float4 a) { return all(a == saturate(a)); } inline float2 uv_to_clipspace(in float2 uv) { diff --git a/WickedEngine/shaders/lightingHF.hlsli b/WickedEngine/shaders/lightingHF.hlsli index 014b930e3..5750dfbdb 100644 --- a/WickedEngine/shaders/lightingHF.hlsli +++ b/WickedEngine/shaders/lightingHF.hlsli @@ -361,10 +361,12 @@ inline float3 GetAmbient(in float3 N) #endif // ENVMAPRENDERING +#ifndef NO_FLAT_AMBIENT // This is not entirely correct if we have probes, because it shouldn't be added twice. // However, it is not correct if we leave it out from probes, because if we render a scene // with dark sky but ambient, we still want some visible result. ambient += GetAmbientColor(); +#endif // NO_FLAT_AMBIENT return ambient; } @@ -461,29 +463,23 @@ inline float4 EnvironmentReflection_Local(in Surface surface, in ShaderEntity pr // VOXEL RADIANCE -inline void VoxelGI(in Surface surface, inout Lighting lighting) +inline void VoxelGI(inout Surface surface, inout Lighting lighting) { - [branch] if (GetFrame().voxelradiance_resolution != 0) + [branch] + if (GetFrame().vxgi.resolution != 0 && GetFrame().vxgi.texture_radiance >= 0) { - // determine blending factor (we will blend out voxel GI on grid edges): - float3 voxelSpacePos = surface.P - GetFrame().voxelradiance_center; - voxelSpacePos *= GetFrame().voxelradiance_size_rcp; - voxelSpacePos *= GetFrame().voxelradiance_resolution_rcp; - voxelSpacePos = saturate(abs(voxelSpacePos)); - float blend = 1 - pow(max(voxelSpacePos.x, max(voxelSpacePos.y, voxelSpacePos.z)), 4); + Texture3D voxels = bindless_textures3D[GetFrame().vxgi.texture_radiance]; // diffuse: - { - float4 trace = ConeTraceDiffuse(texture_voxelgi, surface.P, surface.N); - lighting.indirect.diffuse = lerp(lighting.indirect.diffuse, trace.rgb, trace.a * blend); - } + float4 trace = ConeTraceDiffuse(voxels, surface.P, surface.N); + lighting.indirect.diffuse = mad(lighting.indirect.diffuse, 1 - trace.a, trace.rgb); // specular: [branch] - if (GetFrame().options & OPTION_BIT_VOXELGI_REFLECTIONS_ENABLED) + if (GetFrame().options & OPTION_BIT_VXGI_REFLECTIONS_ENABLED) { - float4 trace = ConeTraceSpecular(texture_voxelgi, surface.P, surface.N, surface.V, surface.roughness); - lighting.indirect.specular = lerp(lighting.indirect.specular, trace.rgb * surface.F, trace.a * blend); + float4 trace = ConeTraceSpecular(voxels, surface.P, surface.N, surface.V, surface.roughnessBRDF, surface.pixel); + lighting.indirect.specular = mad(lighting.indirect.specular, 1 - trace.a, trace.rgb * surface.F); } } } diff --git a/WickedEngine/shaders/objectGS_voxelizer.hlsl b/WickedEngine/shaders/objectGS_voxelizer.hlsl index 8059e74bb..88f3aed9a 100644 --- a/WickedEngine/shaders/objectGS_voxelizer.hlsl +++ b/WickedEngine/shaders/objectGS_voxelizer.hlsl @@ -25,6 +25,8 @@ void main( inout TriangleStream< GSOutput > outputStream ) { + VoxelClipMap clipmap = GetFrame().vxgi.clipmaps[g_xVoxelizer.clipmap_index]; + float3 facenormal = abs(input[0].nor + input[1].nor + input[2].nor); uint maxi = facenormal[1] > facenormal[0] ? 1 : 0; maxi = facenormal[2] > facenormal[maxi] ? 2 : maxi; @@ -34,7 +36,7 @@ void main( GSOutput output; // World space -> Voxel grid space: - output.pos.xyz = (input[i].pos.xyz - GetFrame().voxelradiance_center) * GetFrame().voxelradiance_size_rcp; + output.pos.xyz = (input[i].pos.xyz - clipmap.center) / clipmap.voxelSize; // Project onto dominant axis: [flatten] @@ -48,7 +50,7 @@ void main( } // Voxel grid space -> Clip space - output.pos.xy *= GetFrame().voxelradiance_resolution_rcp; + output.pos.xy *= GetFrame().vxgi.resolution_rcp; output.pos.zw = 1; // Append the rest of the parameters as is: diff --git a/WickedEngine/shaders/objectHF.hlsli b/WickedEngine/shaders/objectHF.hlsli index e10a60450..2e4218251 100644 --- a/WickedEngine/shaders/objectHF.hlsli +++ b/WickedEngine/shaders/objectHF.hlsli @@ -755,7 +755,9 @@ inline void TiledLighting(inout Surface surface, inout Lighting lighting, uint f #endif // DISABLE_ENVMAPS #ifndef DISABLE_VOXELGI +#ifdef TRANSPARENT // opaque tiled forward: voxel GI was rendered in separate pass VoxelGI(surface, lighting); +#endif // TRANSPARENT #endif //DISABLE_VOXELGI [branch] @@ -864,6 +866,19 @@ inline void TiledLighting(inout Surface surface, inout Lighting lighting, uint f lighting.indirect.diffuse = bindless_textures[GetCamera().texture_surfelgi_index][surface.pixel].rgb * GetFrame().gi_boost; surface.flags |= SURFACE_FLAG_GI_APPLIED; } + + [branch] + if ((surface.flags & SURFACE_FLAG_GI_APPLIED) == 0 && GetCamera().texture_vxgi_diffuse_index >= 0) + { + lighting.indirect.diffuse = bindless_textures[GetCamera().texture_vxgi_diffuse_index][surface.pixel].rgb * GetFrame().gi_boost; + surface.flags |= SURFACE_FLAG_GI_APPLIED; + } + [branch] + if (GetCamera().texture_vxgi_specular_index >= 0) + { + float4 vxgi_specular = bindless_textures[GetCamera().texture_vxgi_specular_index][surface.pixel]; + lighting.indirect.specular = vxgi_specular.rgb * surface.F + lighting.indirect.specular * (1 - vxgi_specular.a); + } #endif // TRANSPARENT [branch] diff --git a/WickedEngine/shaders/objectPS_voxelizer.hlsl b/WickedEngine/shaders/objectPS_voxelizer.hlsl index 6c69d6ed3..8acc7bc43 100644 --- a/WickedEngine/shaders/objectPS_voxelizer.hlsl +++ b/WickedEngine/shaders/objectPS_voxelizer.hlsl @@ -1,213 +1,327 @@ #define DISABLE_SOFT_SHADOWMAP #define DISABLE_VOXELGI +#define VOXEL_INITIAL_OFFSET 1 +//#define ENVMAPRENDERING // modified ambient calc +//#define NO_FLAT_AMBIENT #include "objectHF.hlsli" #include "voxelHF.hlsli" #include "volumetricCloudsHF.hlsli" // Note: the voxelizer uses an overall simplified material and lighting model (no normal maps, only diffuse light and emissive) -RWStructuredBuffer output : register(u0); +Texture3D input_previous_radiance : register(t0); + +RWTexture3D output_atomic : register(u0); + +void VoxelAtomicAverage(inout RWTexture3D output, in uint3 dest, in float4 color) +{ + float4 addingColor = float4(color.rgb, 1); + uint newValue = PackVoxelColor(float4(addingColor.rgb, 1.0 / MAX_VOXEL_ALPHA)); + uint expectedValue = 0; + uint actualValue; + + InterlockedCompareExchange(output[dest], expectedValue, newValue, actualValue); + while (actualValue != expectedValue) + { + expectedValue = actualValue; + + color = UnpackVoxelColor(actualValue); + color.a *= MAX_VOXEL_ALPHA; + + color.rgb *= color.a; + + color += addingColor; + + color.rgb /= color.a; + + color.a /= MAX_VOXEL_ALPHA; + newValue = PackVoxelColor(color); + + InterlockedCompareExchange(output[dest], expectedValue, newValue, actualValue); + } +} // Note: centroid interpolation is used to avoid floating voxels in some cases struct PSInput { float4 pos : SV_POSITION; centroid float4 color : COLOR; - centroid float4 uvsets : UVSETS; + float4 uvsets : UVSETS; centroid float3 N : NORMAL; centroid float3 P : POSITION3D; }; -void main(PSInput input) +void main(PSInput input, in uint coverage : SV_Coverage) { float3 N = normalize(input.N); float3 P = input.P; + float coverage_percent = countbits(coverage) / 8.0; - float3 diff = (P - GetFrame().voxelradiance_center) * GetFrame().voxelradiance_resolution_rcp * GetFrame().voxelradiance_size_rcp; - float3 uvw = diff * float3(0.5f, -0.5f, 0.5f) + 0.5f; + float4 baseColor = input.color; + [branch] + if (GetMaterial().textures[BASECOLORMAP].IsValid() && (GetFrame().options & OPTION_BIT_DISABLE_ALBEDO_MAPS) == 0) + { + float lod_bias = 0; + if (GetMaterial().options & SHADERMATERIAL_OPTION_BIT_TRANSPARENT || GetMaterial().alphaTest > 0) + { + // If material is non opaque, then we apply bias to avoid sampling such a low + // mip level in which alpha is completely gone (helps with trees) + lod_bias = -10; + } + baseColor *= GetMaterial().textures[BASECOLORMAP].SampleBias(sampler_linear_wrap, input.uvsets, lod_bias); + } + //baseColor.a = pow(baseColor.a, 64); + //baseColor.a = 1; + //baseColor.a *= coverage_percent; + + float4 color = baseColor; + float3 emissiveColor = GetMaterial().GetEmissive(); + [branch] + if (any(emissiveColor) && GetMaterial().textures[EMISSIVEMAP].IsValid()) + { + float4 emissiveMap = GetMaterial().textures[EMISSIVEMAP].Sample(sampler_linear_wrap, input.uvsets); + emissiveColor *= emissiveMap.rgb * emissiveMap.a; + } + //emissiveColor *= coverage_percent; + + Lighting lighting; + lighting.create(0, 0, 0, 0); [branch] - if (is_saturated(uvw)) + if (any(xForwardLightMask)) { - float4 baseColor; - [branch] - if (GetMaterial().textures[BASECOLORMAP].IsValid() && (GetFrame().options & OPTION_BIT_DISABLE_ALBEDO_MAPS) == 0) + // Loop through light buckets for the draw call: + const uint first_item = 0; + const uint last_item = first_item + GetFrame().lightarray_count - 1; + const uint first_bucket = first_item / 32; + const uint last_bucket = min(last_item / 32, 1); // only 2 buckets max (uint2) for forward pass! + [loop] + for (uint bucket = first_bucket; bucket <= last_bucket; ++bucket) { - baseColor = GetMaterial().textures[BASECOLORMAP].Sample(sampler_linear_wrap, input.uvsets); - } - else - { - baseColor = 1; - } - baseColor *= input.color; - float4 color = baseColor; - float3 emissiveColor = GetMaterial().GetEmissive(); - [branch] - if (any(emissiveColor) && GetMaterial().textures[EMISSIVEMAP].IsValid()) - { - float4 emissiveMap = GetMaterial().textures[EMISSIVEMAP].Sample(sampler_linear_wrap, input.uvsets); - emissiveColor *= emissiveMap.rgb * emissiveMap.a; - } + uint bucket_bits = xForwardLightMask[bucket]; - Lighting lighting; - lighting.create(0, 0, 0, 0); - - [branch] - if (any(xForwardLightMask)) - { - // Loop through light buckets for the draw call: - const uint first_item = 0; - const uint last_item = first_item + GetFrame().lightarray_count - 1; - const uint first_bucket = first_item / 32; - const uint last_bucket = min(last_item / 32, 1); // only 2 buckets max (uint2) for forward pass! [loop] - for (uint bucket = first_bucket; bucket <= last_bucket; ++bucket) + while (bucket_bits != 0) { - uint bucket_bits = xForwardLightMask[bucket]; + // Retrieve global entity index from local bucket, then remove bit from local bucket: + const uint bucket_bit_index = firstbitlow(bucket_bits); + const uint entity_index = bucket * 32 + bucket_bit_index; + bucket_bits ^= 1u << bucket_bit_index; - [loop] - while (bucket_bits != 0) + ShaderEntity light = load_entity(GetFrame().lightarray_offset + entity_index); + + if (light.GetFlags() & ENTITY_FLAG_LIGHT_STATIC) { - // Retrieve global entity index from local bucket, then remove bit from local bucket: - const uint bucket_bit_index = firstbitlow(bucket_bits); - const uint entity_index = bucket * 32 + bucket_bit_index; - bucket_bits ^= 1u << bucket_bit_index; + continue; // static lights will be skipped (they are used in lightmap baking) + } - ShaderEntity light = load_entity(GetFrame().lightarray_offset + entity_index); + switch (light.GetType()) + { + case ENTITY_TYPE_DIRECTIONALLIGHT: + { + float3 L = light.GetDirection(); + const float NdotL = saturate(dot(L, N)); - if (light.GetFlags() & ENTITY_FLAG_LIGHT_STATIC) + [branch] + if (NdotL > 0) { - continue; // static lights will be skipped (they are used in lightmap baking) + float3 lightColor = light.GetColor().rgb * NdotL; + + [branch] + if (light.IsCastingShadow() >= 0) + { + [loop] + for (uint cascade = 0; cascade < GetFrame().shadow_cascade_count; ++cascade) + { + const float3 shadow_pos = mul(load_entitymatrix(light.GetMatrixIndex() + cascade), float4(P, 1)).xyz; // ortho matrix, no divide by .w + const float3 shadow_uv = shadow_pos.xyz * float3(0.5f, -0.5f, 0.5f) + 0.5f; + + if (is_saturated(shadow_uv)) + { + lightColor *= shadow_2D(light, shadow_pos, shadow_uv.xy, cascade); + break; + } + } + + if (GetFrame().options & OPTION_BIT_VOLUMETRICCLOUDS_SHADOWS) + { + lightColor *= shadow_2D_volumetricclouds(P); + } + } + + lighting.direct.diffuse += lightColor; } + } + break; + case ENTITY_TYPE_POINTLIGHT: + { + float3 L = light.position - P; + const float dist2 = dot(L, L); + const float range = light.GetRange(); + const float range2 = range * range; - switch (light.GetType()) + [branch] + if (dist2 < range2) { - case ENTITY_TYPE_DIRECTIONALLIGHT: - { - float3 L = light.GetDirection(); + const float3 Lunnormalized = L; + const float dist = sqrt(dist2); + L /= dist; const float NdotL = saturate(dot(L, N)); [branch] if (NdotL > 0) { - float3 lightColor = light.GetColor().rgb * NdotL; + float3 lightColor = light.GetColor().rgb * NdotL * attenuation_pointlight(dist, dist2, range, range2); [branch] - if (light.IsCastingShadow() >= 0) - { - [loop] - for (uint cascade = 0; cascade < GetFrame().shadow_cascade_count; ++cascade) - { - const float3 shadow_pos = mul(load_entitymatrix(light.GetMatrixIndex() + cascade), float4(P, 1)).xyz; // ortho matrix, no divide by .w - const float3 shadow_uv = shadow_pos.xyz * float3(0.5f, -0.5f, 0.5f) + 0.5f; - - if (is_saturated(shadow_uv)) - { - lightColor *= shadow_2D(light, shadow_pos, shadow_uv.xy, cascade); - break; - } - } - - if (GetFrame().options & OPTION_BIT_VOLUMETRICCLOUDS_SHADOWS) - { - lightColor *= shadow_2D_volumetricclouds(P); - } + if (light.IsCastingShadow() >= 0) { + lightColor *= shadow_cube(light, Lunnormalized); } lighting.direct.diffuse += lightColor; } } - break; - case ENTITY_TYPE_POINTLIGHT: + } + break; + case ENTITY_TYPE_SPOTLIGHT: + { + float3 L = light.position - P; + const float dist2 = dot(L, L); + const float range = light.GetRange(); + const float range2 = range * range; + + [branch] + if (dist2 < range2) { - float3 L = light.position - P; - const float dist2 = dot(L, L); - const float range = light.GetRange(); - const float range2 = range * range; + const float dist = sqrt(dist2); + L /= dist; + const float NdotL = saturate(dot(L, N)); [branch] - if (dist2 < range2) + if (NdotL > 0) { - const float3 Lunnormalized = L; - const float dist = sqrt(dist2); - L /= dist; - const float NdotL = saturate(dot(L, N)); + const float spot_factor = dot(L, light.GetDirection()); + const float spot_cutoff = light.GetConeAngleCos(); [branch] - if (NdotL > 0) + if (spot_factor > spot_cutoff) { - float3 lightColor = light.GetColor().rgb * NdotL * attenuation_pointlight(dist, dist2, range, range2); + float3 lightColor = light.GetColor().rgb * NdotL * attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); [branch] - if (light.IsCastingShadow() >= 0) { - lightColor *= shadow_cube(light, Lunnormalized); + if (light.IsCastingShadow() >= 0) + { + float4 ShPos = mul(load_entitymatrix(light.GetMatrixIndex() + 0), float4(P, 1)); + ShPos.xyz /= ShPos.w; + float2 ShTex = ShPos.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); + [branch] + if ((saturate(ShTex.x) == ShTex.x) && (saturate(ShTex.y) == ShTex.y)) + { + lightColor *= shadow_2D(light, ShPos.xyz, ShTex.xy, 0); + } } lighting.direct.diffuse += lightColor; } } } - break; - case ENTITY_TYPE_SPOTLIGHT: - { - float3 L = light.position - P; - const float dist2 = dot(L, L); - const float range = light.GetRange(); - const float range2 = range * range; - - [branch] - if (dist2 < range2) - { - const float dist = sqrt(dist2); - L /= dist; - const float NdotL = saturate(dot(L, N)); - - [branch] - if (NdotL > 0) - { - const float spot_factor = dot(L, light.GetDirection()); - const float spot_cutoff = light.GetConeAngleCos(); - - [branch] - if (spot_factor > spot_cutoff) - { - float3 lightColor = light.GetColor().rgb * NdotL * attenuation_spotlight(dist, dist2, range, range2, spot_factor, light.GetAngleScale(), light.GetAngleOffset()); - - [branch] - if (light.IsCastingShadow() >= 0) - { - float4 ShPos = mul(load_entitymatrix(light.GetMatrixIndex() + 0), float4(P, 1)); - ShPos.xyz /= ShPos.w; - float2 ShTex = ShPos.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f); - [branch] - if ((saturate(ShTex.x) == ShTex.x) && (saturate(ShTex.y) == ShTex.y)) - { - lightColor *= shadow_2D(light, ShPos.xyz, ShTex.xy, 0); - } - } - - lighting.direct.diffuse += lightColor; - } - } - } - } - break; - } + } + break; } } } - - color.rgb *= lighting.direct.diffuse / PI; - - color.rgb += emissiveColor; - - uint color_encoded = PackVoxelColor(color); - uint normal_encoded = pack_half2(encode_oct(N)); - - // output: - uint3 writecoord = floor(uvw * GetFrame().voxelradiance_resolution); - uint id = flatten3D(writecoord, GetFrame().voxelradiance_resolution); - InterlockedMax(output[id].colorMask, color_encoded); - InterlockedMax(output[id].normalMask, normal_encoded); } + + float4 trace = ConeTraceDiffuse(input_previous_radiance, P, N); + lighting.indirect.diffuse = trace.rgb; + lighting.indirect.diffuse += GetAmbient(N) * (1 - trace.a); + + color.rgb *= lighting.direct.diffuse / PI + lighting.indirect.diffuse; + + color.rgb += emissiveColor; + + // output: + VoxelClipMap clipmap = GetFrame().vxgi.clipmaps[g_xVoxelizer.clipmap_index]; + float3 uvw = GetFrame().vxgi.world_to_clipmap(P, clipmap); + if (!is_saturated(uvw)) + return; + uint3 writecoord = floor(uvw * GetFrame().vxgi.resolution); + + float3 aniso_direction = N; + float3 face_offsets = float3( + aniso_direction.x > 0 ? 0 : 1, + aniso_direction.y > 0 ? 2 : 3, + aniso_direction.z > 0 ? 4 : 5 + ) * GetFrame().vxgi.resolution; + float3 direction_weights = abs(N); + + writecoord.z *= 5; // de-interleaved channels + + if (direction_weights.x > 0) + { + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.x, 0, 0)], PackVoxelChannel(color.r * direction_weights.x)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.x, 0, 1)], PackVoxelChannel(color.g * direction_weights.x)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.x, 0, 2)], PackVoxelChannel(color.b * direction_weights.x)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.x, 0, 3)], PackVoxelChannel(color.a * direction_weights.x)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.x, 0, 4)], 1); + } + if (direction_weights.y > 0) + { + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.y, 0, 0)], PackVoxelChannel(color.r * direction_weights.y)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.y, 0, 1)], PackVoxelChannel(color.g * direction_weights.y)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.y, 0, 2)], PackVoxelChannel(color.b * direction_weights.y)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.y, 0, 3)], PackVoxelChannel(color.a * direction_weights.y)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.y, 0, 4)], 1); + } + if (direction_weights.z > 0) + { + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.z, 0, 0)], PackVoxelChannel(color.r * direction_weights.z)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.z, 0, 1)], PackVoxelChannel(color.g * direction_weights.z)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.z, 0, 2)], PackVoxelChannel(color.b * direction_weights.z)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.z, 0, 3)], PackVoxelChannel(color.a * direction_weights.z)); + InterlockedAdd(output_atomic[writecoord + uint3(face_offsets.z, 0, 4)], 1); + } + + +//#if 0 +// uint face_offset = cubemap_to_uv(aniso_direction).z * GetFrame().vxgi.resolution; +// VoxelAtomicAverage(output_radiance, writecoord + uint3(face_offset, 0, 0), color); +// VoxelAtomicAverage(output_opacity, writecoord + uint3(face_offset, 0, 0), color.aaaa); +//#else +// float3 face_offsets = float3( +// aniso_direction.x > 0 ? 0 : 1, +// aniso_direction.y > 0 ? 2 : 3, +// aniso_direction.z > 0 ? 4 : 5 +// ) * GetFrame().vxgi.resolution; +// float3 direction_weights = abs(N); +// VoxelAtomicAverage(output_radiance, writecoord + uint3(face_offsets.x, 0, 0), color * direction_weights.x); +// VoxelAtomicAverage(output_radiance, writecoord + uint3(face_offsets.y, 0, 0), color * direction_weights.y); +// VoxelAtomicAverage(output_radiance, writecoord + uint3(face_offsets.z, 0, 0), color * direction_weights.z); +// VoxelAtomicAverage(output_opacity, writecoord + uint3(face_offsets.x, 0, 0), color.aaaa * direction_weights.x); +// VoxelAtomicAverage(output_opacity, writecoord + uint3(face_offsets.y, 0, 0), color.aaaa * direction_weights.y); +// VoxelAtomicAverage(output_opacity, writecoord + uint3(face_offsets.z, 0, 0), color.aaaa * direction_weights.z); +//#endif + + //bool done = false; + //while (!done) + //{ + // // acquire lock: + // uint locked; + // InterlockedCompareExchange(lock[writecoord], 0, 1, locked); + // if (locked == 0) + // { + // float4 average = output_albedo[writecoord]; + // float3 average_normal = output_normal[writecoord]; + + // average.a += 1; + // average.rgb += color.rgb; + // average_normal.rgb += N * 0.5 + 0.5; + + // output_albedo[writecoord] = average; + // output_normal[writecoord] = average_normal; + + // InterlockedExchange(lock[writecoord], 0, locked); + // done = true; + // } + //} } diff --git a/WickedEngine/shaders/objectVS_voxelizer.hlsl b/WickedEngine/shaders/objectVS_voxelizer.hlsl index 880e22dba..58428a92e 100644 --- a/WickedEngine/shaders/objectVS_voxelizer.hlsl +++ b/WickedEngine/shaders/objectVS_voxelizer.hlsl @@ -6,8 +6,10 @@ struct VSOut float4 pos : SV_POSITION; float4 color : COLOR; float4 uvsets : UVSETS; - float3 nor : NORMAL; - uint emissiveColor : EMISSIVECOLOR; + float3 N : NORMAL; +#ifndef VOXELIZATION_GEOMETRY_SHADER_ENABLED + float3 P : POSITION3D; +#endif // VOXELIZATION_GEOMETRY_SHADER_ENABLED }; VSOut main(VertexInput input) @@ -19,8 +21,50 @@ VSOut main(VertexInput input) Out.pos = surface.position; Out.color = surface.color; Out.uvsets = surface.uvsets; - Out.nor = surface.normal; - Out.emissiveColor = surface.emissiveColor; + Out.N = surface.normal; + +#ifndef VOXELIZATION_GEOMETRY_SHADER_ENABLED + Out.P = surface.position.xyz; + + VoxelClipMap clipmap = GetFrame().vxgi.clipmaps[g_xVoxelizer.clipmap_index]; + + // World space -> Voxel grid space: + Out.pos.xyz = (Out.pos.xyz - clipmap.center) / clipmap.voxelSize; + + // Project onto dominant axis: + const uint frustum_index = input.GetInstancePointer().GetFrustumIndex(); + switch (frustum_index) + { + default: + case 0: + Out.pos.xyz = Out.pos.xyz; + break; + case 1: + Out.pos.xyz = Out.pos.zyx; + break; + case 2: + Out.pos.xyz = Out.pos.xzy; + break; + + // Test: if rendered with 6 frustums, double sided voxelization happens here: + case 3: + Out.pos.xyz = Out.pos.xyz * float3(1, 1, -1); + Out.N *= -1; + break; + case 4: + Out.pos.xyz = Out.pos.zyx * float3(1, 1, -1); + Out.N *= -1; + break; + case 5: + Out.pos.xyz = Out.pos.xzy * float3(1, 1, -1); + Out.N *= -1; + break; + } + + // Voxel grid space -> Clip space + Out.pos.xy *= GetFrame().vxgi.resolution_rcp; + Out.pos.zw = 1; +#endif // VOXELIZATION_GEOMETRY_SHADER_ENABLED return Out; } diff --git a/WickedEngine/shaders/temporalaaCS.hlsl b/WickedEngine/shaders/temporalaaCS.hlsl index 213c402ed..733583d24 100644 --- a/WickedEngine/shaders/temporalaaCS.hlsl +++ b/WickedEngine/shaders/temporalaaCS.hlsl @@ -99,14 +99,14 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint3 const float2 prevUV = uv + velocity; -#if 0 +#if 1 // Disocclusion fallback: float depth_current = texture_lineardepth[DTid.xy] * GetCamera().z_far; float depth_history = compute_lineardepth(texture_depth_history.SampleLevel(sampler_point_clamp, prevUV, 0)); if (length(velocity) > 0.01 && abs(depth_current - depth_history) > 1) { output[DTid.xy] = current; - output[DTid.xy] = float3(1, 0, 0); + //output[DTid.xy] = float3(1, 0, 0); return; } #endif diff --git a/WickedEngine/shaders/upsample_bilateral_float4CS.hlsl b/WickedEngine/shaders/upsample_bilateral_float4CS.hlsl index 36ecba6f1..581571782 100644 --- a/WickedEngine/shaders/upsample_bilateral_float4CS.hlsl +++ b/WickedEngine/shaders/upsample_bilateral_float4CS.hlsl @@ -33,9 +33,9 @@ void main(uint3 DTid : SV_DispatchThreadID) const float2 lowres_texel_size = postprocess.params1.zw; const float2 uv00 = uv - lowres_texel_size * 0.5; - const float2 uv10 = uv00 + float2(lowres_texel_size.x, 0); - const float2 uv01 = uv00 + float2(0, lowres_texel_size.y); - const float2 uv11 = uv00 + lowres_texel_size; + const float2 uv10 = float2(uv00.x + lowres_texel_size.x, uv00.y); + const float2 uv01 = float2(uv00.x, uv00.y + lowres_texel_size.y); + const float2 uv11 = float2(uv00.x + lowres_texel_size.x, uv00.y + lowres_texel_size.y); const float4 lineardepth_highres = texture_lineardepth[pixel].xxxx; const float4 lineardepth_lowres = float4( @@ -64,7 +64,6 @@ void main(uint3 DTid : SV_DispatchThreadID) { // large error: -#ifdef UPSAMPLE_DISABLE_FILTERING // find nearest sample to current depth: float min_depth_diff = depth_diff[0]; float2 uv_nearest = uv00; @@ -87,20 +86,6 @@ void main(uint3 DTid : SV_DispatchThreadID) min_depth_diff = depth_diff[3]; } color = input[floor(uv_nearest * lowres_size)]; -#else - - // depth-weighted blending: - float4 weights = saturate(1 - abs(lineardepth_highres - lineardepth_lowres)); - float sum = weights[0] + weights[1] + weights[2] + weights[3]; - weights /= max(sum, 0.0001); - - color = 0; - color += input.SampleLevel(sampler_point_clamp, uv00, 0) * weights[0]; - color += input.SampleLevel(sampler_point_clamp, uv10, 0) * weights[1]; - color += input.SampleLevel(sampler_point_clamp, uv01, 0) * weights[2]; - color += input.SampleLevel(sampler_point_clamp, uv11, 0) * weights[3]; - -#endif // UPSAMPLE_DISABLE_FILTERING } #ifdef USE_PIXELSHADER diff --git a/WickedEngine/shaders/visibility_velocityCS.hlsl b/WickedEngine/shaders/visibility_velocityCS.hlsl index 4a27ad6c3..655efc7d4 100644 --- a/WickedEngine/shaders/visibility_velocityCS.hlsl +++ b/WickedEngine/shaders/visibility_velocityCS.hlsl @@ -40,7 +40,6 @@ void main(uint2 DTid : SV_DispatchThreadID) float2 pos2D = clipspace; float4 pos2DPrev = mul(GetCamera().previous_view_projection, float4(pre, 1)); pos2DPrev.xy /= max(0.0001, pos2DPrev.w); // max: avoid nan - pos2DPrev.xy = clamp(pos2DPrev.xy, -1, 1); // clamp: avoid blown up values float2 velocity = ((pos2DPrev.xy - GetCamera().temporalaa_jitter_prev) - (pos2D.xy - GetCamera().temporalaa_jitter)) * float2(0.5, -0.5); output_velocity[pixel] = velocity; diff --git a/WickedEngine/shaders/volumetricCloud_renderCS.hlsl b/WickedEngine/shaders/volumetricCloud_renderCS.hlsl index 8bac0772a..d28ef2887 100644 --- a/WickedEngine/shaders/volumetricCloud_renderCS.hlsl +++ b/WickedEngine/shaders/volumetricCloud_renderCS.hlsl @@ -670,7 +670,7 @@ void RenderClouds(uint3 DTid, float2 uv, float depth, float3 depthWorldPosition, } #ifdef VOLUMETRICCLOUD_CAPTURE -[numthreads(GENERATEMIPCHAIN_2D_BLOCK_SIZE, GENERATEMIPCHAIN_2D_BLOCK_SIZE, 1)] +[numthreads(POSTPROCESS_BLOCKSIZE, POSTPROCESS_BLOCKSIZE, 1)] void main(uint3 DTid : SV_DispatchThreadID) { TextureCubeArray input = bindless_cubearrays[capture.texture_input]; diff --git a/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl b/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl index 21e7b1438..5cc4408ab 100644 --- a/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_DirectionalPS.hlsl @@ -14,7 +14,7 @@ float4 main(VertexToPixel input) : SV_TARGET } float2 ScreenCoord = input.pos2D.xy / input.pos2D.w * float2(0.5f, -0.5f) + 0.5f; - float depth = max(input.pos.z, texture_depth.SampleLevel(sampler_point_clamp, ScreenCoord, 2)); + float depth = max(input.pos.z, texture_depth.SampleLevel(sampler_point_clamp, ScreenCoord, 0)); float3 P = reconstruct_position(ScreenCoord, depth); float3 V = GetCamera().position - P; float cameraDistance = length(V); diff --git a/WickedEngine/shaders/volumetricLight_PointPS.hlsl b/WickedEngine/shaders/volumetricLight_PointPS.hlsl index 71a015602..e697022b0 100644 --- a/WickedEngine/shaders/volumetricLight_PointPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_PointPS.hlsl @@ -7,7 +7,7 @@ float4 main(VertexToPixel input) : SV_TARGET ShaderEntity light = load_entity(GetFrame().lightarray_offset + (uint)g_xColor.x); float2 ScreenCoord = input.pos2D.xy / input.pos2D.w * float2(0.5f, -0.5f) + 0.5f; - float depth = max(input.pos.z, texture_depth.SampleLevel(sampler_point_clamp, ScreenCoord, 2)); + float depth = max(input.pos.z, texture_depth.SampleLevel(sampler_point_clamp, ScreenCoord, 0)); float3 P = reconstruct_position(ScreenCoord, depth); float3 V = GetCamera().position - P; float cameraDistance = length(V); diff --git a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl index 18cd36181..485f0fa63 100644 --- a/WickedEngine/shaders/volumetricLight_SpotPS.hlsl +++ b/WickedEngine/shaders/volumetricLight_SpotPS.hlsl @@ -7,7 +7,7 @@ float4 main(VertexToPixel input) : SV_TARGET ShaderEntity light = load_entity(GetFrame().lightarray_offset + (uint)g_xColor.x); float2 ScreenCoord = input.pos2D.xy / input.pos2D.w * float2(0.5f, -0.5f) + 0.5f; - float depth = max(input.pos.z, texture_depth.SampleLevel(sampler_point_clamp, ScreenCoord, 2)); + float depth = max(input.pos.z, texture_depth.SampleLevel(sampler_point_clamp, ScreenCoord, 0)); float3 P = reconstruct_position(ScreenCoord, depth); float3 V = GetCamera().position - P; float cameraDistance = length(V); diff --git a/WickedEngine/shaders/voxelConeTracingHF.hlsli b/WickedEngine/shaders/voxelConeTracingHF.hlsli index 1173bc264..dd36afc07 100644 --- a/WickedEngine/shaders/voxelConeTracingHF.hlsli +++ b/WickedEngine/shaders/voxelConeTracingHF.hlsli @@ -2,51 +2,126 @@ #define WI_VOXEL_CONERACING_HF #include "globals.hlsli" +// With help from: https://github.com/compix/VoxelConeTracingGI/blob/master/assets/shaders/voxelConeTracing/finalLightingPass.frag + #ifndef VOXEL_INITIAL_OFFSET -#define VOXEL_INITIAL_OFFSET 2 +#define VOXEL_INITIAL_OFFSET 1 #endif // VOXEL_INITIAL_OFFSET +inline float4 SampleVoxelClipMap(in Texture3D voxels, in float3 P, in uint clipmap_index, float step_dist, in float3 face_offsets, in float3 direction_weights, uint precomputed_direction = 0) +{ + VoxelClipMap clipmap = GetFrame().vxgi.clipmaps[clipmap_index]; + float3 tc = GetFrame().vxgi.world_to_clipmap(P, clipmap); + + // half texel correction is applied to avoid sampling over current clipmap: + const float half_texel = 0.5 * GetFrame().vxgi.resolution_rcp; + tc = clamp(tc, half_texel, 1 - half_texel); + + tc.x = (tc.x + precomputed_direction) / (6.0 + DIFFUSE_CONE_COUNT); // remap into anisotropic + tc.y = (tc.y + clipmap_index) / VXGI_CLIPMAP_COUNT; // remap into clipmap + + float4 sam; + if (precomputed_direction == 0) + { + // sample anisotropically 3 times, weighted by cone direction: + sam = + voxels.SampleLevel(sampler_linear_clamp, float3(tc.x + face_offsets.x, tc.y, tc.z), 0) * direction_weights.x + + voxels.SampleLevel(sampler_linear_clamp, float3(tc.x + face_offsets.y, tc.y, tc.z), 0) * direction_weights.y + + voxels.SampleLevel(sampler_linear_clamp, float3(tc.x + face_offsets.z, tc.y, tc.z), 0) * direction_weights.z + ; + } + else + { + // sample once for precomputed anisotropically weighted cone direction (uses precomputed_direction): + sam = voxels.SampleLevel(sampler_linear_clamp, tc, 0); + } + + // correction: + sam *= step_dist / clipmap.voxelSize; + + return sam; +} + // voxels: 3D Texture containing voxel scene with direct diffuse lighting (or direct + secondary indirect bounce) // P: world-space position of receiving surface // N: world-space normal vector of receiving surface // coneDirection: world-space cone direction in the direction to perform the trace -// coneAperture: tan(coneHalfAngle) -inline float4 ConeTrace(in Texture3D voxels, in float3 P, in float3 N, in float3 coneDirection, in float coneAperture) +// coneAperture: cone width +// precomputed_direction : avoid 3x anisotropic weight sampling, and instead directly use a slice that has precomputed cone direction weighted data +inline float4 ConeTrace(in Texture3D voxels, in float3 P, in float3 N, in float3 coneDirection, in float coneAperture, in float stepSize, bool use_sdf = false, uint precomputed_direction = 0) { float3 color = 0; float alpha = 0; + + uint clipmap_index0 = 0; + VoxelClipMap clipmap0 = GetFrame().vxgi.clipmaps[clipmap_index0]; + const float voxelSize0 = clipmap0.voxelSize * 2; // full extent + const float voxelSize0_rcp = rcp(voxelSize0); + + const float coneCoefficient = 2 * tan(coneAperture * 0.5); // We need to offset the cone start position to avoid sampling its own voxel (self-occlusion): - // Unfortunately, it will result in disconnection between nearby surfaces :( - float dist = GetFrame().voxelradiance_size; // offset by cone dir so that first sample of all cones are not the same - float3 startPos = P + N * GetFrame().voxelradiance_size * VOXEL_INITIAL_OFFSET * SQRT2; // sqrt2 is diagonal voxel half-extent + float dist = voxelSize0; // offset by cone dir so that first sample of all cones are not the same + float step_dist = dist; + float3 startPos = P + N * voxelSize0 * VOXEL_INITIAL_OFFSET * SQRT2; // sqrt2 is diagonal voxel half-extent + + float3 aniso_direction = -coneDirection; + float3 face_offsets = float3( + aniso_direction.x > 0 ? 0 : 1, + aniso_direction.y > 0 ? 2 : 3, + aniso_direction.z > 0 ? 4 : 5 + ) / (6.0 + DIFFUSE_CONE_COUNT); + float3 direction_weights = abs(coneDirection); + //float3 direction_weights = sqr(coneDirection); // We will break off the loop if the sampling distance is too far for performance reasons: - while (dist < GetFrame().voxelradiance_max_distance && alpha < 1) + while (dist < GetFrame().vxgi.max_distance && alpha < 1 && clipmap_index0 < VXGI_CLIPMAP_COUNT) { - float diameter = max(GetFrame().voxelradiance_size, 2 * coneAperture * dist); - float mip = log2(diameter * GetFrame().voxelradiance_size_rcp); + float3 p0 = startPos + coneDirection * dist; - // Because we do the ray-marching in world space, we need to remap into 3d texture space before sampling: - // todo: optimization could be doing ray-marching in texture space - float3 tc = startPos + coneDirection * dist; - tc = (tc - GetFrame().voxelradiance_center) * GetFrame().voxelradiance_size_rcp; - tc *= GetFrame().voxelradiance_resolution_rcp; - tc = tc * float3(0.5f, -0.5f, 0.5f) + 0.5f; + float diameter = max(voxelSize0, coneCoefficient * dist); + float lod = clamp(log2(diameter * voxelSize0_rcp), clipmap_index0, VXGI_CLIPMAP_COUNT - 1); - // break if the ray exits the voxel grid, or we sample from the last mip: - if (!is_saturated(tc) || mip >= (float)GetFrame().voxelradiance_mipcount) - break; + float clipmap_index = floor(lod); + float clipmap_blend = frac(lod); - float4 sam = voxels.SampleLevel(sampler_linear_clamp, tc, mip); + VoxelClipMap clipmap = GetFrame().vxgi.clipmaps[clipmap_index]; + float3 tc = GetFrame().vxgi.world_to_clipmap(p0, clipmap); + if (!is_saturated(tc)) + { + clipmap_index0++; + continue; + } - // this is the correct blending to avoid black-staircase artifact (ray stepped front-to back, so blend front to back): + // sample first clipmap level: + float4 sam = SampleVoxelClipMap(voxels, p0, clipmap_index, step_dist, face_offsets, direction_weights, precomputed_direction); + + // sample second clipmap if needed and perform trilinear blend: + if(clipmap_blend > 0 && clipmap_index < VXGI_CLIPMAP_COUNT - 1) + { + sam = lerp(sam, SampleVoxelClipMap(voxels, p0, clipmap_index + 1, step_dist, face_offsets, direction_weights, precomputed_direction), clipmap_blend); + } + + // front-to back blending: float a = 1 - alpha; color += a * sam.rgb; alpha += a * sam.a; + float stepSizeCurrent = stepSize; + + if (use_sdf) + { + // half texel correction is applied to avoid sampling over current clipmap: + const float half_texel = 0.5 * GetFrame().vxgi.resolution_rcp; + tc = clamp(tc, half_texel, 1 - half_texel); + tc.y = (tc.y + clipmap_index) / VXGI_CLIPMAP_COUNT; // remap into clipmap + float sdf = bindless_textures3D[GetFrame().vxgi.texture_sdf].SampleLevel(sampler_linear_clamp, tc, 0).r; + stepSizeCurrent = max(stepSize, sdf); + } + // step along ray: - dist += diameter * GetFrame().voxelradiance_stepsize; + step_dist = diameter * stepSizeCurrent; + dist += step_dist; } return float4(color, alpha); @@ -58,19 +133,20 @@ inline float4 ConeTrace(in Texture3D voxels, in float3 P, in float3 N, i inline float4 ConeTraceDiffuse(in Texture3D voxels, in float3 P, in float3 N) { float4 amount = 0; - float3x3 tangentSpace = get_tangentspace(N); - for (uint cone = 0; cone < GetFrame().voxelradiance_numcones; ++cone) // quality is between 1 and 16 cones + float sum = 0; + for (uint i = 0; i < DIFFUSE_CONE_COUNT; ++i) { - float2 hamm = hammersley2d(cone, GetFrame().voxelradiance_numcones); - float3 hemisphere = hemispherepoint_cos(hamm.x, hamm.y); - float3 coneDirection = mul(hemisphere, tangentSpace); - - amount += ConeTrace(voxels, P, N, coneDirection, tan(PI * 0.5f * 0.33f)); + const float3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i]; + const float cosTheta = dot(N, coneDirection); + if (cosTheta <= 0) + continue; + const uint precomputed_direction = 6 + i; // optimization, avoids sampling 3 times aniso weights + amount += ConeTrace(voxels, P, N, coneDirection, DIFFUSE_CONE_APERTURE, 1, false, precomputed_direction) * cosTheta; + sum += cosTheta; } + amount /= sum; - // final radiance is average of all the cones radiances - amount *= GetFrame().voxelradiance_numcones_rcp; amount.rgb = max(0, amount.rgb); amount.a = saturate(amount.a); @@ -81,12 +157,15 @@ inline float4 ConeTraceDiffuse(in Texture3D voxels, in float3 P, in floa // P: world-space position of receiving surface // N: world-space normal vector of receiving surface // V: world-space view-vector (cameraPosition - P) -inline float4 ConeTraceSpecular(in Texture3D voxels, in float3 P, in float3 N, in float3 V, in float roughness) +inline float4 ConeTraceSpecular(in Texture3D voxels, in float3 P, in float3 N, in float3 V, in float roughness, in uint2 pixel) { - float aperture = tan(roughness * PI * 0.5f * 0.1f); + float aperture = roughness; float3 coneDirection = reflect(-V, N); - float4 amount = ConeTrace(voxels, P, N, coneDirection, aperture); + // some dithering to help with banding at large step size + P += coneDirection * dither(pixel + GetTemporalAASampleRotation()) * GetFrame().vxgi.stepsize; + + float4 amount = ConeTrace(voxels, P, N, coneDirection, aperture, GetFrame().vxgi.stepsize, true); amount.rgb = max(0, amount.rgb); amount.a = saturate(amount.a); diff --git a/WickedEngine/shaders/voxelGS.hlsl b/WickedEngine/shaders/voxelGS.hlsl index acb6b41ed..8662fa268 100644 --- a/WickedEngine/shaders/voxelGS.hlsl +++ b/WickedEngine/shaders/voxelGS.hlsl @@ -1,4 +1,5 @@ #include "globals.hlsli" +#include "cube.hlsli" struct GSOutput { @@ -6,32 +7,95 @@ struct GSOutput float4 col : TEXCOORD; }; -[maxvertexcount(14)] +[maxvertexcount(36)] void main( - point GSOutput input[1], - inout TriangleStream< GSOutput > output + point uint input[1] : VERTEXID, + inout TriangleStream output ) { - [branch] - if (input[0].col.a > 0) + uint voxel_index = input[0]; + uint clipmap_index = (uint)g_xColor.x; + + bool min_clip = false; + if (clipmap_index == VXGI_CLIPMAP_COUNT) { - for (uint i = 0; i < 14; i++) + min_clip = true; + const uint voxel_count = GetFrame().vxgi.resolution * GetFrame().vxgi.resolution * GetFrame().vxgi.resolution; + voxel_index = input[0] % voxel_count; + clipmap_index = input[0] / voxel_count; + } + + uint3 coord = unflatten3D(voxel_index, GetFrame().vxgi.resolution); + float3 uvw = (coord + 0.5) * GetFrame().vxgi.resolution_rcp; + float3 center = uvw * 2 - 1; + center.y *= -1; + center *= GetFrame().vxgi.resolution; + + VoxelClipMap clipmap = GetFrame().vxgi.clipmaps[clipmap_index]; + + if (min_clip && clipmap_index > 0) + { + float3 P = GetFrame().vxgi.clipmap_to_world((coord - 0.5) / (GetFrame().vxgi.resolution - 2), clipmap); + VoxelClipMap clipmap_below = GetFrame().vxgi.clipmaps[clipmap_index - 1]; + float3 diff = (P - clipmap_below.center) * GetFrame().vxgi.resolution_rcp / clipmap_below.voxelSize; + float3 uvw = diff * float3(0.5f, -0.5f, 0.5f) + 0.5f; + if (is_saturated(uvw)) + return; + } + + uint3 pixel = coord; + pixel.y += clipmap_index * GetFrame().vxgi.resolution; + + Texture3D voxels = bindless_textures3D[GetFrame().vxgi.texture_radiance]; + float4 face_colors[6] = { + voxels[pixel + uint3(0 * GetFrame().vxgi.resolution, 0, 0)], + voxels[pixel + uint3(1 * GetFrame().vxgi.resolution, 0, 0)], + voxels[pixel + uint3(2 * GetFrame().vxgi.resolution, 0, 0)], + voxels[pixel + uint3(3 * GetFrame().vxgi.resolution, 0, 0)], + voxels[pixel + uint3(4 * GetFrame().vxgi.resolution, 0, 0)], + voxels[pixel + uint3(5 * GetFrame().vxgi.resolution, 0, 0)], + }; + + if ( + face_colors[0].a == 0 && + face_colors[1].a == 0 && + face_colors[2].a == 0 && + face_colors[3].a == 0 && + face_colors[4].a == 0 && + face_colors[5].a == 0 + ) + { + return; + } + + for (uint i = 0; i < 36; i += 3) + { + GSOutput tri[3]; + uint j = 0; + for (j = 0; j < 3; ++j) { - GSOutput element; - element.pos = input[0].pos; - element.col = input[0].col; - - element.pos.xyz = element.pos.xyz / GetFrame().voxelradiance_resolution * 2 - 1; - element.pos.y = -element.pos.y; - element.pos.xyz *= GetFrame().voxelradiance_resolution; - element.pos.xyz += (vertexID_create_cube(i) - float3(0, 1, 0)) * 2; - element.pos.xyz *= GetFrame().voxelradiance_resolution * GetFrame().voxelradiance_size / GetFrame().voxelradiance_resolution; - - element.pos = mul(g_xTransform, float4(element.pos.xyz, 1)); - element.col *= g_xColor; - - output.Append(element); + tri[j].pos = float4(center, 1); + tri[j].pos.xyz += -CUBE[i + j].xyz; } + + float3 facenormal = -normalize(cross(tri[2].pos.xyz - tri[1].pos.xyz, tri[1].pos.xyz - tri[0].pos.xyz)); + float4 color = face_colors[(uint)cubemap_to_uv(facenormal).z]; + if (color.a == 0) + { + // just to avoid invisible cube faces: + color = float4(0, 0, 0, 1); + } + + if(color.a > 0) + for (j = 0; j < 3; ++j) + { + tri[j].pos.xyz *= clipmap.voxelSize; + tri[j].pos.xyz += clipmap.center; + tri[j].pos = mul(g_xTransform, float4(tri[j].pos.xyz, 1)); + tri[j].col = color; + output.Append(tri[j]); + } + output.RestartStrip(); } } diff --git a/WickedEngine/shaders/voxelHF.hlsli b/WickedEngine/shaders/voxelHF.hlsli index 7e8a52e3e..d6f5fe856 100644 --- a/WickedEngine/shaders/voxelHF.hlsli +++ b/WickedEngine/shaders/voxelHF.hlsli @@ -1,53 +1,45 @@ #ifndef WI_VOXEL_HF #define WI_VOXEL_HF +#include "ColorSpaceUtility.hlsli" -struct VoxelType -{ - uint colorMask; - uint normalMask; -}; +static const float __hdrRange = 10.0f; // HDR to SDR packing scale +static const uint MAX_VOXEL_RGB = 511; // 9 bits for RGB +static const uint MAX_VOXEL_ALPHA = 31; // 5 bits for alpha (alpha is needed for atomic average) +static const float DARK_PACKING_POW = 8; // improves precision for dark colors -static const float __hdrRange = 10.0f; - -// Encode HDR color to a 32 bit uint -// Alpha is 1 bit + 7 bit HDR remapping uint PackVoxelColor(in float4 color) { - // normalize color to LDR - float hdr = length(color.rgb); - color.rgb /= hdr; - - // encode LDR color and HDR range - uint3 iColor = uint3(color.rgb * 255.0f); - uint iHDR = (uint)(saturate(hdr / __hdrRange) * 127); - uint colorMask = (iHDR << 24u) | (iColor.r << 16u) | (iColor.g << 8u) | iColor.b; - - // encode alpha into highest bit - uint iAlpha = (color.a > 0 ? 1u : 0u); - colorMask |= iAlpha << 31u; - - return colorMask; + color.rgb /= __hdrRange; + color = saturate(color); + color.rgb = pow(color.rgb, 1.0 / DARK_PACKING_POW); + uint retVal = 0; + retVal |= (uint)(color.r * MAX_VOXEL_RGB) << 0u; + retVal |= (uint)(color.g * MAX_VOXEL_RGB) << 9u; + retVal |= (uint)(color.b * MAX_VOXEL_RGB) << 18u; + retVal |= (uint)(color.a * MAX_VOXEL_ALPHA) << 27u; + return retVal; } -// Decode 32 bit uint into HDR color with 1 bit alpha float4 UnpackVoxelColor(in uint colorMask) { - float hdr; - float4 color; + float4 retVal; + retVal.r = (float)((colorMask >> 0u) & MAX_VOXEL_RGB) / MAX_VOXEL_RGB; + retVal.g = (float)((colorMask >> 9u) & MAX_VOXEL_RGB) / MAX_VOXEL_RGB; + retVal.b = (float)((colorMask >> 18u) & MAX_VOXEL_RGB) / MAX_VOXEL_RGB; + retVal.a = (float)((colorMask >> 27u) & MAX_VOXEL_ALPHA) / MAX_VOXEL_ALPHA; + retVal = saturate(retVal); + retVal.rgb = pow(retVal.rgb, DARK_PACKING_POW); + retVal.rgb *= __hdrRange; + return retVal; +} - hdr = (colorMask >> 24u) & 0x0000007f; - color.r = (colorMask >> 16u) & 0x000000ff; - color.g = (colorMask >> 8u) & 0x000000ff; - color.b = colorMask & 0x000000ff; - - hdr /= 127.0f; - color.rgb /= 255.0f; - - color.rgb *= hdr * __hdrRange; - - color.a = (colorMask >> 31u) & 0x00000001; - - return color; +uint PackVoxelChannel(float value) +{ + return uint(value * 1024); +} +float UnpackVoxelChannel(uint value) +{ + return float(value) / 1024.0f; } #endif // WI_VOXEL_HF diff --git a/WickedEngine/shaders/voxelRadianceSecondaryBounceCS.hlsl b/WickedEngine/shaders/voxelRadianceSecondaryBounceCS.hlsl deleted file mode 100644 index 23cfb0f7b..000000000 --- a/WickedEngine/shaders/voxelRadianceSecondaryBounceCS.hlsl +++ /dev/null @@ -1,39 +0,0 @@ -#define VOXEL_INITIAL_OFFSET 1 -#include "globals.hlsli" -#include "voxelHF.hlsli" -#include "voxelConeTracingHF.hlsli" -#include "lightingHF.hlsli" - -Texture3D input_emission : register(t0); -StructuredBuffer input_voxelscene : register(t1); - -RWTexture3D output : register(u0); - -[numthreads(8, 8, 8)] -void main( uint3 DTid : SV_DispatchThreadID ) -{ - const uint3 writecoord = DTid; - - float4 emission = input_emission[writecoord]; - - if (emission.a > 0) - { - const uint idx = flatten3D(writecoord, GetFrame().voxelradiance_resolution); - float3 N = decode_oct(unpack_half2(input_voxelscene[idx].normalMask)); - - float3 P = ((float3)writecoord + 0.5f) * GetFrame().voxelradiance_resolution_rcp; - P = P * 2 - 1; - P.y *= -1; - P *= GetFrame().voxelradiance_size; - P *= GetFrame().voxelradiance_resolution; - P += GetFrame().voxelradiance_center; - - emission.rgb += ConeTraceDiffuse(input_emission, P, N).rgb; - - output[writecoord] = emission; - } - else - { - output[writecoord] = 0; - } -} diff --git a/WickedEngine/shaders/voxelSceneCopyClearCS.hlsl b/WickedEngine/shaders/voxelSceneCopyClearCS.hlsl deleted file mode 100644 index f38a792f9..000000000 --- a/WickedEngine/shaders/voxelSceneCopyClearCS.hlsl +++ /dev/null @@ -1,41 +0,0 @@ -#include "globals.hlsli" -#include "voxelHF.hlsli" - -StructuredBuffer input : register(t0); -RWTexture3D output_emission : register(u0); - -[numthreads(256, 1, 1)] -void main( uint3 DTid : SV_DispatchThreadID ) -{ - VoxelType voxel = input[DTid.x]; - - const float4 color = UnpackVoxelColor(voxel.colorMask); - - const uint3 writecoord = unflatten3D(DTid.x, GetFrame().voxelradiance_resolution); - - [branch] - if (color.a > 0) - { -#ifdef TEMPORAL_SMOOTHING - // Blend voxels with the previous frame's data to avoid popping artifacts for dynamic objects: - [branch] - if (GetFrame().options & OPTION_BIT_VOXELGI_RETARGETTED) - { - // Do not perform the blend if an offset happened to the voxel grid's center. - // The offset is not accounted for in the blend operation which can introduce severe light leaking. - output_emission[writecoord] = float4(color.rgb, 1); - } - else - { - // This operation requires Feature: Typed UAV additional format loads! - output_emission[writecoord] = lerp(output_emission[writecoord], float4(color.rgb, 1), 0.2f); - } -#else - output_emission[writecoord] = float4(color.rgb, 1); -#endif - } - else - { - output_emission[writecoord] = 0; - } -} diff --git a/WickedEngine/shaders/voxelSceneCopyClearCS_TemporalSmoothing.hlsl b/WickedEngine/shaders/voxelSceneCopyClearCS_TemporalSmoothing.hlsl deleted file mode 100644 index 7e6589a17..000000000 --- a/WickedEngine/shaders/voxelSceneCopyClearCS_TemporalSmoothing.hlsl +++ /dev/null @@ -1,3 +0,0 @@ -#define TEMPORAL_SMOOTHING - -#include "voxelSceneCopyClearCS.hlsl" diff --git a/WickedEngine/shaders/voxelVS.hlsl b/WickedEngine/shaders/voxelVS.hlsl index 8f2e646f4..bacbafc38 100644 --- a/WickedEngine/shaders/voxelVS.hlsl +++ b/WickedEngine/shaders/voxelVS.hlsl @@ -2,19 +2,7 @@ #include "cube.hlsli" #include "voxelHF.hlsli" -struct VSOut +uint main(uint vertexID : SV_VERTEXID) : VERTEXID { - float4 pos : SV_POSITION; - float4 col : TEXCOORD; -}; - -VSOut main( uint vertexID : SV_VERTEXID ) -{ - VSOut o; - - uint3 coord = unflatten3D(vertexID, GetFrame().voxelradiance_resolution); - o.pos = float4(coord, 1); - o.col = texture_voxelgi[coord]; - - return o; + return vertexID; } diff --git a/WickedEngine/shaders/vxgi_offsetprevCS.hlsl b/WickedEngine/shaders/vxgi_offsetprevCS.hlsl new file mode 100644 index 000000000..d1e543b3b --- /dev/null +++ b/WickedEngine/shaders/vxgi_offsetprevCS.hlsl @@ -0,0 +1,47 @@ +#include "globals.hlsli" +#include "voxelHF.hlsli" + +Texture3D input_previous_radiance : register(t0); +RWTexture3D output_radiance : register(u0); + +[numthreads(8, 8, 8)] +void main(uint3 DTid : SV_DispatchThreadID) +{ + DTid.y += g_xVoxelizer.clipmap_index * GetFrame().vxgi.resolution; + + for (uint i = 0; i < 6 + DIFFUSE_CONE_COUNT; ++i) + { + uint3 dst = DTid; + dst.x += i * GetFrame().vxgi.resolution; + + float4 radiance_prev = 0; + + if (any(g_xVoxelizer.offsetfromPrevFrame)) + { + int3 coord = dst - g_xVoxelizer.offsetfromPrevFrame; + int aniso_face_start_x = i * GetFrame().vxgi.resolution; + int aniso_face_end_x = aniso_face_start_x + GetFrame().vxgi.resolution; + int clipmap_face_start_y = g_xVoxelizer.clipmap_index * GetFrame().vxgi.resolution; + int clipmap_face_end_y = clipmap_face_start_y + GetFrame().vxgi.resolution; + + if ( + coord.x >= aniso_face_start_x && coord.x < aniso_face_end_x && + coord.y >= clipmap_face_start_y && coord.y < clipmap_face_end_y && + coord.z >= 0 && coord.z < GetFrame().vxgi.resolution + ) + { + radiance_prev = input_previous_radiance[coord]; + } + else + { + radiance_prev = 0; + } + } + else + { + radiance_prev = input_previous_radiance[dst]; + } + + output_radiance[dst] = radiance_prev; + } +} diff --git a/WickedEngine/shaders/vxgi_resolve_diffuseCS.hlsl b/WickedEngine/shaders/vxgi_resolve_diffuseCS.hlsl new file mode 100644 index 000000000..4e3c5b888 --- /dev/null +++ b/WickedEngine/shaders/vxgi_resolve_diffuseCS.hlsl @@ -0,0 +1,125 @@ +#include "globals.hlsli" +#include "ShaderInterop_Postprocess.h" +#include "voxelConeTracingHF.hlsli" +#include "lightingHF.hlsli" + +PUSHCONSTANT(postprocess, PostProcess); + +Texture2D input_prev : register(t0); + +RWTexture2D output : register(u0); + +[numthreads(8, 8, 1)] +void main(uint3 DTid : SV_DispatchThreadID) +{ + const uint2 pixel = DTid.xy; + const float2 uv = ((float2)pixel + 0.5) * postprocess.resolution_rcp; + + Texture3D voxels = bindless_textures3D[GetFrame().vxgi.texture_radiance]; + + const float depth = texture_depth[pixel * VXGI_DIFFUSE_UPSAMPLING]; + + float4 color = 0; + [branch] + if (depth > 0) + { + const float roughness = texture_roughness[pixel * VXGI_DIFFUSE_UPSAMPLING]; + const float3 N = decode_oct(texture_normal[pixel * VXGI_DIFFUSE_UPSAMPLING]); + const float3 P = reconstruct_position(uv, depth); + + float4 trace = ConeTraceDiffuse(voxels, P, N); + color = float4(trace.rgb, 1); + color.rgb += GetAmbient(N) * (1 - trace.a); + + output[pixel] = color; + } +} + + +// This was the version with temporal upscaler: + +//PUSHCONSTANT(postprocess, PostProcess); +// +//Texture2D input_prev : register(t0); +// +//RWTexture2D output : register(u0); +// +//// If this is enabled, disocclusion regions will be retraced, otherwise they will be filled by primary sample color: +////#define HIGH_QUALITY_DISOCCLUSION +//static const float disocclusion_threshold = 0.2; +// +//[numthreads(8, 8, 1)] +//void main(uint3 DTid : SV_DispatchThreadID) +//{ +// const uint2 pixel_base = DTid.xy * VXGI_DIFFUSE_UPSAMPLING; +// const uint2 pixel_offset = unflatten2D(GetFrame().frame_count % (VXGI_DIFFUSE_UPSAMPLING * VXGI_DIFFUSE_UPSAMPLING), VXGI_DIFFUSE_UPSAMPLING); +// const uint2 pixel = clamp(pixel_base + pixel_offset, 0, postprocess.resolution - 1); +// const float2 uv = ((float2)pixel + 0.5) * postprocess.resolution_rcp; +// +// Texture3D voxels = bindless_textures3D[GetFrame().vxgi.texture_radiance]; +// +// const float depth = texture_depth[pixel]; +// +// float4 color = 0; +// [branch] +// if (depth > 0) +// { +// const float roughness = texture_roughness[pixel]; +// const float3 N = decode_oct(texture_normal[pixel]); +// const float3 P = reconstruct_position(uv, depth); +// +// float4 trace = ConeTraceDiffuse(voxels, P, N); +// color = float4(trace.rgb, 1); +// color.rgb += GetAmbient(N) * (1 - trace.a); +// +// output[pixel] = color; +// } +// +// for (uint x = 0; x < VXGI_DIFFUSE_UPSAMPLING; ++x) +// { +// for (uint y = 0; y < VXGI_DIFFUSE_UPSAMPLING; ++y) +// { +// const uint2 neighbor_offset = uint2(x, y); +// const uint2 pixel_neighbor = clamp(pixel_base + neighbor_offset, 0, postprocess.resolution - 1); +// if (any(neighbor_offset != pixel_offset)) +// { +// const float2 velocity = texture_velocity[pixel_neighbor]; +// const float2 uv_neighbor = (pixel_neighbor + 0.5) * postprocess.resolution_rcp; +// const float2 uv_neighbor_prev = uv_neighbor + velocity; +// bool reprojection_valid = is_saturated(uv_neighbor_prev); +// +// const float depth_neighbor = texture_depth[pixel_neighbor]; +// if (depth_neighbor == 0) +// continue; +// if (reprojection_valid) +// { +// const float depth_neighbor_prev = texture_depth_history.SampleLevel(sampler_linear_clamp, uv_neighbor_prev, 0); +// const float lineardepth_neighbor = compute_lineardepth(depth_neighbor); +// const float lineardepth_neighbor_prev = compute_lineardepth(depth_neighbor_prev); +// reprojection_valid = reprojection_valid && abs(lineardepth_neighbor - lineardepth_neighbor_prev) < disocclusion_threshold; +// } +// +// if (reprojection_valid) +// { +// output[pixel_neighbor] = input_prev.SampleLevel(sampler_linear_clamp, uv_neighbor_prev, 0); +// } +// else +// { +//#ifdef HIGH_QUALITY_DISOCCLUSION +// const float3 N = decode_oct(texture_normal[pixel_neighbor]); +// const float3 P = reconstruct_position(uv_neighbor, depth_neighbor); +// float4 trace = ConeTraceDiffuse(voxels, P, N); +// float4 color = float4(trace.rgb, 1); +// color.rgb += GetAmbient(N) * (1 - trace.a); +// output[pixel_neighbor] = color; +//#else +// output[pixel_neighbor] = color; +// //output[pixel_neighbor] = float4(1,0,0,1); +//#endif // HIGH_QUALITY_DISOCCLUSION +// } +// +// } +// } +// } +//} + diff --git a/WickedEngine/shaders/vxgi_resolve_specularCS.hlsl b/WickedEngine/shaders/vxgi_resolve_specularCS.hlsl new file mode 100644 index 000000000..970301566 --- /dev/null +++ b/WickedEngine/shaders/vxgi_resolve_specularCS.hlsl @@ -0,0 +1,31 @@ +#include "globals.hlsli" +#include "ShaderInterop_Postprocess.h" +#include "voxelConeTracingHF.hlsli" + +PUSHCONSTANT(postprocess, PostProcess); + +RWTexture2D output : register(u0); + +[numthreads(8, 8, 1)] +void main(uint3 DTid : SV_DispatchThreadID) +{ + const uint2 pixel = DTid.xy; + const float2 uv = ((float2)pixel + 0.5) * postprocess.resolution_rcp; + + Texture3D voxels = bindless_textures3D[GetFrame().vxgi.texture_radiance]; + + const float depth = texture_depth[pixel * VXGI_SPECULAR_UPSAMPLING]; + + float4 color = 0; + [branch] + if (depth > 0) + { + const float roughness = texture_roughness[pixel * VXGI_SPECULAR_UPSAMPLING]; + const float3 N = decode_oct(texture_normal[pixel * VXGI_SPECULAR_UPSAMPLING]); + const float3 P = reconstruct_position(uv, depth); + const float3 V = normalize(GetCamera().position - P); + + color = ConeTraceSpecular(voxels, P, N, V, roughness * roughness, pixel); + output[pixel] = color; + } +} diff --git a/WickedEngine/shaders/vxgi_sdf_jumpfloodCS.hlsl b/WickedEngine/shaders/vxgi_sdf_jumpfloodCS.hlsl new file mode 100644 index 000000000..8ac75e4a9 --- /dev/null +++ b/WickedEngine/shaders/vxgi_sdf_jumpfloodCS.hlsl @@ -0,0 +1,52 @@ +#include "globals.hlsli" + +Texture3D input_sdf : register(t0); + +RWTexture3D output_sdf : register(u0); + +struct Push +{ + float jump_size; +}; +PUSHCONSTANT(push, Push); + +[numthreads(8, 8, 8)] +void main(uint3 DTid : SV_DispatchThreadID) +{ + uint clipmap_start = g_xVoxelizer.clipmap_index * GetFrame().vxgi.resolution; + uint clipmap_end = clipmap_start + GetFrame().vxgi.resolution; + DTid.y += clipmap_start; + + VoxelClipMap clipmap = GetFrame().vxgi.clipmaps[g_xVoxelizer.clipmap_index]; + float voxelSize = clipmap.voxelSize; + + float best_distance = input_sdf[DTid]; + + for (int x = -1; x <= 1; ++x) + { + for (int y = -1; y <= 1; ++y) + { + for (int z = -1; z <= 1; ++z) + { + int3 offset = int3(x, y, z) * push.jump_size; + int3 pixel = DTid + offset; + if ( + pixel.x >= 0 && pixel.x < GetFrame().vxgi.resolution && + pixel.y >= clipmap_start && pixel.y < clipmap_end && + pixel.z >= 0 && pixel.z < GetFrame().vxgi.resolution + ) + { + float sdf = input_sdf[pixel]; + float distance = sdf + length((float3)offset * voxelSize); + + if (distance < best_distance) + { + best_distance = distance; + } + } + } + } + } + + output_sdf[DTid] = best_distance; +} diff --git a/WickedEngine/shaders/vxgi_temporalCS.hlsl b/WickedEngine/shaders/vxgi_temporalCS.hlsl new file mode 100644 index 000000000..ac9e03eb0 --- /dev/null +++ b/WickedEngine/shaders/vxgi_temporalCS.hlsl @@ -0,0 +1,101 @@ +#include "globals.hlsli" +#include "voxelHF.hlsli" + +Texture3D input_previous_radiance : register(t0); +Texture3D input_render_atomic : register(t1); + +RWTexture3D output_radiance : register(u0); +RWTexture3D output_sdf : register(u1); + +static const float blend_speed = 0.5; + +[numthreads(8, 8, 8)] +void main(uint3 DTid : SV_DispatchThreadID) +{ + float4 aniso_colors[6]; + + float sdf = 65504.0; + + for (uint i = 0; i < 6 + DIFFUSE_CONE_COUNT; ++i) + { + uint3 src = DTid; + src.x += i * GetFrame().vxgi.resolution; + + uint3 dst = src; + dst.y += g_xVoxelizer.clipmap_index * GetFrame().vxgi.resolution; + + float4 radiance = 0; + if (i < 6) + { + src.z *= 5; + uint count = input_render_atomic[src + uint3(0, 0, 4)]; + if (count > 0) + { + radiance.r = UnpackVoxelChannel(input_render_atomic[src + uint3(0, 0, 0)]); + radiance.g = UnpackVoxelChannel(input_render_atomic[src + uint3(0, 0, 1)]); + radiance.b = UnpackVoxelChannel(input_render_atomic[src + uint3(0, 0, 2)]); + radiance.a = UnpackVoxelChannel(input_render_atomic[src + uint3(0, 0, 3)]); + radiance /= count; + } + + if (radiance.a > 0) + { + if (any(g_xVoxelizer.offsetfromPrevFrame)) + { + int3 coord = dst - g_xVoxelizer.offsetfromPrevFrame; + int aniso_face_start_x = i * GetFrame().vxgi.resolution; + int aniso_face_end_x = aniso_face_start_x + GetFrame().vxgi.resolution; + int clipmap_face_start_y = g_xVoxelizer.clipmap_index * GetFrame().vxgi.resolution; + int clipmap_face_end_y = clipmap_face_start_y + GetFrame().vxgi.resolution; + + if ( + coord.x >= aniso_face_start_x && coord.x < aniso_face_end_x && + coord.y >= clipmap_face_start_y && coord.y < clipmap_face_end_y && + coord.z >= 0 && coord.z < GetFrame().vxgi.resolution + ) + { + radiance = lerp(input_previous_radiance[dst], radiance, blend_speed); + } + } + else + { + radiance = lerp(input_previous_radiance[dst], radiance, blend_speed); + } + } + else + { + radiance = 0; + } + aniso_colors[i] = radiance; + + if (radiance.a > 0) + { + sdf = 0; + } + } + else + { + // precompute cone sampling: + float3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i - 6]; + float3 aniso_direction = -coneDirection; + uint3 face_offsets = float3( + aniso_direction.x > 0 ? 0 : 1, + aniso_direction.y > 0 ? 2 : 3, + aniso_direction.z > 0 ? 4 : 5 + ); + float3 direction_weights = abs(coneDirection); + float4 sam = + aniso_colors[face_offsets.x] * direction_weights.x + + aniso_colors[face_offsets.y] * direction_weights.y + + aniso_colors[face_offsets.z] * direction_weights.z + ; + radiance = sam; + } + + output_radiance[dst] = radiance; + } + + uint3 dst_sdf = DTid; + dst_sdf.y += g_xVoxelizer.clipmap_index * GetFrame().vxgi.resolution; + output_sdf[dst_sdf] = sdf; +} diff --git a/WickedEngine/wiEnums.h b/WickedEngine/wiEnums.h index f6289c309..e19f533f2 100644 --- a/WickedEngine/wiEnums.h +++ b/WickedEngine/wiEnums.h @@ -86,7 +86,6 @@ namespace wi::enums enum RBTYPES { RBTYPE_ENTITYARRAY, - RBTYPE_VOXELSCENE, RBTYPE_MATRIXARRAY, RBTYPE_COUNT }; @@ -94,8 +93,6 @@ namespace wi::enums // textures enum TEXTYPES { - TEXTYPE_3D_VOXELRADIANCE, - TEXTYPE_3D_VOXELRADIANCE_HELPER, TEXTYPE_2D_VOLUMETRICCLOUDS_SHADOW, TEXTYPE_2D_SKYATMOSPHERE_TRANSMITTANCELUT, TEXTYPE_2D_SKYATMOSPHERE_MULTISCATTEREDLUMINANCELUT, @@ -246,9 +243,11 @@ namespace wi::enums CSTYPE_LIGHTCULLING_ADVANCED, CSTYPE_LIGHTCULLING_ADVANCED_DEBUG, CSTYPE_RESOLVEMSAADEPTHSTENCIL, - CSTYPE_VOXELSCENECOPYCLEAR, - CSTYPE_VOXELSCENECOPYCLEAR_TEMPORALSMOOTHING, - CSTYPE_VOXELRADIANCESECONDARYBOUNCE, + CSTYPE_VXGI_OFFSETPREV, + CSTYPE_VXGI_TEMPORAL, + CSTYPE_VXGI_SDF_JUMPFLOOD, + CSTYPE_VXGI_RESOLVE_DIFFUSE, + CSTYPE_VXGI_RESOLVE_SPECULAR, CSTYPE_SKYATMOSPHERE_TRANSMITTANCELUT, CSTYPE_SKYATMOSPHERE_MULTISCATTEREDLUMINANCELUT, CSTYPE_SKYATMOSPHERE_SKYVIEWLUT, diff --git a/WickedEngine/wiRenderPath3D.cpp b/WickedEngine/wiRenderPath3D.cpp index f5737396d..2f6d74435 100644 --- a/WickedEngine/wiRenderPath3D.cpp +++ b/WickedEngine/wiRenderPath3D.cpp @@ -72,6 +72,7 @@ namespace wi temporalAAResources = {}; visibilityResources = {}; fsr2Resources = {}; + vxgiResources = {}; } void RenderPath3D::ResizeBuffers() @@ -483,6 +484,18 @@ namespace wi } } + if (wi::renderer::GetVXGIEnabled()) + { + if (!vxgiResources.IsValid()) + { + wi::renderer::CreateVXGIResources(vxgiResources, internalResolution); + } + } + else + { + vxgiResources = {}; + } + // Check whether velocity buffer is required: if ( getMotionBlurEnabled() || @@ -493,7 +506,8 @@ namespace wi wi::renderer::GetRaytracedShadowsEnabled() || getAO() == AO::AO_RTAO || wi::renderer::GetVariableRateShadingClassification() || - getFSR2Enabled() + getFSR2Enabled() || + wi::renderer::GetVXGIEnabled() ) { if (!rtVelocity.IsValid()) @@ -585,6 +599,15 @@ namespace wi camera->texture_rtshadow_index = device->GetDescriptorIndex(&rtShadow, SubresourceType::SRV); camera->texture_rtdiffuse_index = device->GetDescriptorIndex(&rtRaytracedDiffuse, SubresourceType::SRV); camera->texture_surfelgi_index = device->GetDescriptorIndex(&surfelGIResources.result, SubresourceType::SRV); + camera->texture_vxgi_diffuse_index = device->GetDescriptorIndex(&vxgiResources.diffuse[0], SubresourceType::SRV); + if (wi::renderer::GetVXGIReflectionsEnabled()) + { + camera->texture_vxgi_specular_index = device->GetDescriptorIndex(&vxgiResources.specular[0], SubresourceType::SRV); + } + else + { + camera->texture_vxgi_specular_index = -1; + } camera_reflection.canvas.init(*this); camera_reflection.width = (float)depthBuffer_Reflection.desc.width; @@ -606,6 +629,8 @@ namespace wi camera_reflection.texture_rtshadow_index = -1; camera_reflection.texture_rtdiffuse_index = -1; camera_reflection.texture_surfelgi_index = -1; + camera_reflection.texture_vxgi_diffuse_index = -1; + camera_reflection.texture_vxgi_specular_index = -1; } void RenderPath3D::Render() const @@ -644,7 +669,7 @@ namespace wi }; device->Barrier(barriers, arraysize(barriers), cmd); - }); + }); // async compute parallel with depth prepass cmd = device->BeginCommandList(QUEUE_COMPUTE); @@ -798,7 +823,8 @@ namespace wi getRaytracedReflectionEnabled() || getRaytracedDiffuseEnabled() || wi::renderer::GetScreenSpaceShadowsEnabled() || - wi::renderer::GetRaytracedShadowsEnabled() + wi::renderer::GetRaytracedShadowsEnabled() || + wi::renderer::GetVXGIEnabled() ) { // These post effects require surface normals and/or roughness @@ -889,6 +915,14 @@ namespace wi }); } + if (wi::renderer::GetVXGIEnabled()) + { + cmd = device->BeginCommandList(); + wi::jobsystem::Execute(ctx, [cmd, this](wi::jobsystem::JobArgs args) { + wi::renderer::VXGI_Voxelize(visibility_main, cmd); + }); + } + // Updating textures: cmd = device->BeginCommandList(); device->WaitCommandList(cmd, cmd_prepareframe_async); @@ -903,16 +937,7 @@ namespace wi wi::renderer::RefreshLightmaps(*scene, cmd, instanceInclusionMask_Lightmap); wi::renderer::RefreshEnvProbes(visibility_main, cmd); wi::renderer::RefreshImpostors(*scene, cmd); - }); - - // Voxel GI: - if (wi::renderer::GetVoxelRadianceEnabled()) - { - cmd = device->BeginCommandList(); - wi::jobsystem::Execute(ctx, [cmd, this](wi::jobsystem::JobArgs args) { - wi::renderer::VoxelRadiance(visibility_main, cmd); - }); - } + }); if (visibility_main.IsRequestedPlanarReflections()) { @@ -1078,6 +1103,15 @@ namespace wi instanceInclusionMask_RTDiffuse ); } + if (wi::renderer::GetVXGIEnabled()) + { + wi::renderer::VXGI_Resolve( + vxgiResources, + *scene, + rtLinearDepth, + cmd + ); + } // Depth buffers were created on COMPUTE queue, so make them available for pixel shaders here: { @@ -1134,7 +1168,7 @@ namespace wi RenderPassImage rp[] = { RenderPassImage::RenderTarget( &rtMain_render, - RenderPassImage::LoadOp::LOAD + RenderPassImage::LoadOp::DONTCARE ), RenderPassImage::DepthStencil( &depthBuffer_Main, @@ -2183,8 +2217,8 @@ namespace wi TextureDesc desc; desc.format = Format::R16G16B16A16_FLOAT; desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS; - desc.width = internalResolution.x / 4; - desc.height = internalResolution.y / 4; + desc.width = internalResolution.x / 2; + desc.height = internalResolution.y / 2; device->CreateTexture(&desc, nullptr, &rtVolumetricLights[0]); device->SetName(&rtVolumetricLights[0], "rtVolumetricLights[0]"); device->CreateTexture(&desc, nullptr, &rtVolumetricLights[1]); diff --git a/WickedEngine/wiRenderPath3D.h b/WickedEngine/wiRenderPath3D.h index 5e624ca08..a895baf99 100644 --- a/WickedEngine/wiRenderPath3D.h +++ b/WickedEngine/wiRenderPath3D.h @@ -137,6 +137,7 @@ namespace wi wi::renderer::TemporalAAResources temporalAAResources; wi::renderer::VisibilityResources visibilityResources; wi::renderer::FSR2Resources fsr2Resources; + wi::renderer::VXGIResources vxgiResources; mutable const wi::graphics::Texture* lastPostprocessRT = &rtPostprocess; // Post-processes are ping-ponged, this function helps to obtain the last postprocess render target that was written diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index a4924545f..7c7853755 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -26,6 +26,7 @@ #include "shaders/ShaderInterop_Raytracing.h" #include "shaders/ShaderInterop_BVH.h" #include "shaders/ShaderInterop_DDGI.h" +#include "shaders/ShaderInterop_VXGI.h" #include "shaders/ShaderInterop_FSR2.h" #include @@ -79,7 +80,6 @@ bool debugForceFields = false; bool debugCameras = false; bool debugColliders = false; bool gridHelper = false; -bool voxelHelper = false; bool advancedLightCulling = true; bool variableRateShadingClassification = false; bool variableRateShadingClassificationDebug = false; @@ -104,23 +104,10 @@ float DDGI_BLEND_SPEED = 0.02f; float GI_BOOST = 1.0f; std::atomic SHADER_ERRORS{ 0 }; std::atomic SHADER_MISSING{ 0 }; - - -struct VoxelizedSceneData -{ - bool enabled = false; - uint32_t res = 128; - float voxelsize = 1; - XMFLOAT3 center = XMFLOAT3(0, 0, 0); - XMFLOAT3 extents = XMFLOAT3(0, 0, 0); - uint32_t numCones = 2; - float rayStepSize = 0.75f; - float maxDistance = 20.0f; - bool secondaryBounceEnabled = false; - bool reflectionsEnabled = true; - bool centerChangedThisFrame = true; - uint32_t mips = 7; -} voxelSceneData; +bool VXGI_ENABLED = false; +bool VXGI_REFLECTIONS_ENABLED = true; +bool VXGI_DEBUG = false; +int VXGI_DEBUG_CLIPMAP = 0; Texture shadowMapAtlas; Texture shadowMapAtlas_Transparent; @@ -434,9 +421,12 @@ SHADERTYPE GetGSTYPE(RENDERPASS renderPass, bool alphatest, bool transparent) switch (renderPass) { +#ifdef VOXELIZATION_GEOMETRY_SHADER_ENABLED case RENDERPASS_VOXELIZE: realGS = GSTYPE_VOXELIZER; break; +#endif // VOXELIZATION_GEOMETRY_SHADER_ENABLED + case RENDERPASS_ENVMAPCAPTURE: if (device->CheckCapability(GraphicsDeviceCapability::RENDERTARGET_AND_VIEWPORT_ARRAYINDEX_WITHOUT_GS)) break; @@ -909,9 +899,11 @@ void LoadShaders() wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_LIGHTCULLING_ADVANCED], "lightCullingCS_ADVANCED.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_LIGHTCULLING_ADVANCED_DEBUG], "lightCullingCS_ADVANCED_DEBUG.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_RESOLVEMSAADEPTHSTENCIL], "resolveMSAADepthStencilCS.cso"); }); - wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VOXELSCENECOPYCLEAR], "voxelSceneCopyClearCS.cso"); }); - wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VOXELSCENECOPYCLEAR_TEMPORALSMOOTHING], "voxelSceneCopyClearCS_TemporalSmoothing.cso"); }); - wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VOXELRADIANCESECONDARYBOUNCE], "voxelRadianceSecondaryBounceCS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VXGI_OFFSETPREV], "vxgi_offsetprevCS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VXGI_TEMPORAL], "vxgi_temporalCS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VXGI_SDF_JUMPFLOOD], "vxgi_sdf_jumpfloodCS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VXGI_RESOLVE_DIFFUSE], "vxgi_resolve_diffuseCS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_VXGI_RESOLVE_SPECULAR], "vxgi_resolve_specularCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_SKYATMOSPHERE_TRANSMITTANCELUT], "skyAtmosphere_transmittanceLutCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_SKYATMOSPHERE_MULTISCATTEREDLUMINANCELUT], "skyAtmosphere_multiScatteredLuminanceLutCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_SKYATMOSPHERE_SKYVIEWLUT], "skyAtmosphere_skyViewLutCS.cso"); }); @@ -3106,19 +3098,20 @@ void UpdatePerFrameData( // Update Voxelization parameters: if (scene.objects.GetCount() > 0) { - // We don't update it if the scene is empty, this even makes it easier to debug - const float f = 0.05f / voxelSceneData.voxelsize; - XMFLOAT3 center = XMFLOAT3(std::floor(vis.camera->Eye.x * f) / f, std::floor(vis.camera->Eye.y * f) / f, std::floor(vis.camera->Eye.z * f) / f); - if (wi::math::DistanceSquared(center, voxelSceneData.center) > 0) + Scene::VXGI::ClipMap& clipmap = scene.vxgi.clipmaps[scene.vxgi.clipmap_to_update]; + clipmap.voxelsize = scene.vxgi.clipmaps[0].voxelsize * (1u << scene.vxgi.clipmap_to_update); + const float texelSize = clipmap.voxelsize * 2; + XMFLOAT3 center = XMFLOAT3(std::floor(vis.camera->Eye.x / texelSize) * texelSize, std::floor(vis.camera->Eye.y / texelSize) * texelSize, std::floor(vis.camera->Eye.z / texelSize) * texelSize); + clipmap.offsetfromPrevFrame.x = int((clipmap.center.x - center.x) / texelSize); + clipmap.offsetfromPrevFrame.y = -int((clipmap.center.y - center.y) / texelSize); + clipmap.offsetfromPrevFrame.z = int((clipmap.center.z - center.z) / texelSize); + clipmap.center = center; + XMFLOAT3 extents = XMFLOAT3(scene.vxgi.res * clipmap.voxelsize, scene.vxgi.res * clipmap.voxelsize, scene.vxgi.res * clipmap.voxelsize); + if (extents.x != clipmap.extents.x || extents.y != clipmap.extents.y || extents.z != clipmap.extents.z) { - voxelSceneData.centerChangedThisFrame = true; + scene.vxgi.pre_clear = true; } - else - { - voxelSceneData.centerChangedThisFrame = false; - } - voxelSceneData.center = center; - voxelSceneData.extents = XMFLOAT3(voxelSceneData.res * voxelSceneData.voxelsize, voxelSceneData.res * voxelSceneData.voxelsize, voxelSceneData.res * voxelSceneData.voxelsize); + clipmap.extents = extents; } // Shadow atlas packing: @@ -3354,16 +3347,17 @@ void UpdatePerFrameData( frameCB.frame_count = (uint)device->GetFrameCount(); frameCB.blue_noise_phase = (frameCB.frame_count & 0xFF) * 1.6180339887f; - frameCB.voxelradiance_max_distance = voxelSceneData.maxDistance; - frameCB.voxelradiance_size = voxelSceneData.voxelsize; - frameCB.voxelradiance_size_rcp = 1.0f / (float)frameCB.voxelradiance_size; - frameCB.voxelradiance_resolution = GetVoxelRadianceEnabled() ? (uint)voxelSceneData.res : 0; - frameCB.voxelradiance_resolution_rcp = GetVoxelRadianceEnabled() ? 1.0f / (float)frameCB.voxelradiance_resolution : 0; //PE: was inf. - frameCB.voxelradiance_mipcount = voxelSceneData.mips; - frameCB.voxelradiance_numcones = std::max(std::min(voxelSceneData.numCones, 16u), 1u); - frameCB.voxelradiance_numcones_rcp = 1.0f / (float)frameCB.voxelradiance_numcones; - frameCB.voxelradiance_stepsize = voxelSceneData.rayStepSize; - frameCB.voxelradiance_center = voxelSceneData.center; + frameCB.vxgi.max_distance = scene.vxgi.maxDistance; + frameCB.vxgi.resolution = GetVXGIEnabled() ? (uint)scene.vxgi.res : 0; + frameCB.vxgi.resolution_rcp = GetVXGIEnabled() ? 1.0f / (float)frameCB.vxgi.resolution : 0; //PE: was inf. + frameCB.vxgi.stepsize = scene.vxgi.rayStepSize; + frameCB.vxgi.texture_radiance = device->GetDescriptorIndex(&scene.vxgi.radiance, SubresourceType::SRV); + frameCB.vxgi.texture_sdf = device->GetDescriptorIndex(&scene.vxgi.sdf, SubresourceType::SRV); + for (uint i = 0; i < VXGI_CLIPMAP_COUNT; ++i) + { + frameCB.vxgi.clipmaps[i].center = scene.vxgi.clipmaps[i].center; + frameCB.vxgi.clipmaps[i].voxelSize = scene.vxgi.clipmaps[i].voxelsize; + } // The order is very important here: frameCB.decalarray_offset = 0; @@ -3418,17 +3412,13 @@ void UpdatePerFrameData( { frameCB.options |= OPTION_BIT_TRANSPARENTSHADOWS_ENABLED; } - if (GetVoxelRadianceEnabled()) + if (GetVXGIEnabled()) { - frameCB.options |= OPTION_BIT_VOXELGI_ENABLED; + frameCB.options |= OPTION_BIT_VXGI_ENABLED; } - if (GetVoxelRadianceReflectionsEnabled()) + if (GetVXGIReflectionsEnabled()) { - frameCB.options |= OPTION_BIT_VOXELGI_REFLECTIONS_ENABLED; - } - if (voxelSceneData.centerChangedThisFrame) - { - frameCB.options |= OPTION_BIT_VOXELGI_RETARGETTED; + frameCB.options |= OPTION_BIT_VXGI_REFLECTIONS_ENABLED; } if (vis.scene->weather.IsRealisticSky()) { @@ -3481,7 +3471,6 @@ void UpdatePerFrameData( frameCB.texture_transmittancelut_index = device->GetDescriptorIndex(&textures[TEXTYPE_2D_SKYATMOSPHERE_TRANSMITTANCELUT], SubresourceType::SRV); frameCB.texture_multiscatteringlut_index = device->GetDescriptorIndex(&textures[TEXTYPE_2D_SKYATMOSPHERE_MULTISCATTEREDLUMINANCELUT], SubresourceType::SRV); frameCB.texture_skyluminancelut_index = device->GetDescriptorIndex(&textures[TEXTYPE_2D_SKYATMOSPHERE_SKYLUMINANCELUT], SubresourceType::SRV); - frameCB.texture_voxelgi_index = device->GetDescriptorIndex(GetVoxelRadianceSecondaryBounceEnabled() ? &textures[TEXTYPE_3D_VOXELRADIANCE_HELPER] : &textures[TEXTYPE_3D_VOXELRADIANCE], SubresourceType::SRV); frameCB.buffer_entityarray_index = device->GetDescriptorIndex(&resourceBuffers[RBTYPE_ENTITYARRAY], SubresourceType::SRV); frameCB.buffer_entitymatrixarray_index = device->GetDescriptorIndex(&resourceBuffers[RBTYPE_MATRIXARRAY], SubresourceType::SRV); @@ -3567,6 +3556,9 @@ void UpdateRenderData( auto prof_updatebuffer_cpu = wi::profiler::BeginRangeCPU("Update Buffers (CPU)"); auto prof_updatebuffer_gpu = wi::profiler::BeginRangeGPU("Update Buffers (GPU)", cmd); + GPUBarrier frame_cb_barrier = GPUBarrier::Buffer(&constantBuffers[CBTYPE_FRAME], ResourceState::CONSTANT_BUFFER, ResourceState::COPY_DST); + device->Barrier(&frame_cb_barrier, 1, cmd); + device->UpdateBuffer(&constantBuffers[CBTYPE_FRAME], &frameCB, cmd); barrier_stack.push_back(GPUBarrier::Buffer(&constantBuffers[CBTYPE_FRAME], ResourceState::COPY_DST, ResourceState::CONSTANT_BUFFER)); @@ -6285,20 +6277,24 @@ void DrawDebugWorld( device->EventEnd(cmd); } - if (voxelHelper && textures[TEXTYPE_3D_VOXELRADIANCE].IsValid()) + if (VXGI_DEBUG && scene.vxgi.radiance.IsValid()) { device->EventBegin("Debug Voxels", cmd); device->BindPipelineState(&PSO_debug[DEBUGRENDERING_VOXEL], cmd); - MiscCB sb; - XMStoreFloat4x4(&sb.g_xTransform, XMMatrixTranslationFromVector(XMLoadFloat3(&voxelSceneData.center)) * camera.GetViewProjection()); - sb.g_xColor = float4(1, 1, 1, 1); + XMStoreFloat4x4(&sb.g_xTransform, camera.GetViewProjection()); + sb.g_xColor = float4((float)VXGI_DEBUG_CLIPMAP, 1, 1, 1); device->BindDynamicConstantBuffer(sb, CB_GETBINDSLOT(MiscCB), cmd); - device->Draw(voxelSceneData.res * voxelSceneData.res * voxelSceneData.res, 0, cmd); + uint32_t vertexCount = scene.vxgi.res * scene.vxgi.res * scene.vxgi.res; + if (VXGI_DEBUG_CLIPMAP == VXGI_CLIPMAP_COUNT) + { + vertexCount *= VXGI_CLIPMAP_COUNT; + } + device->Draw(vertexCount, 0, cmd); device->EventEnd(cmd); } @@ -7064,8 +7060,8 @@ void RefreshEnvProbes(const Visibility& vis, CommandList cmd) } device->Dispatch( - (desc.width + GENERATEMIPCHAIN_2D_BLOCK_SIZE - 1) / GENERATEMIPCHAIN_2D_BLOCK_SIZE, - (desc.height + GENERATEMIPCHAIN_2D_BLOCK_SIZE - 1) / GENERATEMIPCHAIN_2D_BLOCK_SIZE, + (desc.width + POSTPROCESS_BLOCKSIZE - 1) / POSTPROCESS_BLOCKSIZE, + (desc.height + POSTPROCESS_BLOCKSIZE - 1) / POSTPROCESS_BLOCKSIZE, 6, cmd); @@ -7306,23 +7302,54 @@ void RefreshImpostors(const Scene& scene, CommandList cmd) device->EventEnd(cmd); } -void VoxelRadiance(const Visibility& vis, CommandList cmd) + +void CreateVXGIResources(VXGIResources& res, XMUINT2 resolution) { - if (!GetVoxelRadianceEnabled()) + TextureDesc desc; + desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS; + + desc.width = resolution.x; + desc.height = resolution.y; + desc.format = Format::R11G11B10_FLOAT; + device->CreateTexture(&desc, nullptr, &res.diffuse[0]); + device->SetName(&res.diffuse[0], "vxgi.diffuse[0]"); + + desc.width = resolution.x / VXGI_DIFFUSE_UPSAMPLING; + desc.height = resolution.y / VXGI_DIFFUSE_UPSAMPLING; + device->CreateTexture(&desc, nullptr, &res.diffuse[1]); + device->SetName(&res.diffuse[1], "vxgi.diffuse[1]"); + + desc.width = resolution.x; + desc.height = resolution.y; + desc.format = Format::R16G16B16A16_FLOAT; + device->CreateTexture(&desc, nullptr, &res.specular[0]); + device->SetName(&res.specular[0], "vxgi.specular[0]"); + + desc.width = resolution.x / VXGI_SPECULAR_UPSAMPLING; + desc.height = resolution.y / VXGI_SPECULAR_UPSAMPLING; + device->CreateTexture(&desc, nullptr, &res.specular[1]); + device->SetName(&res.specular[1], "vxgi.specular[1]"); + + res.pre_clear = true; +} +void VXGI_Voxelize( + const Visibility& vis, + CommandList cmd +) +{ + if (!GetVXGIEnabled()) { return; } - device->EventBegin("Voxel Radiance", cmd); - auto range = wi::profiler::BeginRangeGPU("Voxel Radiance", cmd); + device->EventBegin("VXGI - Voxelize", cmd); + auto range = wi::profiler::BeginRangeGPU("VXGI - Voxelize", cmd); - Texture* result = &textures[TEXTYPE_3D_VOXELRADIANCE]; + const Scene& scene = *vis.scene; + const Scene::VXGI::ClipMap& clipmap = scene.vxgi.clipmaps[scene.vxgi.clipmap_to_update]; AABB bbox; - XMFLOAT3 extents = voxelSceneData.extents; - XMFLOAT3 center = voxelSceneData.center; - bbox.createFromHalfWidth(center, extents); - + bbox.createFromHalfWidth(clipmap.center, clipmap.extents); static thread_local RenderQueue renderQueue; renderQueue.init(); @@ -7332,7 +7359,7 @@ void VoxelRadiance(const Visibility& vis, CommandList cmd) if (bbox.intersects(aabb)) { const ObjectComponent& object = vis.scene->objects[i]; - if (object.IsRenderable()) + if (object.IsRenderable() && (scene.vxgi.clipmap_to_update < (VXGI_CLIPMAP_COUNT - object.cascadeMask))) { renderQueue.add(object.mesh_index, uint32_t(i), 0, object.sort_bits); } @@ -7341,100 +7368,266 @@ void VoxelRadiance(const Visibility& vis, CommandList cmd) if (!renderQueue.empty()) { - Viewport vp; - vp.width = (float)voxelSceneData.res; - vp.height = (float)voxelSceneData.res; - device->BindViewports(1, &vp, cmd); - - GPUResource* UAVs[] = { &resourceBuffers[RBTYPE_VOXELSCENE] }; - device->BindUAVs(UAVs, 0, 1, cmd); - BindCommonResources(cmd); - device->ClearUAV(&resourceBuffers[RBTYPE_VOXELSCENE], 0, cmd); + VoxelizerCB cb; + cb.offsetfromPrevFrame = clipmap.offsetfromPrevFrame; + cb.clipmap_index = scene.vxgi.clipmap_to_update; + device->BindDynamicConstantBuffer(cb, CBSLOT_RENDERER_VOXELIZER, cmd); + { GPUBarrier barriers[] = { - GPUBarrier::Memory(&resourceBuffers[RBTYPE_VOXELSCENE]), + GPUBarrier::Image(&scene.vxgi.render_atomic, ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), + GPUBarrier::Image(&scene.vxgi.prev_radiance, ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), + GPUBarrier::Image(&scene.vxgi.radiance, ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), }; device->Barrier(barriers, arraysize(barriers), cmd); } - device->RenderPassBegin(nullptr, 0, cmd, RenderPassFlags::ALLOW_UAV_WRITES); - RenderMeshes(vis, renderQueue, RENDERPASS_VOXELIZE, FILTER_OPAQUE, cmd, false, nullptr, 1); - device->RenderPassEnd(cmd); - + if (scene.vxgi.pre_clear) { - GPUBarrier barriers[] = { - GPUBarrier::Buffer(&resourceBuffers[RBTYPE_VOXELSCENE], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), - GPUBarrier::Image(&textures[TEXTYPE_3D_VOXELRADIANCE], ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS) - }; - device->Barrier(barriers, arraysize(barriers), cmd); - } - - // Copy the packed voxel scene data to a 3D texture, then delete the voxel scene emission data. The cone tracing will operate on the 3D texture - device->EventBegin("Voxel Scene Copy - Clear", cmd); - device->BindResource(&resourceBuffers[RBTYPE_VOXELSCENE], 0, cmd); - device->BindUAV(&textures[TEXTYPE_3D_VOXELRADIANCE], 0, cmd); - - static bool smooth_copy = true; - if (smooth_copy) - { - device->BindComputeShader(&shaders[CSTYPE_VOXELSCENECOPYCLEAR_TEMPORALSMOOTHING], cmd); + device->ClearUAV(&scene.vxgi.prev_radiance, 0, cmd); + device->ClearUAV(&scene.vxgi.radiance, 0, cmd); + device->ClearUAV(&scene.vxgi.sdf, 0, cmd); + device->ClearUAV(&scene.vxgi.sdf_temp, 0, cmd); + scene.vxgi.pre_clear = false; } else { - device->BindComputeShader(&shaders[CSTYPE_VOXELSCENECOPYCLEAR], cmd); + device->EventBegin("Offset Previous Voxels", cmd); + device->BindComputeShader(&shaders[CSTYPE_VXGI_OFFSETPREV], cmd); + device->BindResource(&scene.vxgi.radiance, 0, cmd); + device->BindUAV(&scene.vxgi.prev_radiance, 0, cmd); + + device->Dispatch(scene.vxgi.res / 8, scene.vxgi.res / 8, scene.vxgi.res / 8, cmd); + + device->EventEnd(cmd); + } + + { + device->EventBegin("Clear Voxel Atomics", cmd); + + device->ClearUAV(&scene.vxgi.render_atomic, 0, cmd); + device->EventEnd(cmd); } - device->Dispatch((uint32_t)(voxelSceneData.res * voxelSceneData.res * voxelSceneData.res / 256), 1, 1, cmd); - device->EventEnd(cmd); { GPUBarrier barriers[] = { - GPUBarrier::Image(&textures[TEXTYPE_3D_VOXELRADIANCE], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Memory(&scene.vxgi.render_atomic), + GPUBarrier::Image(&scene.vxgi.prev_radiance, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&scene.vxgi.radiance, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&scene.vxgi.sdf, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), }; device->Barrier(barriers, arraysize(barriers), cmd); } - if (voxelSceneData.secondaryBounceEnabled) { - device->EventBegin("Voxel Radiance Secondary Bounce", cmd); - // Pre-integrate the voxel texture by creating blurred mip levels: - GenerateMipChain(textures[TEXTYPE_3D_VOXELRADIANCE], MIPGENFILTER_LINEAR, cmd); + device->EventBegin("Voxelize", cmd); + + Viewport vp; + vp.width = (float)scene.vxgi.res; + vp.height = (float)scene.vxgi.res; + device->BindViewports(1, &vp, cmd); + + device->BindResource(&scene.vxgi.prev_radiance, 0, cmd); + device->BindUAV(&scene.vxgi.render_atomic, 0, cmd); + + device->RenderPassBegin(nullptr, 0, cmd, RenderPassFlags::ALLOW_UAV_WRITES); +#ifdef VOXELIZATION_GEOMETRY_SHADER_ENABLED + const uint32_t frustum_count = 1; // axis will be selected by geometry shader +#else + const uint32_t frustum_count = 3; // just used to replicate 3 times for main axes, but not with real frustums +#endif // VOXELIZATION_GEOMETRY_SHADER_ENABLED + RenderMeshes(vis, renderQueue, RENDERPASS_VOXELIZE, FILTER_OPAQUE, cmd, false, nullptr, frustum_count); + device->RenderPassEnd(cmd); { GPUBarrier barriers[] = { - GPUBarrier::Image(&textures[TEXTYPE_3D_VOXELRADIANCE_HELPER], ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS) + GPUBarrier::Image(&scene.vxgi.render_atomic, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&scene.vxgi.radiance, ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), + GPUBarrier::Image(&scene.vxgi.sdf, ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), }; device->Barrier(barriers, arraysize(barriers), cmd); } - device->BindResource(&textures[TEXTYPE_3D_VOXELRADIANCE], 0, cmd); - device->BindResource(&resourceBuffers[RBTYPE_VOXELSCENE], 1, cmd); - device->BindUAV(&textures[TEXTYPE_3D_VOXELRADIANCE_HELPER], 0, cmd); - device->BindComputeShader(&shaders[CSTYPE_VOXELRADIANCESECONDARYBOUNCE], cmd); - device->Dispatch(voxelSceneData.res / 8, voxelSceneData.res / 8, voxelSceneData.res / 8, cmd); device->EventEnd(cmd); - - { - GPUBarrier barriers[] = { - GPUBarrier::Image(&textures[TEXTYPE_3D_VOXELRADIANCE_HELPER], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), - GPUBarrier::Buffer(&resourceBuffers[RBTYPE_VOXELSCENE], ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), - }; - device->Barrier(barriers, arraysize(barriers), cmd); - } - - result = &textures[TEXTYPE_3D_VOXELRADIANCE_HELPER]; } - // Pre-integrate the voxel texture by creating blurred mip levels: { - GenerateMipChain(*result, MIPGENFILTER_LINEAR, cmd); + device->EventBegin("Temporal Blend Voxels", cmd); + device->BindComputeShader(&shaders[CSTYPE_VXGI_TEMPORAL], cmd); + device->BindResource(&scene.vxgi.prev_radiance, 0, cmd); + device->BindResource(&scene.vxgi.render_atomic, 1, cmd); + device->BindUAV(&scene.vxgi.radiance, 0, cmd); + device->BindUAV(&scene.vxgi.sdf, 1, cmd); + + device->Dispatch(scene.vxgi.res / 8, scene.vxgi.res / 8, scene.vxgi.res / 8, cmd); + + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&scene.vxgi.radiance, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&scene.vxgi.sdf, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + device->EventEnd(cmd); + } + + { + device->EventBegin("SDF Jump Flood", cmd); + device->BindComputeShader(&shaders[CSTYPE_VXGI_SDF_JUMPFLOOD], cmd); + + const Texture* _write = &scene.vxgi.sdf_temp; + const Texture* _read = &scene.vxgi.sdf; + + int passcount = (int)std::ceil(std::log2((float)scene.vxgi.res)); + for (int i = 0; i < passcount; ++i) + { + float jump_size = std::pow(2.0f, float(passcount - i - 1)); + device->PushConstants(&jump_size, sizeof(jump_size), cmd); + + device->BindUAV(_write, 0, cmd); + device->BindResource(_read, 0, cmd); + + device->Dispatch(scene.vxgi.res / 8, scene.vxgi.res / 8, scene.vxgi.res / 8, cmd); + + { + GPUBarrier barriers[] = { + GPUBarrier::Image(_write, ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(_read, ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + + std::swap(_read, _write); + } + + device->EventEnd(cmd); } } wi::profiler::EndRange(range); device->EventEnd(cmd); } +void VXGI_Resolve( + const VXGIResources& res, + const Scene& scene, + Texture texture_lineardepth, + CommandList cmd +) +{ + if (!GetVXGIEnabled() || !scene.vxgi.radiance.IsValid()) + { + return; + } + + device->EventBegin("VXGI - Resolve", cmd); + auto range = wi::profiler::BeginRangeGPU("VXGI - Resolve", cmd); + + BindCommonResources(cmd); + + if (res.pre_clear) + { + res.pre_clear = false; + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&res.diffuse[0], ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), + GPUBarrier::Image(&res.diffuse[1], ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), + GPUBarrier::Image(&res.specular[0], ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), + GPUBarrier::Image(&res.specular[1], ResourceState::SHADER_RESOURCE, ResourceState::UNORDERED_ACCESS), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + device->ClearUAV(&res.diffuse[0], 0, cmd); + device->ClearUAV(&res.diffuse[1], 0, cmd); + device->ClearUAV(&res.specular[0], 0, cmd); + device->ClearUAV(&res.specular[1], 0, cmd); + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&res.diffuse[0], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&res.diffuse[1], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&res.specular[0], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&res.specular[1], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + } + + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&res.diffuse[0], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&res.specular[1], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + + { + device->EventBegin("Diffuse", cmd); + device->BindComputeShader(&shaders[CSTYPE_VXGI_RESOLVE_DIFFUSE], cmd); + device->BindUAV(&res.diffuse[1], 0, cmd); + + PostProcess postprocess; + postprocess.resolution.x = res.diffuse[1].desc.width; + postprocess.resolution.y = res.diffuse[1].desc.height; + postprocess.resolution_rcp.x = 1.0f / postprocess.resolution.x; + postprocess.resolution_rcp.y = 1.0f / postprocess.resolution.y; + device->PushConstants(&postprocess, sizeof(postprocess), cmd); + + uint2 dispatch_dim; + dispatch_dim.x = postprocess.resolution.x; + dispatch_dim.y = postprocess.resolution.y; + + device->Dispatch((dispatch_dim.x + 7u) / 8u, (dispatch_dim.y + 7u) / 8u, 1, cmd); + + device->EventEnd(cmd); + } + + if(VXGI_REFLECTIONS_ENABLED) + { + device->EventBegin("Specular", cmd); + device->BindComputeShader(&shaders[CSTYPE_VXGI_RESOLVE_SPECULAR], cmd); + device->BindUAV(&res.specular[1], 0, cmd); + + PostProcess postprocess; + postprocess.resolution.x = res.specular[1].desc.width; + postprocess.resolution.y = res.specular[1].desc.height; + postprocess.resolution_rcp.x = 1.0f / postprocess.resolution.x; + postprocess.resolution_rcp.y = 1.0f / postprocess.resolution.y; + device->PushConstants(&postprocess, sizeof(postprocess), cmd); + + uint2 dispatch_dim; + dispatch_dim.x = postprocess.resolution.x; + dispatch_dim.y = postprocess.resolution.y; + + device->Dispatch((dispatch_dim.x + 7u) / 8u, (dispatch_dim.y + 7u) / 8u, 1, cmd); + + device->EventEnd(cmd); + } + + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&res.diffuse[0], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + GPUBarrier::Image(&res.specular[1], ResourceState::UNORDERED_ACCESS, ResourceState::SHADER_RESOURCE), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + + Postprocess_Upsample_Bilateral( + res.diffuse[1], + texture_lineardepth, + res.diffuse[0], + cmd + ); + Postprocess_Upsample_Bilateral( + res.specular[1], + texture_lineardepth, + res.specular[0], + cmd + ); + + wi::profiler::EndRange(range); + device->EventEnd(cmd); +} void CreateTiledLightResources(TiledLightResources& res, XMUINT2 resolution) @@ -8215,6 +8408,8 @@ void BindCameraCB( cb.texture_rtdiffuse_index = camera.texture_rtdiffuse_index; cb.texture_surfelgi_index = camera.texture_surfelgi_index; cb.texture_depth_index_prev = camera_previous.texture_depth_index; + cb.texture_vxgi_diffuse_index = camera.texture_vxgi_diffuse_index; + cb.texture_vxgi_specular_index = camera.texture_vxgi_specular_index; device->BindDynamicConstantBuffer(cb, CBSLOT_RENDERER_CAMERA, cmd); } @@ -14584,8 +14779,8 @@ void SetToDrawDebugColliders(bool param) { debugColliders = param; } bool GetToDrawDebugColliders() { return debugColliders; } bool GetToDrawGridHelper() { return gridHelper; } void SetToDrawGridHelper(bool value) { gridHelper = value; } -bool GetToDrawVoxelHelper() { return voxelHelper; } -void SetToDrawVoxelHelper(bool value) { voxelHelper = value; } +bool GetToDrawVoxelHelper() { return VXGI_DEBUG; } +void SetToDrawVoxelHelper(bool value, int clipmap_level) { VXGI_DEBUG = value; VXGI_DEBUG_CLIPMAP = clipmap_level; } void SetDebugLightCulling(bool enabled) { debugLightCulling = enabled; } bool GetDebugLightCulling() { return debugLightCulling; } void SetAdvancedLightCulling(bool enabled) { advancedLightCulling = enabled; } @@ -14605,72 +14800,13 @@ void SetTemporalAADebugEnabled(bool enabled) { temporalAADEBUG = enabled; } bool GetTemporalAADebugEnabled() { return temporalAADEBUG; } void SetFreezeCullingCameraEnabled(bool enabled) { freezeCullingCamera = enabled; } bool GetFreezeCullingCameraEnabled() { return freezeCullingCamera; } -void SetVoxelRadianceEnabled(bool enabled) +void SetVXGIEnabled(bool enabled) { - voxelSceneData.enabled = enabled; - if (!textures[TEXTYPE_3D_VOXELRADIANCE].IsValid()) - { - TextureDesc desc; - desc.type = TextureDesc::Type::TEXTURE_3D; - desc.width = voxelSceneData.res; - desc.height = voxelSceneData.res; - desc.depth = voxelSceneData.res; - desc.mip_levels = 0; - desc.format = Format::R16G16B16A16_FLOAT; - desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE; - desc.usage = Usage::DEFAULT; - - device->CreateTexture(&desc, nullptr, &textures[TEXTYPE_3D_VOXELRADIANCE]); - - for (uint32_t i = 0; i < textures[TEXTYPE_3D_VOXELRADIANCE].GetDesc().mip_levels; ++i) - { - int subresource_index; - subresource_index = device->CreateSubresource(&textures[TEXTYPE_3D_VOXELRADIANCE], SubresourceType::SRV, 0, 1, i, 1); - assert(subresource_index == i); - subresource_index = device->CreateSubresource(&textures[TEXTYPE_3D_VOXELRADIANCE], SubresourceType::UAV, 0, 1, i, 1); - assert(subresource_index == i); - } - } - if (!textures[TEXTYPE_3D_VOXELRADIANCE_HELPER].IsValid()) - { - const TextureDesc& desc = textures[TEXTYPE_3D_VOXELRADIANCE].GetDesc(); - device->CreateTexture(&desc, nullptr, &textures[TEXTYPE_3D_VOXELRADIANCE_HELPER]); - - for (uint32_t i = 0; i < desc.mip_levels; ++i) - { - int subresource_index; - subresource_index = device->CreateSubresource(&textures[TEXTYPE_3D_VOXELRADIANCE_HELPER], SubresourceType::SRV, 0, 1, i, 1); - assert(subresource_index == i); - subresource_index = device->CreateSubresource(&textures[TEXTYPE_3D_VOXELRADIANCE_HELPER], SubresourceType::UAV, 0, 1, i, 1); - assert(subresource_index == i); - } - } - if (!resourceBuffers[RBTYPE_VOXELSCENE].IsValid()) - { - GPUBufferDesc desc; - desc.stride = sizeof(uint32_t) * 2; - desc.size = desc.stride * voxelSceneData.res * voxelSceneData.res * voxelSceneData.res; - desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE; - desc.misc_flags = ResourceMiscFlag::BUFFER_STRUCTURED; - desc.usage = Usage::DEFAULT; - - device->CreateBuffer(&desc, nullptr, &resourceBuffers[RBTYPE_VOXELSCENE]); - } + VXGI_ENABLED = enabled; } -bool GetVoxelRadianceEnabled() { return voxelSceneData.enabled; } -void SetVoxelRadianceSecondaryBounceEnabled(bool enabled) { voxelSceneData.secondaryBounceEnabled = enabled; } -bool GetVoxelRadianceSecondaryBounceEnabled() { return voxelSceneData.secondaryBounceEnabled; } -void SetVoxelRadianceReflectionsEnabled(bool enabled) { voxelSceneData.reflectionsEnabled = enabled; } -bool GetVoxelRadianceReflectionsEnabled() { return voxelSceneData.reflectionsEnabled; } -void SetVoxelRadianceVoxelSize(float value) { voxelSceneData.voxelsize = value; } -float GetVoxelRadianceVoxelSize() { return voxelSceneData.voxelsize; } -void SetVoxelRadianceMaxDistance(float value) { voxelSceneData.maxDistance = value; } -float GetVoxelRadianceMaxDistance() { return voxelSceneData.maxDistance; } -int GetVoxelRadianceResolution() { return voxelSceneData.res; } -void SetVoxelRadianceNumCones(int value) { voxelSceneData.numCones = value; } -int GetVoxelRadianceNumCones() { return voxelSceneData.numCones; } -float GetVoxelRadianceRayStepSize() { return voxelSceneData.rayStepSize; } -void SetVoxelRadianceRayStepSize(float value) { voxelSceneData.rayStepSize = value; } +bool GetVXGIEnabled() { return VXGI_ENABLED; } +void SetVXGIReflectionsEnabled(bool enabled) { VXGI_REFLECTIONS_ENABLED = enabled; } +bool GetVXGIReflectionsEnabled() { return VXGI_REFLECTIONS_ENABLED; } void SetGameSpeed(float value) { GameSpeed = std::max(0.0f, value); } float GetGameSpeed() { return GameSpeed; } void SetRaytraceBounceCount(uint32_t bounces) diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index 6b4e2f73e..3f509bcec 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -261,8 +261,6 @@ namespace wi::renderer void RefreshImpostors(const wi::scene::Scene& scene, wi::graphics::CommandList cmd); // Call once per frame to repack out of date lightmaps in the atlas void RefreshLightmaps(const wi::scene::Scene& scene, wi::graphics::CommandList cmd, uint8_t instanceInclusionMask = 0xFF); - // Voxelize the scene into a voxel grid 3D texture - void VoxelRadiance(const Visibility& vis, wi::graphics::CommandList cmd); // Run a compute shader that will resolve a MSAA depth buffer to a single-sample texture void ResolveMSAADepthBuffer(const wi::graphics::Texture& dst, const wi::graphics::Texture& src, wi::graphics::CommandList cmd); void DownsampleDepthBuffer(const wi::graphics::Texture& src, wi::graphics::CommandList cmd); @@ -358,6 +356,7 @@ namespace wi::renderer wi::graphics::CommandList cmd ); + // Surfel GI: diffuse GI with ray tracing from surfels struct SurfelGIResources { wi::graphics::Texture result; @@ -376,12 +375,34 @@ namespace wi::renderer uint8_t instanceInclusionMask = 0xFF ); + // DDGI: Dynamic Diffuse Global Illumination (probe-based ray tracing) void DDGI( const wi::scene::Scene& scene, wi::graphics::CommandList cmd, uint8_t instanceInclusionMask = 0xFF ); + // VXGI: Voxel-based Global Illumination (voxel cone tracing-based) + struct VXGIResources + { + wi::graphics::Texture diffuse[2]; + wi::graphics::Texture specular[2]; + mutable bool pre_clear = true; + + bool IsValid() const { return diffuse[0].IsValid(); } + }; + void CreateVXGIResources(VXGIResources& res, XMUINT2 resolution); + void VXGI_Voxelize( + const Visibility& vis, + wi::graphics::CommandList cmd + ); + void VXGI_Resolve( + const VXGIResources& res, + const wi::scene::Scene& scene, + wi::graphics::Texture texture_lineardepth, + wi::graphics::CommandList cmd + ); + void Postprocess_Blur_Gaussian( const wi::graphics::Texture& input, const wi::graphics::Texture& temp, @@ -885,7 +906,7 @@ namespace wi::renderer bool GetToDrawGridHelper(); void SetToDrawGridHelper(bool value); bool GetToDrawVoxelHelper(); - void SetToDrawVoxelHelper(bool value); + void SetToDrawVoxelHelper(bool value, int clipmap_level); void SetDebugLightCulling(bool enabled); bool GetDebugLightCulling(); void SetAdvancedLightCulling(bool enabled); @@ -902,21 +923,10 @@ namespace wi::renderer bool GetTemporalAADebugEnabled(); void SetFreezeCullingCameraEnabled(bool enabled); bool GetFreezeCullingCameraEnabled(); - void SetVoxelRadianceEnabled(bool enabled); - bool GetVoxelRadianceEnabled(); - void SetVoxelRadianceSecondaryBounceEnabled(bool enabled); - bool GetVoxelRadianceSecondaryBounceEnabled(); - void SetVoxelRadianceReflectionsEnabled(bool enabled); - bool GetVoxelRadianceReflectionsEnabled(); - void SetVoxelRadianceVoxelSize(float value); - float GetVoxelRadianceVoxelSize(); - void SetVoxelRadianceMaxDistance(float value); - float GetVoxelRadianceMaxDistance(); - int GetVoxelRadianceResolution(); - void SetVoxelRadianceNumCones(int value); - int GetVoxelRadianceNumCones(); - float GetVoxelRadianceRayStepSize(); - void SetVoxelRadianceRayStepSize(float value); + void SetVXGIEnabled(bool enabled); + bool GetVXGIEnabled(); + void SetVXGIReflectionsEnabled(bool enabled); + bool GetVXGIReflectionsEnabled(); void SetGameSpeed(float value); float GetGameSpeed(); void SetRaytraceBounceCount(uint32_t bounces); diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index 6ff4671be..1fff96b32 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -563,6 +563,61 @@ namespace wi::scene ddgi.depth_texture[1] = {}; } + if (wi::renderer::GetVXGIEnabled()) + { + if(!vxgi.radiance.IsValid()) + { + TextureDesc desc; + desc.type = TextureDesc::Type::TEXTURE_3D; + desc.width = vxgi.res * (6 + DIFFUSE_CONE_COUNT); + desc.height = vxgi.res * VXGI_CLIPMAP_COUNT; + desc.depth = vxgi.res; + desc.mip_levels = 1; + desc.format = Format::R16G16B16A16_FLOAT; + desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE; + desc.usage = Usage::DEFAULT; + + device->CreateTexture(&desc, nullptr, &vxgi.radiance); + device->SetName(&vxgi.radiance, "vxgi.radiance"); + + device->CreateTexture(&desc, nullptr, &vxgi.prev_radiance); + device->SetName(&vxgi.prev_radiance, "vxgi.prev_radiance"); + + vxgi.pre_clear = true; + } + if (!vxgi.render_atomic.IsValid()) + { + TextureDesc desc; + desc.type = TextureDesc::Type::TEXTURE_3D; + desc.width = vxgi.res * 6; + desc.height = vxgi.res; + desc.depth = vxgi.res * 5; // r,g,b,a,counter + desc.mip_levels = 1; + desc.usage = Usage::DEFAULT; + desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE; + desc.format = Format::R32_UINT; + device->CreateTexture(&desc, nullptr, &vxgi.render_atomic); + device->SetName(&vxgi.render_atomic, "vxgi.render_atomic"); + } + if (!vxgi.sdf.IsValid()) + { + TextureDesc desc; + desc.type = TextureDesc::Type::TEXTURE_3D; + desc.width = vxgi.res; + desc.height = vxgi.res * VXGI_CLIPMAP_COUNT; + desc.depth = vxgi.res; + desc.mip_levels = 1; + desc.usage = Usage::DEFAULT; + desc.bind_flags = BindFlag::UNORDERED_ACCESS | BindFlag::SHADER_RESOURCE; + desc.format = Format::R16_FLOAT; + device->CreateTexture(&desc, nullptr, &vxgi.sdf); + device->SetName(&vxgi.sdf, "vxgi.sdf"); + device->CreateTexture(&desc, nullptr, &vxgi.sdf_temp); + device->SetName(&vxgi.sdf_temp, "vxgi.sdf_temp"); + } + vxgi.clipmap_to_update = (vxgi.clipmap_to_update + 1) % VXGI_CLIPMAP_COUNT; + } + impostor_ib_format = (((objects.GetCount() * 4) < 655536) ? Format::R16_UINT : Format::R32_UINT); const size_t impostor_index_stride = impostor_ib_format == Format::R16_UINT ? sizeof(uint16_t) : sizeof(uint32_t); const uint64_t required_impostor_buffer_size = objects.GetCount() * (sizeof(impostor_index_stride) * 6 + sizeof(uint4) * 4 + sizeof(uint2)); diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 1273ee8c6..7cc475b41 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -175,6 +175,29 @@ namespace wi::scene void Serialize(wi::Archive& archive); } ddgi; + // Voxel GI resources: + struct VXGI + { + uint32_t res = 64; + float rayStepSize = 2; + float maxDistance = 100.0f; + struct ClipMap + { + float voxelsize = 0.125; + XMFLOAT3 center = XMFLOAT3(0, 0, 0); + XMINT3 offsetfromPrevFrame = XMINT3(0, 0, 0); + XMFLOAT3 extents = XMFLOAT3(0, 0, 0); + } clipmaps[VXGI_CLIPMAP_COUNT]; + uint32_t clipmap_to_update = 0; + + wi::graphics::Texture radiance; + wi::graphics::Texture prev_radiance; + wi::graphics::Texture render_atomic; + wi::graphics::Texture sdf; + wi::graphics::Texture sdf_temp; + mutable bool pre_clear = true; + } vxgi; + // Environment probe cubemap array state: static constexpr uint32_t envmapCount = 16; static constexpr uint32_t envmapRes = 128; diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index 29d8e06a1..b63aebd6e 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -958,6 +958,8 @@ namespace wi::scene int texture_surfelgi_index = -1; int buffer_entitytiles_opaque_index = -1; int buffer_entitytiles_transparent_index = -1; + int texture_vxgi_diffuse_index = -1; + int texture_vxgi_specular_index = -1; void CreatePerspective(float newWidth, float newHeight, float newNear, float newFar, float newFOV = XM_PI / 3.0f); void UpdateCamera(); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 8d7c59ae2..77cf2f068 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 125; + const int revision = 126; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);